diff options
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/host/host.go | 22 | ||||
| -rw-r--r-- | pkg/host/host_linux.go | 38 | ||||
| -rw-r--r-- | pkg/ipc/ipcconfig/ipcconfig.go | 41 | ||||
| -rw-r--r-- | pkg/rpctype/rpctype.go | 11 | ||||
| -rw-r--r-- | pkg/runtest/run.go | 12 |
5 files changed, 63 insertions, 61 deletions
diff --git a/pkg/host/host.go b/pkg/host/host.go index b23dd9ab2..36c42de65 100644 --- a/pkg/host/host.go +++ b/pkg/host/host.go @@ -65,7 +65,7 @@ type Features [numFeatures]Feature var checkFeature [numFeatures]func() string var setupFeature [numFeatures]func() error -var callbFeature [numFeatures]func() +var callbFeature [numFeatures]func(leakFrames [][]byte) func unconditionallyEnabled() string { return "" } @@ -104,11 +104,11 @@ func Check(target *prog.Target) (*Features, error) { // Setup enables and does any one-time setup for the requested features on the host. // Note: this can be called multiple times and must be idempotent. -func Setup(target *prog.Target, features *Features) (func(), error) { +func Setup(target *prog.Target, features *Features) (func(leakFrames [][]byte), error) { if target.OS == "akaros" || target.OS == "test" { return nil, nil } - var callback func() + var callback func([][]byte) for n, setup := range setupFeature { if setup == nil || !features[n].Enabled { continue @@ -117,15 +117,15 @@ func Setup(target *prog.Target, features *Features) (func(), error) { return nil, err } cb := callbFeature[n] - if cb != nil { - prev := callback - callback = func() { - cb() - if prev != nil { - prev() - } + if cb == nil { + continue + } + prev := callback + callback = func(leakFrames [][]byte) { + cb(leakFrames) + if prev != nil { + prev(leakFrames) } - } } return callback, nil diff --git a/pkg/host/host_linux.go b/pkg/host/host_linux.go index 8214b35af..5c5db793b 100644 --- a/pkg/host/host_linux.go +++ b/pkg/host/host_linux.go @@ -469,7 +469,7 @@ func setupLeakChecking() error { return nil } -func callbackLeakChecking() { +func callbackLeakChecking(leakFrames [][]byte) { start := time.Now() fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0) if err != nil { @@ -506,11 +506,15 @@ func callbackLeakChecking() { 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 { @@ -520,14 +524,18 @@ func callbackLeakChecking() { } report := buf[:end] buf = buf[end:] - if kmemleakIgnore(report) { - continue + 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) } } @@ -536,30 +544,6 @@ func callbackLeakChecking() { } } -func kmemleakIgnore(report []byte) bool { - // kmemleak has a bunch of false positives (at least what looks like - // false positives at first glance). So we are conservative with what we report. - // First, we filter out any allocations that don't come from executor processes. - // Second, we ignore a bunch of functions entirely. - // Ideally, someone should debug/fix all these cases and remove ignores. - if !bytes.Contains(report, []byte(`comm "syz-executor`)) { - return true - } - for _, ignore := range []string{ - " copy_process", - " do_execveat_common", - " __ext4_", - " get_empty_filp", - " do_filp_open", - " new_inode", - } { - if bytes.Contains(report, []byte(ignore)) { - return true - } - } - return false -} - func checkSandboxNamespace() string { if err := osutil.IsAccessible("/proc/self/ns/user"); err != nil { return err.Error() diff --git a/pkg/ipc/ipcconfig/ipcconfig.go b/pkg/ipc/ipcconfig/ipcconfig.go index 021978274..b94579102 100644 --- a/pkg/ipc/ipcconfig/ipcconfig.go +++ b/pkg/ipc/ipcconfig/ipcconfig.go @@ -33,18 +33,11 @@ func Default(target *prog.Target) (*ipc.Config, *ipc.ExecOpts, error) { if *flagDebug { c.Flags |= ipc.FlagDebug } - switch *flagSandbox { - case "none": - case "setuid": - c.Flags |= ipc.FlagSandboxSetuid - case "namespace": - c.Flags |= ipc.FlagSandboxNamespace - case "android_untrusted_app": - c.Flags |= ipc.FlagSandboxAndroidUntrustedApp - default: - return nil, nil, fmt.Errorf("flag sandbox must contain one of none/setuid/namespace/android_untrusted_app") + sandboxFlags, err := SandboxToFlags(*flagSandbox) + if err != nil { + return nil, nil, err } - + c.Flags |= sandboxFlags sysTarget := targets.Get(target.OS, target.Arch) if sysTarget.ExecutorUsesShmem { c.Flags |= ipc.FlagUseShmem @@ -65,3 +58,29 @@ func Default(target *prog.Target) (*ipc.Config, *ipc.ExecOpts, error) { return c, opts, nil } + +func SandboxToFlags(sandbox string) (ipc.EnvFlags, error) { + switch sandbox { + case "none": + return 0, nil + case "setuid": + return ipc.FlagSandboxSetuid, nil + case "namespace": + return ipc.FlagSandboxNamespace, nil + case "android_untrusted_app": + return ipc.FlagSandboxAndroidUntrustedApp, nil + default: + return 0, fmt.Errorf("sandbox must contain one of none/setuid/namespace/android_untrusted_app") + } +} + +func FlagsToSandbox(flags ipc.EnvFlags) string { + if flags&ipc.FlagSandboxSetuid != 0 { + return "setuid" + } else if flags&ipc.FlagSandboxNamespace != 0 { + return "namespace" + } else if flags&ipc.FlagSandboxAndroidUntrustedApp != 0 { + return "android_untrusted_app" + } + return "none" +} diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go index eb2303950..68f85eedc 100644 --- a/pkg/rpctype/rpctype.go +++ b/pkg/rpctype/rpctype.go @@ -29,11 +29,12 @@ type ConnectArgs struct { } type ConnectRes struct { - EnabledCalls []int - GitRevision string - TargetRevision string - AllSandboxes bool - CheckResult *CheckArgs + EnabledCalls []int + GitRevision string + TargetRevision string + AllSandboxes bool + CheckResult *CheckArgs + MemoryLeakFrames [][]byte } type CheckArgs struct { diff --git a/pkg/runtest/run.go b/pkg/runtest/run.go index 115f5ecb7..0ec0bb125 100644 --- a/pkg/runtest/run.go +++ b/pkg/runtest/run.go @@ -26,6 +26,7 @@ import ( "github.com/google/syzkaller/pkg/csource" "github.com/google/syzkaller/pkg/host" "github.com/google/syzkaller/pkg/ipc" + "github.com/google/syzkaller/pkg/ipc/ipcconfig" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys/targets" @@ -307,14 +308,11 @@ func (ctx *Context) createSyzTest(p *prog.Prog, sandbox string, threaded, cov bo if sysTarget.ExecutorUsesForkServer { cfg.Flags |= ipc.FlagUseForkServer } - switch sandbox { - case "namespace": - cfg.Flags |= ipc.FlagSandboxNamespace - case "setuid": - cfg.Flags |= ipc.FlagSandboxSetuid - case "android_untrusted_app": - cfg.Flags |= ipc.FlagSandboxAndroidUntrustedApp + sandboxFlags, err := ipcconfig.SandboxToFlags(sandbox) + if err != nil { + return nil, err } + cfg.Flags |= sandboxFlags if threaded { opts.Flags |= ipc.FlagThreaded | ipc.FlagCollide } |
