aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@google.com>2017-01-10 16:45:52 +0100
committerAndrey Konovalov <andreyknvl@google.com>2017-01-17 13:25:33 +0100
commit54e0cede4384b7c1655f9183577bfccc11d9a7d5 (patch)
tree68e1f734a5e82ac7ecbe2968942d9983650ed8f8
parentf6c7b90523285663e51fc6804ae2c0c171bb390c (diff)
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.
-rw-r--r--csource/csource.go11
-rw-r--r--executor/common.h12
-rw-r--r--executor/executor.cc21
-rw-r--r--prog/encodingexec.go10
-rw-r--r--prog/encodingexec_test.go87
-rw-r--r--sys/README.md21
-rw-r--r--sys/align.go69
-rw-r--r--sys/decl.go200
-rw-r--r--sys/test.txt17
-rw-r--r--sysgen/sysgen.go84
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 {