Leak Sanitizer unable to detect leak for global dynamic variables.(Some exceptional cases))

Hi,

Consider a test-case:-
// test.c (Orignal)

#include <stdlib.h>
static int *ga;
int main(void) {
ga = (int *)malloc(10000000 * sizeof(int));
ga[0] = 1;
return 0;
}

$$ clang -fsanitize=address -g test.c

The LSan is not working for the above test case. As per general understanding there is a leak happening in the above test case.

Q. Why LSan can’t detect leaks for above test case?

If we change the code somewhat like below:-
// test.c (Modified)

#include <stdlib.h>
static int *ga;
int main(void) {
ga = (int *)malloc(10000000 * sizeof(int));
ga[0] = 1;
ga=0; // Added statement
return 0;
}

LSan is now reporting leaks.

==========================================================
==3245106==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 40000000 byte(s) in 1 object(s) allocated from:
#0 0x00000030f774 in malloc /home/ampandey/aomp-toolchain/aomp-src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:67:3
#1 0x000000353db8 in main /home/ampandey/aomp-toolchain/test/test-asan/openmp/test-app.c:5:15
#2 0x7749cfc29d8f in __libc_start_call_main csu/…/sysdeps/nptl/libc_start_call_main.h:58:16

Objects leaked above:
0x7349c9401800 (40000000 bytes)

SUMMARY: AddressSanitizer: 40000000 byte(s) leaked in 1 allocation(s).

Q. Is there something internally needs to be enabled in the compiler or in runtime(sanitizers specifically) inorder to detect global leaks at program exiting?

The general convention is that memory is only considered leaked if it’s no longer reachable.

In your first example the allocation is still reachable from the ga global (no leak). In the second one it’s not reachable from anywhere (leak).

Thanks @nikic for the explanation.

If we move the declaration of ga inside main function then the leak is reported without adding below statement.

ga=0

Q. If file scope is the issue[in first example] which makes the global variable ga reachable. Why LSan shouldn’t consider reachable globals and report them as reachable bytes after program exits(main thread terminates)?

I was hoping LSan could also list reachable memory in it’s report just like Valgrind does in its leak summary.

==3902324== Memcheck, a memory error detector
==3902324== Copyright (C) 2002-2017, and GNU GPL’d, by Julian Seward et al.
==3902324== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==3902324== Command: ./a.out
==3902324==
==3902324==
==3902324== HEAP SUMMARY:
==3902324== in use at exit: 40,000,000 bytes in 1 blocks
==3902324== total heap usage: 1 allocs, 0 frees, 40,000,000 bytes allocated
==3902324==
==3902324== 40,000,000 bytes in 1 blocks are still reachable in loss record 1 of 1
==3902324== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==3902324== by 0x201638: main (in /home/ampandey/aomp-toolchain/test/test-asan/openmp/a.out)
==3902324==
==3902324== LEAK SUMMARY:
==3902324== definitely lost: 0 bytes in 0 blocks
==3902324== indirectly lost: 0 bytes in 0 blocks
==3902324== possibly lost: 0 bytes in 0 blocks
==3902324== still reachable: 40,000,000 bytes in 1 blocks
==3902324== suppressed: 0 bytes in 0 blocks
==3902324==
==3902324== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Q. I mean could we add this functionality in the LSan and provide an option in LSAN_OPTIONS environment variable for reachable globals?

There is LSAN_OPTIONS=use_globals=false, but it will produce a lot of false reports.