aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2025-01-17 10:39:47 +0100
committerDmitry Vyukov <dvyukov@google.com>2025-01-20 13:30:27 +0000
commit2b76b86c449fff4c26410164052c32f3b9cf56fe (patch)
treeedcffe7263a530b1e1751d5d3cd48599477c64bf /pkg
parentf2cb035c8f931efff4a020b164e657f16f51934b (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.go64
-rw-r--r--pkg/declextract/entity.go21
-rw-r--r--pkg/declextract/serialization.go12
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, ", "))