Read FILE* input with std::fgetc under MemorySanitizer (#5874)

With glibc and optimization, <stdio.h> turns getc_unlocked into an extern
inline function that reads the stdio buffer (fp->_IO_read_ptr) directly.
glibc, which is not instrumented, fills that buffer, so MemorySanitizer
treats the bytes as uninitialized and reports a false positive when the
parsers read from a FILE* (e.g., the fuzz harness FILE* leg, OSS-Fuzz
575915982). std::fgetc is an opaque call whose result MemorySanitizer treats
as initialized.

Select JSON_UNLOCKED_FILE_IO 0 when compiled with MemorySanitizer. This is a
regression from #5828 and not part of any release.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann authored and GitHub committed 2026-10-21 10:22:50 +02:00
1 parent 4155e2938f
commit 08d7fbb0a1
3 files changed
+25 -4

No files matched your search

@@ -38,8 +38,16 @@
// 2 = Microsoft CRT _lock_file/_fgetc_nolock, 0 = none (std::fgetc, which
// locks the file for every character). Only C libraries known to declare
// the functions are listed; the macros are tested after <cstdio>, which
// defines __GLIBC__.
#if JSON_DISABLE_UNLOCKED_FILE_IO
// defines __GLIBC__. MemorySanitizer builds always use std::fgetc: glibc's
// inline getc_unlocked reads the stdio buffer directly, which glibc (not
// instrumented) filled, so MemorySanitizer would report a false positive.
#if defined(__has_feature)
#if __has_feature(memory_sanitizer)
#define JSON_UNLOCKED_FILE_IO_MSAN 1
#endif
#endif
#if JSON_DISABLE_UNLOCKED_FILE_IO || defined(JSON_UNLOCKED_FILE_IO_MSAN)
#define JSON_UNLOCKED_FILE_IO 0
#elif defined(_MSC_VER)
#define JSON_UNLOCKED_FILE_IO 2
@@ -48,6 +56,7 @@
#else
#define JSON_UNLOCKED_FILE_IO 0
#endif
#undef JSON_UNLOCKED_FILE_IO_MSAN
#endif // JSON_NO_IO
NLOHMANN_JSON_NAMESPACE_BEGIN