Previously I used clang-3.8.1 and sanitizer is crashed when using AddressSanitizer. And leakSanitizer doesn't work at all.
Then I try to use clang-llvm-10.0, AddressSanitizer could detect the address issue and work normally.
But leak issue can NOT be detected when golang using cgo to call C. Is it possible to use leak-sanitizer to detect memory leak issues in C/C++ libs when golang using CGO?
package main
// #include <stdlib.h>
//
// int test()
// {
// int *p = (int *)malloc(10 * sizeof(int));
// free(p);
// p[1] = 42;
// return p[1];
// }
import "C"
import "fmt"
func main() {
fmt.Println(int(C.test()))
// Output: 42
}
[root@380c7770b175 cplusplus]# CC="clang" CGO_CFLAGS="-O0 -g -fsanitize=address" CGO_LDFLAGS="-fsanitize=address" go run cgo-sanitizer.go
=================================================================
==25680==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000000014 at pc 0x00000054fc2d bp 0x7ffd96a943b0 sp 0x7ffd96a943a8
WRITE of size 4 at 0x604000000014 thread T0
#0 0x54fc2c in test (/tmp/go-build237509829/b001/exe/cgo-sanitizer+0x54fc2c)
#1 0x54fcc1 in _cgo_a3187169dba5_Cfunc_test (/tmp/go-build237509829/b001/exe/cgo-sanitizer+0x54fcc1)
#2 0x5159df (/tmp/go-build237509829/b001/exe/cgo-sanitizer+0x5159df)
package main
// #include <stdlib.h>
//
// int *p;
// int test()
// {
// p = (int *)malloc(10 * sizeof(int));
// p = 0;
// return 52;
// }
import "C"
import "fmt"
func main() {
fmt.Println(int(C.test()))
// Output: 52
}
[root@380c7770b175 cplusplus]# CC="clang" CGO_CFLAGS="-O0 -g -fsanitize=leak" CGO_LDFLAGS="-fsanitize=address" go run cgo-sanitizer-leak.go
52
[root@380c7770b175 cplusplus]# cat /proc/version
Linux version 3.10.0-493.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-9) (GCC) ) #1 SMP Tue Aug 16 11:45:26 EDT 2016
[root@380c7770b175 cplusplus]# clang -v
clang version 10.0.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/llvm-10.0/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.8.2
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.8.5
Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.8.5
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: .;@m64
[root@380c7770b175 cplusplus]# go version
go version go1.13.6 linux/amd64
https://github.com/google/sanitizers/issues/1223
Cgo lets Go packages call C code. Given a Go source file written with some special features, cgo outputs Go and C files that can be combined into a single Go package. To lead with an example, here’s a Go package that provides two functions - Random and Seed - that wrap C’s random and srandom functions.
No more leaks with sanitize flags in gcc and clang If you are programming in C and C++, you are probably wasting at least some of your time hunting down memory problems.
So checking leaks is not a separate step. The check is right there, each time you run the program. Same with overflows and so forth. Of course, it is possible to also release the code with sanitizers, and I bet that many teams do that, but I imagine that the release would do away with the sanitizer.
While you are at it, you can add other sanitize flags such as -fsanitize=undefinedto your code. The undefined sanitizer will warn you if you are relying on undefined behavior as per the C or C++ specifications. These flags represent significant steps forward for people programming in C or C++ with gcc or clang.
I have solved it by explicitly calling __lsan_do_leak_check()
, when process exiting.
__lsan_do_leak_check() is declared in
https://github.com/llvm/llvm-project/blob/master/compiler-rt/include/sanitizer/lsan_interface.h
I guess it's related to c-main start mechanism and __lsan_do_leak_check() is not registered for golang start.
Welcome anyone who could continuously dig on it.
@Shawn is correct that you need to invoke __lsan_do_leak_check manually if your main program is written in Go. If your main is in C/C++ and Go is invoked as a library then it's not needed.
Here is a simple example
package main
// #include <stdio.h>
// #include <stdlib.h>
//
// void __lsan_do_leak_check(void);
//
// void leak_a_bit(void)
// {
// char* p = malloc(2000);
// printf("%p\n", p);
// }
import "C"
func main() {
C.leak_a_bit()
C.__lsan_do_leak_check()
}
Compile and run like this
$ CC=clang CGO_ENABLED=1 CGO_LDFLAGS='-fsanitize=address' CGO_CFLAGS='-fsanitize=address' go build -o main
$ ./main
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With