From 76fc461b55cfe334e8d4be121f61fa25d079f8ba Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 16 May 2019 18:05:05 +0200 Subject: [PATCH] pkg/compiler: add offsetof type Similar to C offsetof gives offset of a field from the beginning of the parent struct. We have several TODOs in descriptions asking for this. --- docs/syscall_descriptions_syntax.md | 2 ++ executor/defs.h | 2 +- executor/syscalls.h | 1 + pkg/compiler/check.go | 7 ++++++- pkg/compiler/testdata/all.txt | 2 ++ pkg/compiler/testdata/errors2.txt | 2 ++ pkg/compiler/types.go | 7 ++++++- prog/size.go | 22 +++++++++++++++------- prog/size_test.go | 4 ++++ prog/types.go | 1 + sys/test/gen/64.go | 26 +++++++++++++++++++++++++- sys/test/test.txt | 21 +++++++++++++++++++++ 12 files changed, 86 insertions(+), 11 deletions(-) diff --git a/docs/syscall_descriptions_syntax.md b/docs/syscall_descriptions_syntax.md index 25b0e0e6..cb5249ab 100644 --- a/docs/syscall_descriptions_syntax.md +++ b/docs/syscall_descriptions_syntax.md @@ -50,6 +50,8 @@ rest of the type-options are type-specific: argname of the object "bitsize": similar to "len", but always denotes the size in bits, type-options: argname of the object +"offsetof": offset of the field from the beginning of the parent struct, type-options: + field "vma"/"vma64": a pointer to a set of pages (used as input for mmap/munmap/mremap/madvise), type-options: optional number of pages (e.g. vma[7]), or a range of pages (e.g. vma[2-4]) vma64 has size of 8 bytes regardless of target pointer size diff --git a/executor/defs.h b/executor/defs.h index a6ec8599..bf0d34fe 100644 --- a/executor/defs.h +++ b/executor/defs.h @@ -165,7 +165,7 @@ #if GOARCH_64 #define GOARCH "64" -#define SYZ_REVISION "e07c8657b55348a9385e59d435ee7ad303ccc728" +#define SYZ_REVISION "74c87d08a1f72c38c85e889813b60823a1e5755c" #define SYZ_EXECUTOR_USES_FORK_SERVER 0 #define SYZ_EXECUTOR_USES_SHMEM 0 #define SYZ_PAGE_SIZE 4096 diff --git a/executor/syscalls.h b/executor/syscalls.h index 8c6e6f0f..a0810f52 100644 --- a/executor/syscalls.h +++ b/executor/syscalls.h @@ -15509,6 +15509,7 @@ const call_t syscalls[] = { {"test$length9", 0}, {"test$missing_resource", 0}, {"test$missing_struct", 0}, + {"test$offsetof0", 0}, {"test$opt0", 0}, {"test$opt1", 0}, {"test$opt2", 0}, diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go index 0b296e2e..24acfe50 100644 --- a/pkg/compiler/check.go +++ b/pkg/compiler/check.go @@ -415,7 +415,12 @@ func (comp *compiler) checkLenTargetRec(t0, t *ast.Type, targets []*ast.Type, parent := parents[pi] if parent.name != "" && (parent.name == target.Ident || target.Ident == prog.ParentRef) || parent.name == "" && target.Ident == prog.SyscallRef { - if len(targets) != 0 { + if len(targets) == 0 { + if t.Ident == "offsetof" { + comp.error(target.Pos, "%v must refer to fields", t.Ident) + return + } + } else { parents1 := make([]parentDesc, pi+1) copy(parents1, parents[:pi+1]) comp.checkLenTargetRec(t0, t, targets, parents1, warned) diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt index 25461ff9..68629c68 100644 --- a/pkg/compiler/testdata/all.txt +++ b/pkg/compiler/testdata/all.txt @@ -90,6 +90,8 @@ len_expr3 { f34 bytesize[len_expr1:f11:f22:f41, int32] f35 bytesize[len_expr2:f23:f41, int32] f36 bytesize[len_expr2:f24:f41, int32] + f37 offsetof[f32, int32] + f38 offsetof[len_expr2:f21, int32] } len_expr4 { diff --git a/pkg/compiler/testdata/errors2.txt b/pkg/compiler/testdata/errors2.txt index 311be3ea..db8f87e2 100644 --- a/pkg/compiler/testdata/errors2.txt +++ b/pkg/compiler/testdata/errors2.txt @@ -147,6 +147,8 @@ slen1 { f5 len[slen22:f, int32] ### len path slen22 does not refer to a struct f6 len[syscall, int32] ### no argument name after syscall reference f7 len[syscall:b, int32] ### len target b does not exist + f8 offsetof[parent, int32] ### offsetof must refer to fields + f9 offsetof[slen1, int32] ### offsetof must refer to fields slen2 ptr[in, array[slen2]] slen21 slen2 slen22 array[slen2] diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index 6d921168..18027615 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -204,13 +204,14 @@ var typeArray = &typeDesc{ } var typeLen = &typeDesc{ - Names: []string{"len", "bytesize", "bytesize2", "bytesize4", "bytesize8", "bitsize"}, + Names: []string{"len", "bytesize", "bytesize2", "bytesize4", "bytesize8", "bitsize", "offsetof"}, CanBeArgRet: canBeArg, CantBeOpt: true, NeedBase: true, Args: []namedArg{{Name: "len target", Type: typeArgLenTarget}}, Gen: func(comp *compiler, t *ast.Type, args []*ast.Type, base prog.IntTypeCommon) prog.Type { var bitSize uint64 + var offset bool switch t.Ident { case "bytesize": bitSize = 8 @@ -219,6 +220,9 @@ var typeLen = &typeDesc{ bitSize = byteSize * 8 case "bitsize": bitSize = 1 + case "offsetof": + bitSize = 8 + offset = true } path := []string{args[0].Ident} for _, col := range args[0].Colon { @@ -228,6 +232,7 @@ var typeLen = &typeDesc{ IntTypeCommon: base, Path: path, BitSize: bitSize, + Offset: offset, } }, } diff --git a/prog/size.go b/prog/size.go index 23248c72..a134f0d1 100644 --- a/prog/size.go +++ b/prog/size.go @@ -42,8 +42,12 @@ func (target *Target) assignSizes(args []Arg, parentsMap map[Arg]Arg, syscallArg func (target *Target) assignSize(dst *ConstArg, pos Arg, path []string, args []Arg, parentsMap map[Arg]Arg) { elem := path[0] path = path[1:] + var offset uint64 for _, buf := range args { if elem != buf.Type().FieldName() { + if !buf.Type().BitfieldMiddle() { + offset += buf.Size() + } continue } buf = InnerArg(buf) @@ -52,7 +56,7 @@ func (target *Target) assignSize(dst *ConstArg, pos Arg, path []string, args []A return } if len(path) == 0 { - dst.Val = target.computeSize(buf, dst.Type().(*LenType)) + dst.Val = target.computeSize(buf, offset, dst.Type().(*LenType)) } else { target.assignSize(dst, buf, path, buf.(*GroupArg).Inner, parentsMap) } @@ -61,7 +65,7 @@ func (target *Target) assignSize(dst *ConstArg, pos Arg, path []string, args []A if elem == ParentRef { buf := parentsMap[pos] if len(path) == 0 { - dst.Val = target.computeSize(buf, dst.Type().(*LenType)) + dst.Val = target.computeSize(buf, noOffset, dst.Type().(*LenType)) } else { target.assignSize(dst, buf, path, buf.(*GroupArg).Inner, parentsMap) } @@ -77,7 +81,7 @@ func (target *Target) assignSize(dst *ConstArg, pos Arg, path []string, args []A continue } if len(path) == 0 { - dst.Val = target.computeSize(buf, dst.Type().(*LenType)) + dst.Val = target.computeSize(buf, noOffset, dst.Type().(*LenType)) } else { target.assignSize(dst, buf, path, buf.(*GroupArg).Inner, parentsMap) } @@ -91,10 +95,14 @@ func (target *Target) assignSize(dst *ConstArg, pos Arg, path []string, args []A dst.Type().FieldName(), elem, pos.Type().Name(), pos.Type().FieldName(), argNames)) } -func (target *Target) computeSize(arg Arg, lenType *LenType) uint64 { - if arg == nil { - // Arg is an optional pointer, set size to 0. - return 0 +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 } bitSize := lenType.BitSize if bitSize == 0 { diff --git a/prog/size_test.go b/prog/size_test.go index 613104bf..7b248bd4 100644 --- a/prog/size_test.go +++ b/prog/size_test.go @@ -155,6 +155,10 @@ func TestAssignSize(t *testing.T) { "test$length30(&(0x7f0000000000)={{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, {'a', 'aaa', 'aaaaa', 'aaaaaa'}, &(0x7f0000000000)={'a', 'aaa', 'aaaaa', 'aaaaaa'}, &(0x7f0000000000)=&(0x7f0000000000)={'a', 'aaa', 'aaaaa', 'aaaaaa'}, 0x0}, 0x0}, 0x0, &(0x7f0000000000)=0x0, 0x0)", "test$length30(&(0x7f0000000000)={{{0x0, 0x18, 0x1, 0x3, 0x5, 0x6}, {'a', 'aaa', 'aaaaa', 'aaaaaa'}, &(0x7f0000000000)={'a', 'aaa', 'aaaaa', 'aaaaaa'}, &(0x7f0000000000)=&(0x7f0000000000)={'a', 'aaa', 'aaaaa', 'aaaaaa'}, 0x2}, 0x4}, 0x40, &(0x7f0000000000)=0x18, 0x2)", }, + { + "test$offsetof0(&(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0})", + "test$offsetof0(&(0x7f0000000000)={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x6, 0x8, 0x10, 0x18, 0x18, 0x20})", + }, } for i, test := range tests { p, err := target.Deserialize([]byte(test.unsizedProg), Strict) diff --git a/prog/types.go b/prog/types.go index ebfcbc5a..7f956948 100644 --- a/prog/types.go +++ b/prog/types.go @@ -255,6 +255,7 @@ func (t *FlagsType) isDefaultArg(arg Arg) bool { type LenType struct { IntTypeCommon BitSize uint64 // want size in multiple of bits instead of array size + Offset bool // offset from the beginning of the parent struct or base object Path []string } diff --git a/sys/test/gen/64.go b/sys/test/gen/64.go index 3e20cff6..00cba243 100644 --- a/sys/test/gen/64.go +++ b/sys/test/gen/64.go @@ -152,6 +152,27 @@ var structDescs_64 = []*KeyedStruct{ &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int8", FldName: "f2", TypeSize: 1}}}, &ConstType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "pad", TypeSize: 3}}, IsPad: true}, }}}, + {Key: StructKey{Name: "offsetof0"}, Desc: &StructDesc{TypeCommon: TypeCommon{TypeName: "offsetof0", TypeSize: 72}, Fields: []Type{ + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", FldName: "f0", TypeSize: 4}}}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int8", FldName: "f1", TypeSize: 1}}}, + &ConstType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "pad", TypeSize: 1}}, IsPad: true}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int16", FldName: "f2", TypeSize: 2}}}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int8", FldName: "f3", TypeSize: 1}}}, + &ConstType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "pad", TypeSize: 7}}, IsPad: true}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int64", FldName: "f4", TypeSize: 8}}}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", FldName: "f5", TypeSize: 4}, BitfieldLen: 5, BitfieldMdl: true}}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", FldName: "f6", TypeSize: 4}, BitfieldOff: 5, BitfieldLen: 10}}, + &ConstType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "pad", TypeSize: 4}}, IsPad: true}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int64", FldName: "f7", TypeSize: 8}}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "offsetof", FldName: "o0", TypeSize: 4}}, BitSize: 8, Offset: true, Path: []string{"f0"}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "offsetof", FldName: "o1", TypeSize: 4}}, BitSize: 8, Offset: true, Path: []string{"f1"}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "offsetof", FldName: "o2", TypeSize: 4}}, BitSize: 8, Offset: true, Path: []string{"f2"}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "offsetof", FldName: "o3", TypeSize: 4}}, BitSize: 8, Offset: true, Path: []string{"f3"}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "offsetof", FldName: "o4", TypeSize: 4}}, BitSize: 8, Offset: true, Path: []string{"f4"}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "offsetof", FldName: "o5", TypeSize: 4}}, BitSize: 8, Offset: true, Path: []string{"f5"}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "offsetof", FldName: "o6", TypeSize: 4}}, BitSize: 8, Offset: true, Path: []string{"f6"}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "offsetof", FldName: "o7", TypeSize: 4}}, BitSize: 8, Offset: true, Path: []string{"f7"}}, + }}}, {Key: StructKey{Name: "serialize0_struct"}, Desc: &StructDesc{TypeCommon: TypeCommon{TypeName: "serialize0_struct", TypeSize: 15}, Fields: []Type{ &BufferType{TypeCommon: TypeCommon{TypeName: "string", FldName: "a", TypeSize: 10}, Kind: 2, SubKind: "serialize_strings", Values: []string{"aaa\x00\x00\x00\x00\x00\x00\x00", "bbb\x00\x00\x00\x00\x00\x00\x00"}}, &BufferType{TypeCommon: TypeCommon{TypeName: "string", FldName: "b", TypeSize: 5}, Kind: 2, SubKind: "serialize_strings", Values: []string{"aaa\x00\x00", "bbb\x00\x00"}}, @@ -896,6 +917,9 @@ var syscalls_64 = []*Syscall{ {Name: "test$missing_struct", CallName: "test", MissingArgs: 5, Args: []Type{ &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "a", TypeSize: 8}, Type: &StructType{Key: StructKey{Name: "syz_use_missing"}}}, }}, + {Name: "test$offsetof0", CallName: "test", MissingArgs: 5, Args: []Type{ + &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "a0", TypeSize: 8}, Type: &StructType{Key: StructKey{Name: "offsetof0"}}}, + }}, {Name: "test$opt0", CallName: "test", MissingArgs: 5, Args: []Type{ &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "intptr", FldName: "a0", TypeSize: 8, IsOptional: true}}}, }}, @@ -1010,4 +1034,4 @@ var consts_64 = []ConstValue{ {Name: "SYS_unsupported"}, } -const revision_64 = "e07c8657b55348a9385e59d435ee7ad303ccc728" +const revision_64 = "74c87d08a1f72c38c85e889813b60823a1e5755c" diff --git a/sys/test/test.txt b/sys/test/test.txt index 1ddc67aa..a087dd47 100644 --- a/sys/test/test.txt +++ b/sys/test/test.txt @@ -461,6 +461,27 @@ len_expr4 { test$length30(a0 ptr[in, len_expr1], a1 bytesize[a0:f11], a2 ptr[in, bytesize[a0:f11:f21, int32]], a3 bytesize[a0:f11:f21:f31]) +test$offsetof0(a0 ptr[in, offsetof0]) + +offsetof0 { + f0 int32 + f1 int8 + f2 int16 + f3 int8 + f4 int64 + f5 int32:5 + f6 int32:10 + f7 int64 + o0 offsetof[f0, int32] + o1 offsetof[f1, int32] + o2 offsetof[f2, int32] + o3 offsetof[f3, int32] + o4 offsetof[f4, int32] + o5 offsetof[f5, int32] + o6 offsetof[f6, int32] + o7 offsetof[f7, int32] +} + # Big endian test$end0(a0 ptr[in, syz_end_int_struct])