ASAN Leak detection does not detect new (ptr) memory allocation leak - c++

Is this a feature or a bug in ASAN? If I have this code:
class Foo {
int x = 5;
int z = 10;
};
int main() {
void *memory = malloc(sizeof(Foo));
Foo *ptr = new(memory) Foo();
ptr = nullptr;
}
ASAN reports no leaks. If I remove the Foo *ptr = new(memory) Foo(); suddenly I get leak:
Direct leak of 8 byte(s) in 1 object(s) allocated from:
0x7f00b7a80a89 __interceptor_malloc asan_malloc_linux.cpp:69
0x55dfa164d1ca main main.cpp:12
0x7f00b74a72cf (libc.so.6+0x232cf)
Compiler version: c++ (GCC) 12.2.0
Compile commands:
[1/2] /usr/sbin/c++ -g -fsanitize=address -fsanitize=leak -std=gnu++14 -MD -MT CMakeFiles/test.dir/main.cpp.o -MF CMakeFiles/test.dir/main.cpp.o.d -o CMakeFiles/test.dir/main.cpp.o -c /src/self/test/main.cpp
[2/2] : && /usr/sbin/c++ -g -fsanitize=address -fsanitize=leak CMakeFiles/test.dir/main.cpp.o -o test && :
OS: Linux WORKSTATION 5.19.4-zen1-1-zen #1 ZEN SMP PREEMPT_DYNAMIC Thu, 25 Aug 2022 17:31:19 +0000 x86_64 GNU/Linux

Related

ASAN is not able to detect memory leak for unused delete after new

Hello stackoverflow community, I am creating a memory leak to use ASAN and detect memory leaks.
$ export MallocNanoZone='0' # to avoid https://stackoverflow.com/q/64126942/9497703 on OS X
$ cat new_delete.cc
class Dummy {
public:
Dummy(int x) {
sz = x;
}
private:
int sz;
};
void func(int i) {
Dummy* p = new Dummy(i);
if (i < 50) {
// If return from here then there is a memory leak on the
// heap. Dummy is not freed.
return;
}
// Do useful things.
delete p;
}
int main() {
func(10);
}
$ clang++ -fsanitize=address -g -O0 new_delete.cc
$ ./a.out
I was expecting ASAN to detect this memory leak. However, it didn't.
Can anyone point out what I am missing here? I am using OS X and following clang version:
$ clang++ --version
Apple clang version 12.0.0 (clang-1200.0.32.28)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
This is a known issue - algorithm used in Lsan is probabilistic and does not guarantee that all leaks are detected (see #937 for details). E.g. in your case if we change main to
int main() {
int a[100];
func(10);
}
clang starts to detect the leak:
$ clang++ -fsanitize=address new_delete.cc && ./a.out
=================================================================
==349258==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 4 byte(s) in 1 object(s) allocated from:

Why is address sanitizer not detecting this simple memory leak?

Does anybody have any idea why address sanitizer is not flagging this very obvious memory leak
class A {
public:
A() = default;
};
TEST_F(LibrdfSerializerTests, Test) {
A* a = new A;
}
built with the following added to cmake:
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
You can try using the ASAN_OPTIONS=detect_leaks=1 while executing the binary to detect leaks. Using the example from the documentation
❯ cat leak.c
#include <stdlib.h>
void *p;
int main() {
p = malloc(7);
p = 0; // The memory is leaked here.
return 0;
}
Compile the program
clang -fsanitize=address -g leak.c
and then execute it as follows:
ASAN_OPTIONS=detect_leaks=1 ./a.out
Output:
=================================================================
==63987==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 7 byte(s) in 1 object(s) allocated from:
#0 0x1034c109d in wrap_malloc+0x9d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x4609d)
#1 0x103477ef8 in main leak.c:4
#2 0x7fff6b30fcc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)
SUMMARY: AddressSanitizer: 7 byte(s) leaked in 1 allocation(s).
Same for CPP
❯ cat memory-leak.cpp
#include <cstdlib>
#include <cstdio>
class A {
public:
A() = default;
};
int main() {
char const* asanOpt = std::getenv("ASAN_OPTIONS");
std::printf("%s\n", asanOpt);
A* a = new A;
return 0;
}
Compile it
clang++ -g memory-leak.cpp -fsanitize=address
While executing the binary, use the option to enable leak detections
ASAN_OPTIONS=detect_leaks=1 ./a.out
Output:
detect_leaks=1
=================================================================
==69309==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 1 byte(s) in 1 object(s) allocated from:
#0 0x109ea556d in wrap__Znwm+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5256d)
#1 0x109e4bf48 in main memory-leak.cpp:7
#2 0x7fff6b30fcc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)
SUMMARY: AddressSanitizer: 1 byte(s) leaked in 1 allocation(s).
Tested on:
MacOS 10.15
With clang
clang version 10.0.1
Target: x86_64-apple-darwin19.6.0
Thread model: posix

why does c++ std::min can't use a static field as its parameter when compile on O0?

same code, compile with O0, it will report an error:
//============================================================================
// Name : test.cpp
// Author :
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
#include <stdint.h>
using namespace std;
class foo{
static const int64_t MAX_THREAD_NUM = 10 * 1000;
public:
void test();
};
void foo::test(){
int64_t a = 100;
// int64_t tmp = MAX_THREAD_NUM;
// int64_t min = std::min(tmp, a);
int64_t min = std::min(MAX_THREAD_NUM, a);
cout << min << endl; // prints !!!Hello World!!!
}
int main() {
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
return 0;
}
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/test.d" -MT"src/test.o" -o "src/test.o" "../src/test.cpp"
g++ -o "test" ./src/test.o
./src/test.o: In function `foo::test()':
/home/foo/eclipse-workspace/test/Debug/../src/test.cpp:27: undefined reference to `foo::MAX_THREAD_NUM'
collect2: error: ld returned 1 exit status
/home/foo/eclipse-workspace/test/Debug/../src/test.cpp:27: undefined reference to `foo::MAX_THREAD_NUM'
but with O2 flag, it can compile succeed.
g++ -O2 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/test.d" -MT"src/test.o" -o "src/test.o" "../src/test.cpp"
g++ -o "test" ./src/test.o
g++ version:
g++ (Ubuntu 4.8.5-4ubuntu8) 4.8.5
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
Program is ill-formed no diagnostic required (NDR) as you break One Definition Rule (ODR) by not providing definition of ODR-used symbol (std::min takes its argument by reference).
The optimizer removes that unused code and lets you think it is correct.
As pointed out by #dfri, compiler inlines MAX_THREAD_NUM. If we change it to inline static const int64_t MAX_THREAD_NUM = 10 * 1000;, it will compile fine on O0. Otherwise, declare but define the variable outside of the class, or mark it constexpr instead of const. Using a static const integral member needs definition if it's used where a non-const integral is needed.
class foo {
static const int64_t MAX_THREAD_NUM;
...
};
const int64_t foo::MAX_THREAD_NUM = 10 * 1000;

Clang does not generate profraw file when linking manually

I am trying out the profiling functionality of clang using llvm-cov and llvm-profdata. I have everything setup with CMake, but it doesn't generate the default.profraw as expected. I'v tried the steps manually and discovered that clang does not generate the default.profraw file in case I split the steps between generating the object files and compiling the executable.
For example, The following works:
$ clang++ -g -O0 -fprofile-instr-generate -fcoverage-mapping -std=gnu++2a binoperator.cpp main.cpp
$ ./a.out
38
Done...
$ ls -al default.profraw
-rw-rw-r--. 1 marten marten 224 May 13 13:59 default.profraw
The following doesn't work (this is roughly what CMake tries to do):
$ clang++ -g -O0 -fprofile-instr-generate -fcoverage-mapping -std=gnu++2a -o binoperator.cpp.o -c binoperator.cpp
$ clang++ -g -O0 -fprofile-instr-generate -fcoverage-mapping -std=gnu++2a -o main.cpp.o -c main.cpp
$ clang++ -o a.out binoperator.cpp.o main.cpp.o
$ ./a.out
38
Done...
$ ls -al default.profraw
ls: cannot access 'default.profraw': No such file or directory
Why? What is the difference? How can I make the second case work?
With kind regards,
Marten
Additional info:
main.cpp
#include "binoperator.h"
#include <iostream>
int main()
{
BinOperator bo;
int result = bo.add(5, 33);
std::cout << result << std::endl;
std::cout << "Done..." << std::endl;
return 0;
}
binoperator.h
#ifndef BINOPERATOR_H
#define BINOPERATOR_H
class BinOperator
{
public:
int add(int a, int b) const;
};
#endif
binoperator.cpp
#include "binoperator.h"
int BinOperator::add(int a, int b) const
{
return (a + b);
}
$ clang --version
clang version 8.0.0 (Fedora 8.0.0-1.fc30)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
I've found out that in the second case, the -fprofile-instr-generate -fcoverage-mapping options should also be specified in the linking call to clang++:
$ clang++ -O0 -fprofile-instr-generate -fcoverage-mapping binoperator.cpp.o main.cpp.o -o a.out
In CMake, this can be done with target_link_options().

gcc - how to use address sanitizer

I use gcc 4.8.5 on linux. I want to use address sanitizer but it doesn't return any information about the program. Flags:
SET(CMAKE_CXX_FLAGS "-Wall -Wno-error -g -std=c++11 -fno-omit-frame-pointer -fsanitize=address")
SET(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address")
Linked libraries:
target_link_libraries(testcpp asan)
The test program with a memory leak:
int main()
{
int *prt = new int;
return 0;
}
What is wrong ?
With GCC7 on a recent Debian/Sid/x86-64 I compiled this
// file irbis.cc
int main()
{
int *prt = new int;
return 0;
}
using
g++ -fsanitize=address -g3 -std=c++11 irbis.cc -o irbis
and at execution of ./irbis a leak is rightfully detected :
=================================================================
==22742==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x7f77ea911340 in operator new(unsigned long)
(/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdb340)
#1 0x55ea91cca81b in main /home/basile/tmp/irbis.cc:4
#2 0x7f77e9c1f2e0 in __libc_start_main
(/lib/x86_64-linux-gnu/libc.so.6+0x202e0)
SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s).
So upgrade your GCC compiler (to at least GCC6). I do know that GCC4.8 had incomplete support for address sanitizer & C++11 (BTW, GCC4.8 is obsolete, and so is GCC5, in november 2017).
The cause of the problem might be that the main doesn't use ptr so it probably was optimized out entierly. Consider this instead:
// file irbis.cc
int main()
{
int *prt = new int;
return *ptr;
}