From 54e0cede4384b7c1655f9183577bfccc11d9a7d5 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Tue, 10 Jan 2017 16:45:52 +0100 Subject: prog: add bitfields to templates Now it's possible to use `int32:18` to denote a bitfield of size 18 as a struct field. This fixes #72. --- csource/csource.go | 11 ++- executor/common.h | 12 +++ executor/executor.cc | 21 +++-- prog/encodingexec.go | 10 ++- prog/encodingexec_test.go | 87 +++++++++++--------- sys/README.md | 21 +++++ sys/align.go | 69 +++++++++++++--- sys/decl.go | 200 ++++++++++++++++++++++------------------------ sys/test.txt | 17 ++++ sysgen/sysgen.go | 84 ++++++++++++------- 10 files changed, 347 insertions(+), 185 deletions(-) diff --git a/csource/csource.go b/csource/csource.go index 25276b237..2d18e391a 100644 --- a/csource/csource.go +++ b/csource/csource.go @@ -197,7 +197,13 @@ loop: switch typ { case prog.ExecArgConst: arg := read() - fmt.Fprintf(w, "\tNONFAILING(*(uint%v_t*)0x%x = (uint%v_t)0x%x);\n", size*8, addr, size*8, arg) + bfOff := read() + bfLen := read() + 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) + } case prog.ExecArgResult: fmt.Fprintf(w, "\tNONFAILING(*(uint%v_t*)0x%x = %v);\n", size*8, addr, resultRef()) case prog.ExecArgData: @@ -235,6 +241,9 @@ loop: switch typ { case prog.ExecArgConst: fmt.Fprintf(w, ", 0x%xul", read()) + // Bitfields can't be args of a normal syscall, so just ignore them. + read() // bit field offset + read() // bit field length case prog.ExecArgResult: fmt.Fprintf(w, ", %v", resultRef()) default: diff --git a/executor/common.h b/executor/common.h index 004dcc341..40b2bf8d0 100644 --- a/executor/common.h +++ b/executor/common.h @@ -158,6 +158,18 @@ static void install_segv_handler() __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ } +#define BITMASK_LEN(type, bf_len) (type)((1ul << bf_len) - 1) + +#define BITMASK_LEN_OFF(type, bf_off, bf_len) (type)(BITMASK_LEN(type, bf_len) << bf_off) + +#define STORE_BY_BITMASK(type, addr, val, bf_off, bf_len) \ + do { \ + type new_val = *(type*)addr; \ + new_val &= ~BITMASK_LEN_OFF(type, bf_off, bf_len); \ + new_val |= ((type)val & BITMASK_LEN(type, bf_len)) << bf_off; \ + *(type*)addr = new_val; \ + } while (0) + #ifdef __NR_syz_emit_ethernet static void vsnprintf_check(char* str, size_t size, const char* format, va_list args) { diff --git a/executor/executor.cc b/executor/executor.cc index 94658a5cc..22eea9439 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -117,7 +117,7 @@ uint64_t read_input(uint64_t** input_posp, bool peek = false); uint64_t read_arg(uint64_t** input_posp); uint64_t read_result(uint64_t** input_posp); void write_output(uint32_t v); -void copyin(char* addr, uint64_t val, uint64_t size); +void copyin(char* addr, uint64_t val, uint64_t size, uint64_t bf_off, uint64_t bf_len); uint64_t copyout(char* addr, uint64_t size); thread_t* schedule_call(int n, int call_index, int call_num, uint64_t num_args, uint64_t* args, uint64_t* pos); void execute_call(thread_t* th); @@ -299,12 +299,14 @@ retry: switch (typ) { case arg_const: { uint64_t arg = read_input(&input_pos); - copyin(addr, arg, size); + uint64_t bf_off = read_input(&input_pos); + uint64_t bf_len = read_input(&input_pos); + copyin(addr, arg, size, bf_off, bf_len); break; } case arg_result: { uint64_t val = read_result(&input_pos); - copyin(addr, val, size); + copyin(addr, val, size, 0, 0); break; } case arg_data: { @@ -586,20 +588,20 @@ uint64_t cover_dedup(thread_t* th, uint64_t n) return w; } -void copyin(char* addr, uint64_t val, uint64_t size) +void copyin(char* addr, uint64_t val, uint64_t size, uint64_t bf_off, uint64_t bf_len) { NONFAILING(switch (size) { case 1: - *(uint8_t*)addr = val; + STORE_BY_BITMASK(uint8_t, addr, val, bf_off, bf_len); break; case 2: - *(uint16_t*)addr = val; + STORE_BY_BITMASK(uint16_t, addr, val, bf_off, bf_len); break; case 4: - *(uint32_t*)addr = val; + STORE_BY_BITMASK(uint32_t, addr, val, bf_off, bf_len); break; case 8: - *(uint64_t*)addr = val; + STORE_BY_BITMASK(uint64_t, addr, val, bf_off, bf_len); break; default: fail("copyin: bad argument size %lu", size); @@ -637,6 +639,9 @@ uint64_t read_arg(uint64_t** input_posp) switch (typ) { case arg_const: { arg = read_input(input_posp); + // Bitfields can't be args of a normal syscall, so just ignore them. + read_input(input_posp); // bit field offset + read_input(input_posp); // bit field length break; } case arg_result: { diff --git a/prog/encodingexec.go b/prog/encodingexec.go index 17cb2b553..57ff32eab 100644 --- a/prog/encodingexec.go +++ b/prog/encodingexec.go @@ -46,7 +46,9 @@ func (p *Prog) SerializeForExec(pid int) []byte { w.args[base] = &argInfo{} } w.args[arg] = &argInfo{Offset: w.args[base].CurSize} - w.args[base].CurSize += arg.Size() + if arg.Type.BitfieldLength() == 0 || arg.Type.BitfieldLast() { + w.args[base].CurSize += arg.Size() + } }) // Generate copyin instructions that fill in data into pointer arguments. foreachArg(c, func(arg, _ *Arg, _ *[]*Arg) { @@ -149,6 +151,8 @@ func (w *execContext) writeArg(arg *Arg, pid int) { w.write(ExecArgConst) w.write(arg.Size()) w.write(arg.Value(pid)) + w.write(arg.Type.BitfieldOffset()) + w.write(arg.Type.BitfieldLength()) case ArgResult: w.write(ExecArgResult) w.write(arg.Size()) @@ -159,10 +163,14 @@ func (w *execContext) writeArg(arg *Arg, pid int) { w.write(ExecArgConst) w.write(arg.Size()) w.write(physicalAddr(arg)) + w.write(0) // bit field offset + w.write(0) // bit field length case ArgPageSize: w.write(ExecArgConst) w.write(arg.Size()) w.write(arg.AddrPage * pageSize) + w.write(0) // bit field offset + w.write(0) // bit field length case ArgData: w.write(ExecArgData) w.write(uintptr(len(arg.Data))) diff --git a/prog/encodingexec_test.go b/prog/encodingexec_test.go index d16a6f1e2..25960b338 100644 --- a/prog/encodingexec_test.go +++ b/prog/encodingexec_test.go @@ -62,92 +62,107 @@ func TestSerializeForExec(t *testing.T) { { "syz_test$int(0x1, 0x2, 0x3, 0x4, 0x5)", []uint64{ - callID("syz_test$int"), 5, argConst, 8, 1, argConst, 1, 2, argConst, 2, 3, argConst, 4, 4, argConst, 8, 5, + callID("syz_test$int"), 5, argConst, 8, 1, 0, 0, argConst, 1, 2, 0, 0, argConst, 2, 3, 0, 0, argConst, 4, 4, 0, 0, argConst, 8, 5, 0, 0, instrEOF, }, }, { "syz_test$align0(&(0x7f0000000000)={0x1, 0x2, 0x3, 0x4, 0x5})", []uint64{ - instrCopyin, dataOffset + 0, argConst, 2, 1, - instrCopyin, dataOffset + 4, argConst, 4, 2, - instrCopyin, dataOffset + 8, argConst, 1, 3, - instrCopyin, dataOffset + 10, argConst, 2, 4, - instrCopyin, dataOffset + 16, argConst, 8, 5, - callID("syz_test$align0"), 1, argConst, ptrSize, dataOffset, + instrCopyin, dataOffset + 0, argConst, 2, 1, 0, 0, + instrCopyin, dataOffset + 4, argConst, 4, 2, 0, 0, + instrCopyin, dataOffset + 8, argConst, 1, 3, 0, 0, + instrCopyin, dataOffset + 10, argConst, 2, 4, 0, 0, + instrCopyin, dataOffset + 16, argConst, 8, 5, 0, 0, + callID("syz_test$align0"), 1, argConst, ptrSize, dataOffset, 0, 0, instrEOF, }, }, { "syz_test$align1(&(0x7f0000000000)={0x1, 0x2, 0x3, 0x4, 0x5})", []uint64{ - instrCopyin, dataOffset + 0, argConst, 2, 1, - instrCopyin, dataOffset + 2, argConst, 4, 2, - instrCopyin, dataOffset + 6, argConst, 1, 3, - instrCopyin, dataOffset + 7, argConst, 2, 4, - instrCopyin, dataOffset + 9, argConst, 8, 5, - callID("syz_test$align1"), 1, argConst, ptrSize, dataOffset, + instrCopyin, dataOffset + 0, argConst, 2, 1, 0, 0, + instrCopyin, dataOffset + 2, argConst, 4, 2, 0, 0, + instrCopyin, dataOffset + 6, argConst, 1, 3, 0, 0, + instrCopyin, dataOffset + 7, argConst, 2, 4, 0, 0, + instrCopyin, dataOffset + 9, argConst, 8, 5, 0, 0, + callID("syz_test$align1"), 1, argConst, ptrSize, dataOffset, 0, 0, instrEOF, }, }, { "syz_test$union0(&(0x7f0000000000)={0x1, @f2=0x2})", []uint64{ - instrCopyin, dataOffset + 0, argConst, 8, 1, - instrCopyin, dataOffset + 8, argConst, 1, 2, - callID("syz_test$union0"), 1, argConst, ptrSize, dataOffset, + instrCopyin, dataOffset + 0, argConst, 8, 1, 0, 0, + instrCopyin, dataOffset + 8, argConst, 1, 2, 0, 0, + callID("syz_test$union0"), 1, argConst, ptrSize, dataOffset, 0, 0, instrEOF, }, }, { "syz_test$array0(&(0x7f0000000000)={0x1, [@f0=0x2, @f1=0x3], 0x4})", []uint64{ - instrCopyin, dataOffset + 0, argConst, 1, 1, - instrCopyin, dataOffset + 1, argConst, 2, 2, - instrCopyin, dataOffset + 3, argConst, 8, 3, - instrCopyin, dataOffset + 11, argConst, 8, 4, - callID("syz_test$array0"), 1, argConst, ptrSize, dataOffset, + instrCopyin, dataOffset + 0, argConst, 1, 1, 0, 0, + instrCopyin, dataOffset + 1, argConst, 2, 2, 0, 0, + instrCopyin, dataOffset + 3, argConst, 8, 3, 0, 0, + instrCopyin, dataOffset + 11, argConst, 8, 4, 0, 0, + callID("syz_test$array0"), 1, argConst, ptrSize, dataOffset, 0, 0, instrEOF, }, }, { "syz_test$array1(&(0x7f0000000000)={0x42, \"0102030405\"})", []uint64{ - instrCopyin, dataOffset + 0, argConst, 1, 0x42, + instrCopyin, dataOffset + 0, argConst, 1, 0x42, 0, 0, instrCopyin, dataOffset + 1, argData, 5, 0x0504030201, - callID("syz_test$array1"), 1, argConst, ptrSize, dataOffset, + callID("syz_test$array1"), 1, argConst, ptrSize, dataOffset, 0, 0, instrEOF, }, }, { "syz_test$array2(&(0x7f0000000000)={0x42, \"aaaaaaaabbbbbbbbccccccccdddddddd\", 0x43})", []uint64{ - instrCopyin, dataOffset + 0, argConst, 2, 0x42, + instrCopyin, dataOffset + 0, argConst, 2, 0x42, 0, 0, instrCopyin, dataOffset + 2, argData, 16, 0xbbbbbbbbaaaaaaaa, 0xddddddddcccccccc, - instrCopyin, dataOffset + 18, argConst, 2, 0x43, - callID("syz_test$array2"), 1, argConst, ptrSize, dataOffset, + instrCopyin, dataOffset + 18, argConst, 2, 0x43, 0, 0, + callID("syz_test$array2"), 1, argConst, ptrSize, dataOffset, 0, 0, instrEOF, }, }, { "syz_test$end0(&(0x7f0000000000)={0x42, 0x42, 0x42, 0x42, 0x42})", []uint64{ - instrCopyin, dataOffset + 0, argConst, 1, 0x42, - instrCopyin, dataOffset + 1, argConst, 2, 0x4200, - instrCopyin, dataOffset + 3, argConst, 4, 0x42000000, - instrCopyin, dataOffset + 7, argConst, 8, 0x4200000000000000, - instrCopyin, dataOffset + 15, argConst, 8, 0x4200000000000000, - callID("syz_test$end0"), 1, argConst, ptrSize, dataOffset, + instrCopyin, dataOffset + 0, argConst, 1, 0x42, 0, 0, + instrCopyin, dataOffset + 1, argConst, 2, 0x4200, 0, 0, + instrCopyin, dataOffset + 3, argConst, 4, 0x42000000, 0, 0, + instrCopyin, dataOffset + 7, argConst, 8, 0x4200000000000000, 0, 0, + instrCopyin, dataOffset + 15, argConst, 8, 0x4200000000000000, 0, 0, + callID("syz_test$end0"), 1, argConst, ptrSize, dataOffset, 0, 0, instrEOF, }, }, { "syz_test$end1(&(0x7f0000000000)={0xe, 0x42, 0x1})", []uint64{ - instrCopyin, dataOffset + 0, argConst, 2, 0x0e00, - instrCopyin, dataOffset + 2, argConst, 4, 0x42000000, - instrCopyin, dataOffset + 6, argConst, 8, 0x0100000000000000, - callID("syz_test$end1"), 1, argConst, ptrSize, dataOffset, + instrCopyin, dataOffset + 0, argConst, 2, 0x0e00, 0, 0, + instrCopyin, dataOffset + 2, argConst, 4, 0x42000000, 0, 0, + instrCopyin, dataOffset + 6, argConst, 8, 0x0100000000000000, 0, 0, + callID("syz_test$end1"), 1, argConst, ptrSize, dataOffset, 0, 0, + instrEOF, + }, + }, + { + "syz_test$bf(&(0x7f0000000000)={0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42})", + []uint64{ + instrCopyin, dataOffset + 0, argConst, 2, 0x42, 0, 10, + instrCopyin, dataOffset + 8, argConst, 8, 0x42, 0, 0, + instrCopyin, dataOffset + 16, argConst, 2, 0x42, 0, 5, + instrCopyin, dataOffset + 16, argConst, 2, 0x42, 5, 6, + instrCopyin, dataOffset + 20, argConst, 4, 0x42, 0, 15, + instrCopyin, dataOffset + 24, argConst, 2, 0x42, 0, 11, + instrCopyin, dataOffset + 26, argConst, 2, 0x4200, 0, 11, + instrCopyin, dataOffset + 28, argConst, 1, 0x42, 0, 0, + callID("syz_test$bf"), 1, argConst, ptrSize, dataOffset, 0, 0, instrEOF, }, }, diff --git a/sys/README.md b/sys/README.md index e8f49b5b6..5a139f4f8 100644 --- a/sys/README.md +++ b/sys/README.md @@ -71,6 +71,27 @@ or for string flags as: flagname = "\"" literal "\"" ["," "\"" literal "\""]* ``` +### Ints + +You can use `int8`, `int16`, `int32`, `int64` and `int64` to denote an integer of the corresponding size. + +By appending `be` suffix (like `int16be`) integers become big-endian. + +It's possible to specify range of values for an integer in the format of `int32[0:100]`. + +To denote a bitfield of size N use `int64:N`. + +It's possible to use these various kinds of ints as base types for `const`, `flags`, `len` and `proc`. + +``` +example_struct { + f0 int8 # random 1-byte integer + f1 const[0x42, int16be] # const 2-byte integer with value 0x4200 (big-endian 0x42) + f2 int32[0:100] # random 4-byte integer with values from 0 to 100 inclusive + f3 int64:20 # random 20-bit bitfield +} +``` + ### Structs Structs are described as: diff --git a/sys/align.go b/sys/align.go index 1b1ea66ac..8b9e1a3c2 100644 --- a/sys/align.go +++ b/sys/align.go @@ -3,6 +3,10 @@ package sys +import ( + "fmt" +) + func initAlign() { var rec func(t Type) rec = func(t Type) { @@ -17,6 +21,7 @@ func initAlign() { for _, f := range t1.Fields { rec(f) } + markBitfields(t1) addAlignment(t1) } case *UnionType: @@ -31,6 +36,46 @@ func initAlign() { } } +func setBitfieldOffset(t Type, offset uintptr, last bool) { + switch t1 := t.(type) { + case *IntType: + t1.BitfieldOff = offset + t1.BitfieldLst = last + case *ConstType: + t1.BitfieldOff = offset + t1.BitfieldLst = last + case *LenType: + t1.BitfieldOff = offset + t1.BitfieldLst = last + case *FlagsType: + t1.BitfieldOff = offset + t1.BitfieldLst = last + case *ProcType: + t1.BitfieldOff = offset + t1.BitfieldLst = last + default: + panic(fmt.Sprintf("type %+v can't be a bitfield", t1)) + } +} + +func markBitfields(t *StructType) { + var bfOffset uintptr + for i, f := range t.Fields { + if f.BitfieldLength() == 0 { + continue + } + off, last := bfOffset, false + bfOffset += f.BitfieldLength() + if i == len(t.Fields)-1 || // Last bitfield in a group, if last field of the struct... + t.Fields[i+1].BitfieldLength() == 0 || // or next field is not a bitfield... + f.Size() != t.Fields[i+1].Size() || // or next field is of different size... + bfOffset+t.Fields[i+1].BitfieldLength() > f.Size()*8 { // or next field does not fit into the current group. + last, bfOffset = true, 0 + } + setBitfieldOffset(f, off, last) + } +} + func addAlignment(t *StructType) { if t.packed { return @@ -43,10 +88,13 @@ func addAlignment(t *StructType) { if align < a { align = a } - if off%a != 0 { - pad := a - off%a - off += pad - fields = append(fields, makePad(pad)) + if i > 0 && (t.Fields[i-1].BitfieldLength() == 0 || t.Fields[i-1].BitfieldLast()) { + // Append padding if the last field is not a bitfield or it's the last bitfield in a set. + if off%a != 0 { + pad := a - off%a + off += pad + fields = append(fields, makePad(pad)) + } } fields = append(fields, f) if at, ok := f.(*ArrayType); ok && (at.Kind == ArrayRandLen || (at.Kind == ArrayRangeLen && at.RangeBegin != at.RangeEnd)) { @@ -58,7 +106,8 @@ func addAlignment(t *StructType) { if varLen && i != len(t.Fields)-1 { panic("embed array in middle of a struct") } - if !varLen { + if (f.BitfieldLength() == 0 || f.BitfieldLast()) && !varLen { + // Increase offset if the current field is not a bitfield or it's the last bitfield in a set. off += f.Size() } } @@ -72,9 +121,11 @@ func addAlignment(t *StructType) { func makePad(sz uintptr) Type { return &ConstType{ - TypeCommon: TypeCommon{TypeName: "pad", IsOptional: false}, - TypeSize: sz, - Val: 0, - IsPad: true, + IntTypeCommon: IntTypeCommon{ + TypeCommon: TypeCommon{TypeName: "pad", IsOptional: false}, + TypeSize: sz, + }, + Val: 0, + IsPad: true, } } diff --git a/sys/decl.go b/sys/decl.go index aa59e8e0a..9c7887f99 100644 --- a/sys/decl.go +++ b/sys/decl.go @@ -34,6 +34,9 @@ type Type interface { Default() uintptr Size() uintptr Align() uintptr + BitfieldOffset() uintptr + BitfieldLength() uintptr + BitfieldLast() bool } func IsPad(t Type) bool { @@ -61,6 +64,18 @@ func (t *TypeCommon) Default() uintptr { return 0 } +func (t *TypeCommon) BitfieldOffset() uintptr { + return 0 +} + +func (t *TypeCommon) BitfieldLength() uintptr { + return 0 +} + +func (t *TypeCommon) BitfieldLast() bool { + return false +} + func (t TypeCommon) Dir() Dir { return t.ArgDir } @@ -97,6 +112,88 @@ func (t *ResourceType) Align() uintptr { return t.Desc.Type.Align() } +type IntTypeCommon struct { + TypeCommon + TypeSize uintptr + BigEndian bool + BitfieldOff uintptr + BitfieldLen uintptr + BitfieldLst bool +} + +func (t *IntTypeCommon) Size() uintptr { + return t.TypeSize +} + +func (t *IntTypeCommon) Align() uintptr { + return t.Size() +} + +func (t *IntTypeCommon) BitfieldOffset() uintptr { + return t.BitfieldOff +} + +func (t *IntTypeCommon) BitfieldLength() uintptr { + return t.BitfieldLen +} + +func (t *IntTypeCommon) BitfieldLast() bool { + return t.BitfieldLst +} + +type ConstType struct { + IntTypeCommon + Val uintptr + IsPad bool +} + +type IntKind int + +const ( + IntPlain IntKind = iota + IntSignalno + IntFileoff // offset within a file + IntRange +) + +type IntType struct { + IntTypeCommon + Kind IntKind + RangeBegin int64 + RangeEnd int64 +} + +type FlagsType struct { + IntTypeCommon + Vals []uintptr +} + +type LenType struct { + IntTypeCommon + ByteSize uintptr // want size in multiple of bytes instead of array size + Buf string +} + +type ProcType struct { + IntTypeCommon + ValuesStart int64 + ValuesPerProc uint64 +} + +type VmaType struct { + TypeCommon + RangeBegin int64 // in pages + RangeEnd int64 +} + +func (t *VmaType) Size() uintptr { + return ptrSize +} + +func (t *VmaType) Align() uintptr { + return t.Size() +} + type BufferKind int const ( @@ -153,109 +250,6 @@ func (t *BufferType) Align() uintptr { return 1 } -type VmaType struct { - TypeCommon - RangeBegin int64 // in pages - RangeEnd int64 -} - -func (t *VmaType) Size() uintptr { - return ptrSize -} - -func (t *VmaType) Align() uintptr { - return t.Size() -} - -type LenType struct { - TypeCommon - TypeSize uintptr - BigEndian bool - ByteSize uintptr // want size in multiple of bytes instead of array size - Buf string -} - -func (t *LenType) Size() uintptr { - return t.TypeSize -} - -func (t *LenType) Align() uintptr { - return t.Size() -} - -type FlagsType struct { - TypeCommon - TypeSize uintptr - BigEndian bool - Vals []uintptr -} - -func (t *FlagsType) Size() uintptr { - return t.TypeSize -} - -func (t *FlagsType) Align() uintptr { - return t.Size() -} - -type ConstType struct { - TypeCommon - TypeSize uintptr - BigEndian bool - Val uintptr - IsPad bool -} - -func (t *ConstType) Size() uintptr { - return t.TypeSize -} - -func (t *ConstType) Align() uintptr { - return t.Size() -} - -type IntKind int - -const ( - IntPlain IntKind = iota - IntSignalno - IntFileoff // offset within a file - IntRange -) - -type IntType struct { - TypeCommon - TypeSize uintptr - BigEndian bool - Kind IntKind - RangeBegin int64 - RangeEnd int64 -} - -func (t *IntType) Size() uintptr { - return t.TypeSize -} - -func (t *IntType) Align() uintptr { - return t.Size() -} - -type ProcType struct { - TypeCommon - TypeSize uintptr - BigEndian bool - ValuesStart int64 - ValuesPerProc uint64 -} - -func (t *ProcType) Size() uintptr { - return t.TypeSize -} - -func (t *ProcType) Align() uintptr { - return t.Size() -} - type ArrayKind int const ( diff --git a/sys/test.txt b/sys/test.txt index 2c7eb0d0c..aa9ea1ab0 100644 --- a/sys/test.txt +++ b/sys/test.txt @@ -217,3 +217,20 @@ syz_test$regression0(a0 ptr[inout, syz_regression0_struct]) syz_regression0_struct { f0 buffer[out] } + +# Bitfields. + +syz_bf_flags = 0, 1, 2 + +syz_bf_struct { + f0 flags[syz_bf_flags, int16:10] + f1 int64 + f2 const[0x42, int16:5] + f3 int16:6 + f4 const[0x42, int32:15] + f5 len[parent, int16:11] + f6 len[parent, int16be:11] + f7 int8 +} + +syz_test$bf(a0 ptr[in, syz_bf_struct]) diff --git a/sysgen/sysgen.go b/sysgen/sysgen.go index f93172a53..583be5a40 100644 --- a/sysgen/sysgen.go +++ b/sysgen/sysgen.go @@ -12,6 +12,7 @@ import ( "io" "os" "path/filepath" + "regexp" "sort" "strconv" "strings" @@ -380,23 +381,28 @@ func generateArg( common := func() string { return fmt.Sprintf("TypeCommon: TypeCommon{TypeName: %v, ArgDir: %v, IsOptional: %v}", name, fmtDir(dir), opt) } + intCommon := func(typeSize uint64, bigEndian bool, bitfieldLen uint64) string { + // BitfieldOff and BitfieldLst will be filled in in initAlign(). + return fmt.Sprintf("IntTypeCommon: IntTypeCommon{%v, TypeSize: %v, BigEndian: %v, BitfieldLen: %v}", common(), typeSize, bigEndian, bitfieldLen) + } canBeArg := false switch typ { case "fileoff": canBeArg = true size := uint64(ptrSize) bigEndian := false + bitfieldLen := uint64(0) if isField { if want := 1; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) } - size, bigEndian = decodeIntType(a[0]) + size, bigEndian, bitfieldLen = decodeIntType(a[0]) } else { if want := 0; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) } } - fmt.Fprintf(out, "&IntType{%v, TypeSize: %v, BigEndian: %v, Kind: IntFileoff}", common(), size, bigEndian) + fmt.Fprintf(out, "&IntType{%v, Kind: IntFileoff}", intCommon(size, bigEndian, bitfieldLen)) case "buffer": canBeArg = true if want := 1; len(a) != want { @@ -474,11 +480,12 @@ func generateArg( canBeArg = true size := uint64(ptrSize) bigEndian := false + bitfieldLen := uint64(0) if isField { if want := 2; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) } - size, bigEndian = decodeIntType(a[1]) + size, bigEndian, bitfieldLen = decodeIntType(a[1]) } else { if want := 1; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) @@ -488,16 +495,17 @@ func generateArg( if typ != "len" { byteSize = decodeByteSizeType(typ) } - fmt.Fprintf(out, "&LenType{%v, Buf: \"%v\", TypeSize: %v, BigEndian: %v, ByteSize: %v}", common(), a[0], size, bigEndian, byteSize) + fmt.Fprintf(out, "&LenType{%v, Buf: \"%v\", ByteSize: %v}", intCommon(size, bigEndian, bitfieldLen), a[0], byteSize) case "flags": canBeArg = true size := uint64(ptrSize) bigEndian := false + bitfieldLen := uint64(0) if isField { if want := 2; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) } - size, bigEndian = decodeIntType(a[1]) + size, bigEndian, bitfieldLen = decodeIntType(a[1]) } else { if want := 1; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) @@ -508,19 +516,20 @@ func generateArg( failf("unknown flag %v", a[0]) } if len(vals) == 0 { - fmt.Fprintf(out, "&IntType{%v, TypeSize: %v, BigEndian: %v}", common(), size, bigEndian) + fmt.Fprintf(out, "&IntType{%v}", intCommon(size, bigEndian, bitfieldLen)) } else { - fmt.Fprintf(out, "&FlagsType{%v, TypeSize: %v, BigEndian: %v, Vals: []uintptr{%v}}", common(), size, bigEndian, strings.Join(vals, ",")) + fmt.Fprintf(out, "&FlagsType{%v, Vals: []uintptr{%v}}", intCommon(size, bigEndian, bitfieldLen), strings.Join(vals, ",")) } case "const": canBeArg = true size := uint64(ptrSize) bigEndian := false + bitfieldLen := uint64(0) if isField { if want := 2; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) } - size, bigEndian = decodeIntType(a[1]) + size, bigEndian, bitfieldLen = decodeIntType(a[1]) } else { if want := 1; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) @@ -535,18 +544,19 @@ func generateArg( val = "0" skipSyscall(fmt.Sprintf("missing const %v", a[0])) } - fmt.Fprintf(out, "&ConstType{%v, TypeSize: %v, BigEndian: %v, Val: uintptr(%v)}", common(), size, bigEndian, val) + fmt.Fprintf(out, "&ConstType{%v, Val: uintptr(%v)}", intCommon(size, bigEndian, bitfieldLen), val) case "proc": canBeArg = true size := uint64(ptrSize) bigEndian := false + bitfieldLen := uint64(0) var valuesStart string var valuesPerProc string if isField { if want := 3; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) } - size, bigEndian = decodeIntType(a[0]) + size, bigEndian, bitfieldLen = decodeIntType(a[0]) valuesStart = a[1] valuesPerProc = a[2] } else { @@ -574,25 +584,13 @@ func generateArg( if valuesStartInt+maxPids*valuesPerProcInt >= (1 << (size * 8)) { failf("not enough values starting from '%v' with step '%v' and type size '%v' for 32 procs", valuesStartInt, valuesPerProcInt, size) } - fmt.Fprintf(out, "&ProcType{%v, TypeSize: %v, BigEndian: %v, ValuesStart: %v, ValuesPerProc: %v}", common(), size, bigEndian, valuesStartInt, valuesPerProcInt) - case "int8", "int16", "int32", "int64", "intptr", "int16be", "int32be", "int64be", "intptrbe": - canBeArg = true - size, bigEndian := decodeIntType(typ) - switch len(a) { - case 0: - fmt.Fprintf(out, "&IntType{%v, TypeSize: %v, BigEndian: %v}", common(), size, bigEndian) - case 1: - begin, end := parseRange(a[0], consts) - fmt.Fprintf(out, "&IntType{%v, TypeSize: %v, BigEndian: %v, Kind: IntRange, RangeBegin: %v, RangeEnd: %v}", common(), size, bigEndian, begin, end) - default: - failf("wrong number of arguments for %v arg %v, want 0 or 1, got %v", typ, name, len(a)) - } + fmt.Fprintf(out, "&ProcType{%v, ValuesStart: %v, ValuesPerProc: %v}", intCommon(size, bigEndian, bitfieldLen), valuesStartInt, valuesPerProcInt) case "signalno": canBeArg = true if want := 0; len(a) != want { failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) } - fmt.Fprintf(out, "&IntType{%v, TypeSize: 4, Kind: IntSignalno}", common()) + fmt.Fprintf(out, "&IntType{%v, Kind: IntSignalno}", intCommon(4, false, 0)) case "filename": canBeArg = true if want := 0; len(a) != want { @@ -640,7 +638,21 @@ func generateArg( dir = "in" fmt.Fprintf(out, "&PtrType{%v, Type: %v}", common(), generateType(a[1], a[0], desc, consts)) default: - if strings.HasPrefix(typ, "unnamed") { + intRegExp := regexp.MustCompile("^int([0-9]+|ptr)(be)?(:[0-9]+)?$") + if intRegExp.MatchString(typ) { + canBeArg = true + size, bigEndian, bitfieldLen := decodeIntType(typ) + switch len(a) { + case 0: + fmt.Fprintf(out, "&IntType{%v}", intCommon(size, bigEndian, bitfieldLen)) + case 1: + begin, end := parseRange(a[0], consts) + fmt.Fprintf(out, "&IntType{%v, Kind: IntRange, RangeBegin: %v, RangeEnd: %v}", + intCommon(size, bigEndian, bitfieldLen), begin, end) + default: + failf("wrong number of arguments for %v arg %v, want 0 or 1, got %v", typ, name, len(a)) + } + } else if strings.HasPrefix(typ, "unnamed") { if inner, ok := desc.Unnamed[typ]; ok { generateArg("", "", inner[0], dir, inner[1:], desc, consts, false, isField, out) } else { @@ -686,12 +698,25 @@ func fmtDir(s string) string { } } -func decodeIntType(typ string) (uint64, bool) { +func decodeIntType(typ string) (uint64, bool, uint64) { bigEndian := false + bitfieldLen := uint64(0) + + parts := strings.Split(typ, ":") + if len(parts) == 2 { + var err error + bitfieldLen, err = strconv.ParseUint(parts[1], 10, 64) + if err != nil { + failf("failed to parse bitfield length '%v'", parts[1]) + } + typ = parts[0] + } + if strings.HasSuffix(typ, "be") { bigEndian = true typ = typ[:len(typ)-2] } + switch typ { case "int8", "int16", "int32", "int64", "intptr": default: @@ -701,7 +726,12 @@ func decodeIntType(typ string) (uint64, bool) { if typ != "intptr" { sz, _ = strconv.ParseInt(typ[3:], 10, 64) } - return uint64(sz / 8), bigEndian + + if bitfieldLen >= uint64(sz) { + failf("bitfield of size %v is too large for base type of size %v", bitfieldLen, sz/8) + } + + return uint64(sz / 8), bigEndian, bitfieldLen } func decodeByteSizeType(typ string) uint8 { -- cgit mrf-deployment