diff options
| -rw-r--r-- | prog/analysis.go | 54 | ||||
| -rw-r--r-- | prog/rand.go | 3 | ||||
| -rw-r--r-- | prog/size_test.go | 6 | ||||
| -rw-r--r-- | sys/README.md | 30 | ||||
| -rw-r--r-- | sys/test.txt | 22 |
5 files changed, 97 insertions, 18 deletions
diff --git a/prog/analysis.go b/prog/analysis.go index a6ee3885b..81c17c9ee 100644 --- a/prog/analysis.go +++ b/prog/analysis.go @@ -174,14 +174,10 @@ 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 } @@ -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.FieldName(), 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) { diff --git a/prog/rand.go b/prog/rand.go index 4645db811..4a7bbe08d 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -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 } diff --git a/prog/size_test.go b/prog/size_test.go index 0b7e5fb98..f6e4ab17c 100644 --- a/prog/size_test.go +++ b/prog/size_test.go @@ -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 { diff --git a/sys/README.md b/sys/README.md index 2e5f07ec1..b5b497bc6 100644 --- a/sys/README.md +++ b/sys/README.md @@ -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. diff --git a/sys/test.txt b/sys/test.txt index e36af549d..0ce7ec819 100644 --- a/sys/test.txt +++ b/sys/test.txt @@ -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]) |
