diff options
| author | Andrey Konovalov <andreyknvl@google.com> | 2018-11-22 19:04:06 +0100 |
|---|---|---|
| committer | Andrey Konovalov <andreyknvl@gmail.com> | 2019-01-16 19:19:53 +0100 |
| commit | b5df78dc5d994bc61f1ecee2c5c85313178f392e (patch) | |
| tree | c285f3be9e8d0ef32e607186ec9ce9eae6901cce /pkg | |
| parent | c0d4a12ee72a2279eada43d9476d2f8a074c3818 (diff) | |
all: support extra coverage
Right now syzkaller only supports coverage collected from the threads that
execute syscalls. However some useful things happen in background threads,
and it would be nice to collect coverage from those threads as well.
This change adds extra coverage support to syzkaller. This coverage is not
associated with a particular syscall, but rather with the whole program.
Executor passes extra coverage over the same ipc mechanism to syz-fuzzer
with syscall number set to -1. syz-fuzzer then passes this coverage to
syz-manager with the call name "extra".
This change requires the following kcov patch:
https://github.com/xairy/linux/pull/2
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/csource/generated.go | 7 | ||||
| -rw-r--r-- | pkg/ipc/ipc.go | 57 | ||||
| -rw-r--r-- | pkg/runtest/run.go | 4 |
3 files changed, 54 insertions, 14 deletions
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index cf1c9784a..1d7e6253f 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -3589,7 +3589,12 @@ static void sandbox_common() #endif struct rlimit rlim; - rlim.rlim_cur = rlim.rlim_max = 200 << 20; +#if SYZ_EXECUTOR + rlim.rlim_cur = rlim.rlim_max = (200 << 20) + + (kMaxThreads * kCoverSize + kExtraCoverSize) * sizeof(void*); +#else + rlim.rlim_cur = rlim.rlim_max = (200 << 20); +#endif setrlimit(RLIMIT_AS, &rlim); rlim.rlim_cur = rlim.rlim_max = 32 << 20; setrlimit(RLIMIT_MEMLOCK, &rlim); diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index 6c4538faf..b3799ebfb 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -15,7 +15,9 @@ import ( "time" "unsafe" + "github.com/google/syzkaller/pkg/cover" "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/pkg/signal" "github.com/google/syzkaller/prog" ) @@ -95,7 +97,7 @@ type CallInfo struct { type ProgInfo struct { Calls []CallInfo - // TODO: remote coverage would go here. + Extra CallInfo // stores Signal and Cover collected from background threads } type Env struct { @@ -125,6 +127,8 @@ const ( compSizeMask = 6 compSize8 = 6 compConstMask = 1 + + extraReplyIndex = 0xffffffff // uint32(-1) ) func SandboxToFlags(sandbox string) (EnvFlags, error) { @@ -325,24 +329,31 @@ func (env *Env) parseOutput(p *prog.Prog) (*ProgInfo, error) { return nil, fmt.Errorf("failed to read number of calls") } info := &ProgInfo{Calls: make([]CallInfo, len(p.Calls))} + extraParts := make([]CallInfo, 0) for i := uint32(0); i < ncmd; i++ { if len(out) < int(unsafe.Sizeof(callReply{})) { return nil, fmt.Errorf("failed to read call %v reply", i) } reply := *(*callReply)(unsafe.Pointer(&out[0])) out = out[unsafe.Sizeof(callReply{}):] - if int(reply.index) >= len(info.Calls) { - return nil, fmt.Errorf("bad call %v index %v/%v", i, reply.index, len(info.Calls)) - } - if num := p.Calls[reply.index].Meta.ID; int(reply.num) != num { - return nil, fmt.Errorf("wrong call %v num %v/%v", i, reply.num, num) - } - inf := &info.Calls[reply.index] - if inf.Flags != 0 || inf.Signal != nil { - return nil, fmt.Errorf("duplicate reply for call %v/%v/%v", i, reply.index, reply.num) + var inf *CallInfo + if reply.index != extraReplyIndex { + if int(reply.index) >= len(info.Calls) { + return nil, fmt.Errorf("bad call %v index %v/%v", i, reply.index, len(info.Calls)) + } + if num := p.Calls[reply.index].Meta.ID; int(reply.num) != num { + return nil, fmt.Errorf("wrong call %v num %v/%v", i, reply.num, num) + } + inf = &info.Calls[reply.index] + if inf.Flags != 0 || inf.Signal != nil { + return nil, fmt.Errorf("duplicate reply for call %v/%v/%v", i, reply.index, reply.num) + } + inf.Errno = int(reply.errno) + inf.Flags = CallFlags(reply.flags) + } else { + extraParts = append(extraParts, CallInfo{}) + inf = &extraParts[len(extraParts)-1] } - inf.Errno = int(reply.errno) - inf.Flags = CallFlags(reply.flags) if inf.Signal, ok = readUint32Array(&out, reply.signalSize); !ok { return nil, fmt.Errorf("call %v/%v/%v: signal overflow: %v/%v", i, reply.index, reply.num, reply.signalSize, len(out)) @@ -357,9 +368,31 @@ func (env *Env) parseOutput(p *prog.Prog) (*ProgInfo, error) { } inf.Comps = comps } + if len(extraParts) == 0 { + return info, nil + } + info.Extra = convertExtra(extraParts) return info, nil } +func convertExtra(extraParts []CallInfo) CallInfo { + var extra CallInfo + extraCover := make(cover.Cover) + extraSignal := make(signal.Signal) + for _, part := range extraParts { + extraCover.Merge(part.Cover) + extraSignal.Merge(signal.FromRaw(part.Signal, 0)) + } + extra.Cover = extraCover.Serialize() + extra.Signal = make([]uint32, len(extraSignal)) + i := 0 + for s := range extraSignal { + extra.Signal[i] = uint32(s) + i++ + } + return extra +} + func readComps(outp *[]byte, compsSize uint32) (prog.CompMap, error) { if compsSize == 0 { return nil, nil diff --git a/pkg/runtest/run.go b/pkg/runtest/run.go index efcbe3066..cac5f7334 100644 --- a/pkg/runtest/run.go +++ b/pkg/runtest/run.go @@ -519,11 +519,13 @@ func RunTest(req *RunRequest, executor string) { req.Err = fmt.Errorf("run %v: hanged", run) return } + // Detach Signal and Cover because they point into the output shmem region. for i := range info.Calls { - // Detach them because they point into the output shmem region. info.Calls[i].Signal = append([]uint32{}, info.Calls[i].Signal...) info.Calls[i].Cover = append([]uint32{}, info.Calls[i].Cover...) } + info.Extra.Signal = append([]uint32{}, info.Extra.Signal...) + info.Extra.Cover = append([]uint32{}, info.Extra.Cover...) req.Info = append(req.Info, info) } } |
