From 1905d7c090a13a8b94e5d19a5388104f2d7693fd Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sun, 3 May 2020 16:53:36 +0200 Subject: prog: refactor ANY to not fabricate new types Currently ANY implementation fabricates new types dynamically. This is something we don't do anywhere else, generally types come from compiler and all are static. Dynamic types will conflict with use of Ref in Arg optimization. Move ANY types creation into compiler. Update #1580 --- pkg/compiler/types.go | 23 +++++++++++ pkg/host/syscalls.go | 20 +++++----- prog/any.go | 99 ++++++++++-------------------------------------- prog/decl_test.go | 3 +- prog/rand.go | 2 +- prog/size.go | 2 +- prog/target.go | 19 ++++++---- sys/syz-sysgen/sysgen.go | 6 +-- 8 files changed, 71 insertions(+), 103 deletions(-) diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index 2fddb4917..fd021cefc 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -964,6 +964,29 @@ type optional[T] [ val T void void ] [varlen] + +# prog/any.go knows layout of these types. +ANYUNION [ + ANYBLOB array[int8] + ANYRES16 ANYRES16 + ANYRES32 ANYRES32 + ANYRES64 ANYRES64 + ANYRESDEC fmt[dec, ANYRES64] + ANYRESHEX fmt[hex, ANYRES64] + ANYRESOCT fmt[oct, ANYRES64] +] [varlen] + +ANYPTRS [ + ANYPTR ptr[in, array[ANYUNION]] + ANYPTR64 ptr64[in, array[ANYUNION]] +] + +resource ANYRES16[int16]: -1, 0 +resource ANYRES32[int32]: -1, 0 +resource ANYRES64[int64]: -1, 0 + +syz_builtin0(a ptr[in, ANYPTRS]) (disabled) +syz_builtin1(a ptr[out, ANYUNION]) (disabled) ` func init() { diff --git a/pkg/host/syscalls.go b/pkg/host/syscalls.go index d63c7ceef..e322fc3b1 100644 --- a/pkg/host/syscalls.go +++ b/pkg/host/syscalls.go @@ -16,16 +16,24 @@ func DetectSupportedSyscalls(target *prog.Target, sandbox string) ( log.Logf(1, "detecting supported syscalls") supported := make(map[*prog.Syscall]bool) unsupported := make(map[*prog.Syscall]string) + const disabledAttribute = "has disabled attribute in descriptions" // These do not have own host and parasitize on some other OS. if targets.Get(target.OS, target.Arch).HostFuzzer { for _, c := range target.Syscalls { - supported[c] = true + if c.Attrs.Disabled { + unsupported[c] = disabledAttribute + } else { + supported[c] = true + } } } else { for _, c := range target.Syscalls { ok, reason := false, "" - switch c.CallName { - case "syz_execute_func": + switch { + case c.Attrs.Disabled: + ok = false + reason = disabledAttribute + case c.CallName == "syz_execute_func": // syz_execute_func caused multiple problems: // 1. First it lead to corpus exploision. The program used existing values in registers // to pollute output area. We tried to zero registers (though, not reliably). @@ -55,12 +63,6 @@ func DetectSupportedSyscalls(target *prog.Target, sandbox string) ( } } } - for c := range supported { - if c.Attrs.Disabled { - delete(supported, c) - unsupported[c] = "has disabled attribute in descriptions" - } - } return supported, unsupported, nil } diff --git a/prog/any.go b/prog/any.go index ce3736a4b..7bf38b6be 100644 --- a/prog/any.go +++ b/prog/any.go @@ -21,88 +21,29 @@ type anyTypes struct { resoct *ResourceType } -// This generates type descriptions for: -// -// resource ANYRES16[int16]: 0xffffffffffffffff, 0 -// resource ANYRES32[int32]: 0xffffffffffffffff, 0 -// resource ANYRES64[int64]: 0xffffffffffffffff, 0 -// ANY [ -// bin array[int8] -// res16 ANYRES16 -// res32 ANYRES32 -// res64 ANYRES64 -// resdec fmt[dec, ANYRES64] -// reshex fmt[hex, ANYRES64] -// resoct fmt[oct, ANYRES64] -// ] [varlen] -func initAnyTypes(target *Target) { - target.any.union = &UnionType{ - TypeCommon: TypeCommon{ - TypeName: "ANYUNION", - IsVarlen: true, - }, - } - target.any.array = &ArrayType{ - TypeCommon: TypeCommon{ - TypeName: "ANYARRAY", - IsVarlen: true, - }, - Elem: target.any.union, - } - target.any.ptrPtr = &PtrType{ - TypeCommon: TypeCommon{ - TypeName: "ANYPTR", - TypeSize: target.PtrSize, - IsOptional: true, - }, - Elem: target.any.array, - ElemDir: DirIn, - } - target.any.ptr64 = &PtrType{ - TypeCommon: TypeCommon{ - TypeName: "ANYPTR64", - TypeSize: 8, - IsOptional: true, - }, - Elem: target.any.array, - ElemDir: DirIn, - } - target.any.blob = &BufferType{ - TypeCommon: TypeCommon{ - TypeName: "ANYBLOB", - IsVarlen: true, - }, - } - createResource := func(name, base string, bf BinaryFormat, size uint64) *ResourceType { - return &ResourceType{ - TypeCommon: TypeCommon{ - TypeName: name, - TypeSize: size, - IsOptional: true, - }, - ArgFormat: bf, - Desc: &ResourceDesc{ - Name: name, - Kind: []string{name}, - Values: []uint64{^uint64(0), 0}, - }, +func (target *Target) initAnyTypes() { + var anyPtrs *UnionType + for _, typ := range target.types { + if typ.Name() == "ANYPTRS" { + anyPtrs = typ.(*UnionType) + break } } - target.any.res16 = createResource("ANYRES16", "int16", FormatNative, 2) - target.any.res32 = createResource("ANYRES32", "int32", FormatNative, 4) - target.any.res64 = createResource("ANYRES64", "int64", FormatNative, 8) - target.any.resdec = createResource("ANYRESDEC", "int64", FormatStrDec, 20) - target.any.reshex = createResource("ANYRESHEX", "int64", FormatStrHex, 18) - target.any.resoct = createResource("ANYRESOCT", "int64", FormatStrOct, 23) - target.any.union.Fields = []Field{ - {Name: "ANYBLOB", Type: target.any.blob}, - {Name: "ANYRES16", Type: target.any.res16}, - {Name: "ANYRES32", Type: target.any.res32}, - {Name: "ANYRES64", Type: target.any.res64}, - {Name: "ANYRESDEC", Type: target.any.resdec}, - {Name: "ANYRESHEX", Type: target.any.reshex}, - {Name: "ANYRESOCT", Type: target.any.resoct}, + if anyPtrs == nil { + panic("no builtin ANYPTRS type") } + // These types are generated by builtin descriptions in pkg/compiler/types.go. + target.any.ptrPtr = anyPtrs.Fields[0].Type.(*PtrType) + target.any.ptr64 = anyPtrs.Fields[1].Type.(*PtrType) + target.any.array = target.any.ptrPtr.Elem.(*ArrayType) + target.any.union = target.any.array.Elem.(*UnionType) + target.any.blob = target.any.union.Fields[0].Type.(*BufferType) + target.any.res16 = target.any.union.Fields[1].Type.(*ResourceType) + target.any.res32 = target.any.union.Fields[2].Type.(*ResourceType) + target.any.res64 = target.any.union.Fields[3].Type.(*ResourceType) + target.any.resdec = target.any.union.Fields[4].Type.(*ResourceType) + target.any.reshex = target.any.union.Fields[5].Type.(*ResourceType) + target.any.resoct = target.any.union.Fields[6].Type.(*ResourceType) } func (target *Target) getAnyPtrType(size uint64) *PtrType { diff --git a/prog/decl_test.go b/prog/decl_test.go index f999ae589..5f1795ec6 100644 --- a/prog/decl_test.go +++ b/prog/decl_test.go @@ -13,9 +13,8 @@ func TestResourceCtors(t *testing.T) { t.Skip("too slow") } testEachTarget(t, func(t *testing.T, target *Target) { - expectFail := false for _, res := range target.Resources { - if len(target.calcResourceCtors(res, true)) == 0 != expectFail { + if len(target.calcResourceCtors(res, true)) == 0 && !strings.HasPrefix(res.Name, "ANY") { t.Errorf("resource %v can't be created", res.Name) } } diff --git a/prog/rand.go b/prog/rand.go index 150f4b266..18c86d77a 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -568,7 +568,7 @@ func (target *Target) GenerateAllSyzProg(rs rand.Source) *Prog { s := newState(target, target.DefaultChoiceTable(), nil) handled := make(map[string]bool) for _, meta := range target.Syscalls { - if !strings.HasPrefix(meta.CallName, "syz_") || handled[meta.CallName] { + if !strings.HasPrefix(meta.CallName, "syz_") || handled[meta.CallName] || meta.Attrs.Disabled { continue } handled[meta.CallName] = true diff --git a/prog/size.go b/prog/size.go index 61d382723..6eccec50f 100644 --- a/prog/size.go +++ b/prog/size.go @@ -55,7 +55,7 @@ func (target *Target) assignSize(dst *ConstArg, pos Arg, path []string, args []A offset += buf.Size() continue } - if typ := buf.Type().Name(); typ == target.any.ptrPtr.Name() || typ == target.any.ptr64.Name() { + if typ := buf.Type(); typ == target.any.ptrPtr || typ == target.any.ptr64 { // If path points into squashed argument, we don't have the target argument. // In such case we simply leave size argument as is. It can't happen during generation, // only during mutation and mutation can set size to random values, so it should be fine. diff --git a/prog/target.go b/prog/target.go index af03dc47f..630fb7cbf 100644 --- a/prog/target.go +++ b/prog/target.go @@ -23,7 +23,6 @@ type Target struct { Syscalls []*Syscall Resources []*ResourceDesc Consts []ConstValue - Types []Type // MakeDataMmap creates calls that mmaps target data memory range. MakeDataMmap func() []*Call @@ -57,10 +56,12 @@ type Target struct { SpecialPointers []uint64 // Filled by prog package: + SyscallMap map[string]*Syscall + ConstMap map[string]uint64 + init sync.Once initArch func(target *Target) - SyscallMap map[string]*Syscall - ConstMap map[string]uint64 + types []Type resourceMap map[string]*ResourceDesc // Maps resource name to a list of calls that can create the resource. resourceCtors map[string][]*Syscall @@ -75,12 +76,13 @@ const maxSpecialPointers = 16 var targets = make(map[string]*Target) -func RegisterTarget(target *Target, initArch func(target *Target)) { +func RegisterTarget(target *Target, types []Type, initArch func(target *Target)) { key := target.OS + "/" + target.Arch if targets[key] != nil { panic(fmt.Sprintf("duplicate target %v", key)) } target.initArch = initArch + target.types = types targets[key] = target } @@ -122,7 +124,6 @@ func (target *Target) lazyInit() { target.AnnotateCall = func(c ExecCall) string { return "" } target.initTarget() target.initArch(target) - target.ConstMap = nil // currently used only by initArch // Give these 2 known addresses fixed positions and prepend target-specific ones at the end. target.SpecialPointers = append([]uint64{ 0x0000000000000000, // NULL pointer (keep this first because code uses special index=0 as NULL) @@ -132,6 +133,9 @@ func (target *Target) lazyInit() { if len(target.SpecialPointers) > maxSpecialPointers { panic("too many special pointers") } + // These are used only during lazyInit. + target.ConstMap = nil + target.types = nil } func (target *Target) initTarget() { @@ -140,8 +144,8 @@ func (target *Target) initTarget() { target.ConstMap[c.Name] = c.Value } - target.resourceMap = restoreLinks(target.Syscalls, target.Resources, target.Types) - target.Types = nil + target.resourceMap = restoreLinks(target.Syscalls, target.Resources, target.types) + target.initAnyTypes() target.SyscallMap = make(map[string]*Syscall) for i, c := range target.Syscalls { @@ -156,7 +160,6 @@ func (target *Target) initTarget() { for _, res := range target.Resources { target.resourceCtors[res.Name] = target.calcResourceCtors(res, false) } - initAnyTypes(target) } func (target *Target) GetConst(name string) uint64 { diff --git a/sys/syz-sysgen/sysgen.go b/sys/syz-sysgen/sysgen.go index bf39ba9ee..654664af6 100644 --- a/sys/syz-sysgen/sysgen.go +++ b/sys/syz-sysgen/sysgen.go @@ -189,8 +189,8 @@ func generate(target *targets.Target, prg *compiler.Prog, consts map[string]uint fmt.Fprintf(out, "\tRegisterTarget(&Target{"+ "OS: %q, Arch: %q, Revision: revision_%v, PtrSize: %v, "+ "PageSize: %v, NumPages: %v, DataOffset: %v, Syscalls: syscalls_%v, "+ - "Resources: resources_%v, Types: types_%v, Consts: consts_%v}, "+ - "InitTarget)\n}\n\n", + "Resources: resources_%v, Consts: consts_%v}, "+ + "types_%v, InitTarget)\n}\n\n", target.OS, target.Arch, target.Arch, target.PtrSize, target.PageSize, target.NumPages, target.DataOffset, target.Arch, target.Arch, target.Arch, target.Arch) @@ -267,7 +267,7 @@ func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, Name: c.Name, CallName: c.CallName, NR: int32(c.NR), - NeedCall: !target.SyscallNumbers || strings.HasPrefix(c.CallName, "syz_"), + NeedCall: (!target.SyscallNumbers || strings.HasPrefix(c.CallName, "syz_")) && !c.Attrs.Disabled, Attrs: attrVals[:last+1], }) } -- cgit mrf-deployment