aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/syz-trace2syz/proggen/call_selector.go157
-rw-r--r--tools/syz-trace2syz/proggen/call_selector_test.go42
-rw-r--r--tools/syz-trace2syz/proggen/proggen.go21
-rw-r--r--tools/syz-trace2syz/proggen/proggen_test.go28
4 files changed, 224 insertions, 24 deletions
diff --git a/tools/syz-trace2syz/proggen/call_selector.go b/tools/syz-trace2syz/proggen/call_selector.go
index 00f2e3ea9..989cbe6bd 100644
--- a/tools/syz-trace2syz/proggen/call_selector.go
+++ b/tools/syz-trace2syz/proggen/call_selector.go
@@ -1,8 +1,11 @@
package proggen
import (
+ "bytes"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/tools/syz-trace2syz/parser"
+ "strconv"
+ "unicode"
)
var discriminatorArgs = map[string][]int{
@@ -22,40 +25,70 @@ var discriminatorArgs = map[string][]int{
"sendto": {0},
"sendmsg": {0},
"getsockname": {0},
+ "openat": {1},
}
-type callSelector struct {
+var openDiscriminatorArgs = map[string]int{
+ "open": 0,
+ "openat": 1,
+ "syz_open_dev": 0,
+}
+
+type callSelector interface {
+ Select(call *parser.Syscall) *prog.Syscall
+}
+
+func newSelectors(target *prog.Target, returnCache returnCache) []callSelector {
+ sc := newSelectorCommon(target, returnCache)
+ return []callSelector{
+ &defaultCallSelector{sc},
+ &openCallSelector{sc},
+ }
+}
+
+type selectorCommon struct {
target *prog.Target
returnCache returnCache
callCache map[string][]*prog.Syscall
}
-func newCallSelector(target *prog.Target, returnCache returnCache) *callSelector {
- return &callSelector{
+func newSelectorCommon(target *prog.Target, returnCache returnCache) *selectorCommon {
+ return &selectorCommon{
target: target,
returnCache: returnCache,
callCache: make(map[string][]*prog.Syscall),
}
}
-// Select returns the best matching descrimination for this syscall.
-func (cs *callSelector) Select(call *parser.Syscall) *prog.Syscall {
- match := cs.target.SyscallMap[call.CallName]
- discriminators := discriminatorArgs[call.CallName]
- if len(discriminators) == 0 {
- return match
+// matches strace file string with a constant string in openat or syz_open_dev
+// if the string in openat or syz_open_dev has a # then this method will
+// return the corresponding id from the strace string
+func (cs *selectorCommon) matchFilename(syzFile, straceFile []byte) (bool, int) {
+ syzFile = bytes.Trim(syzFile, "\x00")
+ straceFile = bytes.Trim(straceFile, "\x00")
+ if len(syzFile) != len(straceFile) {
+ return false, -1
}
- score := 0
- for _, meta := range cs.callSet(call.CallName) {
- if score1 := cs.matchCall(meta, call, discriminators); score1 > score {
- match, score = meta, score1
+ var id []byte
+ dev := -1
+ for i, c := range syzFile {
+ x := straceFile[i]
+ if c == x {
+ continue
}
+ if c != '#' || !unicode.IsDigit(rune(x)) {
+ return false, -1
+ }
+ id = append(id, x)
}
- return match
+ if len(id) > 0 {
+ dev, _ = strconv.Atoi(string(id))
+ }
+ return true, dev
}
// callSet returns all syscalls with the given name.
-func (cs *callSelector) callSet(callName string) []*prog.Syscall {
+func (cs *selectorCommon) callSet(callName string) []*prog.Syscall {
calls, ok := cs.callCache[callName]
if ok {
return calls
@@ -69,9 +102,79 @@ func (cs *callSelector) callSet(callName string) []*prog.Syscall {
return calls
}
+type openCallSelector struct {
+ *selectorCommon
+}
+
+// Select returns the best matching descrimination for this syscall.
+func (cs *openCallSelector) Select(call *parser.Syscall) *prog.Syscall {
+ if _, ok := openDiscriminatorArgs[call.CallName]; !ok {
+ return nil
+ }
+ for callName := range openDiscriminatorArgs {
+ for _, variant := range cs.callSet(callName) {
+ match, devID := cs.matchOpen(variant, call)
+ if !match {
+ continue
+ }
+ if call.CallName == "open" && callName == "openat" {
+ cwd := parser.Constant(cs.target.ConstMap["AT_FDCWD"])
+ call.Args = append([]parser.IrType{cwd}, call.Args...)
+ return variant
+ }
+ if match && call.CallName == "open" && callName == "syz_open_dev" {
+ if devID < 0 {
+ return variant
+ }
+ args := []parser.IrType{call.Args[0], parser.Constant(uint64(devID))}
+ call.Args = append(args, call.Args[1:]...)
+ return variant
+ }
+ }
+ }
+ return nil
+}
+
+func (cs *openCallSelector) matchOpen(meta *prog.Syscall, call *parser.Syscall) (bool, int) {
+ straceFileArg := call.Args[openDiscriminatorArgs[call.CallName]]
+ syzFileArg := meta.Args[openDiscriminatorArgs[meta.CallName]]
+ straceBuf := straceFileArg.(*parser.BufferType).Val
+ syzBuf := syzFileArg.(*prog.PtrType).Type.(*prog.BufferType)
+ if syzBuf.Kind != prog.BufferString {
+ return false, -1
+ }
+ for _, val := range syzBuf.Values {
+ match, devID := cs.matchFilename([]byte(val), []byte(straceBuf))
+ if match {
+ return match, devID
+ }
+ }
+ return false, -1
+}
+
+type defaultCallSelector struct {
+ *selectorCommon
+}
+
+// Select returns the best matching descrimination for this syscall.
+func (cs *defaultCallSelector) Select(call *parser.Syscall) *prog.Syscall {
+ var match *prog.Syscall
+ discriminators := discriminatorArgs[call.CallName]
+ if len(discriminators) == 0 {
+ return nil
+ }
+ score := 0
+ for _, meta := range cs.callSet(call.CallName) {
+ if score1 := cs.matchCall(meta, call, discriminators); score1 > score {
+ match, score = meta, score1
+ }
+ }
+ return match
+}
+
// matchCall returns match score between meta and call.
// Higher score means better match, -1 if they are not matching at all.
-func (cs *callSelector) matchCall(meta *prog.Syscall, call *parser.Syscall, discriminators []int) int {
+func (cs *defaultCallSelector) matchCall(meta *prog.Syscall, call *parser.Syscall, discriminators []int) int {
score := 0
for _, i := range discriminators {
if i >= len(meta.Args) || i >= len(call.Args) {
@@ -118,6 +221,28 @@ func (cs *callSelector) matchCall(meta *prog.Syscall, call *parser.Syscall, disc
if !matched {
return -1
}
+ case *prog.PtrType:
+ switch r := t.Type.(type) {
+ case *prog.BufferType:
+ matched := false
+ buffer, ok := arg.(*parser.BufferType)
+ if !ok {
+ return -1
+ }
+ if r.Kind != prog.BufferString {
+ return -1
+ }
+ for _, val := range r.Values {
+ matched, _ = cs.matchFilename([]byte(val), []byte(buffer.Val))
+ if matched {
+ score++
+ break
+ }
+ }
+ if !matched {
+ return -1
+ }
+ }
}
}
return score
diff --git a/tools/syz-trace2syz/proggen/call_selector_test.go b/tools/syz-trace2syz/proggen/call_selector_test.go
new file mode 100644
index 000000000..ca00d8362
--- /dev/null
+++ b/tools/syz-trace2syz/proggen/call_selector_test.go
@@ -0,0 +1,42 @@
+// Copyright 2019 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package proggen
+
+import "testing"
+
+func TestMatchFilename(t *testing.T) {
+ sc := selectorCommon{}
+ type Test struct {
+ file1 string
+ file2 string
+ devID int
+ match bool
+ }
+ tests := []Test{
+ {
+ "/dev/zero", "/dev/zero", -1, true,
+ }, {
+ "/dev/loop#", "/dev/loop1", 1, true,
+ }, {
+ "", "a", -1, false,
+ }, {
+ "/dev/loop#/loop", "/dev/loop0/looq", -1, false,
+ }, {
+ "/dev/i2c-#\x00", "/dev/i2c-1", 1, true,
+ }, {
+ "/dev/some#/some#", "/dev/some1/some1", 11, true,
+ }, {
+ "/dev/some/some#", "/dev/some", -1, false,
+ }, {
+ "/dev/some", "/dev/some/some", -1, false,
+ },
+ }
+ for _, test := range tests {
+ match, devID := sc.matchFilename([]byte(test.file1), []byte(test.file2))
+ if test.match != match || test.devID != devID {
+ t.Errorf("failed to match %s and %s\nexpected: %t, %d\n\ngot: %t, %d\n",
+ test.file1, test.file2, test.match, test.devID, match, devID)
+ }
+ }
+}
diff --git a/tools/syz-trace2syz/proggen/proggen.go b/tools/syz-trace2syz/proggen/proggen.go
index f1adf43c3..6d9ac48f5 100644
--- a/tools/syz-trace2syz/proggen/proggen.go
+++ b/tools/syz-trace2syz/proggen/proggen.go
@@ -53,7 +53,7 @@ func parseTree(tree *parser.TraceTree, pid int64, target *prog.Target, progs *[]
type context struct {
builder *prog.Builder
target *prog.Target
- callSelector *callSelector
+ selectors []callSelector
returnCache returnCache
currentStraceCall *parser.Syscall
currentSyzCall *prog.Call
@@ -63,10 +63,10 @@ type context struct {
func genProg(trace *parser.Trace, target *prog.Target) *prog.Prog {
retCache := newRCache()
ctx := &context{
- builder: prog.MakeProgGen(target),
- target: target,
- callSelector: newCallSelector(target, retCache),
- returnCache: retCache,
+ builder: prog.MakeProgGen(target),
+ target: target,
+ selectors: newSelectors(target, retCache),
+ returnCache: retCache,
}
for _, sCall := range trace.Calls {
if sCall.Paused {
@@ -100,7 +100,7 @@ func (ctx *context) genCall() *prog.Call {
log.Logf(3, "parsing call: %s", ctx.currentStraceCall.CallName)
straceCall := ctx.currentStraceCall
ctx.currentSyzCall = new(prog.Call)
- ctx.currentSyzCall.Meta = ctx.callSelector.Select(straceCall)
+ ctx.currentSyzCall.Meta = ctx.Select(straceCall)
syzCall := ctx.currentSyzCall
if ctx.currentSyzCall.Meta == nil {
log.Logf(2, "skipping call: %s which has no matching description", ctx.currentStraceCall.CallName)
@@ -120,6 +120,15 @@ func (ctx *context) genCall() *prog.Call {
return syzCall
}
+func (ctx *context) Select(syscall *parser.Syscall) *prog.Syscall {
+ for _, selector := range ctx.selectors {
+ if variant := selector.Select(syscall); variant != nil {
+ return variant
+ }
+ }
+ return ctx.target.SyscallMap[syscall.CallName]
+}
+
func (ctx *context) genResult(syzType prog.Type, straceRet int64) {
if straceRet <= 0 {
return
diff --git a/tools/syz-trace2syz/proggen/proggen_test.go b/tools/syz-trace2syz/proggen/proggen_test.go
index 5074bd194..f60495d2c 100644
--- a/tools/syz-trace2syz/proggen/proggen_test.go
+++ b/tools/syz-trace2syz/proggen/proggen_test.go
@@ -160,7 +160,7 @@ connect$inet(r0, &(0x7f0000000000)={0x2, 0x4594}, 0x10)
open("\x2f\x64\x65\x76\x2f\x73\x6e\x64\x2f\x73\x65\x71", 0) = 3
fsetxattr(3, "\x73\x65\x63\x75\x72\x69\x74\x79\x2e\x73\x65\x6c\x69\x6e\x75\x78","\x73\x79\x73", 4, 0) = 0
`, `
-r0 = open(&(0x7f0000000000)='/dev/snd/seq\x00', 0x0, 0x0)
+r0 = syz_open_dev$sndseq(&(0x7f0000000000)='/dev/snd/seq\x00', 0x0, 0x0)
fsetxattr(r0, &(0x7f0000000040)=@known='security.selinux\x00', &(0x7f0000000080)='sys\x00', 0x4, 0x0)
`,
}, {`
@@ -220,7 +220,31 @@ sendto(3, "", 0, 0, {sa_family=0xa, sin6_port="\x4e\x24", sin6_flowinfo="\x00\x0
r0 = socket$inet6_udp(0xa, 0x2, 0x0)
sendto$inet6(r0, &(0x7f0000000000), 0x0, 0x0, &(0x7f0000000040)={0xa, 0x4e24}, 0x1c)
`,
- },
+ }, {`
+open("\x2f\x64\x65\x76\x2f\x7a\x65\x72\x6f", "1") = 3
+`, `
+openat$zero(0xffffffffffffff9c, &(0x7f0000000000)='/dev/zero\x00', 0x31, 0x0)
+`,
+ }, {`
+open("\x2f\x64\x65\x76\x2f\x6c\x6f\x6f\x70\x30", 0) = 3
+`, `
+syz_open_dev$loop(&(0x7f0000000000)='/dev/loop0\x00', 0x0, 0x0)
+`,
+ }, {`
+open("\x2f\x64\x65\x76\x2f\x6c\x6f\x6f\x70\x31", 0) = 3
+`, `
+syz_open_dev$loop(&(0x7f0000000000)='/dev/loop1\x00', 0x1, 0x0)
+`,
+ }, {`
+open("\x2f\x64\x65\x76\x2f\x62\x75\x73\x2f\x75\x73\x62\x2f\x30\x30\x31\x2f\x30\x30\x31", 0) = 3
+`, `
+syz_open_dev$usb(&(0x7f0000000000)='/dev/bus/usb/001/001\x00', 0xb, 0x0)
+`,
+ }, {`
+openat(0xffffffffffffff9c, "\x2f\x64\x65\x76\x2f\x7a\x65\x72\x6f", 0x31, 0) = 3
+`, `
+openat$zero(0xffffffffffffff9c, &(0x7f0000000000)='/dev/zero\x00', 0x31, 0x0)
+`},
}
target, err := prog.GetTarget("linux", "amd64")
if err != nil {