mirror of
https://github.com/reactos/syzkaller.git
synced 2024-12-04 01:11:22 +00:00
306ca0571c
fmt type allows to convert intergers and resources to string representation.
413 lines
9.9 KiB
Go
413 lines
9.9 KiB
Go
package prog
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
type anyTypes struct {
|
|
union *UnionType
|
|
array *ArrayType
|
|
blob *BufferType
|
|
ptrPtr *PtrType
|
|
ptr64 *PtrType
|
|
res16 *ResourceType
|
|
res32 *ResourceType
|
|
res64 *ResourceType
|
|
resdec *ResourceType
|
|
reshex *ResourceType
|
|
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]
|
|
// ptr ptr[in, array[ANY], opt]
|
|
// ptr64 ptr64[in, array[ANY], opt]
|
|
// 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{
|
|
FldName: "ANYUNION",
|
|
}
|
|
target.any.array = &ArrayType{
|
|
TypeCommon: TypeCommon{
|
|
TypeName: "ANYARRAY",
|
|
FldName: "ANYARRAY",
|
|
IsVarlen: true,
|
|
},
|
|
Type: target.any.union,
|
|
}
|
|
target.any.ptrPtr = &PtrType{
|
|
TypeCommon: TypeCommon{
|
|
TypeName: "ptr",
|
|
FldName: "ANYPTR",
|
|
TypeSize: target.PtrSize,
|
|
IsOptional: true,
|
|
},
|
|
Type: target.any.array,
|
|
}
|
|
target.any.ptr64 = &PtrType{
|
|
TypeCommon: TypeCommon{
|
|
TypeName: "ptr64",
|
|
FldName: "ANYPTR64",
|
|
TypeSize: 8,
|
|
IsOptional: true,
|
|
},
|
|
Type: target.any.array,
|
|
}
|
|
target.any.blob = &BufferType{
|
|
TypeCommon: TypeCommon{
|
|
TypeName: "ANYBLOB",
|
|
FldName: "ANYBLOB",
|
|
IsVarlen: true,
|
|
},
|
|
}
|
|
createResource := func(name, base string, bf BinaryFormat, size uint64) *ResourceType {
|
|
return &ResourceType{
|
|
TypeCommon: TypeCommon{
|
|
TypeName: name,
|
|
FldName: name,
|
|
ArgDir: DirIn,
|
|
TypeSize: size,
|
|
IsOptional: true,
|
|
},
|
|
ArgFormat: bf,
|
|
Desc: &ResourceDesc{
|
|
Name: name,
|
|
Kind: []string{name},
|
|
Values: []uint64{^uint64(0), 0},
|
|
Type: &IntType{
|
|
IntTypeCommon: IntTypeCommon{
|
|
TypeCommon: TypeCommon{
|
|
TypeName: base,
|
|
TypeSize: size,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
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.StructDesc = &StructDesc{
|
|
TypeCommon: TypeCommon{
|
|
TypeName: "ANYUNION",
|
|
FldName: "ANYUNION",
|
|
IsVarlen: true,
|
|
ArgDir: DirIn,
|
|
},
|
|
Fields: []Type{
|
|
target.any.blob,
|
|
target.any.ptrPtr,
|
|
target.any.ptr64,
|
|
target.any.res16,
|
|
target.any.res32,
|
|
target.any.res64,
|
|
target.any.resdec,
|
|
target.any.reshex,
|
|
target.any.resoct,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (target *Target) makeAnyPtrType(size uint64, field string) *PtrType {
|
|
// We need to make a copy because type holds field name,
|
|
// and field names are used as len target.
|
|
var typ PtrType
|
|
if size == target.PtrSize {
|
|
typ = *target.any.ptrPtr
|
|
} else if size == 8 {
|
|
typ = *target.any.ptr64
|
|
} else {
|
|
panic(fmt.Sprintf("bad pointer size %v", size))
|
|
}
|
|
typ.TypeSize = size
|
|
if field != "" {
|
|
typ.FldName = field
|
|
}
|
|
return &typ
|
|
}
|
|
|
|
func (target *Target) isAnyPtr(typ Type) bool {
|
|
ptr, ok := typ.(*PtrType)
|
|
return ok && ptr.Type == target.any.array
|
|
}
|
|
|
|
func (p *Prog) complexPtrs() (res []*PointerArg) {
|
|
for _, c := range p.Calls {
|
|
ForeachArg(c, func(arg Arg, ctx *ArgCtx) {
|
|
if ptrArg, ok := arg.(*PointerArg); ok && p.Target.isComplexPtr(ptrArg) {
|
|
res = append(res, ptrArg)
|
|
ctx.Stop = true
|
|
}
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
func (target *Target) isComplexPtr(arg *PointerArg) bool {
|
|
if arg.Res == nil || arg.Type().Dir() != DirIn {
|
|
return false
|
|
}
|
|
if target.isAnyPtr(arg.Type()) {
|
|
return true
|
|
}
|
|
res := false
|
|
ForeachSubArg(arg.Res, func(a1 Arg, ctx *ArgCtx) {
|
|
switch typ := a1.Type().(type) {
|
|
case *StructType:
|
|
if typ.Varlen() {
|
|
res = true
|
|
ctx.Stop = true
|
|
}
|
|
case *UnionType:
|
|
if typ.Varlen() && len(typ.Fields) > 5 {
|
|
res = true
|
|
ctx.Stop = true
|
|
}
|
|
case *PtrType:
|
|
if a1 != arg {
|
|
ctx.Stop = true
|
|
}
|
|
}
|
|
})
|
|
return res
|
|
}
|
|
|
|
func (target *Target) CallContainsAny(c *Call) (res bool) {
|
|
ForeachArg(c, func(arg Arg, ctx *ArgCtx) {
|
|
if target.isAnyPtr(arg.Type()) {
|
|
res = true
|
|
ctx.Stop = true
|
|
}
|
|
})
|
|
return
|
|
}
|
|
|
|
func (target *Target) ArgContainsAny(arg0 Arg) (res bool) {
|
|
ForeachSubArg(arg0, func(arg Arg, ctx *ArgCtx) {
|
|
if target.isAnyPtr(arg.Type()) {
|
|
res = true
|
|
ctx.Stop = true
|
|
}
|
|
})
|
|
return
|
|
}
|
|
|
|
func (target *Target) squashPtr(arg *PointerArg, preserveField bool) {
|
|
if arg.Res == nil || arg.VmaSize != 0 {
|
|
panic("bad ptr arg")
|
|
}
|
|
res0 := arg.Res
|
|
size0 := res0.Size()
|
|
var elems []Arg
|
|
target.squashPtrImpl(arg.Res, &elems)
|
|
field := ""
|
|
if preserveField {
|
|
field = arg.Type().FieldName()
|
|
}
|
|
arg.typ = target.makeAnyPtrType(arg.Type().Size(), field)
|
|
arg.Res = MakeGroupArg(arg.typ.(*PtrType).Type, elems)
|
|
if size := arg.Res.Size(); size != size0 {
|
|
panic(fmt.Sprintf("squash changed size %v->%v for %v", size0, size, res0.Type()))
|
|
}
|
|
}
|
|
|
|
func (target *Target) squashPtrImpl(a Arg, elems *[]Arg) {
|
|
if a.Type().BitfieldLength() != 0 {
|
|
panic("bitfield in squash")
|
|
}
|
|
var pad uint64
|
|
switch arg := a.(type) {
|
|
case *ConstArg:
|
|
target.squashConst(arg, elems)
|
|
case *ResultArg:
|
|
target.squashResult(arg, elems)
|
|
case *PointerArg:
|
|
if arg.Res != nil {
|
|
target.squashPtr(arg, false)
|
|
*elems = append(*elems, MakeUnionArg(target.any.union, arg))
|
|
} else {
|
|
elem := target.ensureDataElem(elems)
|
|
addr := target.PhysicalAddr(arg)
|
|
for i := uint64(0); i < arg.Size(); i++ {
|
|
elem.data = append(elem.Data(), byte(addr))
|
|
addr >>= 8
|
|
}
|
|
}
|
|
case *UnionArg:
|
|
if !arg.Type().Varlen() {
|
|
pad = arg.Size() - arg.Option.Size()
|
|
}
|
|
target.squashPtrImpl(arg.Option, elems)
|
|
case *DataArg:
|
|
if arg.Type().Dir() == DirOut {
|
|
pad = arg.Size()
|
|
} else {
|
|
elem := target.ensureDataElem(elems)
|
|
elem.data = append(elem.Data(), arg.Data()...)
|
|
}
|
|
case *GroupArg:
|
|
target.squashGroup(arg, elems)
|
|
default:
|
|
panic("bad arg kind")
|
|
}
|
|
if pad != 0 {
|
|
elem := target.ensureDataElem(elems)
|
|
elem.data = append(elem.Data(), make([]byte, pad)...)
|
|
}
|
|
}
|
|
|
|
func (target *Target) squashConst(arg *ConstArg, elems *[]Arg) {
|
|
if IsPad(arg.Type()) {
|
|
elem := target.ensureDataElem(elems)
|
|
elem.data = append(elem.Data(), make([]byte, arg.Size())...)
|
|
return
|
|
}
|
|
v, bf := target.squashedValue(arg)
|
|
var data []byte
|
|
switch bf {
|
|
case FormatNative:
|
|
for i := uint64(0); i < arg.Size(); i++ {
|
|
data = append(data, byte(v))
|
|
v >>= 8
|
|
}
|
|
case FormatStrDec:
|
|
data = []byte(fmt.Sprintf("%020v", v))
|
|
case FormatStrHex:
|
|
data = []byte(fmt.Sprintf("0x%016x", v))
|
|
case FormatStrOct:
|
|
data = []byte(fmt.Sprintf("%023o", v))
|
|
default:
|
|
panic(fmt.Sprintf("unknown binary format: %v", bf))
|
|
}
|
|
if uint64(len(data)) != arg.Size() {
|
|
panic("squashed value of wrong size")
|
|
}
|
|
elem := target.ensureDataElem(elems)
|
|
elem.data = append(elem.Data(), data...)
|
|
}
|
|
|
|
func (target *Target) squashResult(arg *ResultArg, elems *[]Arg) {
|
|
switch arg.Type().Format() {
|
|
case FormatNative, FormatBigEndian:
|
|
switch arg.Size() {
|
|
case 2:
|
|
arg.typ = target.any.res16
|
|
case 4:
|
|
arg.typ = target.any.res32
|
|
case 8:
|
|
arg.typ = target.any.res64
|
|
default:
|
|
panic("bad size")
|
|
}
|
|
case FormatStrDec:
|
|
arg.typ = target.any.resdec
|
|
case FormatStrHex:
|
|
arg.typ = target.any.reshex
|
|
case FormatStrOct:
|
|
arg.typ = target.any.resoct
|
|
default:
|
|
panic("bad")
|
|
}
|
|
*elems = append(*elems, MakeUnionArg(target.any.union, arg))
|
|
}
|
|
|
|
func (target *Target) squashGroup(arg *GroupArg, elems *[]Arg) {
|
|
var pad uint64
|
|
if typ, ok := arg.Type().(*StructType); ok && typ.Varlen() && typ.AlignAttr != 0 {
|
|
var fieldsSize uint64
|
|
for _, fld := range arg.Inner {
|
|
if !fld.Type().BitfieldMiddle() {
|
|
fieldsSize += fld.Size()
|
|
}
|
|
}
|
|
if fieldsSize%typ.AlignAttr != 0 {
|
|
pad = typ.AlignAttr - fieldsSize%typ.AlignAttr
|
|
}
|
|
}
|
|
var bitfield uint64
|
|
for _, fld := range arg.Inner {
|
|
// Squash bitfields separately.
|
|
if bfLen := fld.Type().BitfieldLength(); bfLen != 0 {
|
|
bfOff := fld.Type().BitfieldOffset()
|
|
// Note: we can have a ResultArg here as well,
|
|
// but it is unsupported at the moment.
|
|
v, bf := target.squashedValue(fld.(*ConstArg))
|
|
if bf != FormatNative {
|
|
panic(fmt.Sprintf("bitfield has bad format %v", bf))
|
|
}
|
|
bitfield |= (v & ((1 << bfLen) - 1)) << bfOff
|
|
if !fld.Type().BitfieldMiddle() {
|
|
elem := target.ensureDataElem(elems)
|
|
for i := uint64(0); i < fld.Size(); i++ {
|
|
elem.data = append(elem.Data(), byte(bitfield))
|
|
bitfield >>= 8
|
|
}
|
|
bitfield = 0
|
|
}
|
|
continue
|
|
}
|
|
target.squashPtrImpl(fld, elems)
|
|
}
|
|
if pad != 0 {
|
|
elem := target.ensureDataElem(elems)
|
|
elem.data = append(elem.Data(), make([]byte, pad)...)
|
|
}
|
|
}
|
|
|
|
func (target *Target) squashedValue(arg *ConstArg) (uint64, BinaryFormat) {
|
|
bf := arg.Type().Format()
|
|
if _, ok := arg.Type().(*CsumType); ok {
|
|
// We can't compute value for the checksum here,
|
|
// but at least leave something recognizable by hints code.
|
|
// TODO: hints code won't recognize this, because it won't find
|
|
// the const in any arg. We either need to put this const as
|
|
// actual csum arg value, or special case it in hints.
|
|
return 0xabcdef1234567890, FormatNative
|
|
}
|
|
// Note: we need a constant value, but it depends on pid for proc.
|
|
v, _ := arg.Value()
|
|
if bf == FormatBigEndian {
|
|
bf = FormatNative
|
|
switch arg.Size() {
|
|
case 2:
|
|
v = uint64(swap16(uint16(v)))
|
|
case 4:
|
|
v = uint64(swap32(uint32(v)))
|
|
case 8:
|
|
v = swap64(v)
|
|
default:
|
|
panic(fmt.Sprintf("bad const size %v", arg.Size()))
|
|
}
|
|
}
|
|
return v, bf
|
|
}
|
|
|
|
func (target *Target) ensureDataElem(elems *[]Arg) *DataArg {
|
|
if len(*elems) == 0 {
|
|
res := MakeDataArg(target.any.blob, nil)
|
|
*elems = append(*elems, MakeUnionArg(target.any.union, res))
|
|
return res
|
|
}
|
|
res, ok := (*elems)[len(*elems)-1].(*UnionArg).Option.(*DataArg)
|
|
if !ok {
|
|
res = MakeDataArg(target.any.blob, nil)
|
|
*elems = append(*elems, MakeUnionArg(target.any.union, res))
|
|
}
|
|
return res
|
|
}
|