diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2025-01-17 10:39:47 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2025-01-20 13:30:27 +0000 |
| commit | 2b76b86c449fff4c26410164052c32f3b9cf56fe (patch) | |
| tree | edcffe7263a530b1e1751d5d3cd48599477c64bf /pkg | |
| parent | f2cb035c8f931efff4a020b164e657f16f51934b (diff) | |
tools/syz-declextract: fix empty structs and arrays
This fixes 2 bugs:
1. We completly remove empty structs, but they can have
effect on parent struct layout if they have >1 alignment.
Replace empty structs with a special auto_aligner type
that preserves alignment.
2. Arrays of 0 size are currently emitted as dynamically-sized
(we assume 0 size means "this is not a const-size array").
Add separate IsConstSize flag for arrays that marks const-size arrays.
Additionally cross-check that generated structs have exactly
the same size/alignment as the corresponding C structs.
This allows to catch the above bugs.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/declextract/declextract.go | 64 | ||||
| -rw-r--r-- | pkg/declextract/entity.go | 21 | ||||
| -rw-r--r-- | pkg/declextract/serialization.go | 12 |
3 files changed, 73 insertions, 24 deletions
diff --git a/pkg/declextract/declextract.go b/pkg/declextract/declextract.go index 9df449b63..16b2d6cca 100644 --- a/pkg/declextract/declextract.go +++ b/pkg/declextract/declextract.go @@ -9,14 +9,25 @@ import ( "fmt" "io" "os" - "slices" "strings" "github.com/google/syzkaller/pkg/ifaceprobe" ) +type Result struct { + Descriptions []byte + Interfaces []*Interface + IncludeUse map[string]string + StructInfo map[string]*StructInfo +} + +type StructInfo struct { + Size int + Align int +} + func Run(out *Output, probe *ifaceprobe.Info, syscallRename map[string][]string, trace io.Writer) ( - []byte, []*Interface, map[string]string, error) { + *Result, error) { ctx := &context{ Output: out, probe: probe, @@ -31,13 +42,21 @@ func Run(out *Output, probe *ifaceprobe.Info, syscallRename map[string][]string, ctx.processTypingFacts() includeUse := ctx.processConsts() ctx.processEnums() - ctx.processStructs() + structInfo := ctx.processStructs() ctx.processSyscalls() ctx.processIouring() ctx.serialize() ctx.finishInterfaces() - return ctx.descriptions.Bytes(), ctx.interfaces, includeUse, errors.Join(ctx.errs...) + if len(ctx.errs) != 0 { + return nil, errors.Join(ctx.errs...) + } + return &Result{ + Descriptions: ctx.descriptions.Bytes(), + Interfaces: ctx.interfaces, + IncludeUse: includeUse, + StructInfo: structInfo, + }, nil } type context struct { @@ -173,14 +192,16 @@ func (ctx *context) processIouring() { } } -func (ctx *context) processStructs() { +func (ctx *context) processStructs() map[string]*StructInfo { + structInfo := make(map[string]*StructInfo) for _, str := range ctx.Structs { str.Name += autoSuffix ctx.structs[str.Name] = str + structInfo[str.Name] = &StructInfo{ + Size: str.ByteSize, + Align: str.Align, + } } - ctx.Structs = slices.DeleteFunc(ctx.Structs, func(str *Struct) bool { - return str.ByteSize == 0 // Empty structs are not supported. - }) for _, str := range ctx.Structs { ctx.processFields(str.Fields, str.Name, true) name := strings.TrimSuffix(str.Name, autoSuffix) @@ -189,6 +210,7 @@ func (ctx *context) processStructs() { refineFieldType(f, typ, true) } } + return structInfo } func (ctx *context) processFields(fields []*Field, parent string, needBase bool) { @@ -386,8 +408,17 @@ func (ctx *context) fieldTypeArray(f *Field, parent string) string { Type: t.Elem, } elemType := ctx.fieldType(elem, nil, parent, true) - if t.MinSize == 1 && t.MaxSize == 1 { - return elemType + if t.IsConstSize { + switch t.MaxSize { + case 0: + // Empty arrays may still affect parent struct layout, if the element type + // has alignment >1. We don't support arrays of size 0, so emit a special + // aligning type instead. + return fmt.Sprintf("auto_aligner[%v]", t.Align) + case 1: + // Array of size 1 is not really an array, just use the element type itself. + return elemType + } } bounds := ctx.bounds(f.Name, t.MinSize, t.MaxSize) return fmt.Sprintf("array[%v%v]", elemType, bounds) @@ -442,9 +473,16 @@ func (ctx *context) fieldTypeStruct(f *Field) string { case "__kernel_sockaddr_storage": return "sockaddr_storage" } - f.Type.Struct += autoSuffix - if ctx.structs[f.Type.Struct].ByteSize == 0 { - return voidType + // We can get here several times for the same struct. + if !strings.HasSuffix(f.Type.Struct, autoSuffix) { + f.Type.Struct += autoSuffix + } + str := ctx.structs[f.Type.Struct] + if str == nil { + panic(fmt.Sprintf("can't find struct %v", f.Type.Struct)) + } + if str.ByteSize == 0 { + return fmt.Sprintf("auto_aligner[%v]", str.Align) } return f.Type.Struct } diff --git a/pkg/declextract/entity.go b/pkg/declextract/entity.go index 8167d8b99..83f56ba52 100644 --- a/pkg/declextract/entity.go +++ b/pkg/declextract/entity.go @@ -111,12 +111,13 @@ type NetlinkAttr struct { } type Struct struct { - Name string `json:"name,omitempty"` - ByteSize int `json:"byte_size,omitempty"` - IsUnion bool `json:"is_union,omitempty"` - IsPacked bool `json:"is_packed,omitempty"` - Align int `json:"align,omitempty"` - Fields []*Field `json:"fields,omitempty"` + Name string `json:"name,omitempty"` + ByteSize int `json:"byte_size,omitempty"` + Align int `json:"align,omitempty"` + IsUnion bool `json:"is_union,omitempty"` + IsPacked bool `json:"is_packed,omitempty"` + AlignAttr int `json:"align_attr,omitempty"` + Fields []*Field `json:"fields,omitempty"` } type Enum struct { @@ -150,9 +151,11 @@ type PtrType struct { } type ArrayType struct { - Elem *Type `json:"elem,omitempty"` - MinSize int `json:"min_size,omitempty"` - MaxSize int `json:"max_size,omitempty"` + Elem *Type `json:"elem,omitempty"` + MinSize int `json:"min_size,omitempty"` + MaxSize int `json:"max_size,omitempty"` + Align int `json:"align,omitempty"` + IsConstSize bool `json:"is_const_size,omitempty"` } type BufferType struct { diff --git a/pkg/declextract/serialization.go b/pkg/declextract/serialization.go index 571336097..15e4a34a8 100644 --- a/pkg/declextract/serialization.go +++ b/pkg/declextract/serialization.go @@ -32,6 +32,10 @@ type auto_union[INFERRED, RAW] [ raw RAW ] +type auto_aligner[N] { + void void +} [align[N]] + ` func (ctx *context) fmt(msg string, args ...any) { @@ -76,6 +80,10 @@ func (ctx *context) serializeEnums() { func (ctx *context) serializeStructs() { for _, str := range ctx.Structs { + // Empty structs are not supported, but we also shouldn't emit references to them. + if str.ByteSize == 0 { + continue + } delims := "{}" if str.IsUnion { delims = "[]" @@ -89,8 +97,8 @@ func (ctx *context) serializeStructs() { if str.IsPacked { attrs = append(attrs, "packed") } - if str.Align != 0 { - attrs = append(attrs, fmt.Sprintf("align[%v]", str.Align)) + if str.AlignAttr != 0 { + attrs = append(attrs, fmt.Sprintf("align[%v]", str.AlignAttr)) } if len(attrs) != 0 { ctx.fmt(" [%v]", strings.Join(attrs, ", ")) |
