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.
This commit is contained in:
Dmitry Vyukov 2019-05-16 18:05:05 +02:00
parent f59a9cb554
commit 76fc461b55
12 changed files with 86 additions and 11 deletions

View File

@ -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

View File

@ -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

View File

@ -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},

View File

@ -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)

View File

@ -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 {

View File

@ -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]

View File

@ -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,
}
},
}

View File

@ -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 {

View File

@ -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)

View File

@ -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
}

View File

@ -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"

View File

@ -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])