aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/host/host_linux.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-05-18 17:54:03 +0200
committerDmitry Vyukov <dvyukov@google.com>2019-05-20 19:40:20 +0200
commit8285069f89c9942f65ce760a8f0a5a12254bfeeb (patch)
treedf5cc7298195f227005e11489fdad8c25458847e /pkg/host/host_linux.go
parent7b3084af2ea815515ea35f9904ee38b5991e58d5 (diff)
executor: implement support for leak checking
Leak checking support was half done and did not really work. This is heavy-lifting to make it work. 1. Move leak/fault setup into executor. pkg/host was a wrong place for them because we need then in C repros too. The pkg/host periodic callback functionality did not work too, we need it in executor so that we can reuse it in C repros too. Remove setup/callback functions in pkg/host entirely. 2. Do leak setup/checking in C repros. The way leak checking is invoked is slightly different from fuzzer, but much better then no support at all. At least the checking code is shared. 3. Add Leak option to pkg/csource and -leak flag to syz-prog2c. 4. Don't enalbe leak checking in fuzzer while we are triaging initial corpus. It's toooo slow. 5. Fix pkg/repro to do something more sane for leak bugs. Few other minor fixes here and there.
Diffstat (limited to 'pkg/host/host_linux.go')
-rw-r--r--pkg/host/host_linux.go120
1 files changed, 0 insertions, 120 deletions
diff --git a/pkg/host/host_linux.go b/pkg/host/host_linux.go
index 8bd2cc65a..d0c3cd0f4 100644
--- a/pkg/host/host_linux.go
+++ b/pkg/host/host_linux.go
@@ -367,10 +367,7 @@ func init() {
checkFeature[FeatureSandboxNamespace] = checkSandboxNamespace
checkFeature[FeatureSandboxAndroidUntrustedApp] = checkSandboxAndroidUntrustedApp
checkFeature[FeatureFaultInjection] = checkFaultInjection
- setupFeature[FeatureFaultInjection] = setupFaultInjection
checkFeature[FeatureLeakChecking] = checkLeakChecking
- setupFeature[FeatureLeakChecking] = setupLeakChecking
- callbFeature[FeatureLeakChecking] = callbackLeakChecking
checkFeature[FeatureNetworkInjection] = checkNetworkInjection
checkFeature[FeatureNetworkDevices] = checkNetworkDevices
}
@@ -482,28 +479,6 @@ func checkFaultInjection() string {
return ""
}
-func setupFaultInjection() error {
- // Note: these files are also hardcoded in pkg/csource/csource.go.
- if err := osutil.WriteFile("/sys/kernel/debug/failslab/ignore-gfp-wait", []byte("N")); err != nil {
- return fmt.Errorf("failed to write /failslab/ignore-gfp-wait: %v", err)
- }
- // These are enabled by separate configs (e.g. CONFIG_FAIL_FUTEX)
- // and we did not check all of them in checkFaultInjection, so we ignore errors.
- if err := osutil.WriteFile("/sys/kernel/debug/fail_futex/ignore-private", []byte("N")); err != nil {
- log.Logf(0, "failed to write /sys/kernel/debug/fail_futex/ignore-private: %v", err)
- }
- if err := osutil.WriteFile("/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem", []byte("N")); err != nil {
- log.Logf(0, "failed to write /sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem: %v", err)
- }
- if err := osutil.WriteFile("/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait", []byte("N")); err != nil {
- log.Logf(0, "failed to write /sys/kernel/debug/fail_page_alloc/ignore-gfp-wait: %v", err)
- }
- if err := osutil.WriteFile("/sys/kernel/debug/fail_page_alloc/min-order", []byte("0")); err != nil {
- log.Logf(0, "failed to write /sys/kernel/debug/fail_page_alloc/min-order: %v", err)
- }
- return nil
-}
-
func checkLeakChecking() string {
if reason := checkDebugFS(); reason != "" {
return reason
@@ -522,101 +497,6 @@ func checkLeakChecking() string {
return ""
}
-func setupLeakChecking() error {
- fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
- if err != nil {
- return fmt.Errorf("failed to open /sys/kernel/debug/kmemleak: %v", err)
- }
- defer syscall.Close(fd)
- // Flush boot leaks.
- if _, err := syscall.Write(fd, []byte("scan")); err != nil {
- return fmt.Errorf("write(kmemleak, scan) failed: %v", err)
- }
- time.Sleep(5 * time.Second) // account for MSECS_MIN_AGE
- if _, err := syscall.Write(fd, []byte("scan")); err != nil {
- return fmt.Errorf("write(kmemleak, scan) failed: %v", err)
- }
- if _, err := syscall.Write(fd, []byte("clear")); err != nil {
- return fmt.Errorf("write(kmemleak, clear) failed: %v", err)
- }
- return nil
-}
-
-func callbackLeakChecking(leakFrames [][]byte) {
- start := time.Now()
- fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
- if err != nil {
- panic(err)
- }
- defer syscall.Close(fd)
- // KMEMLEAK has false positives. To mitigate most of them, it checksums
- // potentially leaked objects, and reports them only on the next scan
- // iff the checksum does not change. Because of that we do the following
- // intricate dance:
- // Scan, sleep, scan again. At this point we can get some leaks.
- // If there are leaks, we sleep and scan again, this can remove
- // false leaks. Then, read kmemleak again. If we get leaks now, then
- // hopefully these are true positives during the previous testing cycle.
- if _, err := syscall.Write(fd, []byte("scan")); err != nil {
- panic(err)
- }
- time.Sleep(time.Second)
- // Account for MSECS_MIN_AGE
- // (1 second less because scanning will take at least a second).
- for time.Since(start) < 4*time.Second {
- time.Sleep(time.Second)
- }
- if _, err := syscall.Write(fd, []byte("scan")); err != nil {
- panic(err)
- }
- buf := make([]byte, 128<<10)
- n, err := syscall.Read(fd, buf)
- if err != nil {
- panic(err)
- }
- if n != 0 {
- time.Sleep(time.Second)
- if _, err := syscall.Write(fd, []byte("scan")); err != nil {
- panic(err)
- }
- if _, err := syscall.Seek(fd, 0, 0); err != nil {
- panic(err)
- }
- n, err := syscall.Read(fd, buf)
- if err != nil {
- panic(err)
- }
- nleaks := 0
- nextLeak:
- for buf = buf[:n]; len(buf) != 0; {
- end := bytes.Index(buf[1:], []byte("unreferenced object"))
- if end != -1 {
- end++
- } else {
- end = len(buf)
- }
- report := buf[:end]
- buf = buf[end:]
- for _, frame := range leakFrames {
- if bytes.Contains(report, frame) {
- continue nextLeak
- }
- }
- // BUG in output should be recognized by manager.
- fmt.Printf("BUG: memory leak\n%s\n", report)
- nleaks++
- }
- if nleaks != 0 {
- // If we exit right away, dying executors will dump lots of garbage to console.
- time.Sleep(time.Hour)
- os.Exit(1)
- }
- }
- if _, err := syscall.Write(fd, []byte("clear")); err != nil {
- panic(err)
- }
-}
-
func checkSandboxNamespace() string {
if err := osutil.IsAccessible("/proc/self/ns/user"); err != nil {
return err.Error()