https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94849
Bug ID: 94849 Summary: Improper parameter validation in libsanitizer for fopen64 Product: gcc Version: 9.3.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: sanitizer Assignee: unassigned at gcc dot gnu.org Reporter: rachel at rachelmant dot com CC: dodji at gcc dot gnu.org, dvyukov at gcc dot gnu.org, jakub at gcc dot gnu.org, kcc at gcc dot gnu.org, marxin at gcc dot gnu.org Target Milestone: --- Created attachment 48408 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=48408&action=edit A simple fix for the bug fopen() (actually fopen64 because of macro remapping) as implemented by Glibc and other C run-times allows the first "path" or "filename" parameter to be nullptr and causes the function to simply do nothing and return nullptr itself when this happens. Because of this, I have a test case in a project that is specifically verifying this bad, but allowed, use of fopen64() as a way to guarantee it returns nullptr for some further tests and for code coverage reasons. The simplest form of failing program is: ``` #include <cstdio> int main() { return !fopen(nullptr, "w") ? 0 : 1; } ``` which when compiled with `g++ -D_FILE_OFFSET_BITS=64 -o test test.cxx` runs and returns 0, but when compiled with `g++ -D_FILE_OFFSET_BITS=64 -o test -fsanitize=address test.cxx` or the thread sanitizer, instead crashes: ``` $ ./test; echo $? AddressSanitizer:DEADLYSIGNAL ================================================================= ==2187382==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f8867d791e5 bp 0x7ffcc8f95db0 sp 0x7ffcc8f95528 T0) ==2187382==The signal is caused by a READ memory access. ==2187382==Hint: address points to the zero page. #0 0x7f8867d791e4 in __strlen_avx2 (/usr/lib/libc.so.6+0x1611e4) #1 0x7f886817cc35 in __interceptor_fopen64 /build/gcc/src/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:5757 #2 0x56367982918d in main (/tmp/test+0x118d) #3 0x7f8867c3f022 in __libc_start_main (/usr/lib/libc.so.6+0x27022) #4 0x5636798290ad in _start (/tmp/test+0x10ad) AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV (/usr/lib/libc.so.6+0x1611e4) in __strlen_avx2 ==2187382==ABORTING 1 ``` This is on GCC 9.3.0, but I have reproduced this on everything from GCC 5 through to 9. Please note: This is not a problem when the non-64-bit fopen() is used. This is specifically a problem with fopen64(). Unfortunately, libsanitizer does not properly validate the path parameter and ends up calling strlen() on a nullptr - which is UB that on x86 crashes with SEGV as seen above. After triggering the bug and doing some research, it appears that since the introduction of libsanitizer, this has been a thing: https://github.com/gcc-mirror/gcc/blob/master/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc#L5882 Attached is a patch that would fix this bugged behaviour.