// 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 compiler import ( "fmt" "sort" "github.com/google/syzkaller/pkg/ast" "github.com/google/syzkaller/prog" ) const sizeUnassigned = ^uint64(0) func (comp *compiler) genResources() []*prog.ResourceDesc { var resources []*prog.ResourceDesc for name, n := range comp.resources { if !comp.used[name] { continue } resources = append(resources, comp.genResource(n)) } sort.Slice(resources, func(i, j int) bool { return resources[i].Name < resources[j].Name }) return resources } func (comp *compiler) genResource(n *ast.Resource) *prog.ResourceDesc { res := &prog.ResourceDesc{ Name: n.Name.Name, } var base *ast.Type for n != nil { res.Values = append(genIntArray(n.Values), res.Values...) res.Kind = append([]string{n.Name.Name}, res.Kind...) base = n.Base n = comp.resources[n.Base.Ident] } if len(res.Values) == 0 { res.Values = []uint64{0} } res.Type = comp.genType(base, "", prog.DirIn, false) return res } func (comp *compiler) genSyscalls() []*prog.Syscall { var calls []*prog.Syscall for _, decl := range comp.desc.Nodes { if n, ok := decl.(*ast.Call); ok && n.NR != ^uint64(0) { calls = append(calls, comp.genSyscall(n)) } } sort.Slice(calls, func(i, j int) bool { return calls[i].Name < calls[j].Name }) return calls } func (comp *compiler) genSyscall(n *ast.Call) *prog.Syscall { var ret prog.Type if n.Ret != nil { ret = comp.genType(n.Ret, "ret", prog.DirOut, true) } return &prog.Syscall{ Name: n.Name.Name, CallName: n.CallName, NR: n.NR, Args: comp.genFieldArray(n.Args, prog.DirIn, true), Ret: ret, } } 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. prog package will reattach them during init. ctx := &structGen{ comp: comp, padded: make(map[interface{}]bool), detach: make(map[**prog.StructDesc]bool), } // We have to do this in the loop until we pad nothing new // due to recursive structs. for { start := len(ctx.padded) for _, c := range syscalls { for _, a := range c.Args { ctx.walk(a) } if c.Ret != nil { ctx.walk(c.Ret) } } if start == len(ctx.padded) { break } } // Detach StructDesc's from StructType's. prog will reattach them again. for descp := range ctx.detach { *descp = nil } sort.Slice(ctx.structs, func(i, j int) bool { si, sj := ctx.structs[i], ctx.structs[j] if si.Key.Name != sj.Key.Name { return si.Key.Name < sj.Key.Name } return si.Key.Dir < sj.Key.Dir }) return ctx.structs } type structGen struct { comp *compiler padded map[interface{}]bool detach map[**prog.StructDesc]bool structs []*prog.KeyedStruct } func (ctx *structGen) check(key prog.StructKey, descp **prog.StructDesc) bool { ctx.detach[descp] = true desc := *descp if ctx.padded[desc] { return false } ctx.padded[desc] = true for _, f := range desc.Fields { ctx.walk(f) if !f.Varlen() && f.Size() == sizeUnassigned { // An inner struct is not padded yet. // Leave this struct for next iteration. delete(ctx.padded, desc) return false } } if ctx.comp.used[key.Name] { ctx.structs = append(ctx.structs, &prog.KeyedStruct{ Key: key, Desc: desc, }) } return true } func (ctx *structGen) walk(t0 prog.Type) { switch t := t0.(type) { case *prog.PtrType: ctx.walk(t.Type) case *prog.ArrayType: ctx.walkArray(t) case *prog.StructType: ctx.walkStruct(t) case *prog.UnionType: ctx.walkUnion(t) } } func (ctx *structGen) walkArray(t *prog.ArrayType) { if ctx.padded[t] { return } ctx.walk(t.Type) if !t.Type.Varlen() && t.Type.Size() == sizeUnassigned { // An inner struct is not padded yet. // Leave this array for next iteration. return } ctx.padded[t] = true t.TypeSize = 0 if t.Kind == prog.ArrayRangeLen && t.RangeBegin == t.RangeEnd && !t.Type.Varlen() { t.TypeSize = t.RangeBegin * t.Type.Size() } } func (ctx *structGen) walkStruct(t *prog.StructType) { if !ctx.check(t.Key, &t.StructDesc) { return } comp := ctx.comp structNode := comp.structNodes[t.StructDesc] // Add paddings, calculate size, mark bitfields. varlen := false for _, f := range t.Fields { if f.Varlen() { varlen = true } } comp.markBitfields(t.Fields) packed, sizeAttr, alignAttr := comp.parseStructAttrs(structNode) t.Fields = comp.addAlignment(t.Fields, varlen, packed, alignAttr) t.AlignAttr = alignAttr t.TypeSize = 0 if !varlen { for _, f := range t.Fields { if !f.BitfieldMiddle() { t.TypeSize += f.Size() } } if sizeAttr != sizeUnassigned { if t.TypeSize > sizeAttr { comp.error(structNode.Pos, "struct %v has size attribute %v"+ " which is less than struct size %v", structNode.Name.Name, sizeAttr, t.TypeSize) } if pad := sizeAttr - t.TypeSize; pad != 0 { t.Fields = append(t.Fields, genPad(pad)) } t.TypeSize = sizeAttr } } } func (ctx *structGen) walkUnion(t *prog.UnionType) { if !ctx.check(t.Key, &t.StructDesc) { return } comp := ctx.comp structNode := comp.structNodes[t.StructDesc] varlen, sizeAttr := comp.parseUnionAttrs(structNode) t.TypeSize = 0 if !varlen { for _, fld := range t.Fields { sz := fld.Size() if sizeAttr != sizeUnassigned && sz > sizeAttr { comp.error(structNode.Pos, "union %v has size attribute %v"+ " which is less than field %v size %v", structNode.Name.Name, sizeAttr, fld.Name(), sz) } if t.TypeSize < sz { t.TypeSize = sz } } if sizeAttr != sizeUnassigned { t.TypeSize = sizeAttr } } } func (comp *compiler) genStructDesc(res *prog.StructDesc, n *ast.Struct, dir prog.Dir, varlen bool) { // Leave node for genStructDescs to calculate size/padding. comp.structNodes[res] = n common := genCommon(n.Name.Name, "", sizeUnassigned, dir, false) common.IsVarlen = varlen *res = prog.StructDesc{ TypeCommon: common, Fields: comp.genFieldArray(n.Fields, dir, false), } } func (comp *compiler) markBitfields(fields []prog.Type) { var bfOffset uint64 for i, f := range fields { if f.BitfieldLength() == 0 { continue } off, middle := bfOffset, true bfOffset += f.BitfieldLength() if i == len(fields)-1 || // Last bitfield in a group, if last field of the struct... fields[i+1].BitfieldLength() == 0 || // or next field is not a bitfield... f.Size() != fields[i+1].Size() || // or next field is of different size... bfOffset+fields[i+1].BitfieldLength() > f.Size()*8 { // or next field does not fit into the current group. middle, bfOffset = false, 0 } setBitfieldOffset(f, off, middle) } } func setBitfieldOffset(t0 prog.Type, offset uint64, middle bool) { switch t := t0.(type) { case *prog.IntType: t.BitfieldOff, t.BitfieldMdl = offset, middle case *prog.ConstType: t.BitfieldOff, t.BitfieldMdl = offset, middle case *prog.LenType: t.BitfieldOff, t.BitfieldMdl = offset, middle case *prog.FlagsType: t.BitfieldOff, t.BitfieldMdl = offset, middle 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 []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. newFields = fields if !varlen && alignAttr != 0 { size := uint64(0) for _, f := range fields { if !f.BitfieldMiddle() { size += f.Size() } } if tail := size % alignAttr; tail != 0 { newFields = append(newFields, genPad(alignAttr-tail)) } } return newFields } var align, off uint64 for i, f := range fields { if i == 0 || !fields[i-1].BitfieldMiddle() { a := comp.typeAlign(f) if align < a { align = a } // Append padding if the last field is not a bitfield or it's the last bitfield in a set. if off%a != 0 { pad := a - off%a off += pad newFields = append(newFields, genPad(pad)) } } newFields = append(newFields, f) if !f.BitfieldMiddle() && (i != len(fields)-1 || !f.Varlen()) { // Increase offset if the current field is not a bitfield // or it's the last bitfield in a set, except when it's // the last field in a struct and has variable length. off += f.Size() } } if alignAttr != 0 { align = alignAttr } if align != 0 && off%align != 0 && !varlen { pad := align - off%align off += pad newFields = append(newFields, genPad(pad)) } return newFields } func (comp *compiler) typeAlign(t0 prog.Type) uint64 { switch t0.(type) { case *prog.IntType, *prog.ConstType, *prog.LenType, *prog.FlagsType, *prog.ProcType, *prog.CsumType, *prog.PtrType, *prog.VmaType, *prog.ResourceType: return t0.Size() case *prog.BufferType: return 1 } switch t := t0.(type) { case *prog.ArrayType: return comp.typeAlign(t.Type) case *prog.StructType: packed, _, alignAttr := comp.parseStructAttrs(comp.structNodes[t.StructDesc]) if alignAttr != 0 { return alignAttr // overrided by user attribute } if packed { return 1 } align := uint64(0) for _, f := range t.Fields { if a := comp.typeAlign(f); align < a { align = a } } return align case *prog.UnionType: align := uint64(0) for _, f := range t.Fields { if a := comp.typeAlign(f); align < a { align = a } } return align default: panic(fmt.Sprintf("unknown type: %#v", t)) } } 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 prog.Dir, isArg bool) prog.Type { return comp.genType(f.Type, f.Name.Name, dir, isArg) } 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 prog.Dir, isArg bool) prog.Type { desc, args, base := comp.getArgsBase(t, field, dir, isArg) if desc.Gen == nil { panic(fmt.Sprintf("no gen for %v %#v", field, t)) } base.IsVarlen = desc.Varlen != nil && desc.Varlen(comp, t, args) return desc.Gen(comp, t, args, base) } func genCommon(name, field string, size uint64, dir prog.Dir, opt bool) prog.TypeCommon { return prog.TypeCommon{ TypeName: name, TypeSize: size, FldName: field, ArgDir: dir, IsOptional: opt, } } func genIntCommon(com prog.TypeCommon, bitLen uint64, bigEndian bool) prog.IntTypeCommon { bf := prog.FormatNative if bigEndian { bf = prog.FormatBigEndian } return prog.IntTypeCommon{ TypeCommon: com, ArgFormat: bf, BitfieldLen: bitLen, } } func genIntArray(a []*ast.Int) []uint64 { r := make([]uint64, len(a)) for i, v := range a { r[i] = v.Value } return r } func genStrArray(a []*ast.String) []string { r := make([]string, len(a)) for i, v := range a { r[i] = v.Value } return r }