Internal change

PiperOrigin-RevId: 547990675
diff --git a/centipede/centipede_callbacks.h b/centipede/centipede_callbacks.h
index c1c0056..92191d6 100644
--- a/centipede/centipede_callbacks.h
+++ b/centipede/centipede_callbacks.h
@@ -52,8 +52,10 @@
       : env_(env),
         byte_array_mutator_(env.knobs, GetRandomSeed(env.seed)),
         fuzztest_mutator_(GetRandomSeed(env.seed)),
-        inputs_blobseq_(shmem_name1_.c_str(), env.shmem_size_mb << 20),
-        outputs_blobseq_(shmem_name2_.c_str(), env.shmem_size_mb << 20) {
+        inputs_blobseq_(shmem_name1_.c_str(), env.shmem_size_mb << 20,
+                        env.use_posix_shmem),
+        outputs_blobseq_(shmem_name2_.c_str(), env.shmem_size_mb << 20,
+                         env.use_posix_shmem) {
     if (env.use_legacy_default_mutator)
       CHECK(byte_array_mutator_.set_max_len(env.max_len));
     else
diff --git a/centipede/environment.cc b/centipede/environment.cc
index 6d8d4ab..ce02fac 100644
--- a/centipede/environment.cc
+++ b/centipede/environment.cc
@@ -340,6 +340,12 @@
 ABSL_FLAG(size_t, shmem_size_mb, 1024,
           "Size of the shared memory regions used to communicate between the "
           "ending and the runner.");
+ABSL_FLAG(
+    bool, use_posix_shmem, false,
+    "[INTERNAL] When true, uses shm_open/shm_unlink instead of memfd_create to "
+    "allocate shared memory. You may want this if your target for doesn't have "
+    "access to /proc/<arbitrary_pid> subdirs or the memfd_create syscall is "
+    "not supported.");
 ABSL_FLAG(bool, dry_run, false,
           "Initializes as much of Centipede as possible without actually "
           "running any fuzzing. Useful to validate the rest of the command "
@@ -460,6 +466,7 @@
       max_num_crash_reports(absl::GetFlag(FLAGS_num_crash_reports)),
       minimize_crash_file_path(absl::GetFlag(FLAGS_minimize_crash)),
       shmem_size_mb(absl::GetFlag(FLAGS_shmem_size_mb)),
+      use_posix_shmem(absl::GetFlag(FLAGS_use_posix_shmem)),
       dry_run(absl::GetFlag(FLAGS_dry_run)),
       cmd(binary),
       binary_name(std::filesystem::path(coverage_binary).filename().string()),
diff --git a/centipede/environment.h b/centipede/environment.h
index 98cf2d3..bb7ab99 100644
--- a/centipede/environment.h
+++ b/centipede/environment.h
@@ -108,6 +108,7 @@
   size_t max_num_crash_reports;
   std::string minimize_crash_file_path;
   size_t shmem_size_mb;
+  bool use_posix_shmem = false;
   bool dry_run = false;
 
   // Command line-related fields -----------------------------------------------
diff --git a/centipede/execution_metadata_test.cc b/centipede/execution_metadata_test.cc
index d060215..2c8e6d7 100644
--- a/centipede/execution_metadata_test.cc
+++ b/centipede/execution_metadata_test.cc
@@ -93,7 +93,8 @@
 TEST(ExecutionMetadata, ReadAndWriteKeepsCmpEntries) {
   ExecutionMetadata metadata_in;
   ASSERT_TRUE(metadata_in.AppendCmpEntry({1, 2}, {3, 4}));
-  SharedMemoryBlobSequence blobseq("test", /*size=*/1024);
+  SharedMemoryBlobSequence blobseq("test", /*size=*/1024,
+                                   /*use_posix_shmem=*/false);
   EXPECT_TRUE(metadata_in.Write(/*tag=*/1, blobseq));
   blobseq.Reset();
   Blob blob = blobseq.Read();
diff --git a/centipede/shared_memory_blob_sequence.cc b/centipede/shared_memory_blob_sequence.cc
index dd6b378..2377f0b 100644
--- a/centipede/shared_memory_blob_sequence.cc
+++ b/centipede/shared_memory_blob_sequence.cc
@@ -93,25 +93,43 @@
 }
 
 SharedMemoryBlobSequence::SharedMemoryBlobSequence(const char *name,
-                                                   size_t size) {
+                                                   size_t size,
+                                                   bool use_posix_shmem) {
   ErrorOnFailure(size < sizeof(Blob::size), "Size too small");
   size_ = size;
-  fd_ = memfd_create(name, MFD_CLOEXEC);
-  ErrorOnFailure(fd_ < 0, "memfd_create() failed");
-  const size_t path_size =
-      snprintf(path_, PATH_MAX, "/proc/%d/fd/%d", getpid(), fd_);
-  ErrorOnFailure(path_size >= PATH_MAX,
-                 "internal fd path length reaches PATH_MAX.");
+  if (use_posix_shmem) {
+    fd_ = shm_open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+    ErrorOnFailure(fd_ < 0, "shm_open() failed");
+    strncpy(path_, name, PATH_MAX);
+    ErrorOnFailure(path_[PATH_MAX - 1] != 0,
+                   "shm_open() path length exceeds PATH_MAX.");
+    path_is_owned_ = true;
+  } else {
+    fd_ = memfd_create(name, MFD_CLOEXEC);
+    ErrorOnFailure(fd_ < 0, "memfd_create() failed");
+    const size_t path_size =
+        snprintf(path_, PATH_MAX, "/proc/%d/fd/%d", getpid(), fd_);
+    ErrorOnFailure(path_size >= PATH_MAX,
+                   "internal fd path length exceeds PATH_MAX.");
+    // memfd_create descriptors are automatically freed on close().
+    path_is_owned_ = false;
+  }
   ErrorOnFailure(ftruncate(fd_, static_cast<__off_t>(size_)),
                  "ftruncate() failed)");
   MmapData();
 }
 
 SharedMemoryBlobSequence::SharedMemoryBlobSequence(const char *path) {
-  fd_ = open(path, O_RDWR, O_CLOEXEC);
+  // This is a quick way to tell shm-allocated paths from memfd paths without
+  // requiring the caller to specify.
+  if (strncmp(path, "/proc/", 6) == 0) {
+    fd_ = open(path, O_RDWR, O_CLOEXEC);
+  } else {
+    fd_ = shm_open(path, O_RDWR, 0);
+  }
   ErrorOnFailure(fd_ < 0, "open() failed");
   strncpy(path_, path, PATH_MAX);
-  ErrorOnFailure(path_[PATH_MAX - 1] != 0, "path length reaches PATH_MAX.");
+  ErrorOnFailure(path_[PATH_MAX - 1] != 0, "path length exceeds PATH_MAX.");
   struct stat statbuf = {};
   ErrorOnFailure(fstat(fd_, &statbuf), "fstat() failed");
   size_ = statbuf.st_size;
@@ -125,6 +143,9 @@
 }
 
 SharedMemoryBlobSequence::~SharedMemoryBlobSequence() {
+  if (path_is_owned_) {
+    ErrorOnFailure(shm_unlink(path_), "shm_unlink() failed");
+  }
   ErrorOnFailure(munmap(data_, size_), "munmap() failed");
   ErrorOnFailure(close(fd_), "close() failed");
 }
diff --git a/centipede/shared_memory_blob_sequence.h b/centipede/shared_memory_blob_sequence.h
index 9bbb28f..05a2c7e 100644
--- a/centipede/shared_memory_blob_sequence.h
+++ b/centipede/shared_memory_blob_sequence.h
@@ -146,7 +146,10 @@
   // actual path). Aborts on any failure. `size` is the size of the shared
   // memory region in bytes, must be >= 8. The amount of actual data that can be
   // written is slightly less.
-  SharedMemoryBlobSequence(const char *name, size_t size);
+  // The `use_posix_shmem` argument specifies which API to use to allocate the
+  // shared memory. When true, shm_open(2) will be used, otherwise
+  // memfd_create(2).
+  SharedMemoryBlobSequence(const char *name, size_t size, bool use_posix_shmem);
 
   // Opens an existing shared blob sequence with the file `path`.
   // Aborts on any failure.
@@ -173,7 +176,10 @@
   // Will be initialized as a generated internal path or a copy of `path`
   // passed in.
   char path_[PATH_MAX] = {0};
-  int fd_ = 0;  // file descriptor used to mmap the shared memory region.
+  int fd_ = -1;  // file descriptor used to mmap the shared memory region.
+  // Whether the file pointed to by path_ is owned by this and needs to be
+  // deallocated on destruction.
+  bool path_is_owned_ = false;
 };
 
 }  // namespace centipede
diff --git a/centipede/shared_memory_blob_sequence_test.cc b/centipede/shared_memory_blob_sequence_test.cc
index 37e5c66..f30e975 100644
--- a/centipede/shared_memory_blob_sequence_test.cc
+++ b/centipede/shared_memory_blob_sequence_test.cc
@@ -53,13 +53,20 @@
   EXPECT_EQ(blob.tag, 1);
 }
 
-TEST(SharedMemoryBlobSequence, ParentChild) {
+class SharedMemoryBlobSequenceTest
+    : public testing::TestWithParam</* use_shm */ bool> {};
+
+INSTANTIATE_TEST_SUITE_P(SharedMemoryBlobSequenceParametrizedTest,
+                         SharedMemoryBlobSequenceTest,
+                         testing::Values(true, false));
+
+TEST_P(SharedMemoryBlobSequenceTest, ParentChild) {
   std::vector<uint8_t> kTestData1 = {1, 2, 3};
   std::vector<uint8_t> kTestData2 = {4, 5, 6, 7};
   std::vector<uint8_t> kTestData3 = {8, 9};
   std::vector<uint8_t> kTestData4 = {'a', 'b', 'c', 'd', 'e'};
 
-  SharedMemoryBlobSequence parent(ShmemName().c_str(), 1000);
+  SharedMemoryBlobSequence parent(ShmemName().c_str(), 1000, GetParam());
   // Parent writes data.
   EXPECT_TRUE(parent.Write(Blob(kTestData1, 123)));
   EXPECT_TRUE(parent.Write(Blob(kTestData2, 456)));
@@ -87,18 +94,18 @@
   EXPECT_FALSE(parent.Read().IsValid());
 }
 
-TEST(SharedMemoryBlobSequence, CheckForResourceLeaks) {
+TEST_P(SharedMemoryBlobSequenceTest, CheckForResourceLeaks) {
   const int kNumIters = 1 << 17;  // Some large number of iterations.
   const int kBlobSize = 1 << 30;  // Some large blob size.
   // Create and destroy lots of parent/child blob pairs.
   for (int iter = 0; iter < kNumIters; iter++) {
-    SharedMemoryBlobSequence parent(ShmemName().c_str(), kBlobSize);
+    SharedMemoryBlobSequence parent(ShmemName().c_str(), kBlobSize, GetParam());
     parent.Write(Blob({1, 2, 3}));
     SharedMemoryBlobSequence child(parent.path());
     EXPECT_EQ(child.Read().size, 3);
   }
   // Create a parent blob, then create and destroy lots of child blobs.
-  SharedMemoryBlobSequence parent(ShmemName().c_str(), kBlobSize);
+  SharedMemoryBlobSequence parent(ShmemName().c_str(), kBlobSize, GetParam());
   parent.Write(Blob({1, 2, 3, 4}));
   for (int iter = 0; iter < kNumIters; iter++) {
     SharedMemoryBlobSequence child(parent.path());
@@ -107,8 +114,8 @@
 }
 
 // Tests that Read-after-Write or Write-after-Read w/o Reset crashes.
-TEST(SharedMemoryBlobSequence, ReadVsWriteWithoutReset) {
-  SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 1000);
+TEST_P(SharedMemoryBlobSequenceTest, ReadVsWriteWithoutReset) {
+  SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 1000, GetParam());
   blobseq.Write(Blob({1, 2, 3}));
   EXPECT_DEATH(blobseq.Read(), "Had writes after reset");
   blobseq.Reset();
@@ -119,13 +126,14 @@
 }
 
 // Check cases when SharedMemoryBlobSequence is nearly full.
-TEST(SharedMemoryBlobSequence, WriteToFullSequence) {
+TEST_P(SharedMemoryBlobSequenceTest, WriteToFullSequence) {
   // Can't create SharedMemoryBlobSequence with sizes < 8.
-  EXPECT_DEATH(SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 7),
-               "Size too small");
+  EXPECT_DEATH(
+      SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 7, GetParam()),
+      "Size too small");
 
   // Allocate a blob sequence with 28 bytes of storage.
-  SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 28);
+  SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 28, GetParam());
 
   // 17 bytes: 8 bytes size, 8 bytes tag, 1 byte payload.
   EXPECT_TRUE(blobseq.Write(Blob({1})));
@@ -171,9 +179,9 @@
 }
 
 // Test Write-Reset-Write-Read scenario.
-TEST(SharedMemoryBlobSequence, WriteAfterReset) {
+TEST_P(SharedMemoryBlobSequenceTest, WriteAfterReset) {
   // Allocate a blob sequence with 28 bytes of storage.
-  SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 100);
+  SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 100, GetParam());
   const std::vector<uint8_t> kFirstWriteData(/*count=*/64, /*value=*/255);
   EXPECT_TRUE(blobseq.Write(Blob(kFirstWriteData)));
   blobseq.Reset();  // The data in shmem is unchanged.
@@ -188,9 +196,9 @@
 }
 
 // Test ReleaseSharedMemory and NumBytesUsed.
-TEST(SharedMemoryBlobSequence, ReleaseSharedMemory) {
+TEST_P(SharedMemoryBlobSequenceTest, ReleaseSharedMemory) {
   // Allocate a blob sequence with 1M bytes of storage.
-  SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 1 << 20);
+  SharedMemoryBlobSequence blobseq(ShmemName().c_str(), 1 << 20, GetParam());
   EXPECT_EQ(blobseq.NumBytesUsed(), 0);
   EXPECT_TRUE(blobseq.Write(Blob({1, 2, 3, 4})));
   EXPECT_GT(blobseq.NumBytesUsed(), 5);