aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2018-06-29 20:34:43 +0200
committerDmitry Vyukov <dvyukov@google.com>2018-06-29 20:34:43 +0200
commitf7498af7af98d89cb5dc49c872213a3a5ef8268a (patch)
tree32abb56358551c45dbce9a4c865781009fddd122
parent0c4b1960d01985c2d8d6da62e8c82395b70838c8 (diff)
prog: add stronger fallback signal
Also mixin resource constructors and some signature of flags values for successful calls into fallback coverage.
-rw-r--r--pkg/ipc/ipc.go15
-rw-r--r--pkg/signal/signal.go8
-rw-r--r--prog/analysis.go105
-rw-r--r--syz-manager/html.go43
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(`
<table>
<tr>
<th>Call</th>
- <th>Succeeded</th>
+ <th>Successful</th>
<th>Errnos</th>
</tr>
{{range $c := $.Calls}}
<tr>
<td>{{$c.Name}}</td>
- <td>{{if $c.OK}}YES{{end}}</td>
- <td>
- {{range $e := $c.Errnos}}{{$e}}&nbsp;{{end}}
- </td>
+ <td>{{if $c.Successful}}{{$c.Successful}}{{end}}</td>
+ <td>{{range $e := $c.Errnos}}{{$e}}&nbsp;{{end}}</td>
</tr>
{{end}}
</table>