From ffe7e17368d7ae6c2b40da2ce0703d8ad8a116ac Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 5 Sep 2017 13:31:14 +0200 Subject: prog, sys: move types to prog Large overhaul moves syscalls and arg types from sys to prog. Sys package now depends on prog and contains only generated descriptions of syscalls. Introduce prog.Target type that encapsulates all targer properties, like syscall list, ptr/page size, etc. Also moves OS-dependent pieces like mmap call generation from prog to sys. Update #191 --- pkg/compiler/check.go | 22 +- pkg/compiler/compiler.go | 26 +- pkg/compiler/gen.go | 112 ++++---- pkg/compiler/types.go | 172 ++++++------ pkg/csource/csource.go | 4 +- pkg/csource/csource_test.go | 1 + pkg/host/host.go | 32 +-- pkg/host/host_test.go | 11 +- pkg/ipc/ipc_test.go | 1 + prog/analysis.go | 124 +-------- prog/checksum.go | 2 - prog/checksum_test.go | 11 +- prog/decl_test.go | 62 +++++ prog/encoding.go | 24 +- prog/encoding_test.go | 7 +- prog/encodingexec.go | 5 - prog/encodingexec_test.go | 13 +- prog/export_test.go | 47 ++++ prog/hints.go | 6 +- prog/hints_test.go | 2 - prog/mutation.go | 25 +- prog/mutation_test.go | 27 +- prog/prio.go | 2 - prog/prog.go | 26 +- prog/prog_test.go | 35 +-- prog/rand.go | 141 ++-------- prog/size.go | 14 +- prog/size_test.go | 13 +- prog/target.go | 117 +++++++++ prog/types.go | 457 ++++++++++++++++++++++++++++++++ prog/validation.go | 2 - sys/decl.go | 522 ------------------------------------- sys/decl_test.go | 62 ----- sys/init.go | 257 ++++++++++++++++++ sys/sys_386.go | 6 +- sys/sys_amd64.go | 6 +- sys/sys_arm.go | 6 +- sys/sys_arm64.go | 6 +- sys/sys_ppc64le.go | 6 +- sys/syz-sysgen/syscallnr.go | 4 +- sys/syz-sysgen/sysgen.go | 5 +- syz-fuzzer/fuzzer.go | 16 +- syz-manager/html.go | 5 +- syz-manager/manager.go | 1 + syz-manager/mgrconfig/mgrconfig.go | 13 +- tools/syz-execprog/execprog.go | 1 + tools/syz-mutate/mutate.go | 1 + tools/syz-stress/stress.go | 12 +- tools/syz-upgrade/upgrade.go | 1 + 49 files changed, 1306 insertions(+), 1167 deletions(-) create mode 100644 prog/decl_test.go create mode 100644 prog/export_test.go create mode 100644 prog/target.go create mode 100644 prog/types.go delete mode 100644 sys/decl.go delete mode 100644 sys/decl_test.go create mode 100644 sys/init.go diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go index 7b0cf34aa..99ac1d447 100644 --- a/pkg/compiler/check.go +++ b/pkg/compiler/check.go @@ -9,7 +9,7 @@ import ( "fmt" "github.com/google/syzkaller/pkg/ast" - "github.com/google/syzkaller/sys" + "github.com/google/syzkaller/prog" ) func (comp *compiler) check() { @@ -191,7 +191,7 @@ func (comp *compiler) checkLenType(t *ast.Type, name string, fields []*ast.Field } return } - _, args, _ := comp.getArgsBase(t, "", sys.DirIn, isArg) + _, args, _ := comp.getArgsBase(t, "", prog.DirIn, isArg) for i, arg := range args { argDesc := desc.Args[i] if argDesc.Type == typeArgLenTarget { @@ -232,7 +232,7 @@ func (comp *compiler) checkLenTarget(t *ast.Type, name, target string, fields [] type structDir struct { Struct string - Dir sys.Dir + Dir prog.Dir } func (comp *compiler) checkConstructors() { @@ -242,10 +242,10 @@ func (comp *compiler) checkConstructors() { switch n := decl.(type) { case *ast.Call: for _, arg := range n.Args { - comp.checkTypeCtors(arg.Type, sys.DirIn, true, ctors, checked) + comp.checkTypeCtors(arg.Type, prog.DirIn, true, ctors, checked) } if n.Ret != nil { - comp.checkTypeCtors(n.Ret, sys.DirOut, true, ctors, checked) + comp.checkTypeCtors(n.Ret, prog.DirOut, true, ctors, checked) } } } @@ -261,16 +261,16 @@ func (comp *compiler) checkConstructors() { } } -func (comp *compiler) checkTypeCtors(t *ast.Type, dir sys.Dir, isArg bool, +func (comp *compiler) checkTypeCtors(t *ast.Type, dir prog.Dir, isArg bool, ctors map[string]bool, checked map[structDir]bool) { desc := comp.getTypeDesc(t) if desc == typeResource { - // TODO(dvyukov): consider changing this to "dir == sys.DirOut". + // TODO(dvyukov): consider changing this to "dir == prog.DirOut". // We have few questionable cases where resources can be created // only by inout struct fields. These structs should be split // into two different structs: one is in and second is out. // But that will require attaching dir to individual fields. - if dir != sys.DirIn { + if dir != prog.DirIn { r := comp.resources[t.Ident] for r != nil && !ctors[r.Name.Name] { ctors[r.Name.Name] = true @@ -376,7 +376,7 @@ func (comp *compiler) recurseField(checked map[string]bool, t *ast.Type, path [] comp.checkStructRecursion(checked, comp.structs[t.Ident], path) return } - _, args, base := comp.getArgsBase(t, "", sys.DirIn, false) + _, args, base := comp.getArgsBase(t, "", prog.DirIn, false) if desc == typePtr && base.IsOptional { return // optional pointers prune recursion } @@ -464,7 +464,7 @@ func (comp *compiler) checkType(t *ast.Type, isArg, isRet, isStruct, isResourceB return } if desc.Check != nil { - _, args, base := comp.getArgsBase(t, "", sys.DirIn, isArg) + _, args, base := comp.getArgsBase(t, "", prog.DirIn, isArg) desc.Check(comp, t, args, base) } } @@ -574,7 +574,7 @@ func (comp *compiler) checkVarlens() { } func (comp *compiler) isVarlen(t *ast.Type) bool { - desc, args, base := comp.getArgsBase(t, "", sys.DirIn, false) + desc, args, base := comp.getArgsBase(t, "", prog.DirIn, false) return desc.Varlen != nil && desc.Varlen(comp, t, args, base) } diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 8cccf53c4..f6641f9c9 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -12,7 +12,7 @@ import ( "strings" "github.com/google/syzkaller/pkg/ast" - "github.com/google/syzkaller/sys" + "github.com/google/syzkaller/prog" ) // Overview of compilation process: @@ -22,20 +22,20 @@ import ( // This step also does verification of include/incdir/define AST nodes. // 3. User translates constants to values. // 4. Compile on AST and const values does the rest of the work and returns Prog -// containing generated sys objects. +// containing generated prog objects. // 4.1. assignSyscallNumbers: uses consts to assign syscall numbers. // This step also detects unsupported syscalls and discards no longer // needed AST nodes (inlcude, define, comments, etc). // 4.2. patchConsts: patches Int nodes refering to consts with corresponding values. // Also detects unsupported syscalls, structs, resources due to missing consts. // 4.3. check: does extensive semantical checks of AST. -// 4.4. gen: generates sys objects from AST. +// 4.4. gen: generates prog objects from AST. // Prog is description compilation result. type Prog struct { - Resources []*sys.ResourceDesc - Syscalls []*sys.Syscall - StructDescs []*sys.KeyedStruct + Resources []*prog.ResourceDesc + Syscalls []*prog.Syscall + StructDescs []*prog.KeyedStruct // Set of unsupported syscalls/flags. Unsupported map[string]bool } @@ -54,8 +54,8 @@ func Compile(desc *ast.Description, consts map[string]uint64, ptrSize uint64, eh structs: make(map[string]*ast.Struct), intFlags: make(map[string]*ast.IntFlags), strFlags: make(map[string]*ast.StrFlags), - structDescs: make(map[sys.StructKey]*sys.StructDesc), - structNodes: make(map[*sys.StructDesc]*ast.Struct), + structDescs: make(map[prog.StructKey]*prog.StructDesc), + structNodes: make(map[*prog.StructDesc]*ast.Struct), structVarlen: make(map[string]bool), } comp.assignSyscallNumbers(consts) @@ -89,8 +89,8 @@ type compiler struct { intFlags map[string]*ast.IntFlags strFlags map[string]*ast.StrFlags - structDescs map[sys.StructKey]*sys.StructDesc - structNodes map[*sys.StructDesc]*ast.Struct + structDescs map[prog.StructKey]*prog.StructDesc + structNodes map[*prog.StructDesc]*ast.Struct structVarlen map[string]bool } @@ -161,8 +161,8 @@ func (comp *compiler) getTypeDesc(t *ast.Type) *typeDesc { return nil } -func (comp *compiler) getArgsBase(t *ast.Type, field string, dir sys.Dir, isArg bool) ( - *typeDesc, []*ast.Type, sys.IntTypeCommon) { +func (comp *compiler) getArgsBase(t *ast.Type, field string, dir prog.Dir, isArg bool) ( + *typeDesc, []*ast.Type, prog.IntTypeCommon) { desc := comp.getTypeDesc(t) args, opt := removeOpt(t) size := sizeUnassigned @@ -173,7 +173,7 @@ func (comp *compiler) getArgsBase(t *ast.Type, field string, dir sys.Dir, isArg if !isArg { baseType := args[len(args)-1] args = args[:len(args)-1] - base = typeInt.Gen(comp, baseType, nil, base).(*sys.IntType).IntTypeCommon + base = typeInt.Gen(comp, baseType, nil, base).(*prog.IntType).IntTypeCommon } } return desc, args, base diff --git a/pkg/compiler/gen.go b/pkg/compiler/gen.go index e3b7ffd66..eeeed011c 100644 --- a/pkg/compiler/gen.go +++ b/pkg/compiler/gen.go @@ -8,13 +8,13 @@ import ( "sort" "github.com/google/syzkaller/pkg/ast" - "github.com/google/syzkaller/sys" + "github.com/google/syzkaller/prog" ) const sizeUnassigned = ^uint64(0) -func (comp *compiler) genResources() []*sys.ResourceDesc { - var resources []*sys.ResourceDesc +func (comp *compiler) genResources() []*prog.ResourceDesc { + var resources []*prog.ResourceDesc for _, decl := range comp.desc.Nodes { if n, ok := decl.(*ast.Resource); ok { resources = append(resources, comp.genResource(n)) @@ -26,8 +26,8 @@ func (comp *compiler) genResources() []*sys.ResourceDesc { return resources } -func (comp *compiler) genResource(n *ast.Resource) *sys.ResourceDesc { - res := &sys.ResourceDesc{ +func (comp *compiler) genResource(n *ast.Resource) *prog.ResourceDesc { + res := &prog.ResourceDesc{ Name: n.Name.Name, } var base *ast.Type @@ -40,12 +40,12 @@ func (comp *compiler) genResource(n *ast.Resource) *sys.ResourceDesc { if len(res.Values) == 0 { res.Values = []uint64{0} } - res.Type = comp.genType(base, "", sys.DirIn, false) + res.Type = comp.genType(base, "", prog.DirIn, false) return res } -func (comp *compiler) genSyscalls() []*sys.Syscall { - var calls []*sys.Syscall +func (comp *compiler) genSyscalls() []*prog.Syscall { + var calls []*prog.Syscall for _, decl := range comp.desc.Nodes { if n, ok := decl.(*ast.Call); ok { calls = append(calls, comp.genSyscall(n)) @@ -60,32 +60,32 @@ func (comp *compiler) genSyscalls() []*sys.Syscall { return calls } -func (comp *compiler) genSyscall(n *ast.Call) *sys.Syscall { - var ret sys.Type +func (comp *compiler) genSyscall(n *ast.Call) *prog.Syscall { + var ret prog.Type if n.Ret != nil { - ret = comp.genType(n.Ret, "ret", sys.DirOut, true) + ret = comp.genType(n.Ret, "ret", prog.DirOut, true) } - return &sys.Syscall{ + return &prog.Syscall{ Name: n.Name.Name, CallName: n.CallName, NR: n.NR, - Args: comp.genFieldArray(n.Args, sys.DirIn, true), + Args: comp.genFieldArray(n.Args, prog.DirIn, true), Ret: ret, } } -func (comp *compiler) genStructDescs(syscalls []*sys.Syscall) []*sys.KeyedStruct { +func (comp *compiler) genStructDescs(syscalls []*prog.Syscall) []*prog.KeyedStruct { // Calculate struct/union/array sizes, add padding to structs and detach // StructDesc's from StructType's. StructType's can be recursive so it's // not possible to write them out inline as other types. To break the // recursion detach them, and write StructDesc's out as separate array - // of KeyedStruct's. sys package will reattach them during init. + // of KeyedStruct's. prog package will reattach them during init. padded := make(map[interface{}]bool) - detach := make(map[**sys.StructDesc]bool) - var structs []*sys.KeyedStruct - var rec func(t sys.Type) - checkStruct := func(key sys.StructKey, descp **sys.StructDesc) bool { + detach := make(map[**prog.StructDesc]bool) + var structs []*prog.KeyedStruct + var rec func(t prog.Type) + checkStruct := func(key prog.StructKey, descp **prog.StructDesc) bool { detach[descp] = true desc := *descp if padded[desc] { @@ -101,17 +101,17 @@ func (comp *compiler) genStructDescs(syscalls []*sys.Syscall) []*sys.KeyedStruct return false } } - structs = append(structs, &sys.KeyedStruct{ + structs = append(structs, &prog.KeyedStruct{ Key: key, Desc: desc, }) return true } - rec = func(t0 sys.Type) { + rec = func(t0 prog.Type) { switch t := t0.(type) { - case *sys.PtrType: + case *prog.PtrType: rec(t.Type) - case *sys.ArrayType: + case *prog.ArrayType: if padded[t] { return } @@ -123,10 +123,10 @@ func (comp *compiler) genStructDescs(syscalls []*sys.Syscall) []*sys.KeyedStruct } padded[t] = true t.TypeSize = 0 - if t.Kind == sys.ArrayRangeLen && t.RangeBegin == t.RangeEnd && !t.Type.Varlen() { + if t.Kind == prog.ArrayRangeLen && t.RangeBegin == t.RangeEnd && !t.Type.Varlen() { t.TypeSize = t.RangeBegin * t.Type.Size() } - case *sys.StructType: + case *prog.StructType: if !checkStruct(t.Key, &t.StructDesc) { return } @@ -149,7 +149,7 @@ func (comp *compiler) genStructDescs(syscalls []*sys.Syscall) []*sys.KeyedStruct } } } - case *sys.UnionType: + case *prog.UnionType: if !checkStruct(t.Key, &t.StructDesc) { return } @@ -182,7 +182,7 @@ func (comp *compiler) genStructDescs(syscalls []*sys.Syscall) []*sys.KeyedStruct } } - // Detach StructDesc's from StructType's. sys will reattach them again. + // Detach StructDesc's from StructType's. prog will reattach them again. for descp := range detach { *descp = nil } @@ -197,10 +197,10 @@ func (comp *compiler) genStructDescs(syscalls []*sys.Syscall) []*sys.KeyedStruct return structs } -func (comp *compiler) genStructDesc(res *sys.StructDesc, n *ast.Struct, dir sys.Dir) { +func (comp *compiler) genStructDesc(res *prog.StructDesc, n *ast.Struct, dir prog.Dir) { // Leave node for genStructDescs to calculate size/padding. comp.structNodes[res] = n - *res = sys.StructDesc{ + *res = prog.StructDesc{ TypeCommon: genCommon(n.Name.Name, "", sizeUnassigned, dir, false), Fields: comp.genFieldArray(n.Fields, dir, false), } @@ -227,7 +227,7 @@ func (comp *compiler) isStructVarlen(name string) bool { return varlen } -func (comp *compiler) markBitfields(fields []sys.Type) { +func (comp *compiler) markBitfields(fields []prog.Type) { var bfOffset uint64 for i, f := range fields { if f.BitfieldLength() == 0 { @@ -245,25 +245,25 @@ func (comp *compiler) markBitfields(fields []sys.Type) { } } -func setBitfieldOffset(t0 sys.Type, offset uint64, middle bool) { +func setBitfieldOffset(t0 prog.Type, offset uint64, middle bool) { switch t := t0.(type) { - case *sys.IntType: + case *prog.IntType: t.BitfieldOff, t.BitfieldMdl = offset, middle - case *sys.ConstType: + case *prog.ConstType: t.BitfieldOff, t.BitfieldMdl = offset, middle - case *sys.LenType: + case *prog.LenType: t.BitfieldOff, t.BitfieldMdl = offset, middle - case *sys.FlagsType: + case *prog.FlagsType: t.BitfieldOff, t.BitfieldMdl = offset, middle - case *sys.ProcType: + case *prog.ProcType: t.BitfieldOff, t.BitfieldMdl = offset, middle default: panic(fmt.Sprintf("type %#v can't be a bitfield", t)) } } -func (comp *compiler) addAlignment(fields []sys.Type, varlen, packed bool, alignAttr uint64) []sys.Type { - var newFields []sys.Type +func (comp *compiler) addAlignment(fields []prog.Type, varlen, packed bool, alignAttr uint64) []prog.Type { + var newFields []prog.Type if packed { // If a struct is packed, statically sized and has explicitly set alignment, // add a padding at the end. @@ -312,19 +312,19 @@ func (comp *compiler) addAlignment(fields []sys.Type, varlen, packed bool, align return newFields } -func (comp *compiler) typeAlign(t0 sys.Type) uint64 { +func (comp *compiler) typeAlign(t0 prog.Type) uint64 { switch t0.(type) { - case *sys.IntType, *sys.ConstType, *sys.LenType, *sys.FlagsType, *sys.ProcType, - *sys.CsumType, *sys.PtrType, *sys.VmaType, *sys.ResourceType: + case *prog.IntType, *prog.ConstType, *prog.LenType, *prog.FlagsType, *prog.ProcType, + *prog.CsumType, *prog.PtrType, *prog.VmaType, *prog.ResourceType: return t0.Size() - case *sys.BufferType: + case *prog.BufferType: return 1 } switch t := t0.(type) { - case *sys.ArrayType: + case *prog.ArrayType: return comp.typeAlign(t.Type) - case *sys.StructType: + case *prog.StructType: packed, alignAttr := comp.parseStructAttrs(comp.structNodes[t.StructDesc]) if alignAttr != 0 { return alignAttr // overrided by user attribute @@ -339,7 +339,7 @@ func (comp *compiler) typeAlign(t0 sys.Type) uint64 { } } return align - case *sys.UnionType: + case *prog.UnionType: align := uint64(0) for _, f := range t.Fields { if a := comp.typeAlign(f); align < a { @@ -352,32 +352,32 @@ func (comp *compiler) typeAlign(t0 sys.Type) uint64 { } } -func genPad(size uint64) sys.Type { - return &sys.ConstType{ - IntTypeCommon: genIntCommon(genCommon("pad", "", size, sys.DirIn, false), 0, false), +func genPad(size uint64) prog.Type { + return &prog.ConstType{ + IntTypeCommon: genIntCommon(genCommon("pad", "", size, prog.DirIn, false), 0, false), IsPad: true, } } -func (comp *compiler) genField(f *ast.Field, dir sys.Dir, isArg bool) sys.Type { +func (comp *compiler) genField(f *ast.Field, dir prog.Dir, isArg bool) prog.Type { return comp.genType(f.Type, f.Name.Name, dir, isArg) } -func (comp *compiler) genFieldArray(fields []*ast.Field, dir sys.Dir, isArg bool) []sys.Type { - var res []sys.Type +func (comp *compiler) genFieldArray(fields []*ast.Field, dir prog.Dir, isArg bool) []prog.Type { + var res []prog.Type for _, f := range fields { res = append(res, comp.genField(f, dir, isArg)) } return res } -func (comp *compiler) genType(t *ast.Type, field string, dir sys.Dir, isArg bool) sys.Type { +func (comp *compiler) genType(t *ast.Type, field string, dir prog.Dir, isArg bool) prog.Type { desc, args, base := comp.getArgsBase(t, field, dir, isArg) return desc.Gen(comp, t, args, base) } -func genCommon(name, field string, size uint64, dir sys.Dir, opt bool) sys.TypeCommon { - return sys.TypeCommon{ +func genCommon(name, field string, size uint64, dir prog.Dir, opt bool) prog.TypeCommon { + return prog.TypeCommon{ TypeName: name, TypeSize: size, FldName: field, @@ -386,8 +386,8 @@ func genCommon(name, field string, size uint64, dir sys.Dir, opt bool) sys.TypeC } } -func genIntCommon(com sys.TypeCommon, bitLen uint64, bigEndian bool) sys.IntTypeCommon { - return sys.IntTypeCommon{ +func genIntCommon(com prog.TypeCommon, bitLen uint64, bigEndian bool) prog.IntTypeCommon { + return prog.IntTypeCommon{ TypeCommon: com, BigEndian: bigEndian, BitfieldLen: bitLen, diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index 2a398e3fb..193617574 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -8,7 +8,7 @@ import ( "strconv" "github.com/google/syzkaller/pkg/ast" - "github.com/google/syzkaller/sys" + "github.com/google/syzkaller/prog" ) // typeDesc is arg/field type descriptor. @@ -23,11 +23,11 @@ type typeDesc struct { OptArgs int // number of optional arguments in Args array Args []namedArg // type arguments // Check does custom verification of the type (optional). - Check func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) + Check func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) // Varlen returns if the type is variable-length (false if not set). - Varlen func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) bool - // Gen generates corresponding sys.Type. - Gen func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type + Varlen func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool + // Gen generates corresponding prog.Type. + Gen func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type } // typeArg describes a type argument. @@ -58,17 +58,17 @@ var typeInt = &typeDesc{ ResourceBase: true, OptArgs: 1, Args: []namedArg{{"range", typeArgRange}}, - Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) { + Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { typeArgBase.Type.Check(comp, t) }, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { size, be := comp.parseIntType(t.Ident) - kind, rangeBegin, rangeEnd := sys.IntPlain, uint64(0), uint64(0) + kind, rangeBegin, rangeEnd := prog.IntPlain, uint64(0), uint64(0) if len(args) > 0 { - kind, rangeBegin, rangeEnd = sys.IntRange, args[0].Value, args[0].Value2 + kind, rangeBegin, rangeEnd = prog.IntRange, args[0].Value, args[0].Value2 } base.TypeSize = size - return &sys.IntType{ + return &prog.IntType{ IntTypeCommon: genIntCommon(base.TypeCommon, t.Value2, be), Kind: kind, RangeBegin: rangeBegin, @@ -81,13 +81,13 @@ var typePtr = &typeDesc{ Names: []string{"ptr", "ptr64"}, CanBeArg: true, Args: []namedArg{{"direction", typeArgDir}, {"type", typeArgType}}, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { - base.ArgDir = sys.DirIn // pointers are always in + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { + base.ArgDir = prog.DirIn // pointers are always in base.TypeSize = comp.ptrSize if t.Ident == "ptr64" { base.TypeSize = 8 } - return &sys.PtrType{ + return &prog.PtrType{ TypeCommon: base.TypeCommon, Type: comp.genType(args[1], "", genDir(args[0]), false), } @@ -99,13 +99,13 @@ var typeArray = &typeDesc{ CantBeOpt: true, OptArgs: 1, Args: []namedArg{{"type", typeArgType}, {"size", typeArgRange}}, - Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) { + Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { if len(args) > 1 && args[1].Value == 0 && args[1].Value2 == 0 { // This is the only case that can yield 0 static type size. comp.error(args[1].Pos, "arrays of size 0 are not supported") } }, - Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) bool { + Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool { if comp.isVarlen(args[0]) { return true } @@ -114,23 +114,23 @@ var typeArray = &typeDesc{ } return true }, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { elemType := comp.genType(args[0], "", base.ArgDir, false) - kind, begin, end := sys.ArrayRandLen, uint64(0), uint64(0) + kind, begin, end := prog.ArrayRandLen, uint64(0), uint64(0) if len(args) > 1 { - kind, begin, end = sys.ArrayRangeLen, args[1].Value, args[1].Value2 + kind, begin, end = prog.ArrayRangeLen, args[1].Value, args[1].Value2 } - if it, ok := elemType.(*sys.IntType); ok && it.Kind == sys.IntPlain && it.TypeSize == 1 { + if it, ok := elemType.(*prog.IntType); ok && it.Kind == prog.IntPlain && it.TypeSize == 1 { // Special case: buffer is better mutated. - bufKind := sys.BufferBlobRand + bufKind := prog.BufferBlobRand base.TypeSize = 0 - if kind == sys.ArrayRangeLen { - bufKind = sys.BufferBlobRange + if kind == prog.ArrayRangeLen { + bufKind = prog.BufferBlobRange if begin == end { base.TypeSize = begin * elemType.Size() } } - return &sys.BufferType{ + return &prog.BufferType{ TypeCommon: base.TypeCommon, Kind: bufKind, RangeBegin: begin, @@ -138,7 +138,7 @@ var typeArray = &typeDesc{ } } // TypeSize is assigned later in genStructDescs. - return &sys.ArrayType{ + return &prog.ArrayType{ TypeCommon: base.TypeCommon, Type: elemType, Kind: kind, @@ -155,7 +155,7 @@ var typeLen = &typeDesc{ CantBeRet: true, NeedBase: true, Args: []namedArg{{"len target", typeArgLenTarget}}, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { var byteSize uint64 switch t.Ident { case "bytesize": @@ -163,7 +163,7 @@ var typeLen = &typeDesc{ case "bytesize2", "bytesize4", "bytesize8": byteSize, _ = strconv.ParseUint(t.Ident[8:], 10, 8) } - return &sys.LenType{ + return &prog.LenType{ IntTypeCommon: base, Buf: args[0].Ident, ByteSize: byteSize, @@ -177,8 +177,8 @@ var typeConst = &typeDesc{ CantBeOpt: true, NeedBase: true, Args: []namedArg{{"value", typeArgInt}}, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { - return &sys.ConstType{ + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { + return &prog.ConstType{ IntTypeCommon: base, Val: args[0].Value, } @@ -195,18 +195,18 @@ var typeFlags = &typeDesc{ CantBeOpt: true, NeedBase: true, Args: []namedArg{{"flags", typeArgFlags}}, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { name := args[0].Ident base.TypeName = name f := comp.intFlags[name] if len(f.Values) == 0 { // We can get this if all values are unsupported consts. - return &sys.IntType{ + return &prog.IntType{ IntTypeCommon: base, - Kind: sys.IntPlain, + Kind: prog.IntPlain, } } - return &sys.FlagsType{ + return &prog.FlagsType{ IntTypeCommon: base, Vals: genIntArray(f.Values), } @@ -226,14 +226,14 @@ var typeArgFlags = &typeArg{ var typeFilename = &typeDesc{ Names: []string{"filename"}, CantBeOpt: true, - Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) bool { + Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool { return true }, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { base.TypeSize = 0 - return &sys.BufferType{ + return &prog.BufferType{ TypeCommon: base.TypeCommon, - Kind: sys.BufferFilename, + Kind: prog.BufferFilename, } }, } @@ -243,10 +243,10 @@ var typeFileoff = &typeDesc{ CanBeArg: true, CantBeOpt: true, NeedBase: true, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { - return &sys.IntType{ + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { + return &prog.IntType{ IntTypeCommon: base, - Kind: sys.IntFileoff, + Kind: prog.IntFileoff, } }, } @@ -256,13 +256,13 @@ var typeVMA = &typeDesc{ CanBeArg: true, OptArgs: 1, Args: []namedArg{{"size range", typeArgRange}}, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { begin, end := uint64(0), uint64(0) if len(args) > 0 { begin, end = args[0].Value, args[0].Value2 } base.TypeSize = comp.ptrSize - return &sys.VmaType{ + return &prog.VmaType{ TypeCommon: base.TypeCommon, RangeBegin: begin, RangeEnd: end, @@ -277,11 +277,11 @@ var typeSignalno = &typeDesc{ Names: []string{"signalno"}, CanBeArg: true, CantBeOpt: true, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { base.TypeSize = 4 - return &sys.IntType{ + return &prog.IntType{ IntTypeCommon: base, - Kind: sys.IntRange, + Kind: prog.IntRange, RangeBegin: 0, RangeEnd: 65, } @@ -295,17 +295,17 @@ var typeCsum = &typeDesc{ CantBeRet: true, OptArgs: 1, Args: []namedArg{{"csum target", typeArgLenTarget}, {"kind", typeArgCsumType}, {"proto", typeArgInt}}, - Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) { - if len(args) > 2 && genCsumKind(args[1]) != sys.CsumPseudo { + Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { + if len(args) > 2 && genCsumKind(args[1]) != prog.CsumPseudo { comp.error(args[2].Pos, "only pseudo csum can have proto") } }, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { var proto uint64 if len(args) > 2 { proto = args[2].Value } - return &sys.CsumType{ + return &prog.CsumType{ IntTypeCommon: base, Buf: args[0].Ident, Kind: genCsumKind(args[1]), @@ -319,12 +319,12 @@ var typeArgCsumType = &typeArg{ Names: []string{"inet", "pseudo"}, } -func genCsumKind(t *ast.Type) sys.CsumKind { +func genCsumKind(t *ast.Type) prog.CsumKind { switch t.Ident { case "inet": - return sys.CsumInet + return prog.CsumInet case "pseudo": - return sys.CsumPseudo + return prog.CsumPseudo default: panic(fmt.Sprintf("unknown csum kind %q", t.Ident)) } @@ -336,7 +336,7 @@ var typeProc = &typeDesc{ CantBeOpt: true, NeedBase: true, Args: []namedArg{{"range start", typeArgInt}, {"per-proc values", typeArgInt}}, - Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) { + Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { start := args[0].Value perProc := args[1].Value if perProc == 0 { @@ -354,8 +354,8 @@ var typeProc = &typeDesc{ } } }, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { - return &sys.ProcType{ + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { + return &prog.ProcType{ IntTypeCommon: base, ValuesStart: args[0].Value, ValuesPerProc: args[1].Value, @@ -367,14 +367,14 @@ var typeText = &typeDesc{ Names: []string{"text"}, CantBeOpt: true, Args: []namedArg{{"kind", typeArgTextType}}, - Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) bool { + Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool { return true }, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { base.TypeSize = 0 - return &sys.BufferType{ + return &prog.BufferType{ TypeCommon: base.TypeCommon, - Kind: sys.BufferText, + Kind: prog.BufferText, Text: genTextType(args[0]), } }, @@ -385,18 +385,18 @@ var typeArgTextType = &typeArg{ Names: []string{"x86_real", "x86_16", "x86_32", "x86_64", "arm64"}, } -func genTextType(t *ast.Type) sys.TextKind { +func genTextType(t *ast.Type) prog.TextKind { switch t.Ident { case "x86_real": - return sys.Text_x86_real + return prog.Text_x86_real case "x86_16": - return sys.Text_x86_16 + return prog.Text_x86_16 case "x86_32": - return sys.Text_x86_32 + return prog.Text_x86_32 case "x86_64": - return sys.Text_x86_64 + return prog.Text_x86_64 case "arm64": - return sys.Text_arm64 + return prog.Text_arm64 default: panic(fmt.Sprintf("unknown text type %q", t.Ident)) } @@ -406,14 +406,14 @@ var typeBuffer = &typeDesc{ Names: []string{"buffer"}, CanBeArg: true, Args: []namedArg{{"direction", typeArgDir}}, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { base.TypeSize = comp.ptrSize - return &sys.PtrType{ + return &prog.PtrType{ TypeCommon: base.TypeCommon, - Type: &sys.BufferType{ + Type: &prog.BufferType{ // BufferBlobRand is always varlen. TypeCommon: genCommon("", "", 0, genDir(args[0]), false), - Kind: sys.BufferBlobRand, + Kind: prog.BufferBlobRand, }, } }, @@ -423,7 +423,7 @@ var typeString = &typeDesc{ Names: []string{"string"}, OptArgs: 2, Args: []namedArg{{"literal or flags", typeArgStringFlags}, {"size", typeArgInt}}, - Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) { + Check: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) { if len(args) > 1 { size := args[1].Value vals := []string{args[0].String} @@ -439,10 +439,10 @@ var typeString = &typeDesc{ } } }, - Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) bool { + Varlen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool { return comp.stringSize(args) == 0 }, - Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { subkind := "" var vals []string if len(args) > 0 { @@ -465,9 +465,9 @@ var typeString = &typeDesc{ vals[i] = s } base.TypeSize = comp.stringSize(args) - return &sys.BufferType{ + return &prog.BufferType{ TypeCommon: base.TypeCommon, - Kind: sys.BufferString, + Kind: prog.BufferString, SubKind: subkind, Values: vals, } @@ -527,15 +527,15 @@ var typeResource = &typeDesc{ } func init() { - typeResource.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + typeResource.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { // Find and generate base type to get its size. var baseType *ast.Type for r := comp.resources[t.Ident]; r != nil; { baseType = r.Base r = comp.resources[r.Base.Ident] } - base.TypeSize = comp.genType(baseType, "", sys.DirIn, false).Size() - return &sys.ResourceType{ + base.TypeSize = comp.genType(baseType, "", prog.DirIn, false).Size() + return &prog.ResourceType{ TypeCommon: base.TypeCommon, } } @@ -548,27 +548,27 @@ var typeStruct = &typeDesc{ } func init() { - typeStruct.Varlen = func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) bool { + typeStruct.Varlen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) bool { return comp.isStructVarlen(t.Ident) } - typeStruct.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base sys.IntTypeCommon) sys.Type { + typeStruct.Gen = func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { s := comp.structs[t.Ident] - key := sys.StructKey{t.Ident, base.ArgDir} + key := prog.StructKey{t.Ident, base.ArgDir} desc := comp.structDescs[key] if desc == nil { // Need to assign to structDescs before calling genStructDesc to break recursion. - desc = new(sys.StructDesc) + desc = new(prog.StructDesc) comp.structDescs[key] = desc comp.genStructDesc(desc, s, base.ArgDir) } if s.IsUnion { - return &sys.UnionType{ + return &prog.UnionType{ Key: key, FldName: base.FldName, StructDesc: desc, } } else { - return &sys.StructType{ + return &prog.StructType{ Key: key, FldName: base.FldName, StructDesc: desc, @@ -582,14 +582,14 @@ var typeArgDir = &typeArg{ Names: []string{"in", "out", "inout"}, } -func genDir(t *ast.Type) sys.Dir { +func genDir(t *ast.Type) prog.Dir { switch t.Ident { case "in": - return sys.DirIn + return prog.DirIn case "out": - return sys.DirOut + return prog.DirOut case "inout": - return sys.DirInOut + return prog.DirInOut default: panic(fmt.Sprintf("unknown direction %q", t.Ident)) } diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index b46fda351..e1b32e752 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -20,7 +20,7 @@ import ( "unsafe" "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/sys" + _ "github.com/google/syzkaller/sys" ) type Options struct { @@ -367,7 +367,7 @@ loop: fmt.Fprintf(w, "\twrite_file(\"/sys/kernel/debug/fail_futex/ignore-private\", \"N\");\n") fmt.Fprintf(w, "\tinject_fault(%v);\n", opts.FaultNth) } - meta := sys.Syscalls[instr] + meta := prog.Syscalls[instr] emitCall := true if meta.CallName == "syz_test" { emitCall = false diff --git a/pkg/csource/csource_test.go b/pkg/csource/csource_test.go index e4e9e1f7f..fbe984b3c 100644 --- a/pkg/csource/csource_test.go +++ b/pkg/csource/csource_test.go @@ -13,6 +13,7 @@ import ( "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) func initTest(t *testing.T) (rand.Source, int) { diff --git a/pkg/host/host.go b/pkg/host/host.go index 9aafbce6e..0fa66dfa4 100644 --- a/pkg/host/host.go +++ b/pkg/host/host.go @@ -12,13 +12,13 @@ import ( "syscall" "github.com/google/syzkaller/pkg/osutil" - "github.com/google/syzkaller/sys" + "github.com/google/syzkaller/prog" ) // DetectSupportedSyscalls returns list on supported syscalls on host. -func DetectSupportedSyscalls() (map[*sys.Syscall]bool, error) { +func DetectSupportedSyscalls() (map[*prog.Syscall]bool, error) { // There are 3 possible strategies: - // 1. Executes all syscalls with presumably invalid arguments and check for ENOSYS. + // 1. Executes all syscalls with presumably invalid arguments and check for ENOprog. // But not all syscalls are safe to execute. For example, pause will hang, // while setpgrp will push the process into own process group. // 2. Check presence of /sys/kernel/debug/tracing/events/syscalls/sys_enter_* files. @@ -28,8 +28,8 @@ func DetectSupportedSyscalls() (map[*sys.Syscall]bool, error) { // Requires CONFIG_KALLSYMS. Seems to be the most reliable. That's what we use here. kallsyms, _ := ioutil.ReadFile("/proc/kallsyms") - supported := make(map[*sys.Syscall]bool) - for _, c := range sys.Syscalls { + supported := make(map[*prog.Syscall]bool) + for _, c := range prog.Syscalls { if isSupported(kallsyms, c) { supported[c] = true } @@ -37,7 +37,7 @@ func DetectSupportedSyscalls() (map[*sys.Syscall]bool, error) { return supported, nil } -func isSupported(kallsyms []byte, c *sys.Syscall) bool { +func isSupported(kallsyms []byte, c *prog.Syscall) bool { if c.NR == ^uint64(0) { return false // don't even have a syscall number } @@ -59,12 +59,12 @@ func isSupported(kallsyms []byte, c *sys.Syscall) bool { return bytes.Index(kallsyms, []byte(" T sys_"+c.CallName+"\n")) != -1 } -func isSupportedSyzkall(c *sys.Syscall) bool { +func isSupportedSyzkall(c *prog.Syscall) bool { switch c.CallName { case "syz_test": return false case "syz_open_dev": - if _, ok := c.Args[0].(*sys.ConstType); ok { + if _, ok := c.Args[0].(*prog.ConstType); ok { // This is for syz_open_dev$char/block. // They are currently commented out, but in case one enables them. return true @@ -112,8 +112,8 @@ func isSupportedSyzkall(c *sys.Syscall) bool { panic("unknown syzkall: " + c.Name) } -func isSupportedSocket(c *sys.Syscall) bool { - af, ok := c.Args[0].(*sys.ConstType) +func isSupportedSocket(c *prog.Syscall) bool { + af, ok := c.Args[0].(*prog.ConstType) if !ok { println(c.Name) panic("socket family is not const") @@ -125,7 +125,7 @@ func isSupportedSocket(c *sys.Syscall) bool { return err != syscall.ENOSYS && err != syscall.EAFNOSUPPORT } -func isSupportedOpen(c *sys.Syscall) bool { +func isSupportedOpen(c *prog.Syscall) bool { fname, ok := extractStringConst(c.Args[0]) if !ok { return true @@ -137,7 +137,7 @@ func isSupportedOpen(c *sys.Syscall) bool { return err == nil } -func isSupportedOpenAt(c *sys.Syscall) bool { +func isSupportedOpenAt(c *prog.Syscall) bool { fname, ok := extractStringConst(c.Args[1]) if !ok { return true @@ -149,13 +149,13 @@ func isSupportedOpenAt(c *sys.Syscall) bool { return err == nil } -func extractStringConst(typ sys.Type) (string, bool) { - ptr, ok := typ.(*sys.PtrType) +func extractStringConst(typ prog.Type) (string, bool) { + ptr, ok := typ.(*prog.PtrType) if !ok { panic("first open arg is not a pointer to string const") } - str, ok := ptr.Type.(*sys.BufferType) - if !ok || str.Kind != sys.BufferString || len(str.Values) != 1 { + str, ok := ptr.Type.(*prog.BufferType) + if !ok || str.Kind != prog.BufferString || len(str.Values) != 1 { return "", false } v := str.Values[0] diff --git a/pkg/host/host_test.go b/pkg/host/host_test.go index 1eea00fe6..dc9225d5a 100644 --- a/pkg/host/host_test.go +++ b/pkg/host/host_test.go @@ -7,7 +7,8 @@ import ( "syscall" "testing" - "github.com/google/syzkaller/sys" + "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) func TestLog(t *testing.T) { @@ -18,7 +19,7 @@ func TestLog(t *testing.T) { t.Skipf("skipping: %v", err) } t.Logf("unsupported:") - for _, c := range sys.Syscalls { + for _, c := range prog.Syscalls { s, ok := supp[c] if ok && !s { t.Fatalf("map contains false value") @@ -27,9 +28,9 @@ func TestLog(t *testing.T) { t.Logf("\t%v", c.Name) } } - trans := sys.TransitivelyEnabledCalls(supp) + trans := prog.TransitivelyEnabledCalls(supp) t.Logf("transitively unsupported:") - for _, c := range sys.Syscalls { + for _, c := range prog.Syscalls { s, ok := trans[c] if ok && !s { t.Fatalf("map contains false value") @@ -58,7 +59,7 @@ func TestSupportedSyscalls(t *testing.T) { "stat", } for _, name := range safe { - c := sys.SyscallMap[name] + c := prog.SyscallMap[name] if c == nil { t.Fatalf("can't find syscall '%v'", name) } diff --git a/pkg/ipc/ipc_test.go b/pkg/ipc/ipc_test.go index 68fbb94cd..b6bfca81d 100644 --- a/pkg/ipc/ipc_test.go +++ b/pkg/ipc/ipc_test.go @@ -13,6 +13,7 @@ import ( "github.com/google/syzkaller/pkg/csource" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) const timeout = 10 * time.Second diff --git a/prog/analysis.go b/prog/analysis.go index 05837131a..009a15a21 100644 --- a/prog/analysis.go +++ b/prog/analysis.go @@ -10,8 +10,6 @@ package prog import ( "fmt" - - . "github.com/google/syzkaller/sys" ) const ( @@ -68,46 +66,18 @@ func (s *state) analyze(c *Call) { } } }) - switch c.Meta.Name { - case "mmap": - // Filter out only very wrong arguments. - length := c.Args[1].(*ConstArg) - if length.Val == 0 { - break + start, npages, mapped := analyzeMmap(c) + if npages != 0 { + if start+npages > uint64(len(s.pages)) { + panic(fmt.Sprintf("address is out of bounds: page=%v len=%v bound=%v", + start, npages, len(s.pages))) } - flags := c.Args[3].(*ConstArg) - fd := c.Args[4].(*ResultArg) - if flags.Val&MAP_ANONYMOUS == 0 && fd.Val == InvalidFD { - break - } - s.addressable(c.Args[0].(*PointerArg), length, true) - case "munmap": - s.addressable(c.Args[0].(*PointerArg), c.Args[1].(*ConstArg), false) - case "mremap": - s.addressable(c.Args[4].(*PointerArg), c.Args[2].(*ConstArg), true) - case "io_submit": - if arr := c.Args[2].(*PointerArg).Res; arr != nil { - for _, ptr := range arr.(*GroupArg).Inner { - p := ptr.(*PointerArg) - if p.Res != nil && p.Res.Type().Name() == "iocb" { - s.resources["iocbptr"] = append(s.resources["iocbptr"], ptr) - } - } + for i := uint64(0); i < npages; i++ { + s.pages[start+i] = mapped } } } -func (s *state) addressable(addr *PointerArg, size *ConstArg, ok bool) { - sizePages := size.Val / pageSize - if addr.PageIndex+sizePages > uint64(len(s.pages)) { - panic(fmt.Sprintf("address is out of bounds: page=%v len=%v bound=%v\naddr: %+v\nsize: %+v", - addr.PageIndex, sizePages, len(s.pages), addr, size)) - } - for i := uint64(0); i < sizePages; i++ { - s.pages[addr.PageIndex+i] = ok - } -} - func foreachSubargImpl(arg Arg, parent *[]Arg, f func(arg, base Arg, parent *[]Arg)) { var rec func(arg, base Arg, parent *[]Arg) rec = func(arg, base Arg, parent *[]Arg) { @@ -181,86 +151,6 @@ func foreachSubargOffset(arg Arg, f func(arg Arg, offset uint64)) { rec(arg, 0) } -func sanitizeCall(c *Call) { - switch c.Meta.CallName { - case "mmap": - // Add MAP_FIXED flag, otherwise it produces non-deterministic results. - _, ok := c.Args[0].(*PointerArg) - if !ok { - panic("mmap address is not ArgPointer") - } - _, ok = c.Args[1].(*ConstArg) - if !ok { - panic("mmap length is not ArgPageSize") - } - flags, ok := c.Args[3].(*ConstArg) - if !ok { - panic("mmap flag arg is not const") - } - flags.Val |= MAP_FIXED - case "mremap": - // Add MREMAP_FIXED flag, otherwise it produces non-deterministic results. - flags, ok := c.Args[3].(*ConstArg) - if !ok { - panic("mremap flag arg is not const") - } - if flags.Val&MREMAP_MAYMOVE != 0 { - flags.Val |= MREMAP_FIXED - } - case "mknod", "mknodat": - mode, ok1 := c.Args[1].(*ConstArg) - dev, ok2 := c.Args[2].(*ConstArg) - if c.Meta.CallName == "mknodat" { - mode, ok1 = c.Args[2].(*ConstArg) - dev, ok2 = c.Args[3].(*ConstArg) - } - if !ok1 || !ok2 { - panic("mknod mode is not const") - } - // Char and block devices read/write io ports, kernel memory and do other nasty things. - // TODO: not required if executor drops privileges. - switch mode.Val & (S_IFREG | S_IFCHR | S_IFBLK | S_IFIFO | S_IFSOCK) { - case S_IFREG, S_IFIFO, S_IFSOCK: - case S_IFBLK: - if dev.Val>>8 == 7 { - break // loop - } - mode.Val &^= S_IFBLK - mode.Val |= S_IFREG - case S_IFCHR: - mode.Val &^= S_IFCHR - mode.Val |= S_IFREG - } - case "syslog": - cmd := c.Args[0].(*ConstArg) - // These disable console output, but we need it. - if cmd.Val == SYSLOG_ACTION_CONSOLE_OFF || cmd.Val == SYSLOG_ACTION_CONSOLE_ON { - cmd.Val = SYSLOG_ACTION_SIZE_UNREAD - } - case "ioctl": - cmd := c.Args[1].(*ConstArg) - // Freeze kills machine. Though, it is an interesting functions, - // so we need to test it somehow. - // TODO: not required if executor drops privileges. - if uint32(cmd.Val) == FIFREEZE { - cmd.Val = FITHAW - } - case "ptrace": - req := c.Args[0].(*ConstArg) - // PTRACE_TRACEME leads to unkillable processes, see: - // https://groups.google.com/forum/#!topic/syzkaller/uGzwvhlCXAw - if req.Val == PTRACE_TRACEME { - req.Val = ^uint64(0) - } - case "exit", "exit_group": - code := c.Args[0].(*ConstArg) - // These codes are reserved by executor. - if code.Val%128 == 67 || code.Val%128 == 68 { - code.Val = 1 - } - } -} - func RequiresBitmasks(p *Prog) bool { result := false for _, c := range p.Calls { diff --git a/prog/checksum.go b/prog/checksum.go index 0c8f7210d..b5226e715 100644 --- a/prog/checksum.go +++ b/prog/checksum.go @@ -5,8 +5,6 @@ package prog import ( "fmt" - - . "github.com/google/syzkaller/sys" ) type CsumChunkKind int diff --git a/prog/checksum_test.go b/prog/checksum_test.go index 205450255..004769328 100644 --- a/prog/checksum_test.go +++ b/prog/checksum_test.go @@ -1,23 +1,26 @@ // Copyright 2016 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 prog +package prog_test import ( "testing" + + . "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) func TestChecksumCalcRandom(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) for i := 0; i < iters; i++ { p := Generate(rs, 10, nil) for _, call := range p.Calls { - calcChecksumsCall(call, i%32) + CalcChecksumsCall(call, i%32) } for try := 0; try <= 10; try++ { p.Mutate(rs, 10, nil, nil) for _, call := range p.Calls { - calcChecksumsCall(call, i%32) + CalcChecksumsCall(call, i%32) } } } diff --git a/prog/decl_test.go b/prog/decl_test.go new file mode 100644 index 000000000..e716cdf5e --- /dev/null +++ b/prog/decl_test.go @@ -0,0 +1,62 @@ +// Copyright 2015 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 prog + +import ( + "testing" +) + +func TestResourceCtors(t *testing.T) { + for _, c := range Syscalls { + for _, res := range c.InputResources() { + if len(calcResourceCtors(res.Desc.Kind, true)) == 0 { + t.Errorf("call %v requires input resource %v, but there are no calls that can create this resource", c.Name, res.Desc.Name) + } + } + } +} + +func TestTransitivelyEnabledCalls(t *testing.T) { + t.Parallel() + calls := make(map[*Syscall]bool) + for _, c := range Syscalls { + calls[c] = true + } + if trans := TransitivelyEnabledCalls(calls); len(calls) != len(trans) { + for c := range calls { + if !trans[c] { + t.Logf("disabled %v", c.Name) + } + } + t.Fatalf("can't create some resource") + } + delete(calls, SyscallMap["epoll_create"]) + if trans := TransitivelyEnabledCalls(calls); len(calls) != len(trans) { + t.Fatalf("still must be able to create epoll fd with epoll_create1") + } + delete(calls, SyscallMap["epoll_create1"]) + trans := TransitivelyEnabledCalls(calls) + if len(calls)-5 != len(trans) || + trans[SyscallMap["epoll_ctl$EPOLL_CTL_ADD"]] || + trans[SyscallMap["epoll_ctl$EPOLL_CTL_MOD"]] || + trans[SyscallMap["epoll_ctl$EPOLL_CTL_DEL"]] || + trans[SyscallMap["epoll_wait"]] || + trans[SyscallMap["epoll_pwait"]] { + t.Fatalf("epoll fd is not disabled") + } +} + +func TestClockGettime(t *testing.T) { + t.Parallel() + calls := make(map[*Syscall]bool) + for _, c := range Syscalls { + calls[c] = true + } + // Removal of clock_gettime should disable all calls that accept timespec/timeval. + delete(calls, SyscallMap["clock_gettime"]) + trans := TransitivelyEnabledCalls(calls) + if len(trans)+10 > len(calls) { + t.Fatalf("clock_gettime did not disable enough calls: before %v, after %v", len(calls), len(trans)) + } +} diff --git a/prog/encoding.go b/prog/encoding.go index 2081b1abb..a6ded73ca 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -10,8 +10,6 @@ import ( "fmt" "io" "strconv" - - . "github.com/google/syzkaller/sys" ) // String generates a very compact program description (mostly for debug output). @@ -146,7 +144,7 @@ func Deserialize(data []byte) (prog *Prog, err error) { } c := &Call{ Meta: meta, - Ret: returnArg(meta.Ret), + Ret: MakeReturnArg(meta.Ret), } prog.Calls = append(prog.Calls, c) p.Parse('(') @@ -213,13 +211,13 @@ func parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, error) { } switch typ.(type) { case *ConstType, *IntType, *FlagsType, *ProcType, *LenType, *CsumType: - arg = constArg(typ, v) + arg = MakeConstArg(typ, v) case *ResourceType: - arg = resultArg(typ, nil, v) + arg = MakeResultArg(typ, nil, v) case *PtrType: - arg = pointerArg(typ, 0, 0, 0, nil) + arg = MakePointerArg(typ, 0, 0, 0, nil) case *VmaType: - arg = pointerArg(typ, 0, 0, 0, nil) + arg = MakePointerArg(typ, 0, 0, 0, nil) default: return nil, fmt.Errorf("bad const type %+v", typ) } @@ -229,7 +227,7 @@ func parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, error) { if !ok || v == nil { return nil, fmt.Errorf("result %v references unknown variable (vars=%+v)", id, vars) } - arg = resultArg(typ, v, 0) + arg = MakeResultArg(typ, v, 0) if p.Char() == '/' { p.Parse('/') op := p.Ident() @@ -267,7 +265,7 @@ func parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, error) { if err != nil { return nil, err } - arg = pointerArg(typ, page, off, size, inner) + arg = MakePointerArg(typ, page, off, size, inner) case '(': // This used to parse length of VmaType and return ArgPageSize, which is now removed. // Leaving this for now for backwards compatibility. @@ -275,7 +273,7 @@ func parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, error) { if err != nil { return nil, err } - arg = constArg(typ, pages*pageSize) + arg = MakeConstArg(typ, pages*pageSize) case '"': p.Parse('"') val := "" @@ -301,7 +299,7 @@ func parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, error) { } fld := t1.Fields[i] if IsPad(fld) { - inner = append(inner, constArg(fld, 0)) + inner = append(inner, MakeConstArg(fld, 0)) } else { arg, err := parseArg(fld, p, vars) if err != nil { @@ -317,7 +315,7 @@ func parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, error) { for len(inner) < len(t1.Fields) { inner = append(inner, defaultArg(t1.Fields[len(inner)])) } - arg = groupArg(typ, inner) + arg = MakeGroupArg(typ, inner) case '[': t1, ok := typ.(*ArrayType) if !ok { @@ -336,7 +334,7 @@ func parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, error) { } } p.Parse(']') - arg = groupArg(typ, inner) + arg = MakeGroupArg(typ, inner) case '@': t1, ok := typ.(*UnionType) if !ok { diff --git a/prog/encoding_test.go b/prog/encoding_test.go index f4036b458..c58fe7dc8 100644 --- a/prog/encoding_test.go +++ b/prog/encoding_test.go @@ -1,7 +1,7 @@ // Copyright 2016 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 prog +package prog_test import ( "fmt" @@ -9,6 +9,9 @@ import ( "regexp" "sort" "testing" + + . "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) func setToArray(s map[string]struct{}) []string { @@ -76,7 +79,7 @@ func TestCallSet(t *testing.T) { } func TestCallSetRandom(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) for i := 0; i < iters; i++ { p := Generate(rs, 10, nil) calls0 := make(map[string]struct{}) diff --git a/prog/encodingexec.go b/prog/encodingexec.go index d50e4f3be..a4f41a694 100644 --- a/prog/encodingexec.go +++ b/prog/encodingexec.go @@ -9,8 +9,6 @@ package prog import ( "fmt" "sort" - - . "github.com/google/syzkaller/sys" ) const ( @@ -37,9 +35,6 @@ const ( const ( ExecBufferSize = 2 << 20 - - pageSize = 4 << 10 - dataOffset = 512 << 20 ) type Args []Arg diff --git a/prog/encodingexec_test.go b/prog/encodingexec_test.go index 37bfe04c0..9a2dd1d1e 100644 --- a/prog/encodingexec_test.go +++ b/prog/encodingexec_test.go @@ -1,7 +1,7 @@ // Copyright 2016 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 prog +package prog_test import ( "bytes" @@ -9,13 +9,12 @@ import ( "fmt" "testing" - . "github.com/google/syzkaller/sys" + . "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) -const ptrSize = 8 - func TestSerializeForExecRandom(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) buf := make([]byte, ExecBufferSize) for i := 0; i < iters; i++ { p := Generate(rs, 10, nil) @@ -46,6 +45,10 @@ func TestSerializeForExec(t *testing.T) { argResult = uint64(ExecArgResult) argData = uint64(ExecArgData) ) + var ( + dataOffset = DataOffset() + ptrSize = PtrSize() + ) callID := func(name string) uint64 { c := SyscallMap[name] if c == nil { diff --git a/prog/export_test.go b/prog/export_test.go new file mode 100644 index 000000000..c5337d695 --- /dev/null +++ b/prog/export_test.go @@ -0,0 +1,47 @@ +// Copyright 2017 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 prog + +import ( + "math/rand" + "testing" + "time" +) + +// Export guts for testing. + +func init() { + debug = true +} + +var ( + CalcChecksumsCall = calcChecksumsCall + AssignSizesCall = assignSizesCall + DefaultArg = defaultArg + InitTest = initTest +) + +func PtrSize() uint64 { + return ptrSize +} + +func DataOffset() uint64 { + return dataOffset +} + +func PageSize() uint64 { + return pageSize +} + +func initTest(t *testing.T) (rand.Source, int) { + t.Parallel() + iters := 10000 + if testing.Short() { + iters = 100 + } + seed := int64(time.Now().UnixNano()) + rs := rand.NewSource(seed) + t.Logf("seed=%v", seed) + return rs, iters +} diff --git a/prog/hints.go b/prog/hints.go index ba8131081..e268d46a6 100644 --- a/prog/hints.go +++ b/prog/hints.go @@ -20,8 +20,6 @@ package prog import ( "encoding/binary" - - . "github.com/google/syzkaller/sys" ) type uint64Set map[uint64]bool @@ -95,7 +93,7 @@ func generateHints(p *Prog, compMap CompMap, c *Call, arg Arg, exec func(p *Prog switch a := arg.(type) { case *ConstArg: - originalArg = constArg(a.Type(), a.Val) + originalArg = MakeConstArg(a.Type(), a.Val) checkConstArg(a, compMap, constArgCandidate) case *DataArg: originalArg = dataArg(a.Type(), a.Data) @@ -105,7 +103,7 @@ func generateHints(p *Prog, compMap CompMap, c *Call, arg Arg, exec func(p *Prog func checkConstArg(arg *ConstArg, compMap CompMap, cb func(newArg Arg)) { for replacer := range shrinkExpand(arg.Val, compMap) { - cb(constArg(arg.typ, replacer)) + cb(MakeConstArg(arg.typ, replacer)) } } diff --git a/prog/hints_test.go b/prog/hints_test.go index 5409925bf..3f0a10726 100644 --- a/prog/hints_test.go +++ b/prog/hints_test.go @@ -7,8 +7,6 @@ import ( "fmt" "reflect" "testing" - - . "github.com/google/syzkaller/sys" ) type ConstArgTest struct { diff --git a/prog/mutation.go b/prog/mutation.go index a45a02fe1..e793e28e4 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -7,8 +7,6 @@ import ( "fmt" "math/rand" "unsafe" - - . "github.com/google/syzkaller/sys" ) func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Prog) { @@ -178,11 +176,11 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Pro arg1, calls1 := r.addr(s, t, size, a.Res) p.replaceArg(c, arg, arg1, calls1) case *StructType: - ctor := isSpecialStruct(t) - if ctor == nil { + gen := specialStructs[t.Name()] + if gen == nil { panic("bad arg returned by mutationArgs: StructType") } - arg1, calls1 := ctor(r, s) + arg1, calls1 := gen(&Gen{r, s}, t, arg.(*GroupArg)) for i, f := range arg1.(*GroupArg).Inner { p.replaceArg(c, arg.(*GroupArg).Inner[i], f, calls1) calls1 = nil @@ -255,7 +253,16 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Pro // predicate pred. It iteratively generates simpler programs and asks pred // whether it is equal to the orginal program or not. If it is equivalent then // the simplification attempt is committed and the process continues. -func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool, crash bool) (*Prog, int) { +func Minimize(p0 *Prog, callIndex0 int, pred0 func(*Prog, int) bool, crash bool) (*Prog, int) { + pred := pred0 + if debug { + pred = func(p *Prog, callIndex int) bool { + if err := p.validate(); err != nil { + panic(err) + } + return pred0(p, callIndex) + } + } name0 := "" if callIndex0 != -1 { if callIndex0 < 0 || callIndex0 >= len(p0.Calls) { @@ -291,7 +298,7 @@ func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool, crash bool) } } // Prepend uber-mmap. - mmap := createMmapCall(uint64(lo), uint64(hi-lo)+1) + mmap := makeMmap(uint64(lo), uint64(hi-lo)+1) p.Calls = append([]*Call{mmap}, p.Calls...) if callIndex != -1 { callIndex++ @@ -496,7 +503,7 @@ func mutationArgs(c *Call) (args, bases []Arg) { foreachArg(c, func(arg, base Arg, _ *[]Arg) { switch typ := arg.Type().(type) { case *StructType: - if isSpecialStruct(typ) == nil { + if specialStructs[typ.Name()] == nil { // For structs only individual fields are updated. return } @@ -524,7 +531,7 @@ func mutationArgs(c *Call) (args, bases []Arg) { return } if base != nil { - if _, ok := base.Type().(*StructType); ok && isSpecialStruct(base.Type()) != nil { + if _, ok := base.Type().(*StructType); ok && specialStructs[base.Type().Name()] != nil { // These special structs are mutated as a whole. return } diff --git a/prog/mutation_test.go b/prog/mutation_test.go index 2f70e0a5d..060fe5f33 100644 --- a/prog/mutation_test.go +++ b/prog/mutation_test.go @@ -1,16 +1,19 @@ // Copyright 2015 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 prog +package prog_test import ( "bytes" "fmt" "testing" + + . "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) func TestClone(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) for i := 0; i < iters; i++ { p := Generate(rs, 10, nil) p1 := p.Clone() @@ -23,7 +26,7 @@ func TestClone(t *testing.T) { } func TestMutate(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) next: for i := 0; i < iters; i++ { p := Generate(rs, 10, nil) @@ -47,7 +50,7 @@ next: } func TestMutateCorpus(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) var corpus []*Prog for i := 0; i < 100; i++ { p := Generate(rs, 10, nil) @@ -137,7 +140,7 @@ func TestMutateTable(t *testing.T) { "readv(r0, &(0x7f0000000000)=[{&(0x7f0000001000)=\"00\", 0x1}, {&(0x7f0000002000)=\"00\", 0x2}, {&(0x7f0000000000)=\"00\", 0x3}], 0x3)\n", }, } - rs, _ := initTest(t) + rs, _ := InitTest(t) nextTest: for ti, test := range tests { p, err := Deserialize([]byte(test[0])) @@ -288,35 +291,23 @@ func TestMinimize(t *testing.T) { } func TestMinimizeRandom(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) iters /= 10 // Long test. for i := 0; i < iters; i++ { p := Generate(rs, 5, nil) Minimize(p, len(p.Calls)-1, func(p1 *Prog, callIndex int) bool { - if err := p1.validate(); err != nil { - t.Fatalf("invalid program: %v", err) - } return false }, true) Minimize(p, len(p.Calls)-1, func(p1 *Prog, callIndex int) bool { - if err := p1.validate(); err != nil { - t.Fatalf("invalid program: %v", err) - } return true }, true) } for i := 0; i < iters; i++ { p := Generate(rs, 5, nil) Minimize(p, len(p.Calls)-1, func(p1 *Prog, callIndex int) bool { - if err := p1.validate(); err != nil { - t.Fatalf("invalid program: %v", err) - } return false }, false) Minimize(p, len(p.Calls)-1, func(p1 *Prog, callIndex int) bool { - if err := p1.validate(); err != nil { - t.Fatalf("invalid program: %v", err) - } return true }, false) } diff --git a/prog/prio.go b/prog/prio.go index 92d2ea3db..c64ae319b 100644 --- a/prog/prio.go +++ b/prog/prio.go @@ -7,8 +7,6 @@ import ( "fmt" "math/rand" "sort" - - . "github.com/google/syzkaller/sys" ) // Calulation of call-to-call priorities. diff --git a/prog/prog.go b/prog/prog.go index 6e18f3a4a..579bac4e0 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -5,8 +5,6 @@ package prog import ( "fmt" - - . "github.com/google/syzkaller/sys" ) type Prog struct { @@ -224,11 +222,11 @@ func encodeValue(value uint64, size uint64, bigEndian bool) uint64 { } } -func constArg(t Type, v uint64) Arg { +func MakeConstArg(t Type, v uint64) Arg { return &ConstArg{ArgCommon: ArgCommon{typ: t}, Val: v} } -func resultArg(t Type, r Arg, v uint64) Arg { +func MakeResultArg(t Type, r Arg, v uint64) Arg { arg := &ResultArg{ArgCommon: ArgCommon{typ: t}, Res: r, Val: v} if r == nil { return arg @@ -249,11 +247,11 @@ func dataArg(t Type, data []byte) Arg { return &DataArg{ArgCommon: ArgCommon{typ: t}, Data: append([]byte{}, data...)} } -func pointerArg(t Type, page uint64, off int, npages uint64, obj Arg) Arg { +func MakePointerArg(t Type, page uint64, off int, npages uint64, obj Arg) Arg { return &PointerArg{ArgCommon: ArgCommon{typ: t}, PageIndex: page, PageOffset: off, PagesNum: npages, Res: obj} } -func groupArg(t Type, inner []Arg) Arg { +func MakeGroupArg(t Type, inner []Arg) Arg { return &GroupArg{ArgCommon: ArgCommon{typ: t}, Inner: inner} } @@ -261,16 +259,16 @@ func unionArg(t Type, opt Arg, typ Type) Arg { return &UnionArg{ArgCommon: ArgCommon{typ: t}, Option: opt, OptionType: typ} } -func returnArg(t Type) Arg { +func MakeReturnArg(t Type) Arg { return &ReturnArg{ArgCommon: ArgCommon{typ: t}} } func defaultArg(t Type) Arg { switch typ := t.(type) { case *IntType, *ConstType, *FlagsType, *LenType, *ProcType, *CsumType: - return constArg(t, t.Default()) + return MakeConstArg(t, t.Default()) case *ResourceType: - return resultArg(t, nil, typ.Desc.Type.Default()) + return MakeResultArg(t, nil, typ.Desc.Type.Default()) case *BufferType: var data []byte if typ.Kind == BufferString && typ.TypeSize != 0 { @@ -278,23 +276,23 @@ func defaultArg(t Type) Arg { } return dataArg(t, data) case *ArrayType: - return groupArg(t, nil) + return MakeGroupArg(t, nil) case *StructType: var inner []Arg for _, field := range typ.Fields { inner = append(inner, defaultArg(field)) } - return groupArg(t, inner) + return MakeGroupArg(t, inner) case *UnionType: return unionArg(t, defaultArg(typ.Fields[0]), typ.Fields[0]) case *VmaType: - return pointerArg(t, 0, 0, 1, nil) + return MakePointerArg(t, 0, 0, 1, nil) case *PtrType: var res Arg if !t.Optional() && t.Dir() != DirOut { res = defaultArg(typ.Type) } - return pointerArg(t, 0, 0, 0, res) + return MakePointerArg(t, 0, 0, 0, res) default: panic("unknown arg type") } @@ -364,7 +362,7 @@ func (p *Prog) removeArg(c *Call, arg0 Arg) { if _, ok := arg1.(*ResultArg); !ok { panic("use references not ArgResult") } - arg2 := resultArg(arg1.Type(), nil, arg1.Type().Default()) + arg2 := MakeResultArg(arg1.Type(), nil, arg1.Type().Default()) p.replaceArg(c, arg1, arg2, nil) } } diff --git a/prog/prog_test.go b/prog/prog_test.go index b377846fb..69aa9c166 100644 --- a/prog/prog_test.go +++ b/prog/prog_test.go @@ -6,47 +6,29 @@ package prog import ( "bytes" "fmt" - "math/rand" "testing" - "time" - - . "github.com/google/syzkaller/sys" + //. "github.com/google/syzkaller/prog" + //_ "github.com/google/syzkaller/sys" ) -func init() { - debug = true -} - -func initTest(t *testing.T) (rand.Source, int) { - t.Parallel() - iters := 10000 - if testing.Short() { - iters = 100 - } - seed := int64(time.Now().UnixNano()) - rs := rand.NewSource(seed) - t.Logf("seed=%v", seed) - return rs, iters -} - func TestGeneration(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) for i := 0; i < iters; i++ { Generate(rs, 20, nil) } } func TestDefault(t *testing.T) { - initTest(t) + InitTest(t) for _, meta := range SyscallMap { for _, t := range meta.Args { - defaultArg(t) + DefaultArg(t) } } } func TestDefaultCallArgs(t *testing.T) { - initTest(t) + InitTest(t) for _, meta := range SyscallMap { // Ensure that we can restore all arguments of all calls. prog := fmt.Sprintf("%v()", meta.Name) @@ -61,7 +43,7 @@ func TestDefaultCallArgs(t *testing.T) { } func TestSerialize(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) for i := 0; i < iters; i++ { p := Generate(rs, 10, nil) data := p.Serialize() @@ -83,9 +65,10 @@ func TestSerialize(t *testing.T) { } func TestVmaType(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) meta := SyscallMap["syz_test$vma0"] r := newRand(rs) + pageSize := PageSize() for i := 0; i < iters; i++ { s := newState(nil) calls := r.generateParticularCall(s, meta) diff --git a/prog/rand.go b/prog/rand.go index 110436e37..1993d7461 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -12,7 +12,6 @@ import ( "sync" "github.com/google/syzkaller/pkg/ifuzz" - . "github.com/google/syzkaller/sys" ) var pageStartPool = sync.Pool{New: func() interface{} { return new([]uint64) }} @@ -224,102 +223,6 @@ func (r *randGen) randStringImpl(s *state, vals []string) []byte { return buf.Bytes() } -func isSpecialStruct(typ Type) func(r *randGen, s *state) (Arg, []*Call) { - a, ok := typ.(*StructType) - if !ok { - panic("must be a struct") - } - switch typ.Name() { - case "timespec": - return func(r *randGen, s *state) (Arg, []*Call) { - return r.timespec(s, a, false) - } - case "timeval": - return func(r *randGen, s *state) (Arg, []*Call) { - return r.timespec(s, a, true) - } - } - return nil -} - -func (r *randGen) timespec(s *state, typ *StructType, usec bool) (arg Arg, calls []*Call) { - // We need to generate timespec/timeval that are either (1) definitely in the past, - // or (2) definitely in unreachable fututre, or (3) few ms ahead of now. - // Note timespec/timeval can be absolute or relative to now. - switch { - case r.nOutOf(1, 4): - // now for relative, past for absolute - arg = groupArg(typ, []Arg{ - resultArg(typ.Fields[0], nil, 0), - resultArg(typ.Fields[1], nil, 0), - }) - case r.nOutOf(1, 3): - // few ms ahead for relative, past for absolute - nsec := uint64(10 * 1e6) - if usec { - nsec /= 1e3 - } - arg = groupArg(typ, []Arg{ - resultArg(typ.Fields[0], nil, 0), - resultArg(typ.Fields[1], nil, nsec), - }) - case r.nOutOf(1, 2): - // unreachable fututre for both relative and absolute - arg = groupArg(typ, []Arg{ - resultArg(typ.Fields[0], nil, 2e9), - resultArg(typ.Fields[1], nil, 0), - }) - default: - // few ms ahead for absolute - meta := SyscallMap["clock_gettime"] - ptrArgType := meta.Args[1].(*PtrType) - argType := ptrArgType.Type.(*StructType) - tp := groupArg(argType, []Arg{ - resultArg(argType.Fields[0], nil, 0), - resultArg(argType.Fields[1], nil, 0), - }) - var tpaddr Arg - tpaddr, calls = r.addr(s, ptrArgType, 16, tp) - gettime := &Call{ - Meta: meta, - Args: []Arg{ - constArg(meta.Args[0], CLOCK_REALTIME), - tpaddr, - }, - Ret: returnArg(meta.Ret), - } - calls = append(calls, gettime) - sec := resultArg(typ.Fields[0], tp.(*GroupArg).Inner[0], 0) - nsec := resultArg(typ.Fields[1], tp.(*GroupArg).Inner[1], 0) - if usec { - nsec.(*ResultArg).OpDiv = 1e3 - nsec.(*ResultArg).OpAdd = 10 * 1e3 - } else { - nsec.(*ResultArg).OpAdd = 10 * 1e6 - } - arg = groupArg(typ, []Arg{sec, nsec}) - } - return -} - -// createMmapCall creates a "normal" mmap call that maps [start, start+npages) page range. -func createMmapCall(start, npages uint64) *Call { - meta := SyscallMap["mmap"] - mmap := &Call{ - Meta: meta, - Args: []Arg{ - pointerArg(meta.Args[0], start, 0, npages, nil), - constArg(meta.Args[1], npages*pageSize), - constArg(meta.Args[2], PROT_READ|PROT_WRITE), - constArg(meta.Args[3], MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED), - resultArg(meta.Args[4], nil, InvalidFD), - constArg(meta.Args[5], 0), - }, - Ret: returnArg(meta.Ret), - } - return mmap -} - func (r *randGen) addr1(s *state, typ Type, size uint64, data Arg) (Arg, []*Call) { npages := (size + pageSize - 1) / pageSize if npages == 0 { @@ -339,8 +242,8 @@ func (r *randGen) addr1(s *state, typ Type, size uint64, data Arg) (Arg, []*Call if !free { continue } - c := createMmapCall(i, npages) - return pointerArg(typ, i, 0, 0, data), []*Call{c} + c := makeMmap(i, npages) + return MakePointerArg(typ, i, 0, 0, data), []*Call{c} } return r.randPageAddr(s, typ, npages, data, false), nil } @@ -357,7 +260,7 @@ func (r *randGen) addr(s *state, typ Type, size uint64, data Arg) (Arg, []*Call) case r.nOutOf(50, 52): a.PageOffset = -int(size) case r.nOutOf(1, 2): - a.PageOffset = r.Intn(pageSize) + a.PageOffset = r.Intn(int(pageSize)) default: if size > 0 { a.PageOffset = -r.Intn(int(size)) @@ -395,13 +298,13 @@ func (r *randGen) randPageAddr(s *state, typ Type, npages uint64, data Arg, vma } *poolPtr = starts pageStartPool.Put(poolPtr) - return pointerArg(typ, page, 0, npages, data) + return MakePointerArg(typ, page, 0, npages, data) } func (r *randGen) createResource(s *state, res *ResourceType) (arg Arg, calls []*Call) { if r.inCreateResource { special := res.SpecialValues() - return resultArg(res, nil, special[r.Intn(len(special))]), nil + return MakeResultArg(res, nil, special[r.Intn(len(special))]), nil } r.inCreateResource = true defer func() { r.inCreateResource = false }() @@ -428,7 +331,7 @@ func (r *randGen) createResource(s *state, res *ResourceType) (arg Arg, calls [] metas = append(metas, meta) } if len(metas) == 0 { - return resultArg(res, nil, res.Default()), nil + return MakeResultArg(res, nil, res.Default()), nil } // Now we have a set of candidate calls that can create the necessary resource. @@ -447,7 +350,7 @@ func (r *randGen) createResource(s *state, res *ResourceType) (arg Arg, calls [] } if len(allres) != 0 { // Bingo! - arg := resultArg(res, allres[r.Intn(len(allres))], 0) + arg := MakeResultArg(res, allres[r.Intn(len(allres))], 0) return arg, calls } // Discard unsuccessful calls. @@ -550,7 +453,7 @@ func (r *randGen) generateCall(s *state, p *Prog) []*Call { func (r *randGen) generateParticularCall(s *state, meta *Syscall) (calls []*Call) { c := &Call{ Meta: meta, - Ret: returnArg(meta.Ret), + Ret: MakeReturnArg(meta.Ret), } c.Args, calls = r.generateArgs(s, meta.Args) assignSizesCall(c) @@ -629,7 +532,7 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) { } }() if r.recDepth[str.Name()] >= 3 { - return pointerArg(typ, 0, 0, 0, nil), nil + return MakePointerArg(typ, 0, 0, 0, nil), nil } } } @@ -650,7 +553,7 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) { } } if len(allres) != 0 { - arg = resultArg(a, allres[r.Intn(len(allres))], 0) + arg = MakeResultArg(a, allres[r.Intn(len(allres))], 0) } else { arg, calls = r.createResource(s, a) } @@ -659,7 +562,7 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) { arg, calls = r.createResource(s, a) default: special := a.SpecialValues() - arg = resultArg(a, nil, special[r.Intn(len(special))]) + arg = MakeResultArg(a, nil, special[r.Intn(len(special))]) } return arg, calls case *BufferType: @@ -707,9 +610,9 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) { arg := r.randPageAddr(s, a, npages, nil, true) return arg, nil case *FlagsType: - return constArg(a, r.flags(a.Vals)), nil + return MakeConstArg(a, r.flags(a.Vals)), nil case *ConstType: - return constArg(a, a.Val), nil + return MakeConstArg(a, a.Val), nil case *IntType: v := r.randInt() switch a.Kind { @@ -725,9 +628,9 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) { case IntRange: v = r.randRangeInt(a.RangeBegin, a.RangeEnd) } - return constArg(a, v), nil + return MakeConstArg(a, v), nil case *ProcType: - return constArg(a, r.rand(int(a.ValuesPerProc))), nil + return MakeConstArg(a, r.rand(int(a.ValuesPerProc))), nil case *ArrayType: var count uint64 switch a.Kind { @@ -743,14 +646,14 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) { inner = append(inner, arg1) calls = append(calls, calls1...) } - return groupArg(a, inner), calls + return MakeGroupArg(a, inner), calls case *StructType: - if ctor := isSpecialStruct(a); ctor != nil && a.Dir() != DirOut { - arg, calls = ctor(r, s) + if gen := specialStructs[a.Name()]; gen != nil && a.Dir() != DirOut { + arg, calls = gen(&Gen{r, s}, a, nil) return } args, calls := r.generateArgs(s, a.Fields) - group := groupArg(a, args) + group := MakeGroupArg(a, args) return group, calls case *UnionType: optType := a.Fields[r.Intn(len(a.Fields))] @@ -763,7 +666,7 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) { // So try to reuse a previously used address. addrs := s.resources["iocbptr"] addr := addrs[r.Intn(len(addrs))].(*PointerArg) - arg = pointerArg(a, addr.PageIndex, addr.PageOffset, addr.PagesNum, inner) + arg = MakePointerArg(a, addr.PageIndex, addr.PageOffset, addr.PagesNum, inner) return arg, calls } arg, calls1 := r.addr(s, a, inner.Size(), inner) @@ -771,9 +674,9 @@ func (r *randGen) generateArg(s *state, typ Type) (arg Arg, calls []*Call) { return arg, calls case *LenType: // Return placeholder value of 0 while generating len arg. - return constArg(a, 0), nil + return MakeConstArg(a, 0), nil case *CsumType: - return constArg(a, 0), nil + return MakeConstArg(a, 0), nil default: panic("unknown argument type") } diff --git a/prog/size.go b/prog/size.go index b715f6f26..b9920c19b 100644 --- a/prog/size.go +++ b/prog/size.go @@ -5,32 +5,30 @@ package prog import ( "fmt" - - . "github.com/google/syzkaller/sys" ) func generateSize(arg Arg, lenType *LenType) Arg { if arg == nil { // Arg is an optional pointer, set size to 0. - return constArg(lenType, 0) + return MakeConstArg(lenType, 0) } switch arg.Type().(type) { case *VmaType: a := arg.(*PointerArg) - return constArg(lenType, a.PagesNum*pageSize) + return MakeConstArg(lenType, a.PagesNum*pageSize) case *ArrayType: a := arg.(*GroupArg) if lenType.ByteSize != 0 { - return constArg(lenType, a.Size()/lenType.ByteSize) + return MakeConstArg(lenType, a.Size()/lenType.ByteSize) } else { - return constArg(lenType, uint64(len(a.Inner))) + return MakeConstArg(lenType, uint64(len(a.Inner))) } default: if lenType.ByteSize != 0 { - return constArg(lenType, arg.Size()/lenType.ByteSize) + return MakeConstArg(lenType, arg.Size()/lenType.ByteSize) } else { - return constArg(lenType, arg.Size()) + return MakeConstArg(lenType, arg.Size()) } } } diff --git a/prog/size_test.go b/prog/size_test.go index c72e5283f..778725d49 100644 --- a/prog/size_test.go +++ b/prog/size_test.go @@ -1,21 +1,24 @@ // Copyright 2016 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 prog +package prog_test import ( "bytes" "strings" "testing" + + . "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) func TestAssignSizeRandom(t *testing.T) { - rs, iters := initTest(t) + rs, iters := InitTest(t) for i := 0; i < iters; i++ { p := Generate(rs, 10, nil) data0 := p.Serialize() for _, call := range p.Calls { - assignSizesCall(call) + AssignSizesCall(call) } if data1 := p.Serialize(); !bytes.Equal(data0, data1) { t.Fatalf("different lens assigned, initial: %v, new: %v", data0, data1) @@ -23,7 +26,7 @@ func TestAssignSizeRandom(t *testing.T) { p.Mutate(rs, 10, nil, nil) data0 = p.Serialize() for _, call := range p.Calls { - assignSizesCall(call) + AssignSizesCall(call) } if data1 := p.Serialize(); !bytes.Equal(data0, data1) { t.Fatalf("different lens assigned, initial: %v, new: %v", data0, data1) @@ -128,7 +131,7 @@ func TestAssignSize(t *testing.T) { t.Fatalf("failed to deserialize prog %v: %v", i, err) } for _, call := range p.Calls { - assignSizesCall(call) + AssignSizesCall(call) } p1 := strings.TrimSpace(string(p.Serialize())) if p1 != test.sizedProg { diff --git a/prog/target.go b/prog/target.go new file mode 100644 index 000000000..f5dde79f4 --- /dev/null +++ b/prog/target.go @@ -0,0 +1,117 @@ +// Copyright 2017 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 prog + +import ( + "fmt" +) + +// Target describes target OS/arch pair. +type Target struct { + OS string + Arch string + PtrSize uint64 + PageSize uint64 + DataOffset uint64 + + Syscalls []*Syscall + Resources []*ResourceDesc + + // MakeMmap creates call that maps [start, start+npages) page range. + MakeMmap func(start, npages uint64) *Call + + // AnalyzeMmap analyzes the call c regarding mapping/unmapping memory. + // If it maps/unmaps any memory returns [start, start+npages) range, + // otherwise returns npages = 0. + AnalyzeMmap func(c *Call) (start, npages uint64, mapped bool) + + // SanitizeCall neutralizes harmful calls. + SanitizeCall func(c *Call) + + // SpecialStructs allows target to do custom generation/mutation for some struct types. + // Map key is struct name for which custom generation/mutation is required. + // Map value is custom generation/mutation function that will be called + // for the corresponding structs. g is helper object that allows generate random numbers, + // allocate memory, etc. typ is the struct type. old is the old value of the struct + // for mutation, or nil for generation. The function returns a new value of the struct, + // and optionally any calls that need to be inserted before the arg reference. + SpecialStructs map[string]func(g *Gen, typ *StructType, old *GroupArg) (Arg, []*Call) + + resourceMap map[string]*ResourceDesc + syscallMap map[string]*Syscall + resourceCtors map[string][]*Syscall +} + +type StructGen func(g *Gen, typ *StructType, old *GroupArg) (Arg, []*Call) + +var targets = make(map[string]*Target) + +func RegisterTarget(target *Target) { + key := target.OS + "/" + target.Arch + if targets[key] != nil { + panic(fmt.Sprintf("duplicate target %v", key)) + } + initTarget(target) + targets[key] = target + + // For now we copy target to global vars + // because majority of the code is not prepared for multiple targets. + if len(targets) > 1 { + panic("only 1 target is supported") + } + Syscalls = target.Syscalls + SyscallMap = target.syscallMap + Resources = target.resourceMap + resourceCtors = target.resourceCtors + ptrSize = target.PtrSize + pageSize = target.PageSize + dataOffset = target.DataOffset + + makeMmap = target.MakeMmap + analyzeMmap = target.AnalyzeMmap + sanitizeCall = target.SanitizeCall + specialStructs = target.SpecialStructs +} + +func initTarget(target *Target) { + target.syscallMap = make(map[string]*Syscall) + for _, c := range target.Syscalls { + target.syscallMap[c.Name] = c + } + target.resourceMap = make(map[string]*ResourceDesc) + target.resourceCtors = make(map[string][]*Syscall) + for _, r := range target.Resources { + target.resourceMap[r.Name] = r + target.resourceCtors[r.Name] = calcResourceCtors(r.Kind, false) + } +} + +type Gen struct { + r *randGen + s *state +} + +func (g *Gen) NOutOf(n, outOf int) bool { + return g.r.nOutOf(n, outOf) +} + +func (g *Gen) Alloc(ptrType Type, data Arg) (Arg, []*Call) { + return g.r.addr(g.s, ptrType, data.Size(), data) +} + +var ( + ptrSize uint64 + pageSize uint64 + dataOffset uint64 + + Syscalls []*Syscall + SyscallMap map[string]*Syscall + Resources map[string]*ResourceDesc + resourceCtors map[string][]*Syscall + + makeMmap func(start, npages uint64) *Call + analyzeMmap func(c *Call) (start, npages uint64, mapped bool) + sanitizeCall func(c *Call) + specialStructs map[string]func(g *Gen, typ *StructType, old *GroupArg) (Arg, []*Call) +) diff --git a/prog/types.go b/prog/types.go new file mode 100644 index 000000000..6fdc0d975 --- /dev/null +++ b/prog/types.go @@ -0,0 +1,457 @@ +// Copyright 2015/2016 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 prog + +import ( + "fmt" +) + +type Syscall struct { + ID int + NR uint64 // kernel syscall number + Name string + CallName string + Args []Type + Ret Type +} + +type Dir int + +const ( + DirIn Dir = iota + DirOut + DirInOut +) + +type Type interface { + Name() string + FieldName() string + Dir() Dir + Optional() bool + Default() uint64 + Varlen() bool + Size() uint64 + BitfieldOffset() uint64 + BitfieldLength() uint64 + BitfieldMiddle() bool // returns true for all but last bitfield in a group +} + +func IsPad(t Type) bool { + if ct, ok := t.(*ConstType); ok && ct.IsPad { + return true + } + return false +} + +type TypeCommon struct { + TypeName string + FldName string // for struct fields and named args + TypeSize uint64 // static size of the type, or 0 for variable size types + ArgDir Dir + IsOptional bool +} + +func (t *TypeCommon) Name() string { + return t.TypeName +} + +func (t *TypeCommon) FieldName() string { + return t.FldName +} + +func (t *TypeCommon) Optional() bool { + return t.IsOptional +} + +func (t *TypeCommon) Default() uint64 { + return 0 +} + +func (t *TypeCommon) Size() uint64 { + if t.Varlen() { + panic(fmt.Sprintf("static type size is not known: %#v", t)) + } + return t.TypeSize +} + +func (t *TypeCommon) Varlen() bool { + return t.TypeSize == 0 +} + +func (t *TypeCommon) BitfieldOffset() uint64 { + return 0 +} + +func (t *TypeCommon) BitfieldLength() uint64 { + return 0 +} + +func (t *TypeCommon) BitfieldMiddle() bool { + return false +} + +func (t TypeCommon) Dir() Dir { + return t.ArgDir +} + +type ResourceDesc struct { + Name string + Type Type + Kind []string + Values []uint64 +} + +type ResourceType struct { + TypeCommon + Desc *ResourceDesc +} + +func (t *ResourceType) Default() uint64 { + return t.Desc.Values[0] +} + +func (t *ResourceType) SpecialValues() []uint64 { + return t.Desc.Values +} + +type IntTypeCommon struct { + TypeCommon + BitfieldOff uint64 + BitfieldLen uint64 + BigEndian bool + BitfieldMdl bool +} + +func (t *IntTypeCommon) BitfieldOffset() uint64 { + return t.BitfieldOff +} + +func (t *IntTypeCommon) BitfieldLength() uint64 { + return t.BitfieldLen +} + +func (t *IntTypeCommon) BitfieldMiddle() bool { + return t.BitfieldMdl +} + +type ConstType struct { + IntTypeCommon + Val uint64 + IsPad bool +} + +type IntKind int + +const ( + IntPlain IntKind = iota + IntFileoff // offset within a file + IntRange +) + +type IntType struct { + IntTypeCommon + Kind IntKind + RangeBegin uint64 + RangeEnd uint64 +} + +type FlagsType struct { + IntTypeCommon + Vals []uint64 +} + +type LenType struct { + IntTypeCommon + ByteSize uint64 // want size in multiple of bytes instead of array size + Buf string +} + +type ProcType struct { + IntTypeCommon + ValuesStart uint64 + ValuesPerProc uint64 +} + +type CsumKind int + +const ( + CsumInet CsumKind = iota + CsumPseudo +) + +type CsumType struct { + IntTypeCommon + Kind CsumKind + Buf string + Protocol uint64 // for CsumPseudo +} + +type VmaType struct { + TypeCommon + RangeBegin uint64 // in pages + RangeEnd uint64 +} + +type BufferKind int + +const ( + BufferBlobRand BufferKind = iota + BufferBlobRange + BufferString + BufferFilename + BufferText +) + +type TextKind int + +const ( + Text_x86_real TextKind = iota + Text_x86_16 + Text_x86_32 + Text_x86_64 + Text_arm64 +) + +type BufferType struct { + TypeCommon + Kind BufferKind + RangeBegin uint64 // for BufferBlobRange kind + RangeEnd uint64 // for BufferBlobRange kind + Text TextKind // for BufferText + SubKind string + Values []string // possible values for BufferString kind +} + +type ArrayKind int + +const ( + ArrayRandLen ArrayKind = iota + ArrayRangeLen +) + +type ArrayType struct { + TypeCommon + Type Type + Kind ArrayKind + RangeBegin uint64 + RangeEnd uint64 +} + +type PtrType struct { + TypeCommon + Type Type +} + +type StructType struct { + Key StructKey + FldName string + *StructDesc +} + +func (t *StructType) FieldName() string { + return t.FldName +} + +type UnionType struct { + Key StructKey + FldName string + *StructDesc +} + +func (t *UnionType) FieldName() string { + return t.FldName +} + +type StructDesc struct { + TypeCommon + Fields []Type + AlignAttr uint64 +} + +func (t *StructDesc) FieldName() string { + panic("must not be called") +} + +type StructKey struct { + Name string + Dir Dir +} + +type KeyedStruct struct { + Key StructKey + Desc *StructDesc +} + +// ResourceConstructors returns a list of calls that can create a resource of the given kind. +func ResourceConstructors(name string) []*Syscall { + return resourceCtors[name] +} + +func calcResourceCtors(kind []string, precise bool) []*Syscall { + // Find calls that produce the necessary resources. + var metas []*Syscall + for _, meta := range Syscalls { + // Recurse into arguments to see if there is an out/inout arg of necessary type. + ok := false + ForeachType(meta, func(typ Type) { + if ok { + return + } + switch typ1 := typ.(type) { + case *ResourceType: + if typ1.Dir() != DirIn && isCompatibleResource(kind, typ1.Desc.Kind, precise) { + ok = true + } + } + }) + if ok { + metas = append(metas, meta) + } + } + return metas +} + +// IsCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst. +func IsCompatibleResource(dst, src string) bool { + dstRes := Resources[dst] + if dstRes == nil { + panic(fmt.Sprintf("unknown resource '%v'", dst)) + } + srcRes := Resources[src] + if srcRes == nil { + panic(fmt.Sprintf("unknown resource '%v'", src)) + } + return isCompatibleResource(dstRes.Kind, srcRes.Kind, false) +} + +// isCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst. +// If precise is true, then it does not allow passing a less specialized resource (e.g. fd) +// as a more specialized resource (e.g. socket). Otherwise it does. +func isCompatibleResource(dst, src []string, precise bool) bool { + if len(dst) > len(src) { + // dst is more specialized, e.g dst=socket, src=fd. + if precise { + return false + } + dst = dst[:len(src)] + } + if len(src) > len(dst) { + // src is more specialized, e.g dst=fd, src=socket. + src = src[:len(dst)] + } + for i, k := range dst { + if k != src[i] { + return false + } + } + return true +} + +func (c *Syscall) InputResources() []*ResourceType { + var resources []*ResourceType + ForeachType(c, func(typ Type) { + switch typ1 := typ.(type) { + case *ResourceType: + if typ1.Dir() != DirOut && !typ1.IsOptional { + resources = append(resources, typ1) + } + } + }) + return resources +} + +func TransitivelyEnabledCalls(enabled map[*Syscall]bool) map[*Syscall]bool { + supported := make(map[*Syscall]bool) + for c := range enabled { + supported[c] = true + } + inputResources := make(map[*Syscall][]*ResourceType) + ctors := make(map[string][]*Syscall) + for c := range supported { + inputs := c.InputResources() + inputResources[c] = inputs + for _, res := range inputs { + if _, ok := ctors[res.Desc.Name]; ok { + continue + } + ctors[res.Desc.Name] = calcResourceCtors(res.Desc.Kind, true) + } + } + for { + n := len(supported) + haveGettime := supported[SyscallMap["clock_gettime"]] + for c := range supported { + canCreate := true + for _, res := range inputResources[c] { + noctors := true + for _, ctor := range ctors[res.Desc.Name] { + if supported[ctor] { + noctors = false + break + } + } + if noctors { + canCreate = false + break + } + } + // We need to support structs as resources, + // but for now we just special-case timespec/timeval. + if canCreate && !haveGettime { + ForeachType(c, func(typ Type) { + if a, ok := typ.(*StructType); ok && a.Dir() != DirOut && (a.Name() == "timespec" || a.Name() == "timeval") { + canCreate = false + } + }) + } + if !canCreate { + delete(supported, c) + } + } + if n == len(supported) { + break + } + } + return supported +} + +func ForeachType(meta *Syscall, f func(Type)) { + seen := make(map[*StructDesc]bool) + var rec func(t Type) + rec = func(t Type) { + f(t) + switch a := t.(type) { + case *PtrType: + rec(a.Type) + case *ArrayType: + rec(a.Type) + case *StructType: + if seen[a.StructDesc] { + return // prune recursion via pointers to structs/unions + } + seen[a.StructDesc] = true + for _, f := range a.Fields { + rec(f) + } + case *UnionType: + if seen[a.StructDesc] { + return // prune recursion via pointers to structs/unions + } + seen[a.StructDesc] = true + for _, opt := range a.Fields { + rec(opt) + } + case *ResourceType, *BufferType, *VmaType, *LenType, + *FlagsType, *ConstType, *IntType, *ProcType, *CsumType: + default: + panic("unknown type") + } + } + for _, t := range meta.Args { + rec(t) + } + if meta.Ret != nil { + rec(meta.Ret) + } +} diff --git a/prog/validation.go b/prog/validation.go index 51520b29b..d5b4a4c04 100644 --- a/prog/validation.go +++ b/prog/validation.go @@ -5,8 +5,6 @@ package prog import ( "fmt" - - . "github.com/google/syzkaller/sys" ) var debug = false // enabled in tests diff --git a/sys/decl.go b/sys/decl.go deleted file mode 100644 index 82d27e915..000000000 --- a/sys/decl.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2015/2016 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 sys - -import ( - "fmt" -) - -type Syscall struct { - ID int - NR uint64 // kernel syscall number - Name string - CallName string - Args []Type - Ret Type -} - -type Dir int - -const ( - DirIn Dir = iota - DirOut - DirInOut -) - -type Type interface { - Name() string - FieldName() string - Dir() Dir - Optional() bool - Default() uint64 - Varlen() bool - Size() uint64 - BitfieldOffset() uint64 - BitfieldLength() uint64 - BitfieldMiddle() bool // returns true for all but last bitfield in a group -} - -func IsPad(t Type) bool { - if ct, ok := t.(*ConstType); ok && ct.IsPad { - return true - } - return false -} - -type TypeCommon struct { - TypeName string - FldName string // for struct fields and named args - TypeSize uint64 // static size of the type, or 0 for variable size types - ArgDir Dir - IsOptional bool -} - -func (t *TypeCommon) Name() string { - return t.TypeName -} - -func (t *TypeCommon) FieldName() string { - return t.FldName -} - -func (t *TypeCommon) Optional() bool { - return t.IsOptional -} - -func (t *TypeCommon) Default() uint64 { - return 0 -} - -func (t *TypeCommon) Size() uint64 { - if t.Varlen() { - panic(fmt.Sprintf("static type size is not known: %#v", t)) - } - return t.TypeSize -} - -func (t *TypeCommon) Varlen() bool { - return t.TypeSize == 0 -} - -func (t *TypeCommon) BitfieldOffset() uint64 { - return 0 -} - -func (t *TypeCommon) BitfieldLength() uint64 { - return 0 -} - -func (t *TypeCommon) BitfieldMiddle() bool { - return false -} - -func (t TypeCommon) Dir() Dir { - return t.ArgDir -} - -const ( - InvalidFD = ^uint64(0) -) - -type ResourceDesc struct { - Name string - Type Type - Kind []string - Values []uint64 -} - -type ResourceType struct { - TypeCommon - Desc *ResourceDesc -} - -func (t *ResourceType) Default() uint64 { - return t.Desc.Values[0] -} - -func (t *ResourceType) SpecialValues() []uint64 { - return t.Desc.Values -} - -type IntTypeCommon struct { - TypeCommon - BitfieldOff uint64 - BitfieldLen uint64 - BigEndian bool - BitfieldMdl bool -} - -func (t *IntTypeCommon) BitfieldOffset() uint64 { - return t.BitfieldOff -} - -func (t *IntTypeCommon) BitfieldLength() uint64 { - return t.BitfieldLen -} - -func (t *IntTypeCommon) BitfieldMiddle() bool { - return t.BitfieldMdl -} - -type ConstType struct { - IntTypeCommon - Val uint64 - IsPad bool -} - -type IntKind int - -const ( - IntPlain IntKind = iota - IntFileoff // offset within a file - IntRange -) - -type IntType struct { - IntTypeCommon - Kind IntKind - RangeBegin uint64 - RangeEnd uint64 -} - -type FlagsType struct { - IntTypeCommon - Vals []uint64 -} - -type LenType struct { - IntTypeCommon - ByteSize uint64 // want size in multiple of bytes instead of array size - Buf string -} - -type ProcType struct { - IntTypeCommon - ValuesStart uint64 - ValuesPerProc uint64 -} - -type CsumKind int - -const ( - CsumInet CsumKind = iota - CsumPseudo -) - -type CsumType struct { - IntTypeCommon - Kind CsumKind - Buf string - Protocol uint64 // for CsumPseudo -} - -type VmaType struct { - TypeCommon - RangeBegin uint64 // in pages - RangeEnd uint64 -} - -type BufferKind int - -const ( - BufferBlobRand BufferKind = iota - BufferBlobRange - BufferString - BufferFilename - BufferText -) - -type TextKind int - -const ( - Text_x86_real TextKind = iota - Text_x86_16 - Text_x86_32 - Text_x86_64 - Text_arm64 -) - -type BufferType struct { - TypeCommon - Kind BufferKind - RangeBegin uint64 // for BufferBlobRange kind - RangeEnd uint64 // for BufferBlobRange kind - Text TextKind // for BufferText - SubKind string - Values []string // possible values for BufferString kind -} - -type ArrayKind int - -const ( - ArrayRandLen ArrayKind = iota - ArrayRangeLen -) - -type ArrayType struct { - TypeCommon - Type Type - Kind ArrayKind - RangeBegin uint64 - RangeEnd uint64 -} - -type PtrType struct { - TypeCommon - Type Type -} - -type StructType struct { - Key StructKey - FldName string - *StructDesc -} - -func (t *StructType) FieldName() string { - return t.FldName -} - -type UnionType struct { - Key StructKey - FldName string - *StructDesc -} - -func (t *UnionType) FieldName() string { - return t.FldName -} - -var ( - SyscallMap = make(map[string]*Syscall) - Resources map[string]*ResourceDesc - ctors = make(map[string][]*Syscall) -) - -type StructDesc struct { - TypeCommon - Fields []Type - AlignAttr uint64 -} - -func (t *StructDesc) FieldName() string { - panic("must not be called") -} - -type StructKey struct { - Name string - Dir Dir -} - -type KeyedStruct struct { - Key StructKey - Desc *StructDesc -} - -func initStructFields() { - keyedStructs := make(map[StructKey]*StructDesc) - for _, desc := range structDescs { - keyedStructs[desc.Key] = desc.Desc - } - - for _, c := range Syscalls { - ForeachType(c, func(t Type) { - switch s := t.(type) { - case *StructType: - s.StructDesc = keyedStructs[s.Key] - if s.StructDesc == nil { - panic("no struct desc") - } - case *UnionType: - s.StructDesc = keyedStructs[s.Key] - if s.StructDesc == nil { - panic("no union desc") - } - } - }) - } -} - -// ResourceConstructors returns a list of calls that can create a resource of the given kind. -func ResourceConstructors(name string) []*Syscall { - return ctors[name] -} - -func initResources() { - Resources = make(map[string]*ResourceDesc) - for _, res := range resourceArray { - Resources[res.Name] = res - } - for _, c := range Syscalls { - ForeachType(c, func(t Type) { - if r, ok := t.(*ResourceType); ok { - r.Desc = Resources[r.TypeName] - } - }) - } - for _, res := range resourceArray { - ctors[res.Name] = resourceCtors(res.Kind, false) - } -} - -func resourceCtors(kind []string, precise bool) []*Syscall { - // Find calls that produce the necessary resources. - var metas []*Syscall - for _, meta := range Syscalls { - // Recurse into arguments to see if there is an out/inout arg of necessary type. - ok := false - ForeachType(meta, func(typ Type) { - if ok { - return - } - switch typ1 := typ.(type) { - case *ResourceType: - if typ1.Dir() != DirIn && isCompatibleResource(kind, typ1.Desc.Kind, precise) { - ok = true - } - } - }) - if ok { - metas = append(metas, meta) - } - } - return metas -} - -// IsCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst. -func IsCompatibleResource(dst, src string) bool { - dstRes := Resources[dst] - if dstRes == nil { - panic(fmt.Sprintf("unknown resource '%v'", dst)) - } - srcRes := Resources[src] - if srcRes == nil { - panic(fmt.Sprintf("unknown resource '%v'", src)) - } - return isCompatibleResource(dstRes.Kind, srcRes.Kind, false) -} - -// isCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst. -// If precise is true, then it does not allow passing a less specialized resource (e.g. fd) -// as a more specialized resource (e.g. socket). Otherwise it does. -func isCompatibleResource(dst, src []string, precise bool) bool { - if len(dst) > len(src) { - // dst is more specialized, e.g dst=socket, src=fd. - if precise { - return false - } - dst = dst[:len(src)] - } - if len(src) > len(dst) { - // src is more specialized, e.g dst=fd, src=socket. - src = src[:len(dst)] - } - for i, k := range dst { - if k != src[i] { - return false - } - } - return true -} - -func (c *Syscall) InputResources() []*ResourceType { - var resources []*ResourceType - ForeachType(c, func(typ Type) { - switch typ1 := typ.(type) { - case *ResourceType: - if typ1.Dir() != DirOut && !typ1.IsOptional { - resources = append(resources, typ1) - } - } - }) - return resources -} - -func TransitivelyEnabledCalls(enabled map[*Syscall]bool) map[*Syscall]bool { - supported := make(map[*Syscall]bool) - for c := range enabled { - supported[c] = true - } - inputResources := make(map[*Syscall][]*ResourceType) - ctors := make(map[string][]*Syscall) - for c := range supported { - inputs := c.InputResources() - inputResources[c] = inputs - for _, res := range inputs { - if _, ok := ctors[res.Desc.Name]; ok { - continue - } - ctors[res.Desc.Name] = resourceCtors(res.Desc.Kind, true) - } - } - for { - n := len(supported) - haveGettime := supported[SyscallMap["clock_gettime"]] - for c := range supported { - canCreate := true - for _, res := range inputResources[c] { - noctors := true - for _, ctor := range ctors[res.Desc.Name] { - if supported[ctor] { - noctors = false - break - } - } - if noctors { - canCreate = false - break - } - } - // We need to support structs as resources, - // but for now we just special-case timespec/timeval. - if canCreate && !haveGettime { - ForeachType(c, func(typ Type) { - if a, ok := typ.(*StructType); ok && a.Dir() != DirOut && (a.Name() == "timespec" || a.Name() == "timeval") { - canCreate = false - } - }) - } - if !canCreate { - delete(supported, c) - } - } - if n == len(supported) { - break - } - } - return supported -} - -func ForeachType(meta *Syscall, f func(Type)) { - seen := make(map[*StructDesc]bool) - var rec func(t Type) - rec = func(t Type) { - f(t) - switch a := t.(type) { - case *PtrType: - rec(a.Type) - case *ArrayType: - rec(a.Type) - case *StructType: - if seen[a.StructDesc] { - return // prune recursion via pointers to structs/unions - } - seen[a.StructDesc] = true - for _, f := range a.Fields { - rec(f) - } - case *UnionType: - if seen[a.StructDesc] { - return // prune recursion via pointers to structs/unions - } - seen[a.StructDesc] = true - for _, opt := range a.Fields { - rec(opt) - } - case *ResourceType, *BufferType, *VmaType, *LenType, - *FlagsType, *ConstType, *IntType, *ProcType, *CsumType: - default: - panic("unknown type") - } - } - for _, t := range meta.Args { - rec(t) - } - if meta.Ret != nil { - rec(meta.Ret) - } -} - -func init() { - initStructFields() - initResources() - structDescs = nil - - for _, c := range Syscalls { - if SyscallMap[c.Name] != nil { - println(c.Name) - panic("duplicate syscall") - } - SyscallMap[c.Name] = c - } -} diff --git a/sys/decl_test.go b/sys/decl_test.go deleted file mode 100644 index a156f4f61..000000000 --- a/sys/decl_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2015 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 sys - -import ( - "testing" -) - -func TestResourceCtors(t *testing.T) { - for _, c := range Syscalls { - for _, res := range c.InputResources() { - if len(resourceCtors(res.Desc.Kind, true)) == 0 { - t.Errorf("call %v requires input resource %v, but there are no calls that can create this resource", c.Name, res.Desc.Name) - } - } - } -} - -func TestTransitivelyEnabledCalls(t *testing.T) { - t.Parallel() - calls := make(map[*Syscall]bool) - for _, c := range Syscalls { - calls[c] = true - } - if trans := TransitivelyEnabledCalls(calls); len(calls) != len(trans) { - for c := range calls { - if !trans[c] { - t.Logf("disabled %v", c.Name) - } - } - t.Fatalf("can't create some resource") - } - delete(calls, SyscallMap["epoll_create"]) - if trans := TransitivelyEnabledCalls(calls); len(calls) != len(trans) { - t.Fatalf("still must be able to create epoll fd with epoll_create1") - } - delete(calls, SyscallMap["epoll_create1"]) - trans := TransitivelyEnabledCalls(calls) - if len(calls)-5 != len(trans) || - trans[SyscallMap["epoll_ctl$EPOLL_CTL_ADD"]] || - trans[SyscallMap["epoll_ctl$EPOLL_CTL_MOD"]] || - trans[SyscallMap["epoll_ctl$EPOLL_CTL_DEL"]] || - trans[SyscallMap["epoll_wait"]] || - trans[SyscallMap["epoll_pwait"]] { - t.Fatalf("epoll fd is not disabled") - } -} - -func TestClockGettime(t *testing.T) { - t.Parallel() - calls := make(map[*Syscall]bool) - for _, c := range Syscalls { - calls[c] = true - } - // Removal of clock_gettime should disable all calls that accept timespec/timeval. - delete(calls, SyscallMap["clock_gettime"]) - trans := TransitivelyEnabledCalls(calls) - if len(trans)+10 > len(calls) { - t.Fatalf("clock_gettime did not disable enough calls: before %v, after %v", len(calls), len(trans)) - } -} diff --git a/sys/init.go b/sys/init.go new file mode 100644 index 000000000..edefe3a52 --- /dev/null +++ b/sys/init.go @@ -0,0 +1,257 @@ +// Copyright 2017 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 sys + +import ( + "runtime" + + "github.com/google/syzkaller/prog" +) + +func init() { + lazyInit() + target := &prog.Target{ + OS: runtime.GOOS, + Arch: runtime.GOARCH, + PtrSize: ptrSize, + PageSize: pageSize, + DataOffset: dataOffset, + Syscalls: syscalls, + Resources: resources, + MakeMmap: makeMmap, + AnalyzeMmap: analyzeMmap, + SanitizeCall: sanitizeCall, + SpecialStructs: map[string]func(g *prog.Gen, typ *prog.StructType, old *prog.GroupArg) (prog.Arg, []*prog.Call){ + "timespec": generateTimespec, + "timeval": generateTimespec, + }, + } + prog.RegisterTarget(target) +} + +const ( + // TODO(dvyukov): dehardcode + ptrSize = 8 + pageSize = 4 << 10 + dataOffset = 512 << 20 + invalidFD = ^uint64(0) +) + +var ( + mmapSyscall *prog.Syscall + clockGettimeSyscall *prog.Syscall +) + +// createMmapCall creates a "normal" mmap call that maps [start, start+npages) page range. +func makeMmap(start, npages uint64) *prog.Call { + return &prog.Call{ + Meta: mmapSyscall, + Args: []prog.Arg{ + prog.MakePointerArg(mmapSyscall.Args[0], start, 0, npages, nil), + prog.MakeConstArg(mmapSyscall.Args[1], npages*pageSize), + prog.MakeConstArg(mmapSyscall.Args[2], PROT_READ|PROT_WRITE), + prog.MakeConstArg(mmapSyscall.Args[3], MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED), + prog.MakeResultArg(mmapSyscall.Args[4], nil, invalidFD), + prog.MakeConstArg(mmapSyscall.Args[5], 0), + }, + Ret: prog.MakeReturnArg(mmapSyscall.Ret), + } +} + +func analyzeMmap(c *prog.Call) (start, npages uint64, mapped bool) { + switch c.Meta.Name { + case "mmap": + // Filter out only very wrong arguments. + npages = c.Args[1].(*prog.ConstArg).Val / pageSize + if npages == 0 { + return + } + flags := c.Args[3].(*prog.ConstArg).Val + fd := c.Args[4].(*prog.ResultArg).Val + if flags&MAP_ANONYMOUS == 0 && fd == invalidFD { + return + } + start = c.Args[0].(*prog.PointerArg).PageIndex + mapped = true + return + case "munmap": + start = c.Args[0].(*prog.PointerArg).PageIndex + npages = c.Args[1].(*prog.ConstArg).Val / pageSize + mapped = false + return + case "mremap": + start = c.Args[4].(*prog.PointerArg).PageIndex + npages = c.Args[2].(*prog.ConstArg).Val / pageSize + mapped = true + return + default: + return + } +} + +func sanitizeCall(c *prog.Call) { + switch c.Meta.CallName { + case "mmap": + // Add MAP_FIXED flag, otherwise it produces non-deterministic results. + c.Args[3].(*prog.ConstArg).Val |= MAP_FIXED + case "mremap": + // Add MREMAP_FIXED flag, otherwise it produces non-deterministic results. + flags := c.Args[3].(*prog.ConstArg) + if flags.Val&MREMAP_MAYMOVE != 0 { + flags.Val |= MREMAP_FIXED + } + case "mknod", "mknodat": + pos := 1 + if c.Meta.CallName == "mknodat" { + pos = 2 + } + mode := c.Args[pos].(*prog.ConstArg) + dev := c.Args[pos+1].(*prog.ConstArg) + // Char and block devices read/write io ports, kernel memory and do other nasty things. + // TODO: not required if executor drops privileges. + switch mode.Val & (S_IFREG | S_IFCHR | S_IFBLK | S_IFIFO | S_IFSOCK) { + case S_IFREG, S_IFIFO, S_IFSOCK: + case S_IFBLK: + if dev.Val>>8 == 7 { + break // loop + } + mode.Val &^= S_IFBLK + mode.Val |= S_IFREG + case S_IFCHR: + mode.Val &^= S_IFCHR + mode.Val |= S_IFREG + } + case "syslog": + cmd := c.Args[0].(*prog.ConstArg) + // These disable console output, but we need it. + if cmd.Val == SYSLOG_ACTION_CONSOLE_OFF || cmd.Val == SYSLOG_ACTION_CONSOLE_ON { + cmd.Val = SYSLOG_ACTION_SIZE_UNREAD + } + case "ioctl": + cmd := c.Args[1].(*prog.ConstArg) + // Freeze kills machine. Though, it is an interesting functions, + // so we need to test it somehow. + // TODO: not required if executor drops privileges. + if uint32(cmd.Val) == FIFREEZE { + cmd.Val = FITHAW + } + case "ptrace": + req := c.Args[0].(*prog.ConstArg) + // PTRACE_TRACEME leads to unkillable processes, see: + // https://groups.google.com/forum/#!topic/syzkaller/uGzwvhlCXAw + if req.Val == PTRACE_TRACEME { + req.Val = ^uint64(0) + } + case "exit", "exit_group": + code := c.Args[0].(*prog.ConstArg) + // These codes are reserved by executor. + if code.Val%128 == 67 || code.Val%128 == 68 { + code.Val = 1 + } + } +} + +func generateTimespec(g *prog.Gen, typ *prog.StructType, old *prog.GroupArg) (arg prog.Arg, calls []*prog.Call) { + // We need to generate timespec/timeval that are either + // (1) definitely in the past, or + // (2) definitely in unreachable fututre, or + // (3) few ms ahead of now. + // Note timespec/timeval can be absolute or relative to now. + usec := typ.Name() == "timeval" + switch { + case g.NOutOf(1, 4): + // Now for relative, past for absolute. + arg = prog.MakeGroupArg(typ, []prog.Arg{ + prog.MakeResultArg(typ.Fields[0], nil, 0), + prog.MakeResultArg(typ.Fields[1], nil, 0), + }) + case g.NOutOf(1, 3): + // Few ms ahead for relative, past for absolute + nsec := uint64(10 * 1e6) + if usec { + nsec /= 1e3 + } + arg = prog.MakeGroupArg(typ, []prog.Arg{ + prog.MakeResultArg(typ.Fields[0], nil, 0), + prog.MakeResultArg(typ.Fields[1], nil, nsec), + }) + case g.NOutOf(1, 2): + // Unreachable fututre for both relative and absolute + arg = prog.MakeGroupArg(typ, []prog.Arg{ + prog.MakeResultArg(typ.Fields[0], nil, 2e9), + prog.MakeResultArg(typ.Fields[1], nil, 0), + }) + default: + // Few ms ahead for absolute. + meta := clockGettimeSyscall + ptrArgType := meta.Args[1].(*prog.PtrType) + argType := ptrArgType.Type.(*prog.StructType) + tp := prog.MakeGroupArg(argType, []prog.Arg{ + prog.MakeResultArg(argType.Fields[0], nil, 0), + prog.MakeResultArg(argType.Fields[1], nil, 0), + }) + var tpaddr prog.Arg + tpaddr, calls = g.Alloc(ptrArgType, tp) + gettime := &prog.Call{ + Meta: meta, + Args: []prog.Arg{ + prog.MakeConstArg(meta.Args[0], CLOCK_REALTIME), + tpaddr, + }, + Ret: prog.MakeReturnArg(meta.Ret), + } + calls = append(calls, gettime) + sec := prog.MakeResultArg(typ.Fields[0], tp.(*prog.GroupArg).Inner[0], 0) + nsec := prog.MakeResultArg(typ.Fields[1], tp.(*prog.GroupArg).Inner[1], 0) + if usec { + nsec.(*prog.ResultArg).OpDiv = 1e3 + nsec.(*prog.ResultArg).OpAdd = 10 * 1e3 + } else { + nsec.(*prog.ResultArg).OpAdd = 10 * 1e6 + } + arg = prog.MakeGroupArg(typ, []prog.Arg{sec, nsec}) + } + return +} + +func lazyInit() { + resourceMap := make(map[string]*prog.ResourceDesc) + for _, res := range resources { + resourceMap[res.Name] = res + } + + keyedStructs := make(map[prog.StructKey]*prog.StructDesc) + for _, desc := range structDescs { + keyedStructs[desc.Key] = desc.Desc + } + structDescs = nil + + for _, c := range syscalls { + prog.ForeachType(c, func(t0 prog.Type) { + switch t := t0.(type) { + case *prog.ResourceType: + t.Desc = resourceMap[t.TypeName] + if t.Desc == nil { + panic("no resource desc") + } + case *prog.StructType: + t.StructDesc = keyedStructs[t.Key] + if t.StructDesc == nil { + panic("no struct desc") + } + case *prog.UnionType: + t.StructDesc = keyedStructs[t.Key] + if t.StructDesc == nil { + panic("no union desc") + } + } + }) + switch c.Name { + case "mmap": + mmapSyscall = c + case "clock_gettime": + clockGettimeSyscall = c + } + } +} diff --git a/sys/sys_386.go b/sys/sys_386.go index f0eebd212..a699a8893 100644 --- a/sys/sys_386.go +++ b/sys/sys_386.go @@ -1,7 +1,9 @@ // AUTOGENERATED FILE package sys -var resourceArray = []*ResourceDesc{ +import . "github.com/google/syzkaller/prog" + +var resources = []*ResourceDesc{ {Name: "assoc_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"assoc_id"}, Values: []uint64{0}}, {Name: "bpf_map_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_map_id"}, Values: []uint64{0, 4294967295}}, {Name: "bpf_prog_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_prog_id"}, Values: []uint64{0, 4294967295}}, @@ -5892,7 +5894,7 @@ var structDescs = []*KeyedStruct{ }}}, } -var Syscalls = []*Syscall{ +var syscalls = []*Syscall{ {NR: 18446744073709551615, Name: "accept", CallName: "accept", Args: []Type{ &ResourceType{TypeCommon: TypeCommon{TypeName: "sock", FldName: "fd", TypeSize: 4}}, &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "peer", TypeSize: 4, IsOptional: true}, Type: &UnionType{Key: StructKey{Name: "sockaddr_storage", Dir: 1}}}, diff --git a/sys/sys_amd64.go b/sys/sys_amd64.go index 0b85dda44..5325d7f13 100644 --- a/sys/sys_amd64.go +++ b/sys/sys_amd64.go @@ -1,7 +1,9 @@ // AUTOGENERATED FILE package sys -var resourceArray = []*ResourceDesc{ +import . "github.com/google/syzkaller/prog" + +var resources = []*ResourceDesc{ {Name: "assoc_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"assoc_id"}, Values: []uint64{0}}, {Name: "bpf_map_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_map_id"}, Values: []uint64{0, 4294967295}}, {Name: "bpf_prog_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_prog_id"}, Values: []uint64{0, 4294967295}}, @@ -5941,7 +5943,7 @@ var structDescs = []*KeyedStruct{ }}}, } -var Syscalls = []*Syscall{ +var syscalls = []*Syscall{ {NR: 43, Name: "accept", CallName: "accept", Args: []Type{ &ResourceType{TypeCommon: TypeCommon{TypeName: "sock", FldName: "fd", TypeSize: 4}}, &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "peer", TypeSize: 8, IsOptional: true}, Type: &UnionType{Key: StructKey{Name: "sockaddr_storage", Dir: 1}}}, diff --git a/sys/sys_arm.go b/sys/sys_arm.go index adcd5dbef..128fd836d 100644 --- a/sys/sys_arm.go +++ b/sys/sys_arm.go @@ -1,7 +1,9 @@ // AUTOGENERATED FILE package sys -var resourceArray = []*ResourceDesc{ +import . "github.com/google/syzkaller/prog" + +var resources = []*ResourceDesc{ {Name: "assoc_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"assoc_id"}, Values: []uint64{0}}, {Name: "bpf_map_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_map_id"}, Values: []uint64{0, 4294967295}}, {Name: "bpf_prog_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_prog_id"}, Values: []uint64{0, 4294967295}}, @@ -5892,7 +5894,7 @@ var structDescs = []*KeyedStruct{ }}}, } -var Syscalls = []*Syscall{ +var syscalls = []*Syscall{ {NR: 9437469, Name: "accept", CallName: "accept", Args: []Type{ &ResourceType{TypeCommon: TypeCommon{TypeName: "sock", FldName: "fd", TypeSize: 4}}, &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "peer", TypeSize: 4, IsOptional: true}, Type: &UnionType{Key: StructKey{Name: "sockaddr_storage", Dir: 1}}}, diff --git a/sys/sys_arm64.go b/sys/sys_arm64.go index 435eb92b6..bfc975594 100644 --- a/sys/sys_arm64.go +++ b/sys/sys_arm64.go @@ -1,7 +1,9 @@ // AUTOGENERATED FILE package sys -var resourceArray = []*ResourceDesc{ +import . "github.com/google/syzkaller/prog" + +var resources = []*ResourceDesc{ {Name: "assoc_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"assoc_id"}, Values: []uint64{0}}, {Name: "bpf_map_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_map_id"}, Values: []uint64{0, 4294967295}}, {Name: "bpf_prog_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_prog_id"}, Values: []uint64{0, 4294967295}}, @@ -5941,7 +5943,7 @@ var structDescs = []*KeyedStruct{ }}}, } -var Syscalls = []*Syscall{ +var syscalls = []*Syscall{ {NR: 202, Name: "accept", CallName: "accept", Args: []Type{ &ResourceType{TypeCommon: TypeCommon{TypeName: "sock", FldName: "fd", TypeSize: 4}}, &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "peer", TypeSize: 8, IsOptional: true}, Type: &UnionType{Key: StructKey{Name: "sockaddr_storage", Dir: 1}}}, diff --git a/sys/sys_ppc64le.go b/sys/sys_ppc64le.go index 52a8bca35..332c3e6b2 100644 --- a/sys/sys_ppc64le.go +++ b/sys/sys_ppc64le.go @@ -1,7 +1,9 @@ // AUTOGENERATED FILE package sys -var resourceArray = []*ResourceDesc{ +import . "github.com/google/syzkaller/prog" + +var resources = []*ResourceDesc{ {Name: "assoc_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"assoc_id"}, Values: []uint64{0}}, {Name: "bpf_map_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_map_id"}, Values: []uint64{0, 4294967295}}, {Name: "bpf_prog_id", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"bpf_prog_id"}, Values: []uint64{0, 4294967295}}, @@ -5941,7 +5943,7 @@ var structDescs = []*KeyedStruct{ }}}, } -var Syscalls = []*Syscall{ +var syscalls = []*Syscall{ {NR: 330, Name: "accept", CallName: "accept", Args: []Type{ &ResourceType{TypeCommon: TypeCommon{TypeName: "sock", FldName: "fd", TypeSize: 4}}, &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "peer", TypeSize: 8, IsOptional: true}, Type: &UnionType{Key: StructKey{Name: "sockaddr_storage", Dir: 1}}}, diff --git a/sys/syz-sysgen/syscallnr.go b/sys/syz-sysgen/syscallnr.go index 02c676cf0..da427a553 100644 --- a/sys/syz-sysgen/syscallnr.go +++ b/sys/syz-sysgen/syscallnr.go @@ -9,7 +9,7 @@ import ( "strings" "text/template" - "github.com/google/syzkaller/sys" + "github.com/google/syzkaller/prog" ) type Arch struct { @@ -26,7 +26,7 @@ var archs = []*Arch{ {"ppc64le", 8, []string{"__ppc64__", "__PPC64__", "__powerpc64__"}}, } -func generateExecutorSyscalls(arch *Arch, syscalls []*sys.Syscall) []byte { +func generateExecutorSyscalls(arch *Arch, syscalls []*prog.Syscall) []byte { data := ArchData{ CARCH: arch.CARCH, } diff --git a/sys/syz-sysgen/sysgen.go b/sys/syz-sysgen/sysgen.go index 7381f1edb..f09835aef 100644 --- a/sys/syz-sysgen/sysgen.go +++ b/sys/syz-sysgen/sysgen.go @@ -116,8 +116,9 @@ func main() { func generate(arch string, prog *compiler.Prog, consts map[string]uint64, out io.Writer) { fmt.Fprintf(out, "// AUTOGENERATED FILE\n") fmt.Fprintf(out, "package sys\n\n") + fmt.Fprintf(out, "import . \"github.com/google/syzkaller/prog\"\n\n") - fmt.Fprintf(out, "var resourceArray = ") + fmt.Fprintf(out, "var resources = ") serializer.Write(out, prog.Resources) fmt.Fprintf(out, "\n\n") @@ -133,7 +134,7 @@ func generate(arch string, prog *compiler.Prog, consts map[string]uint64, out io serializer.Write(out, prog.StructDescs) fmt.Fprintf(out, "\n\n") - fmt.Fprintf(out, "var Syscalls = ") + fmt.Fprintf(out, "var syscalls = ") serializer.Write(out, prog.Syscalls) fmt.Fprintf(out, "\n\n") diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index 8f8a62829..8c9e5fbdf 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -202,10 +202,10 @@ func main() { if err != nil { panic(err) } - if _, ok := calls[sys.SyscallMap["syz_emit_ethernet"]]; ok { + if _, ok := calls[prog.SyscallMap["syz_emit_ethernet"]]; ok { config.Flags |= ipc.FlagEnableTun } - if _, ok := calls[sys.SyscallMap["syz_extract_tcp_res"]]; ok { + if _, ok := calls[prog.SyscallMap["syz_extract_tcp_res"]]; ok { config.Flags |= ipc.FlagEnableTun } if faultInjectionEnabled { @@ -407,18 +407,18 @@ func main() { } } -func buildCallList(enabledCalls string) map[*sys.Syscall]bool { - calls := make(map[*sys.Syscall]bool) +func buildCallList(enabledCalls string) map[*prog.Syscall]bool { + calls := make(map[*prog.Syscall]bool) if enabledCalls != "" { for _, id := range strings.Split(enabledCalls, ",") { n, err := strconv.ParseUint(id, 10, 64) - if err != nil || n >= uint64(len(sys.Syscalls)) { + if err != nil || n >= uint64(len(prog.Syscalls)) { panic(fmt.Sprintf("invalid syscall in -calls flag: '%v", id)) } - calls[sys.Syscalls[n]] = true + calls[prog.Syscalls[n]] = true } } else { - for _, c := range sys.Syscalls { + for _, c := range prog.Syscalls { calls[c] = true } } @@ -434,7 +434,7 @@ func buildCallList(enabledCalls string) map[*sys.Syscall]bool { } } - trans := sys.TransitivelyEnabledCalls(calls) + trans := prog.TransitivelyEnabledCalls(calls) for c := range calls { if !trans[c] { Logf(1, "disabling transitively unsupported syscall: %v", c.Name) diff --git a/syz-manager/html.go b/syz-manager/html.go index 566809457..a9a9b641a 100644 --- a/syz-manager/html.go +++ b/syz-manager/html.go @@ -24,7 +24,6 @@ import ( "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/report" "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/sys" ) const dateFormat = "Jan 02 2006 15:04:05 MST" @@ -196,7 +195,7 @@ func (mgr *Manager) httpPrio(w http.ResponseWriter, r *http.Request) { mgr.minimizeCorpus() call := r.FormValue("call") idx := -1 - for i, c := range sys.Syscalls { + for i, c := range prog.Syscalls { if c.CallName == call { idx = i break @@ -209,7 +208,7 @@ func (mgr *Manager) httpPrio(w http.ResponseWriter, r *http.Request) { data := &UIPrioData{Call: call} for i, p := range mgr.prios[idx] { - data.Prios = append(data.Prios, UIPrio{sys.Syscalls[i].Name, p}) + data.Prios = append(data.Prios, UIPrio{prog.Syscalls[i].Name, p}) } sort.Sort(UIPrioArray(data.Prios)) diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 653e0b428..16bd7edbd 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -29,6 +29,7 @@ import ( "github.com/google/syzkaller/pkg/repro" . "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" "github.com/google/syzkaller/syz-manager/mgrconfig" "github.com/google/syzkaller/vm" ) diff --git a/syz-manager/mgrconfig/mgrconfig.go b/syz-manager/mgrconfig/mgrconfig.go index 4eacbfd7f..8423ddd46 100644 --- a/syz-manager/mgrconfig/mgrconfig.go +++ b/syz-manager/mgrconfig/mgrconfig.go @@ -12,7 +12,8 @@ import ( "github.com/google/syzkaller/pkg/config" "github.com/google/syzkaller/pkg/osutil" - "github.com/google/syzkaller/sys" + "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" "github.com/google/syzkaller/vm" ) @@ -149,7 +150,7 @@ func load(data []byte, filename string) (*Config, map[int]bool, error) { } func parseSyscalls(cfg *Config) (map[int]bool, error) { - match := func(call *sys.Syscall, str string) bool { + match := func(call *prog.Syscall, str string) bool { if str == call.CallName || str == call.Name { return true } @@ -163,7 +164,7 @@ func parseSyscalls(cfg *Config) (map[int]bool, error) { if len(cfg.Enable_Syscalls) != 0 { for _, c := range cfg.Enable_Syscalls { n := 0 - for _, call := range sys.Syscalls { + for _, call := range prog.Syscalls { if match(call, c) { syscalls[call.ID] = true n++ @@ -174,13 +175,13 @@ func parseSyscalls(cfg *Config) (map[int]bool, error) { } } } else { - for _, call := range sys.Syscalls { + for _, call := range prog.Syscalls { syscalls[call.ID] = true } } for _, c := range cfg.Disable_Syscalls { n := 0 - for _, call := range sys.Syscalls { + for _, call := range prog.Syscalls { if match(call, c) { delete(syscalls, call.ID) n++ @@ -191,7 +192,7 @@ func parseSyscalls(cfg *Config) (map[int]bool, error) { } } // mmap is used to allocate memory. - syscalls[sys.SyscallMap["mmap"].ID] = true + syscalls[prog.SyscallMap["mmap"].ID] = true return syscalls, nil } diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go index de2e1f7f3..1bedc09ea 100644 --- a/tools/syz-execprog/execprog.go +++ b/tools/syz-execprog/execprog.go @@ -23,6 +23,7 @@ import ( . "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) var ( diff --git a/tools/syz-mutate/mutate.go b/tools/syz-mutate/mutate.go index 9824db0b1..adda12df7 100644 --- a/tools/syz-mutate/mutate.go +++ b/tools/syz-mutate/mutate.go @@ -13,6 +13,7 @@ import ( "time" "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) var ( diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go index d3ad6e9ed..159e7ddde 100644 --- a/tools/syz-stress/stress.go +++ b/tools/syz-stress/stress.go @@ -18,7 +18,7 @@ import ( "github.com/google/syzkaller/pkg/ipc" . "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/sys" + _ "github.com/google/syzkaller/sys" ) var ( @@ -129,21 +129,21 @@ func readCorpus() []*prog.Prog { return progs } -func buildCallList() map[*sys.Syscall]bool { +func buildCallList() map[*prog.Syscall]bool { calls, err := host.DetectSupportedSyscalls() if err != nil { Logf(0, "failed to detect host supported syscalls: %v", err) - calls = make(map[*sys.Syscall]bool) - for _, c := range sys.Syscalls { + calls = make(map[*prog.Syscall]bool) + for _, c := range prog.Syscalls { calls[c] = true } } - for _, c := range sys.Syscalls { + for _, c := range prog.Syscalls { if !calls[c] { Logf(0, "disabling unsupported syscall: %v", c.Name) } } - trans := sys.TransitivelyEnabledCalls(calls) + trans := prog.TransitivelyEnabledCalls(calls) for c := range calls { if !trans[c] { Logf(0, "disabling transitively unsupported syscall: %v", c.Name) diff --git a/tools/syz-upgrade/upgrade.go b/tools/syz-upgrade/upgrade.go index 0d7c42112..0a28a27fb 100644 --- a/tools/syz-upgrade/upgrade.go +++ b/tools/syz-upgrade/upgrade.go @@ -18,6 +18,7 @@ import ( "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" + _ "github.com/google/syzkaller/sys" ) func main() { -- cgit mrf-deployment