aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorShankara Pailoor <shankarapailoor@gmail.com>2019-01-15 14:32:24 -0800
committerDmitry Vyukov <dvyukov@google.com>2019-01-16 16:34:17 +0100
commit505b1767d09d0cec51e1ebe090afb71c61eba1aa (patch)
tree7a5bc0198a15d7954c753c1669ca4d0d6689168b /tools
parentd538790b533961dc27ea57611e326f857747a4ee (diff)
tools/syz-trace2syz/proggen: add support for open, openat, syz_open_dev variants
Add support to accurately select variants for open and openat system calls. This is needed because in order to accurately select ioctl variants we need to use device resource types. The device resource types can only be created by syz_open_dev and openat variants.
Diffstat (limited to 'tools')
-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 {