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);