From f7498af7af98d89cb5dc49c872213a3a5ef8268a Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 29 Jun 2018 20:34:43 +0200 Subject: prog: add stronger fallback signal Also mixin resource constructors and some signature of flags values for successful calls into fallback coverage. --- pkg/ipc/ipc.go | 15 ++++---- pkg/signal/signal.go | 8 ---- prog/analysis.go | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++ syz-manager/html.go | 43 +++++++++++---------- 4 files changed, 134 insertions(+), 37 deletions(-) diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index 5ab5e26a7..65e883acf 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -19,7 +19,6 @@ import ( "unsafe" "github.com/google/syzkaller/pkg/osutil" - "github.com/google/syzkaller/pkg/signal" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys/targets" ) @@ -329,12 +328,14 @@ func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallIn // We use syscall number or-ed with returned errno value as signal. // At least this gives us all combinations of syscall+errno. func addFallbackSignal(p *prog.Prog, info []CallInfo) { - for i, call := range p.Calls { - inf := &info[i] - if !inf.Executed || len(inf.Signal) != 0 { - continue - } - inf.Signal = []uint32{signal.EncodeFallback(call.Meta.ID, inf.Errno)} + callInfos := make([]prog.CallInfo, len(info)) + for i, inf := range info { + callInfos[i].Executed = inf.Executed + callInfos[i].Errno = inf.Errno + } + p.FallbackSignal(callInfos) + for i, inf := range callInfos { + info[i].Signal = inf.Signal } } diff --git a/pkg/signal/signal.go b/pkg/signal/signal.go index 9af1bcce1..20deba46a 100644 --- a/pkg/signal/signal.go +++ b/pkg/signal/signal.go @@ -191,11 +191,3 @@ func Minimize(corpus []Context) []interface{} { } return result } - -func EncodeFallback(id, errno int) uint32 { - return uint32(id)<<16 | uint32(errno)&0x3ff -} - -func DecodeFallback(s uint32) (int, int) { - return int(s >> 16), int(s & 0x3ff) -} diff --git a/prog/analysis.go b/prog/analysis.go index a7ef720f7..bb57ec463 100644 --- a/prog/analysis.go +++ b/prog/analysis.go @@ -9,6 +9,9 @@ package prog import ( + "bytes" + "crypto/sha1" + "encoding/binary" "fmt" ) @@ -170,3 +173,105 @@ func RequiredFeatures(p *Prog) (bitmasks, csums bool) { } return } + +type CallInfo struct { + Executed bool + Errno int + Signal []uint32 +} + +const ( + fallbackSignalErrno = iota + fallbackSignalCtor + fallbackSignalFlags + fallbackCallMask = 0x3fff +) + +func (p *Prog) FallbackSignal(info []CallInfo) { + resources := make(map[*ResultArg]*Call) + for i, c := range p.Calls { + inf := &info[i] + if !inf.Executed { + continue + } + id := c.Meta.ID + inf.Signal = append(inf.Signal, encodeFallbackSignal(fallbackSignalErrno, id, inf.Errno)) + if inf.Errno != 0 { + continue + } + ForeachArg(c, func(arg Arg, _ *ArgCtx) { + if a, ok := arg.(*ResultArg); ok { + resources[a] = c + } + }) + // Specifically look only at top-level arguments, + // deeper arguments can produce too much false signal. + flags := new(bytes.Buffer) + for _, arg := range c.Args { + switch a := arg.(type) { + case *ResultArg: + if a.Res != nil { + ctor := resources[a.Res] + if ctor != nil { + inf.Signal = append(inf.Signal, + encodeFallbackSignal(fallbackSignalCtor, id, ctor.Meta.ID)) + } + } else { + for _, v := range a.Type().(*ResourceType).SpecialValues() { + if a.Val == v { + binary.Write(flags, binary.LittleEndian, v) + } + } + } + case *ConstArg: + switch typ := a.Type().(type) { + case *FlagsType: + var mask uint64 + for _, v := range typ.Vals { + mask |= v + } + binary.Write(flags, binary.LittleEndian, a.Val&mask) + case *LenType: + if a.Val == 0 { + flags.WriteByte(0x42) + } + } + case *PointerArg: + if a.IsNull() { + flags.WriteByte(0x43) + } + } + if flags.Len() != 0 { + hash := sha1.Sum(flags.Bytes()) + inf.Signal = append(inf.Signal, + encodeFallbackSignal(fallbackSignalFlags, id, int(hash[0]))) + } + } + } +} + +func DecodeFallbackSignal(s uint32) (callID, errno int) { + typ, id, aux := decodeFallbackSignal(s) + switch typ { + case fallbackSignalErrno: + return id, aux + case fallbackSignalCtor, fallbackSignalFlags: + return id, 0 + default: + panic(fmt.Sprintf("bad fallback signal type %v", typ)) + } +} + +func encodeFallbackSignal(typ, id, aux int) uint32 { + if typ & ^3 != 0 { + panic(fmt.Sprintf("bad fallback signal type %v", typ)) + } + if id & ^fallbackCallMask != 0 { + panic(fmt.Sprintf("bad call id in fallback signal %v", id)) + } + return uint32(typ) | uint32(id&0x3fff)<<2 | uint32(aux)<<16 +} + +func decodeFallbackSignal(s uint32) (typ, id, aux int) { + return int(s & 3), int((s >> 2) & fallbackCallMask), int(s >> 16) +} diff --git a/syz-manager/html.go b/syz-manager/html.go index 6e4e28054..df4946ac6 100644 --- a/syz-manager/html.go +++ b/syz-manager/html.go @@ -23,7 +23,7 @@ import ( "github.com/google/syzkaller/pkg/cover" "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/osutil" - "github.com/google/syzkaller/pkg/signal" + "github.com/google/syzkaller/prog" ) const dateFormat = "Jan 02 2006 15:04:05 MST" @@ -203,6 +203,10 @@ func (mgr *Manager) httpCover(w http.ResponseWriter, r *http.Request) { mgr.mu.Lock() defer mgr.mu.Unlock() + if mgr.checkResult == nil { + http.Error(w, fmt.Sprintf("machine is not checked yet"), http.StatusInternalServerError) + return + } if mgr.cfg.Cover { mgr.httpCoverCover(w, r) } else { @@ -236,26 +240,23 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request) { func (mgr *Manager) httpCoverFallback(w http.ResponseWriter, r *http.Request) { calls := make(map[int][]int) - for s, _ := range mgr.maxSignal { - id, errno := signal.DecodeFallback(uint32(s)) + for s := range mgr.maxSignal { + id, errno := prog.DecodeFallbackSignal(uint32(s)) calls[id] = append(calls[id], errno) } data := &UIFallbackCoverData{} - for id, errnos := range calls { - if id < 0 || id >= len(mgr.target.Syscalls) { - http.Error(w, fmt.Sprintf("bad call ID %v", id), http.StatusInternalServerError) - return - } + for _, id := range mgr.checkResult.EnabledCalls { + errnos := calls[id] sort.Ints(errnos) - ok := false - if errnos[0] == 0 { - ok = true + successful := 0 + for len(errnos) != 0 && errnos[0] == 0 { + successful++ errnos = errnos[1:] } data.Calls = append(data.Calls, UIFallbackCall{ - Name: mgr.target.Syscalls[id].Name, - Errnos: errnos, - OK: ok, + Name: mgr.target.Syscalls[id].Name, + Successful: successful, + Errnos: errnos, }) } sort.Slice(data.Calls, func(i, j int) bool { @@ -761,9 +762,9 @@ type UIFallbackCoverData struct { } type UIFallbackCall struct { - Name string - OK bool - Errnos []int + Name string + Successful int + Errnos []int } var fallbackCoverTemplate = template.Must(template.New("").Parse(addStyle(` @@ -777,16 +778,14 @@ var fallbackCoverTemplate = template.Must(template.New("").Parse(addStyle(` - + {{range $c := $.Calls}} - - + + {{end}}
CallSucceededSuccessful Errnos
{{$c.Name}}{{if $c.OK}}YES{{end}} - {{range $e := $c.Errnos}}{{$e}} {{end}} - {{if $c.Successful}}{{$c.Successful}}{{end}}{{range $e := $c.Errnos}}{{$e}} {{end}}
-- cgit mrf-deployment