support kmemleak

If you set "leak":true in manager config, it will do leak checking.
It's quite slow, though. Also there seems to be false positives
and/or non-reproducible leaks.
This commit is contained in:
Dmitry Vyukov 2015-12-03 20:38:33 +01:00
parent d823a9221a
commit cf2089138e
5 changed files with 74 additions and 5 deletions

View File

@ -20,6 +20,7 @@ import (
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/google/syzkaller/cover"
@ -39,6 +40,7 @@ var (
flagNoCover = flag.Bool("nocover", false, "disable coverage collection/handling")
flagDropPrivs = flag.Bool("dropprivs", true, "impersonate into nobody")
flagProcs = flag.Int("procs", 1, "number of parallel test processes")
flagLeak = flag.Bool("leak", false, "detect memory leaks")
flagV = flag.Int("v", 0, "verbosity")
)
@ -82,6 +84,8 @@ var (
statExecTriage uint64
statExecMinimize uint64
statNewInput uint64
allTriaged uint32
)
func main() {
@ -116,6 +120,8 @@ func main() {
}
ct := prog.BuildChoiceTable(r.Prios, calls)
kmemleakInit()
flags := ipc.FlagThreaded | ipc.FlagCollide
if *flagStrace {
flags |= ipc.FlagStrace
@ -141,7 +147,7 @@ func main() {
pid := pid
go func() {
rs := rand.NewSource(time.Now().UnixNano())
rs := rand.NewSource(time.Now().UnixNano() + int64(pid)*1e12)
rnd := rand.New(rs)
for i := 0; ; i++ {
@ -245,6 +251,14 @@ func main() {
triageMu.Unlock()
}
}
if len(r.Candidates) == 0 {
if atomic.LoadUint32(&allTriaged) == 0 {
if *flagLeak {
kmemleakScan(false)
}
atomic.StoreUint32(&allTriaged, 1)
}
}
if len(r.NewInputs) == 0 && len(r.Candidates) == 0 {
lastPoll = time.Now()
}
@ -425,6 +439,12 @@ func execute1(pid int, env *ipc.Env, p *prog.Prog, stat *uint64) []cover.Cover {
retry:
atomic.AddUint64(stat, 1)
output, strace, rawCover, failed, hanged, err := env.Exec(p)
if failed {
// BUG in output should be recognized by manager.
logf(0, "BUG: executor-detected bug:\n%s", output)
// Don't return any cover so that the input is not added to corpus.
return make([]cover.Cover, len(p.Calls))
}
if err != nil {
if try > 10 {
panic(err)
@ -484,9 +504,55 @@ func (g *Gate) Leave(idx int) {
if !g.busy[idx] {
panic("broken gate")
}
if idx == 0 && *flagLeak && atomic.LoadUint32(&allTriaged) != 0 {
// Scan for leaks once in a while (it is damn slow).
kmemleakScan(true)
}
g.busy[idx] = false
if idx == g.pos {
g.cv.Broadcast()
}
g.cv.L.Unlock()
}
func kmemleakInit() {
fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
if err != nil {
if !*flagLeak {
panic(err)
}
}
defer syscall.Close(fd)
if _, err := syscall.Write(fd, []byte("scan=off")); err != nil {
panic(err)
}
}
var kmemleakBuf []byte
func kmemleakScan(report bool) {
fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
if err != nil {
panic(err)
}
defer syscall.Close(fd)
if _, err := syscall.Write(fd, []byte("scan")); err != nil {
panic(err)
}
if report {
if kmemleakBuf == nil {
kmemleakBuf = make([]byte, 128<<10)
}
n, err := syscall.Read(fd, kmemleakBuf)
if err != nil {
panic(err)
}
if n != 0 {
// BUG in output should be recognized by manager.
logf(0, "BUG: memory leak:\n%s\n", kmemleakBuf[:n])
}
}
if _, err := syscall.Write(fd, []byte("clear")); err != nil {
panic(err)
}
}

View File

@ -36,6 +36,7 @@ type Config struct {
Procs int // number of parallel processes inside of every VM
Port int
Nocover bool
Leak bool // do memory leak checking
Params map[string]interface{}
Enable_Syscalls []string
Disable_Syscalls []string
@ -64,6 +65,7 @@ func main() {
Params: params,
EnabledSyscalls: enabledSyscalls,
NoCover: cfg.Nocover,
Leak: cfg.Leak,
Procs: cfg.Procs,
}

View File

@ -384,8 +384,8 @@ var Calls = []*Call{
&Call{ID: 379, Name: "modify_ldt$write", CallName: "modify_ldt", Args: []Type{ConstType{TypeCommon: TypeCommon{TypeName: "func", IsOptional: false}, TypeSize: 0, Val: uintptr(1)}, PtrType{TypeCommon: TypeCommon{TypeName: "buf", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "user_desc", IsOptional: false}, Fields: []Type{IntType{TypeCommon: TypeCommon{TypeName: "entry", IsOptional: false}, TypeSize: 4}, VmaType{TypeCommon: TypeCommon{TypeName: "base", IsOptional: false}}, LenType{TypeCommon: TypeCommon{TypeName: "limit", IsOptional: false}, Buf: "base", TypeSize: 4}, IntType{TypeCommon: TypeCommon{TypeName: "flags", IsOptional: false}, TypeSize: 1}, ConstType{TypeCommon: TypeCommon{TypeName: "pad", IsOptional: false}, TypeSize: 1, Val: uintptr(0)}, ConstType{TypeCommon: TypeCommon{TypeName: "pad", IsOptional: false}, TypeSize: 2, Val: uintptr(0)}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "buf", TypeSize: 0}}},
&Call{ID: 380, Name: "modify_ldt$read_default", CallName: "modify_ldt", Args: []Type{ConstType{TypeCommon: TypeCommon{TypeName: "func", IsOptional: false}, TypeSize: 0, Val: uintptr(2)}, PtrType{TypeCommon: TypeCommon{TypeName: "buf", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "buf", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "buf", TypeSize: 0}}},
&Call{ID: 381, Name: "modify_ldt$write2", CallName: "modify_ldt", Args: []Type{ConstType{TypeCommon: TypeCommon{TypeName: "func", IsOptional: false}, TypeSize: 0, Val: uintptr(17)}, PtrType{TypeCommon: TypeCommon{TypeName: "buf", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "user_desc", IsOptional: false}, Fields: []Type{IntType{TypeCommon: TypeCommon{TypeName: "entry", IsOptional: false}, TypeSize: 4}, VmaType{TypeCommon: TypeCommon{TypeName: "base", IsOptional: false}}, LenType{TypeCommon: TypeCommon{TypeName: "limit", IsOptional: false}, Buf: "base", TypeSize: 4}, IntType{TypeCommon: TypeCommon{TypeName: "flags", IsOptional: false}, TypeSize: 1}, ConstType{TypeCommon: TypeCommon{TypeName: "pad", IsOptional: false}, TypeSize: 1, Val: uintptr(0)}, ConstType{TypeCommon: TypeCommon{TypeName: "pad", IsOptional: false}, TypeSize: 2, Val: uintptr(0)}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "buf", TypeSize: 0}}},
&Call{ID: 382, Name: "process_vm_readv", CallName: "process_vm_readv", Args: []Type{ResourceType{TypeCommon: TypeCommon{TypeName: "pid", IsOptional: false}, Kind: ResPid}, PtrType{TypeCommon: TypeCommon{TypeName: "loc_vec", IsOptional: false}, Type: ArrayType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "iovec_out", IsOptional: false}, Fields: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "addr", TypeSize: 8}}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "loc_vlen", IsOptional: false}, Buf: "vec", TypeSize: 0}, PtrType{TypeCommon: TypeCommon{TypeName: "rem_vec", IsOptional: false}, Type: ArrayType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "iovec_out", IsOptional: false}, Fields: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "addr", TypeSize: 8}}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "rem_vlen", IsOptional: false}, Buf: "vec", TypeSize: 0}, ConstType{TypeCommon: TypeCommon{TypeName: "flags", IsOptional: false}, TypeSize: 0, Val: uintptr(0)}}},
&Call{ID: 383, Name: "process_vm_writev", CallName: "process_vm_writev", Args: []Type{ResourceType{TypeCommon: TypeCommon{TypeName: "pid", IsOptional: false}, Kind: ResPid}, PtrType{TypeCommon: TypeCommon{TypeName: "loc_vec", IsOptional: false}, Type: ArrayType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "iovec_out", IsOptional: false}, Fields: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "addr", TypeSize: 8}}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "loc_vlen", IsOptional: false}, Buf: "vec", TypeSize: 0}, PtrType{TypeCommon: TypeCommon{TypeName: "rem_vec", IsOptional: false}, Type: ArrayType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "iovec_out", IsOptional: false}, Fields: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "addr", TypeSize: 8}}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "rem_vlen", IsOptional: false}, Buf: "vec", TypeSize: 0}, ConstType{TypeCommon: TypeCommon{TypeName: "flags", IsOptional: false}, TypeSize: 0, Val: uintptr(0)}}},
&Call{ID: 382, Name: "process_vm_readv", CallName: "process_vm_readv", Args: []Type{ResourceType{TypeCommon: TypeCommon{TypeName: "pid", IsOptional: false}, Kind: ResPid}, PtrType{TypeCommon: TypeCommon{TypeName: "loc_vec", IsOptional: false}, Type: ArrayType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "iovec_out", IsOptional: false}, Fields: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "addr", TypeSize: 8}}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "loc_vlen", IsOptional: false}, Buf: "loc_vec", TypeSize: 0}, PtrType{TypeCommon: TypeCommon{TypeName: "rem_vec", IsOptional: false}, Type: ArrayType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "iovec_out", IsOptional: false}, Fields: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "addr", TypeSize: 8}}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "rem_vlen", IsOptional: false}, Buf: "rem_vec", TypeSize: 0}, ConstType{TypeCommon: TypeCommon{TypeName: "flags", IsOptional: false}, TypeSize: 0, Val: uintptr(0)}}},
&Call{ID: 383, Name: "process_vm_writev", CallName: "process_vm_writev", Args: []Type{ResourceType{TypeCommon: TypeCommon{TypeName: "pid", IsOptional: false}, Kind: ResPid}, PtrType{TypeCommon: TypeCommon{TypeName: "loc_vec", IsOptional: false}, Type: ArrayType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "iovec_out", IsOptional: false}, Fields: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "addr", TypeSize: 8}}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "loc_vlen", IsOptional: false}, Buf: "loc_vec", TypeSize: 0}, PtrType{TypeCommon: TypeCommon{TypeName: "rem_vec", IsOptional: false}, Type: ArrayType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, Type: StructType{TypeCommon: TypeCommon{TypeName: "iovec_out", IsOptional: false}, Fields: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Dir: DirOut, Type: BufferType{TypeCommon: TypeCommon{TypeName: "addr", IsOptional: false}, Kind: BufferBlob}}, LenType{TypeCommon: TypeCommon{TypeName: "len", IsOptional: false}, Buf: "addr", TypeSize: 8}}}}, Dir: DirIn}, LenType{TypeCommon: TypeCommon{TypeName: "rem_vlen", IsOptional: false}, Buf: "rem_vec", TypeSize: 0}, ConstType{TypeCommon: TypeCommon{TypeName: "flags", IsOptional: false}, TypeSize: 0, Val: uintptr(0)}}},
&Call{ID: 384, Name: "set_tid_address", CallName: "set_tid_address", Args: []Type{PtrType{TypeCommon: TypeCommon{TypeName: "tidptr", IsOptional: false}, Type: IntType{TypeCommon: TypeCommon{TypeName: "", IsOptional: false}, TypeSize: 4}, Dir: DirOut}}},
&Call{ID: 385, Name: "getpriority", CallName: "getpriority", Args: []Type{FlagsType{TypeCommon: TypeCommon{TypeName: "which", IsOptional: false}, TypeSize: 0, Vals: []uintptr{0, 1, 2}}, ResourceType{TypeCommon: TypeCommon{TypeName: "who", IsOptional: false}, Kind: ResPid}}},
&Call{ID: 386, Name: "setpriority", CallName: "setpriority", Args: []Type{FlagsType{TypeCommon: TypeCommon{TypeName: "which", IsOptional: false}, TypeSize: 0, Vals: []uintptr{0, 1, 2}}, ResourceType{TypeCommon: TypeCommon{TypeName: "who", IsOptional: false}, Kind: ResPid}, IntType{TypeCommon: TypeCommon{TypeName: "prio", IsOptional: false}, TypeSize: 8}}},

View File

@ -272,8 +272,8 @@ func (inst *Instance) Run() {
inst.CreateSSHCommand("echo -n 0 > /proc/sys/debug/exception-trace").Wait(10 * time.Second)
// Run the binary.
cmd := inst.CreateSSHCommand(fmt.Sprintf("/syzkaller_fuzzer -name %v -executor /syzkaller_executor -manager %v:%v -procs %v %v",
inst.name, hostAddr, inst.cfg.ManagerPort, inst.cfg.Procs, inst.callsFlag))
cmd := inst.CreateSSHCommand(fmt.Sprintf("/syzkaller_fuzzer -name %v -executor /syzkaller_executor -manager %v:%v -procs %v -leak=%v %v",
inst.name, hostAddr, inst.cfg.ManagerPort, inst.cfg.Procs, inst.cfg.Leak, inst.callsFlag))
deadline := start.Add(time.Hour)
lastOutput := time.Now()

View File

@ -19,6 +19,7 @@ type Config struct {
EnabledSyscalls string
Suppressions []*regexp.Regexp
NoCover bool
Leak bool
Procs int
}