Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sanitizer-leak is NOT detected when golang using cgo to call c library

Summary

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?

Example

  • cgo-sanitizer.go: address issue is detected expectedly.
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
}
  • Output
[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)
  • cgo-sanitizer-leak.go: leak issue was not detected. Why?
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

Environment

[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

Original question

https://github.com/google/sanitizers/issues/1223

like image 948
Shawn ZHANG Avatar asked Apr 20 '20 10:04

Shawn ZHANG


People also ask

What is CGO in Golang?

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.

Should you sanitize flags in GCC and Clang?

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.

Is it possible to check for code leaks with sanitizers?

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.

How do I sanitize my code in C++?

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.


2 Answers

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.

like image 198
Shawn ZHANG Avatar answered Oct 21 '22 19:10

Shawn ZHANG


@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
like image 32
KristianR Avatar answered Oct 21 '22 17:10

KristianR