aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/host/host_linux.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2018-09-28 14:25:01 +0200
committerDmitry Vyukov <dvyukov@google.com>2018-09-28 14:57:20 +0200
commit7296c0747fa69e6f20a507aafcb3e1a77ea0f430 (patch)
tree0bdef730c82bba28e0abed8876b40dd9be8406fb /pkg/host/host_linux.go
parenta6143bc982398127935fc6669e685ef1b3d44d29 (diff)
pkg/host: improve KMEMLEAK support
Rewind kmemleak fd before reading it second time, otherwise we will read truncated reports. Auto-learn what leak reports we've already seen and ignore them in future. This is required because there are some false positives and some fire too frequently. So now we will hit each leak only once per manager run, but we still will try to reproduce them.
Diffstat (limited to 'pkg/host/host_linux.go')
-rw-r--r--pkg/host/host_linux.go38
1 files changed, 11 insertions, 27 deletions
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()