Clang LeakSanitizer memory leak detection with optimized code

43 views Asked by At

1.Background

  • Let's consider a C file a.c with following content as a memory leak MRE:
#include <stdlib.h>
void Foo() { malloc(1); }
int main() { Foo();  return 0;}
  • On one hand, as expected, Clang LeakSanitizer (which is integrated into Clang AddresSanitizer) detects the memory leak if the following command is run:
$ clang++ -g -fsanitize=address a.c; ./a.out

=================================================================
==...==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 1 byte(s) in 1 object(s) allocated from:
    #0 0x... in malloc (/.../a.out+0xa114e) (BuildId: ...)
    #1 0x... in Baz() (/.../a.c:2:14
    #2 0x... in main (/.../a.c:3:14
    #3 0x... in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: 1 byte(s) leaked in 1 allocation(s).
  • On the other hand, Clang AddressSanitizer documentation Usage section mentions:

To get a reasonable performance add -O1 or higher.

2.Concern

  • Clang LeakSanitizer does not detect the memory leak if the following command is run (the code generation option -O1 has been added):
$ clang++ -O1 -g -fsanitize=address a.c; ./a.out

3.Question

Can memory leak still be detected by Clang LeakSanitizer when code optimization level 1 or higher is used?

  • If the answer is yes, please provide a minimal reproducible example (MRE) proving it.

  • If the answer is no, please explain why (link to Clang compiler source code, ...).

4.Remarks

  • Clang LeakSanitizer documentation Usage section does not explicitly mention that code optimization shall not be used.

  • Above observations were made with version 18 of Clang, installed on Linux Ubuntu 22.04.3 using LLVM automatic installation script.

  • As explained by user17732522 in a comment, the a.c file example in the Background section does not lead to memory leak detection when the -O1 option is used because:

The optimized program won't have any actual memory leak. The malloc call will be optimized away since it doesn't affect observable behavior

1

There are 1 answers

0
Employed Russian On BEST ANSWER

Your minimal example is too minimal -- clang was able to optimize everything away from it:

(gdb) disas main
Dump of assembler code for function main:
   0x0000000000401120 <+0>:     xor    %eax,%eax
   0x0000000000401122 <+2>:     ret

As you can see, none of Foo or malloc remain.

To make it a bit more realistic, try this:

#include <stdlib.h>
#include <stdio.h>

void* Foo() { return malloc(1); }
void Bar(void *p) { printf("p = %p\n", p); }
int main() { Bar(Foo());  return 0;}
clang -g -O0 -fsanitize=address a1.c && ./a.out
p = 0x502000000010

=================================================================
==191==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 1 byte(s) in 1 object(s) allocated from:
    #0 0x4c8822 in malloc (/tmp/a.out+0x4c8822) (BuildId: a27851507d14179bca1fa40aa114a3e5ffa99c23)
    #1 0x50644d in Foo /tmp/a1.c:4:22
    #2 0x506493 in main /tmp/a1.c:6:18
    #3 0x7f9a74195149 in __libc_start_call_main (/lib64/libc.so.6+0x28149) (BuildId: 7ea8d85df0e89b90c63ac7ed2b3578b2e7728756)
    #4 0x7f9a7419520a in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x2820a) (BuildId: 7ea8d85df0e89b90c63ac7ed2b3578b2e7728756)
    #5 0x42a324 in _start (/tmp/a.out+0x42a324) (BuildId: a27851507d14179bca1fa40aa114a3e5ffa99c23)

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

And now with -O1:

clang -g -O1 -fsanitize=address a1.c && ./a.out
p = 0x502000000010

=================================================================
==197==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 1 byte(s) in 1 object(s) allocated from:
    #0 0x4c8822 in malloc (/tmp/a.out+0x4c8822) (BuildId: 1ffaa6630367e5a8f0a95663181cd0d01efdc513)
    #1 0x50646a in Foo /tmp/a1.c:4:22
    #2 0x50646a in main /tmp/a1.c:6:18
    #3 0x7f804f1c020a in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x2820a) (BuildId: 7ea8d85df0e89b90c63ac7ed2b3578b2e7728756)
    #4 0x42a324 in _start (/tmp/a.out+0x42a324) (BuildId: 1ffaa6630367e5a8f0a95663181cd0d01efdc513)

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