Skip to content

Commit bde4ffe

Browse files
authored
Don't pass null pointers to memcmp and memcpy in libFuzzer (#96775)
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 2596464 commit bde4ffe

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)