Skip to content

Commit 6ddda79

Browse files
committed
Don't pass null pointers to memcmp and memcpy in libFuzzer
In C, it is UB to call memcmp(NULL, NULL, 0), memcpy(NULL, NULL, 0), etc. Unfortunately, (NULL, 0) is the natural representation of an empty sequence of objects and extremely common in real world code. As a result, all C code, and C++ code which calls into C functions, must carefully guard all calls to memcpy. This is a serious, real world usability issue in C and should be fixed in the language (see #49459). In the meantime, pay the cost of the extra branch to avoid tripping UBSan in libFuzzer. Once the usability problem in C has been fixed, these checks can be removed. Fixes #96772
1 parent b579aac commit 6ddda79

File tree

2 files changed

+9
-2
lines changed

2 files changed

+9
-2
lines changed

compiler-rt/lib/fuzzer/FuzzerDictionary.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ template <size_t kMaxSizeT> class FixedWord {
2929
static_assert(kMaxSizeT <= std::numeric_limits<uint8_t>::max(),
3030
"FixedWord::kMaxSizeT cannot fit in a uint8_t.");
3131
assert(S <= kMaxSize);
32-
memcpy(Data, B, S);
32+
// memcpy cannot take null pointer arguments even if Size is 0.
33+
if (S)
34+
memcpy(Data, B, S);
3335
Size = static_cast<uint8_t>(S);
3436
}
3537

compiler-rt/lib/fuzzer/FuzzerLoop.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,9 @@ void Fuzzer::CrashOnOverwrittenData() {
579579
// Compare two arrays, but not all bytes if the arrays are large.
580580
static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) {
581581
const size_t Limit = 64;
582+
// memcmp cannot take null pointer arguments even if Size is 0.
583+
if (!Size)
584+
return true;
582585
if (Size <= 64)
583586
return !memcmp(A, B, Size);
584587
// Compare first and last Limit/2 bytes.
@@ -596,7 +599,9 @@ ATTRIBUTE_NOINLINE bool Fuzzer::ExecuteCallback(const uint8_t *Data,
596599
// We copy the contents of Unit into a separate heap buffer
597600
// so that we reliably find buffer overflows in it.
598601
uint8_t *DataCopy = new uint8_t[Size];
599-
memcpy(DataCopy, Data, Size);
602+
// memcpy cannot take null pointer arguments even if Size is 0.
603+
if (Size)
604+
memcpy(DataCopy, Data, Size);
600605
if (EF->__msan_unpoison)
601606
EF->__msan_unpoison(DataCopy, Size);
602607
if (EF->__msan_unpoison_param)

0 commit comments

Comments
 (0)