google / sanitizers

AddressSanitizer, ThreadSanitizer, MemorySanitizer
Other
11.41k stars 1.03k forks source link

False negative LeakSanitizer when using `getline` from Ubuntu 22.04's libstdc++ when AddressSanitizer is enabled #1757

Open yeputons opened 4 months ago

yeputons commented 4 months ago

Consider the following C code in a.c:

#include <stdlib.h>
#include <stdio.h>
void foo(void *arg) {}
int main() {
    char *s = malloc(10);
    size_t n = 10;
    if (0) {
        getline(&s, &n, stdin);
    }
    char x[8] = {};
    foo(&x);
}

Note how the only external code called is malloc, the if (0) is skipped, foo(&x) is no-op. Hence, the allocated memory should leak.

Expected result: the leak is detected with Leak Sanitizer.

Real result: the leak is not detected with -fsanitize=address (that enables Leak Sanitizer) on my Ubuntu 22.04 machine with either GCC 11, GCC 12 or Clang-15.

Details:

$ gcc-12 a.c
$ valgrind ./a.out  # Leak is detected as expected
==4074== Memcheck, a memory error detector
==4074== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4074== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==4074== Command: ./a.out
==4074==
==4074==
==4074== HEAP SUMMARY:
==4074==     in use at exit: 10 bytes in 1 blocks
==4074==   total heap usage: 1 allocs, 0 frees, 10 bytes allocated
==4074==
==4074== LEAK SUMMARY:
==4074==    definitely lost: 10 bytes in 1 blocks
==4074==    indirectly lost: 0 bytes in 0 blocks
==4074==      possibly lost: 0 bytes in 0 blocks
==4074==    still reachable: 0 bytes in 0 blocks
==4074==         suppressed: 0 bytes in 0 blocks
==4074== Rerun with --leak-check=full to see details of leaked memory
==4074==
==4074== For lists of detected and suppressed errors, rerun with: -s
==4074== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$ gcc-12 -fsanitize=address a.c
$ ./a.out  # Leak is not detected!
$ gcc-12 -fsanitize=leak a.c
$ ./a.out  # Leak is detected as expected

=================================================================
==4003==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 10 byte(s) in 1 object(s) allocated from:
    #0 0x7fd1a7501302 in __interceptor_malloc ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:75
    #1 0x557ab5a2019c in main (/home/yeputons/a.out+0x119c)
    #2 0x7fd1a72eed8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: LeakSanitizer: 10 byte(s) leaked in 1 allocation(s).

The same effect happens if I use gcc-11 or clang-15: both -fsanitize=leak and Valgrind find the leaked s, while -fsanitize=address fails to do so.

Note that the if (0) is important for some reason: if I remove it, the leak is detected with -fsanitize=address too. Order of definition of s and n is important as well. Having Ubuntu 22.04 is apparently important: I was unable to reproduce on Godbolt.

However, adding -stdlib=libc++ for Clang resolves the issue. Hence, I assume it's between Leak Sanitizer and libstdc++.