aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@gmail.com>2017-01-23 18:15:39 +0100
committerGitHub <noreply@github.com>2017-01-23 18:15:39 +0100
commitbb1ff0b5592262d20e88397f56f18e48d47d56ea (patch)
tree6eb2d4bd3090f730f9b91fddd38e2726f1a31034
parentcd23722cf2dabd28d83fa321c3cbf50a956d3fb7 (diff)
parent07880f3c010a2a5d4078b8668e05a4894fd82046 (diff)
Merge pull request #113 from xairy/parent-parent
Make it possible to specify length of parent of parent
-rw-r--r--csource/csource.go2
-rw-r--r--prog/analysis.go56
-rw-r--r--prog/encoding.go4
-rw-r--r--prog/mutation.go10
-rw-r--r--prog/rand.go3
-rw-r--r--prog/size_test.go6
-rw-r--r--prog/validation.go3
-rw-r--r--sys/README.md30
-rw-r--r--sys/decl.go6
-rw-r--r--sys/test.txt22
-rw-r--r--sysgen/sysgen.go10
11 files changed, 120 insertions, 32 deletions
diff --git a/csource/csource.go b/csource/csource.go
index 08ccae03a..183c787ab 100644
--- a/csource/csource.go
+++ b/csource/csource.go
@@ -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())
diff --git a/prog/analysis.go b/prog/analysis.go
index a4f399f98..81c17c9ee 100644
--- a/prog/analysis.go
+++ b/prog/analysis.go
@@ -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) {
diff --git a/prog/encoding.go b/prog/encoding.go
index 8c7c1674a..274e94a2d 100644
--- a/prog/encoding.go
+++ b/prog/encoding.go
@@ -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
}
diff --git a/prog/mutation.go b/prog/mutation.go
index 2136c86f8..9841d31ca 100644
--- a/prog/mutation.go
+++ b/prog/mutation.go
@@ -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 {
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/prog/validation.go b/prog/validation.go
index 5eea42e65..f03ec10b8 100644
--- a/prog/validation.go
+++ b/prog/validation.go
@@ -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
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/decl.go b/sys/decl.go
index a495ffd4a..3c98d1038 100644
--- a/sys/decl.go
+++ b/sys/decl.go
@@ -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
}
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])
diff --git a/sysgen/sysgen.go b/sysgen/sysgen.go
index 867561ad7..6906c7697 100644
--- a/sysgen/sysgen.go
+++ b/sysgen/sysgen.go
@@ -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().