| // 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" |
| ) |
| |
| const ( |
| // Special reference to the outer struct used in len targets. |
| ParentRef = "parent" |
| // Special reference directly to syscall arguments used in len targets. |
| SyscallRef = "syscall" |
| ) |
| |
| func (target *Target) assignSizes(args []Arg, fields []Field, parents parentStack, |
| syscallArgs []Arg, syscallFields []Field, autos map[Arg]bool, overlayField int) { |
| for _, arg := range args { |
| target.assignArgSize(arg, args, fields, parents, syscallArgs, |
| syscallFields, autos, overlayField) |
| } |
| } |
| |
| func (target *Target) assignArgSize(arg Arg, args []Arg, fields []Field, parents parentStack, |
| syscallArgs []Arg, syscallFields []Field, autos map[Arg]bool, overlayField int) { |
| if arg = InnerArg(arg); arg == nil { |
| return // Pointer to optional len field, no need to fill in value. |
| } |
| typ, ok := arg.Type().(*LenType) |
| if !ok { |
| return |
| } |
| if autos != nil { |
| if !autos[arg] { |
| return |
| } |
| delete(autos, arg) |
| } |
| a := arg.(*ConstArg) |
| if typ.Path[0] == SyscallRef { |
| target.assignSize(a, nil, typ.Path[1:], syscallArgs, syscallFields, parents, 0) |
| } else { |
| target.assignSize(a, a, typ.Path, args, fields, parents, overlayField) |
| } |
| } |
| |
| func (target *Target) assignSize(dst *ConstArg, pos Arg, path []string, args []Arg, |
| fields []Field, parents parentStack, overlayField int) { |
| found := target.findArg(pos, path, args, fields, parents, overlayField) |
| if found != nil && !found.isAnyPtr { |
| dst.Val = target.computeSize(found.arg, found.offset, dst.Type().(*LenType)) |
| } |
| } |
| |
| type foundArg struct { |
| arg Arg |
| offset uint64 |
| isAnyPtr bool |
| } |
| |
| func (target *Target) findFieldStruct(buf Arg, path []string, parents parentStack) *foundArg { |
| switch arg := buf.(type) { |
| case *GroupArg: |
| typ := arg.Type().(*StructType) |
| return target.findArg(buf, path, arg.Inner, typ.Fields, parents, typ.OverlayField) |
| case *UnionArg: |
| return target.findArg(buf, path, nil, nil, parents, 0) |
| default: |
| panic(fmt.Sprintf("unexpected arg type %#v", arg)) |
| } |
| } |
| |
| func (target *Target) findArg(pos Arg, path []string, args []Arg, fields []Field, |
| parents parentStack, overlayField int) *foundArg { |
| elem := path[0] |
| path = path[1:] |
| var offset uint64 |
| for i, buf := range args { |
| if i == overlayField { |
| offset = 0 |
| } |
| if buf == nil { |
| continue |
| } |
| if elem != fields[i].Name { |
| offset += buf.Size() |
| continue |
| } |
| 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. |
| return &foundArg{buf, offset, true} |
| } |
| buf = InnerArg(buf) |
| if buf == nil { |
| return &foundArg{nil, offset, false} |
| } |
| if len(path) != 0 { |
| return target.findFieldStruct(buf, path, parents) |
| } |
| return &foundArg{buf, offset, false} |
| } |
| if elem == ParentRef { |
| parents, buf := popStack(parents) |
| if len(path) != 0 { |
| return target.findFieldStruct(buf, path, parents) |
| } |
| return &foundArg{buf, noOffset, false} |
| } |
| for parents, buf := popStack(parents); buf != nil; parents, buf = popStack(parents) { |
| if elem != buf.Type().TemplateName() { |
| continue |
| } |
| if len(path) != 0 { |
| return target.findFieldStruct(buf, path, parents) |
| } |
| return &foundArg{buf, noOffset, false} |
| } |
| var fieldNames []string |
| for _, field := range fields { |
| fieldNames = append(fieldNames, field.Name) |
| } |
| posName := "nil" |
| if pos != nil { |
| posName = pos.Type().Name() |
| } |
| panic(fmt.Sprintf("path references non existent field %q, pos=%q, argsMap: %v, path: %v", |
| elem, posName, fieldNames, path)) |
| } |
| |
| const noOffset = ^uint64(0) |
| |
| func (target *Target) computeSize(arg Arg, offset uint64, lenType *LenType) uint64 { |
| if lenType.Offset { |
| if offset == noOffset { |
| panic("offset of a non-field") |
| } |
| return offset * 8 / lenType.BitSize |
| } |
| if arg == nil { |
| // For e.g. optional pointers. |
| return 0 |
| } |
| bitSize := lenType.BitSize |
| if bitSize == 0 { |
| bitSize = 8 |
| } |
| switch arg.Type().(type) { |
| case *VmaType: |
| a := arg.(*PointerArg) |
| return a.VmaSize * 8 / bitSize |
| case *ArrayType: |
| a := arg.(*GroupArg) |
| if lenType.BitSize != 0 { |
| return a.Size() * 8 / bitSize |
| } |
| return uint64(len(a.Inner)) |
| default: |
| return arg.Size() * 8 / bitSize |
| } |
| } |
| |
| func (target *Target) assignSizesArray(args []Arg, fields []Field, autos map[Arg]bool) { |
| target.assignSizes(args, fields, nil, args, fields, autos, 0) |
| for _, arg := range args { |
| foreachSubArgWithStack(arg, func(arg Arg, ctx *ArgCtx) { |
| if typ, ok := arg.Type().(*StructType); ok { |
| target.assignSizes(arg.(*GroupArg).Inner, typ.Fields, ctx.parentStack, args, fields, autos, typ.OverlayField) |
| } |
| if v, ok := arg.(*UnionArg); ok { |
| target.assignArgSize(v.Option, nil, nil, ctx.parentStack, args, fields, autos, 0) |
| } |
| }) |
| } |
| } |
| |
| func (target *Target) assignSizesCall(c *Call) { |
| target.assignSizesArray(c.Args, c.Meta.Args, nil) |
| } |
| |
| func (r *randGen) mutateSize(arg *ConstArg, parent []Arg, fields []Field) bool { |
| typ := arg.Type().(*LenType) |
| elemSize := typ.BitSize / 8 |
| if elemSize == 0 { |
| elemSize = 1 |
| // TODO(dvyukov): implement path support for size mutation. |
| if len(typ.Path) == 1 { |
| for i, field := range parent { |
| if typ.Path[0] != fields[i].Name { |
| continue |
| } |
| if inner := InnerArg(field); inner != nil { |
| switch targetType := inner.Type().(type) { |
| case *VmaType: |
| return false |
| case *BufferType: |
| // Don't mutate size of compressed images. |
| // If we do, then our code will fail/crash on decompression. |
| if targetType.Kind == BufferCompressed { |
| return false |
| } |
| case *ArrayType: |
| if targetType.Elem.Varlen() { |
| return false |
| } |
| elemSize = targetType.Elem.Size() |
| } |
| } |
| break |
| } |
| } |
| } |
| if r.oneOf(100) { |
| arg.Val = r.rand64() |
| return true |
| } |
| if r.bin() { |
| // Small adjustment to trigger missed size checks. |
| if arg.Val != 0 && r.bin() { |
| arg.Val = r.randRangeInt(0, arg.Val-1, arg.Type().TypeBitSize(), 0) |
| } else { |
| arg.Val = r.randRangeInt(arg.Val+1, arg.Val+100, arg.Type().TypeBitSize(), 0) |
| } |
| return true |
| } |
| // Try to provoke int overflows. |
| max := ^uint64(0) |
| if r.oneOf(3) { |
| max = 1<<32 - 1 |
| if r.oneOf(2) { |
| max = 1<<16 - 1 |
| if r.oneOf(2) { |
| max = 1<<8 - 1 |
| } |
| } |
| } |
| n := max / elemSize |
| delta := uint64(1000 - r.biasedRand(1000, 10)) |
| if elemSize == 1 || r.oneOf(10) { |
| n -= delta |
| } else { |
| n += delta |
| } |
| arg.Val = n |
| return true |
| } |