Merge pull request #113 from xairy/parent-parent

Make it possible to specify length of parent of parent
This commit is contained in:
Andrey Konovalov 2017-01-23 18:15:39 +01:00 committed by GitHub
commit bb1ff0b559
11 changed files with 120 additions and 32 deletions

View File

@ -205,7 +205,7 @@ loop:
if bfOff == 0 && bfLen == 0 {
fmt.Fprintf(w, "\tNONFAILING(*(uint%v_t*)0x%x = (uint%v_t)0x%x);\n", size*8, addr, size*8, arg)
} else {
fmt.Fprintf(w, "\tNONFAILING(STORE_BY_BITMASK(uint%v_t, %v, %v, %v, %v));\n", size*8, addr, arg, bfOff, bfLen)
fmt.Fprintf(w, "\tNONFAILING(STORE_BY_BITMASK(uint%v_t, 0x%x, 0x%x, %v, %v));\n", size*8, addr, arg, bfOff, bfLen)
}
case prog.ExecArgResult:
fmt.Fprintf(w, "\tNONFAILING(*(uint%v_t*)0x%x = %v);\n", size*8, addr, resultRef())

View File

@ -174,18 +174,14 @@ func generateSize(arg *Arg, lenType *sys.LenType) *Arg {
}
}
func assignSizes(args []*Arg) {
func assignSizes(args []*Arg, parentsMap map[*Arg]*Arg) {
// Create a map of args and calculate size of the whole struct.
argsMap := make(map[string]*Arg)
var parentSize uintptr
for _, arg := range args {
if arg.Type.BitfieldLength() == 0 || arg.Type.BitfieldLast() {
parentSize += arg.Size()
}
if sys.IsPad(arg.Type) {
continue
}
argsMap[arg.Type.Name()] = arg
argsMap[arg.Type.FieldName()] = arg
}
// Fill in size arguments.
@ -194,32 +190,60 @@ func assignSizes(args []*Arg) {
continue // Pointer to optional len field, no need to fill in value.
}
if typ, ok := arg.Type.(*sys.LenType); ok {
buf, ok := argsMap[typ.Buf]
if ok {
*arg = *generateSize(buf.InnerArg(), typ)
continue
}
if typ.Buf == "parent" {
arg.Val = parentSize
arg.Val = parentsMap[arg].Size()
if typ.ByteSize != 0 {
arg.Val /= typ.ByteSize
}
continue
}
buf, ok := argsMap[typ.Buf]
if !ok {
panic(fmt.Sprintf("len field '%v' references non existent field '%v', argsMap: %+v",
typ.Name(), typ.Buf, argsMap))
sizeAssigned := false
for parent := parentsMap[arg]; parent != nil; parent = parentsMap[parent] {
if typ.Buf == parent.Type.Name() {
arg.Val = parent.Size()
if typ.ByteSize != 0 {
arg.Val /= typ.ByteSize
}
sizeAssigned = true
break
}
}
if sizeAssigned {
continue
}
*arg = *generateSize(buf.InnerArg(), typ)
panic(fmt.Sprintf("len field '%v' references non existent field '%v', argsMap: %+v",
typ.FieldName(), typ.Buf, argsMap))
}
}
}
func assignSizesCall(c *Call) {
assignSizes(c.Args)
foreachArg(c, func(arg, base *Arg, parent *[]*Arg) {
func assignSizesArray(args []*Arg) {
parentsMap := make(map[*Arg]*Arg)
foreachArgArray(&args, nil, func(arg, base *Arg, _ *[]*Arg) {
if _, ok := arg.Type.(*sys.StructType); ok {
assignSizes(arg.Inner)
for _, field := range arg.Inner {
parentsMap[field.InnerArg()] = arg
}
}
})
assignSizes(args, parentsMap)
foreachArgArray(&args, nil, func(arg, base *Arg, _ *[]*Arg) {
if _, ok := arg.Type.(*sys.StructType); ok {
assignSizes(arg.Inner, parentsMap)
}
})
}
func assignSizesCall(c *Call) {
assignSizesArray(c.Args)
}
func sanitizeCall(c *Call) {

View File

@ -108,7 +108,7 @@ func (a *Arg) serialize(buf io.Writer, vars map[*Arg]int, varSeq *int) {
}
buf.Write([]byte{delims[1]})
case ArgUnion:
fmt.Fprintf(buf, "@%v=", a.OptionType.Name())
fmt.Fprintf(buf, "@%v=", a.OptionType.FieldName())
a.Option.serialize(buf, vars, varSeq)
default:
panic("unknown arg kind")
@ -318,7 +318,7 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) {
p.Parse('=')
var optType sys.Type
for _, t2 := range t1.Options {
if name == t2.Name() {
if name == t2.FieldName() {
optType = t2
break
}

View File

@ -184,8 +184,12 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Pro
}
case *sys.UnionType:
optType := a.Options[r.Intn(len(a.Options))]
for optType.Name() == arg.OptionType.Name() {
maxIters := 1000
for i := 0; optType.FieldName() == arg.OptionType.FieldName(); i++ {
optType = a.Options[r.Intn(len(a.Options))]
if i >= maxIters {
panic(fmt.Sprintf("couldn't generate a different union option after %v iterations, type: %+v", maxIters, a))
}
}
p.removeArg(c, arg.Option)
opt, calls := r.generateArg(s, optType)
@ -303,7 +307,7 @@ func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool, crash bool)
var rec func(p *Prog, call *Call, arg *Arg, path string) bool
rec = func(p *Prog, call *Call, arg *Arg, path string) bool {
path += fmt.Sprintf("-%v", arg.Type.Name())
path += fmt.Sprintf("-%v", arg.Type.FieldName())
switch typ := arg.Type.(type) {
case *sys.StructType:
for _, innerArg := range arg.Inner {
@ -438,7 +442,7 @@ func (p *Prog) TrimAfter(idx int) {
}
func mutationArgs(c *Call) (args, bases []*Arg) {
foreachArg(c, func(arg, base *Arg, parent *[]*Arg) {
foreachArg(c, func(arg, base *Arg, _ *[]*Arg) {
switch typ := arg.Type.(type) {
case *sys.StructType:
if isSpecialStruct(typ) == nil {

View File

@ -557,6 +557,7 @@ func (r *randGen) generateParticularCall(s *state, meta *sys.Call) (calls []*Cal
Ret: returnArg(meta.Ret),
}
c.Args, calls = r.generateArgs(s, meta.Args)
assignSizesCall(c)
calls = append(calls, c)
for _, c1 := range calls {
sanitizeCall(c1)
@ -601,8 +602,6 @@ func (r *randGen) generateArgs(s *state, types []sys.Type) ([]*Arg, []*Call) {
calls = append(calls, calls1...)
}
assignSizes(args)
return args, calls
}

View File

@ -72,7 +72,7 @@ func TestAssignSize(t *testing.T) {
},
{
"syz_test$length8(&(0x7f000001f000)={0x00, {0xff, 0x0, 0x00, [0xff, 0xff, 0xff]}, [{0xff, 0x0, 0x00, [0xff, 0xff, 0xff]}], 0x00, 0x0, [0xff, 0xff]})",
"syz_test$length8(&(0x7f000001f000)={0x32, {0xff, 0x1, 0x10, [0xff, 0xff, 0xff]}, [{0xff, 0x1, 0x10, [0xff, 0xff, 0xff]}], 0x10, 0x1, [0xff, 0xff]})",
"syz_test$length8(&(0x7f000001f000)={0x38, {0xff, 0x1, 0x10, [0xff, 0xff, 0xff]}, [{0xff, 0x1, 0x10, [0xff, 0xff, 0xff]}], 0x10, 0x1, [0xff, 0xff]})",
},
{
"syz_test$length9(&(0x7f000001f000)={&(0x7f0000000000/0x5000)=nil, (0x0000)})",
@ -118,6 +118,10 @@ func TestAssignSize(t *testing.T) {
"syz_test$length19(&(0x7f0000000000)={{0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0xff}, 0xff, 0xff, 0xff})",
"syz_test$length19(&(0x7f0000000000)={{0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x14}, 0x14, 0x14, 0x5})",
},
{
"syz_test$length20(&(0x7f0000000000)={{{0xff, 0xff, 0xff, 0xff}, 0xff, 0xff, 0xff}, 0xff, 0xff})",
"syz_test$length20(&(0x7f0000000000)={{{0x4, 0x4, 0x7, 0x9}, 0x7, 0x7, 0x9}, 0x9, 0x9})",
},
}
for i, test := range tests {

View File

@ -59,6 +59,9 @@ func (c *Call) validate(ctx *validCtx) error {
if arg.Type.Name() != typ.Name() {
return fmt.Errorf("syscall %v: type name mismatch: %v vs %v", c.Meta.Name, arg.Type.Name(), typ.Name())
}
if arg.Type.FieldName() != typ.FieldName() {
return fmt.Errorf("syscall %v: field name mismatch: %v vs %v", c.Meta.Name, arg.Type.FieldName(), typ.FieldName())
}
if arg.Type.Dir() == sys.DirOut {
if (arg.Val != 0 && arg.Val != arg.Type.Default()) || arg.AddrPage != 0 || arg.AddrOffset != 0 {
// We generate output len arguments, which makes sense

View File

@ -133,6 +133,36 @@ accept(fd sock, ...) sock
listen(fd sock, backlog int32)
```
### Length
You can specify length of a particular field in struct or a named argument by using `len` and `bytesize` types, for example:
```
write(fd fd, buf buffer[in], count len[buf]) len[buf]
sock_fprog {
len len[filter, int16]
filter ptr[in, array[sock_filter]]
}
```
If `len`'s argument is a pointer (or a `buffer`), then the length of the pointee argument is used.
To denote the length of a field in N-byte words use `bytesizeN`, possible values for N are 1, 2, 4 and 8.
To denote the length of the parent struct, you can use `len[parent, int8]`.
To denote the length of the higher level parent when structs are embedded into one another, you can specify the type name of the particular parent:
```
struct s1 {
f0 len[s2] # length of s2
}
struct s2 {
f0 s1
f1 array[int32]
}
```
### Proc
The `proc` type can be used to denote per process integers.

View File

@ -29,6 +29,7 @@ const (
type Type interface {
Name() string
FieldName() string
Dir() Dir
Optional() bool
Default() uintptr
@ -49,6 +50,7 @@ func IsPad(t Type) bool {
type TypeCommon struct {
TypeName string
FldName string // for struct fields and named args
ArgDir Dir
IsOptional bool
}
@ -57,6 +59,10 @@ func (t *TypeCommon) Name() string {
return t.TypeName
}
func (t *TypeCommon) FieldName() string {
return t.FldName
}
func (t *TypeCommon) Optional() bool {
return t.IsOptional
}

View File

@ -186,6 +186,8 @@ syz_test$length17(a0 ptr[in, syz_length_bytesize2_struct])
syz_test$length18(a0 ptr[in, syz_length_bytesize3_struct])
syz_test$length19(a0 ptr[in, syz_length_bf_struct])
syz_test$length20(a0 ptr[in, syz_length_parent2_struct])
syz_length_flags = 0, 1
syz_length_int_struct {
@ -299,6 +301,26 @@ syz_length_bf_struct {
f3 bytesize4[f0, int8]
}
syz_length_parent2_struct_inner_inner {
f1 len[parent, int8]
f2 len[syz_length_parent2_struct_inner_inner, int8]
f3 len[syz_length_parent2_struct_inner, int8]
f4 len[syz_length_parent2_struct, int8]
}
syz_length_parent2_struct_inner {
f0 syz_length_parent2_struct_inner_inner
f1 len[parent, int8]
f2 len[syz_length_parent2_struct_inner, int8]
f3 len[syz_length_parent2_struct, int8]
}
syz_length_parent2_struct {
f0 syz_length_parent2_struct_inner
f1 len[parent, int8]
f2 len[syz_length_parent2_struct, int8]
}
# Big endian
syz_test$end0(a0 ptr[in, syz_end_int_struct])

View File

@ -265,10 +265,6 @@ func generateStructEntry(str Struct, key structKey, out io.Writer) {
if str.IsUnion {
typ = "UnionType"
}
name := key.field
if name == "" {
name = key.name
}
packed := ""
if str.Packed {
packed = ", packed: true"
@ -281,8 +277,8 @@ func generateStructEntry(str Struct, key structKey, out io.Writer) {
if str.Align != 0 {
align = fmt.Sprintf(", align: %v", str.Align)
}
fmt.Fprintf(out, "\"%v\": &%v{TypeCommon: TypeCommon{TypeName: \"%v\", ArgDir: %v, IsOptional: %v} %v %v %v},\n",
key, typ, name, fmtDir(key.dir), false, packed, align, varlen)
fmt.Fprintf(out, "\"%v\": &%v{TypeCommon: TypeCommon{TypeName: \"%v\", FldName: \"%v\", ArgDir: %v, IsOptional: %v} %v %v %v},\n",
key, typ, key.name, key.field, fmtDir(key.dir), false, packed, align, varlen)
}
func generateStructFields(str Struct, key structKey, desc *Description, consts map[string]uint64, out io.Writer) {
@ -379,7 +375,7 @@ func generateArg(
}
}
common := func() string {
return fmt.Sprintf("TypeCommon: TypeCommon{TypeName: %v, ArgDir: %v, IsOptional: %v}", name, fmtDir(dir), opt)
return fmt.Sprintf("TypeCommon: TypeCommon{TypeName: \"%v\", FldName: %v, ArgDir: %v, IsOptional: %v}", typ, name, fmtDir(dir), opt)
}
intCommon := func(typeSize uint64, bigEndian bool, bitfieldLen uint64) string {
// BitfieldOff and BitfieldLst will be filled in in initAlign().