aboutsummaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--pkg/declextract/declextract.go64
-rw-r--r--pkg/declextract/entity.go21
-rw-r--r--pkg/declextract/serialization.go12
-rw-r--r--tools/syz-declextract/clangtool/declextract.cpp13
-rw-r--r--tools/syz-declextract/clangtool/output.h12
-rw-r--r--tools/syz-declextract/declextract.go40
-rw-r--r--tools/syz-declextract/declextract_test.go20
-rw-r--r--tools/syz-declextract/testdata/arch/x86/syscalls.tbl3
-rw-r--r--tools/syz-declextract/testdata/file_operations.c.json1
-rw-r--r--tools/syz-declextract/testdata/netlink.c.json2
-rw-r--r--tools/syz-declextract/testdata/types.c35
-rw-r--r--tools/syz-declextract/testdata/types.c.info1
-rw-r--r--tools/syz-declextract/testdata/types.c.json284
-rw-r--r--tools/syz-declextract/testdata/types.c.txt39
14 files changed, 472 insertions, 75 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, ", "))
diff --git a/tools/syz-declextract/clangtool/declextract.cpp b/tools/syz-declextract/clangtool/declextract.cpp
index 4012900d6..d7d6bc824 100644
--- a/tools/syz-declextract/clangtool/declextract.cpp
+++ b/tools/syz-declextract/clangtool/declextract.cpp
@@ -128,6 +128,7 @@ private:
const T* findFirstMatch(const Node* Expr, const Condition& Cond);
std::optional<QualType> getSizeofType(const Expr* E);
int sizeofType(const Type* T);
+ int alignofType(const Type* T);
std::vector<IoctlCmd> extractIoctlCommands(const std::string& Ioctl);
std::optional<TypingEntity> getTypingEntity(const std::string& CurrentFunc,
std::unordered_map<const VarDecl*, int>& LocalVars,
@@ -206,11 +207,15 @@ FieldType Extractor::genType(QualType QT, const std::string& BackupName) {
return extractRecord(QT, Typ, BackupName);
}
if (auto* Typ = llvm::dyn_cast<ConstantArrayType>(T)) {
+ // TODO: the size may be a macro that is different for each arch, e.g.:
+ // long foo[FOOSIZE/sizeof(long)];
int Size = Typ->getSize().getZExtValue();
return ArrType{
.Elem = genType(Typ->getElementType(), BackupName),
.MinSize = Size,
.MaxSize = Size,
+ .Align = alignofType(Typ),
+ .IsConstSize = true,
};
}
if (auto* Typ = llvm::dyn_cast<PointerType>(T)) {
@@ -275,12 +280,12 @@ FieldType Extractor::extractRecord(QualType QT, const RecordType* Typ, const std
.Type = std::move(FieldType),
});
}
- int Align = 0;
+ int AlignAttr = 0;
bool Packed = false;
if (Decl->isStruct() && Decl->hasAttrs()) {
for (const auto& A : Decl->getAttrs()) {
if (auto* Attr = llvm::dyn_cast<AlignedAttr>(A))
- Align = Attr->getAlignment(*Context) / 8;
+ AlignAttr = Attr->getAlignment(*Context) / 8;
else if (llvm::isa<PackedAttr>(A))
Packed = true;
}
@@ -288,9 +293,10 @@ FieldType Extractor::extractRecord(QualType QT, const RecordType* Typ, const std
Output.emit(Struct{
.Name = Name,
.ByteSize = sizeofType(Typ),
+ .Align = alignofType(Typ),
.IsUnion = Decl->isUnion(),
.IsPacked = Packed,
- .Align = Align,
+ .AlignAttr = AlignAttr,
.Fields = std::move(Fields),
});
return Name;
@@ -445,6 +451,7 @@ std::vector<std::pair<int, std::string>> Extractor::extractDesignatedInitConsts(
}
int Extractor::sizeofType(const Type* T) { return static_cast<int>(Context->getTypeInfo(T).Width) / 8; }
+int Extractor::alignofType(const Type* T) { return static_cast<int>(Context->getTypeInfo(T).Align) / 8; }
template <typename T> T Extractor::evaluate(const Expr* E) {
Expr::EvalResult Res;
diff --git a/tools/syz-declextract/clangtool/output.h b/tools/syz-declextract/clangtool/output.h
index d89d64afb..ffa0844c9 100644
--- a/tools/syz-declextract/clangtool/output.h
+++ b/tools/syz-declextract/clangtool/output.h
@@ -53,6 +53,8 @@ struct ArrType {
FieldType Elem;
int MinSize = 0;
int MaxSize = 0;
+ int Align = 0;
+ bool IsConstSize = false;
};
struct BufferType {
@@ -79,9 +81,10 @@ struct Field {
struct Struct {
std::string Name;
int ByteSize = 0;
+ int Align = 0;
bool IsUnion = false;
bool IsPacked = false;
- int Align = 0;
+ int AlignAttr = 0;
std::vector<Field> Fields;
};
@@ -217,10 +220,11 @@ inline void print(JSONPrinter& Printer, const Field& V) {
inline void print(JSONPrinter& Printer, const Struct& V) {
JSONPrinter::Scope Scope(Printer);
Printer.Field("name", V.Name);
+ Printer.Field("align", V.Align);
Printer.Field("byte_size", V.ByteSize);
Printer.Field("is_union", V.IsUnion);
Printer.Field("is_packed", V.IsPacked);
- Printer.Field("align", V.Align);
+ Printer.Field("align_attr", V.AlignAttr);
Printer.Field("fields", V.Fields, true);
}
@@ -265,7 +269,9 @@ inline void print(JSONPrinter& Printer, const ArrType& V) {
JSONPrinter::Scope Scope(Printer);
Printer.Field("elem", V.Elem);
Printer.Field("min_size", V.MinSize);
- Printer.Field("max_size", V.MaxSize, true);
+ Printer.Field("max_size", V.MaxSize);
+ Printer.Field("align", V.Align);
+ Printer.Field("is_const_size", V.IsConstSize, true);
}
inline void print(JSONPrinter& Printer, const BufferType& V) {
diff --git a/tools/syz-declextract/declextract.go b/tools/syz-declextract/declextract.go
index 7d85adaf8..dcf957431 100644
--- a/tools/syz-declextract/declextract.go
+++ b/tools/syz-declextract/declextract.go
@@ -46,7 +46,7 @@ func main() {
loadProbeInfo := func() (*ifaceprobe.Info, error) {
return probe(cfg, *flagConfig)
}
- if err := run(filepath.FromSlash("sys/linux/auto.txt"), loadProbeInfo, &clangtool.Config{
+ if _, err := run(filepath.FromSlash("sys/linux/auto.txt"), loadProbeInfo, &clangtool.Config{
ToolBin: *flagBinary,
KernelSrc: cfg.KernelSrc,
KernelObj: cfg.KernelObj,
@@ -57,20 +57,21 @@ func main() {
}
}
-func run(autoFile string, loadProbeInfo func() (*ifaceprobe.Info, error), cfg *clangtool.Config) error {
+func run(autoFile string, loadProbeInfo func() (*ifaceprobe.Info, error), cfg *clangtool.Config) (
+ *declextract.Result, error) {
out, probeInfo, syscallRename, err := prepare(loadProbeInfo, cfg)
if err != nil {
- return err
+ return nil, err
}
- descriptions, interfaces, includeUse, err := declextract.Run(out, probeInfo, syscallRename, cfg.DebugTrace)
+ res, err := declextract.Run(out, probeInfo, syscallRename, cfg.DebugTrace)
if err != nil {
- return err
+ return nil, err
}
- if err := osutil.WriteFile(autoFile, descriptions); err != nil {
- return err
+ if err := osutil.WriteFile(autoFile, res.Descriptions); err != nil {
+ return nil, err
}
- if err := osutil.WriteFile(autoFile+".info", serialize(interfaces)); err != nil {
- return err
+ if err := osutil.WriteFile(autoFile+".info", serialize(res.Interfaces)); err != nil {
+ return nil, err
}
// In order to remove unused bits of the descriptions, we need to write them out first,
// and then parse all descriptions back b/c auto descriptions use some types defined
@@ -79,32 +80,35 @@ func run(autoFile string, loadProbeInfo func() (*ifaceprobe.Info, error), cfg *c
eh, errors := errorHandler()
desc := ast.ParseGlob(filepath.Join(filepath.Dir(autoFile), "*.txt"), eh)
if desc == nil {
- return fmt.Errorf("failed to parse descriptions\n%s", errors.Bytes())
+ return nil, fmt.Errorf("failed to parse descriptions\n%s", errors.Bytes())
}
// Need to clone descriptions b/c CollectUnused changes them slightly during type checking.
unusedNodes, err := compiler.CollectUnused(desc.Clone(), target, eh)
if err != nil {
- return fmt.Errorf("failed to typecheck descriptions: %w\n%s", err, errors.Bytes())
+ return nil, fmt.Errorf("failed to typecheck descriptions: %w\n%s", err, errors.Bytes())
}
consts := compiler.ExtractConsts(desc.Clone(), target, eh)
if consts == nil {
- return fmt.Errorf("failed to typecheck descriptions: %w\n%s", err, errors.Bytes())
+ return nil, fmt.Errorf("failed to typecheck descriptions: %w\n%s", err, errors.Bytes())
}
- finishInterfaces(interfaces, consts, autoFile)
- if err := osutil.WriteFile(autoFile+".info", serialize(interfaces)); err != nil {
- return err
+ finishInterfaces(res.Interfaces, consts, autoFile)
+ if err := osutil.WriteFile(autoFile+".info", serialize(res.Interfaces)); err != nil {
+ return nil, err
}
removeUnused(desc, "", unusedNodes)
// Second pass to remove unused defines/includes. This needs to be done after removing
// other garbage b/c they may be used by other garbage.
- unusedConsts, err := compiler.CollectUnusedConsts(desc.Clone(), target, includeUse, eh)
+ unusedConsts, err := compiler.CollectUnusedConsts(desc.Clone(), target, res.IncludeUse, eh)
if err != nil {
- return fmt.Errorf("failed to typecheck descriptions: %w\n%s", err, errors.Bytes())
+ return nil, fmt.Errorf("failed to typecheck descriptions: %w\n%s", err, errors.Bytes())
}
removeUnused(desc, autoFile, unusedConsts)
// We need re-parse them again b/c new lines are fixed up during parsing.
formatted := ast.Format(ast.Parse(ast.Format(desc), autoFile, nil))
- return osutil.WriteFile(autoFile, formatted)
+ if err := osutil.WriteFile(autoFile, formatted); err != nil {
+ return nil, err
+ }
+ return res, nil
}
func removeUnused(desc *ast.Description, autoFile string, unusedNodes []ast.Node) {
diff --git a/tools/syz-declextract/declextract_test.go b/tools/syz-declextract/declextract_test.go
index ec1071327..222a96970 100644
--- a/tools/syz-declextract/declextract_test.go
+++ b/tools/syz-declextract/declextract_test.go
@@ -68,7 +68,8 @@ func TestDeclextract(t *testing.T) {
return probeInfo, nil
}
autoFile := filepath.Join(cfg.KernelObj, filepath.Base(file)+".txt")
- if err := run(autoFile, loadProbeInfo, cfg); err != nil {
+ res, err := run(autoFile, loadProbeInfo, cfg)
+ if err != nil {
if *flagUpdate {
osutil.CopyFile(autoFile, file+".txt")
osutil.CopyFile(autoFile+".info", file+".info")
@@ -93,11 +94,24 @@ func TestDeclextract(t *testing.T) {
consts[c.Name] = uint64(i + 1)
}
}
- res := compiler.Compile(full, consts, target, eh)
- if res == nil {
+ desc := compiler.Compile(full, consts, target, eh)
+ if desc == nil {
t.Fatalf("failed to compile full descriptions:\n%s", errors)
}
+ // Check that generated structs have the same size/align as they had in C.
+ // We assume size/align do not depend on const values (which we fabricated).
+ for _, typ := range desc.Types {
+ info := res.StructInfo[typ.Name()]
+ if info == nil {
+ continue
+ }
+ if typ.Size() != uint64(info.Size) || typ.Alignment() != uint64(info.Align) {
+ t.Errorf("incorrect generated type %v: size %v/%v align %v/%v",
+ typ.Name(), typ.Size(), info.Size, typ.Alignment(), info.Align)
+ }
+ }
+
// TODO: Ensure that none of the syscalls will be disabled by TransitivelyEnabledCalls.
compareGoldenFile(t, file+".txt", autoFile)
diff --git a/tools/syz-declextract/testdata/arch/x86/syscalls.tbl b/tools/syz-declextract/testdata/arch/x86/syscalls.tbl
index d79726fdf..7f728f3f2 100644
--- a/tools/syz-declextract/testdata/arch/x86/syscalls.tbl
+++ b/tools/syz-declextract/testdata/arch/x86/syscalls.tbl
@@ -4,4 +4,5 @@
1 64 open sys_open
2 64 chmod sys_chmod
3 64 types_syscall sys_types_syscall
-4 64 functions sys_functions
+4 64 align_syscall sys_align_syscall
+5 64 functions sys_functions
diff --git a/tools/syz-declextract/testdata/file_operations.c.json b/tools/syz-declextract/testdata/file_operations.c.json
index 716dbec38..c8fd5a9dc 100644
--- a/tools/syz-declextract/testdata/file_operations.c.json
+++ b/tools/syz-declextract/testdata/file_operations.c.json
@@ -94,6 +94,7 @@
{
"name": "foo_ioctl_arg",
"byte_size": 8,
+ "align": 4,
"fields": [
{
"name": "a",
diff --git a/tools/syz-declextract/testdata/netlink.c.json b/tools/syz-declextract/testdata/netlink.c.json
index e1c2754b1..35b2a79b5 100644
--- a/tools/syz-declextract/testdata/netlink.c.json
+++ b/tools/syz-declextract/testdata/netlink.c.json
@@ -108,6 +108,7 @@
{
"name": "netlink_foo_struct1",
"byte_size": 12,
+ "align": 4,
"fields": [
{
"name": "a",
@@ -147,6 +148,7 @@
{
"name": "netlink_foo_struct2",
"byte_size": 24,
+ "align": 8,
"fields": [
{
"name": "a",
diff --git a/tools/syz-declextract/testdata/types.c b/tools/syz-declextract/testdata/types.c
index 20f92673f..014318d42 100644
--- a/tools/syz-declextract/testdata/types.c
+++ b/tools/syz-declextract/testdata/types.c
@@ -46,7 +46,8 @@ struct various {
};
struct recursive {
- struct various various;
+ // This is not handled properly yet.
+ // struct various various;
};
SYSCALL_DEFINE1(types_syscall, struct anon_struct* p, struct empty_struct* y,
@@ -64,3 +65,35 @@ void anon_flow(int x) {
s.ptr->a = x;
s.ptr_array[1]->b = x;
}
+
+struct aligned_empty_struct {} __attribute__((aligned(8)));
+struct large_struct { long foo[10]; };
+
+struct align1 {
+ char f1;
+ long aligner[0];
+ char f2;
+};
+
+struct align2 {
+ char f1;
+ struct empty_struct aligner;
+ char f2;
+};
+
+struct align3 {
+ char f1;
+ struct aligned_empty_struct aligner;
+ char f2;
+};
+
+struct align4 {
+ char f1;
+ struct large_struct aligner[0];
+ char f2;
+};
+
+SYSCALL_DEFINE1(align_syscall, struct align1* a1, struct align2* a2, struct align3* a3, struct align4* a4) {
+ return 0;
+}
+
diff --git a/tools/syz-declextract/testdata/types.c.info b/tools/syz-declextract/testdata/types.c.info
index cf7001874..ac9903fbe 100644
--- a/tools/syz-declextract/testdata/types.c.info
+++ b/tools/syz-declextract/testdata/types.c.info
@@ -1 +1,2 @@
+SYSCALL align_syscall func:__do_sys_align_syscall loc:1 access:unknown manual_desc:false auto_desc:true file:types.c subsystem:kernel
SYSCALL types_syscall func:__do_sys_types_syscall loc:2 access:unknown manual_desc:false auto_desc:true file:types.c subsystem:kernel
diff --git a/tools/syz-declextract/testdata/types.c.json b/tools/syz-declextract/testdata/types.c.json
index 82d45e3fe..99d0adc2b 100644
--- a/tools/syz-declextract/testdata/types.c.json
+++ b/tools/syz-declextract/testdata/types.c.json
@@ -1,6 +1,11 @@
{
"functions": [
{
+ "name": "__do_sys_align_syscall",
+ "file": "types.c",
+ "loc": 1
+ },
+ {
"name": "__do_sys_types_syscall",
"file": "types.c",
"loc": 2
@@ -140,8 +145,174 @@
],
"structs": [
{
+ "name": "align1",
+ "byte_size": 16,
+ "align": 8,
+ "fields": [
+ {
+ "name": "f1",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ },
+ {
+ "name": "aligner",
+ "counted_by": -1,
+ "type": {
+ "array": {
+ "elem": {
+ "int": {
+ "byte_size": 8,
+ "name": "long",
+ "base": "long"
+ }
+ },
+ "align": 8,
+ "is_const_size": true
+ }
+ }
+ },
+ {
+ "name": "f2",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "align2",
+ "byte_size": 2,
+ "align": 1,
+ "fields": [
+ {
+ "name": "f1",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ },
+ {
+ "name": "aligner",
+ "counted_by": -1,
+ "type": {
+ "struct": "empty_struct"
+ }
+ },
+ {
+ "name": "f2",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "align3",
+ "byte_size": 16,
+ "align": 8,
+ "fields": [
+ {
+ "name": "f1",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ },
+ {
+ "name": "aligner",
+ "counted_by": -1,
+ "type": {
+ "struct": "aligned_empty_struct"
+ }
+ },
+ {
+ "name": "f2",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "align4",
+ "byte_size": 16,
+ "align": 8,
+ "fields": [
+ {
+ "name": "f1",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ },
+ {
+ "name": "aligner",
+ "counted_by": -1,
+ "type": {
+ "array": {
+ "elem": {
+ "struct": "large_struct"
+ },
+ "align": 8,
+ "is_const_size": true
+ }
+ }
+ },
+ {
+ "name": "f2",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "aligned_empty_struct",
+ "align": 8,
+ "align_attr": 8
+ },
+ {
"name": "anon_struct",
"byte_size": 104,
+ "align": 8,
"fields": [
{
"name": "a",
@@ -204,7 +375,9 @@
"struct": "anon_struct_array"
},
"min_size": 4,
- "max_size": 4
+ "max_size": 4,
+ "align": 4,
+ "is_const_size": true
}
}
},
@@ -232,7 +405,9 @@
}
},
"min_size": 4,
- "max_size": 4
+ "max_size": 4,
+ "align": 8,
+ "is_const_size": true
}
}
}
@@ -241,6 +416,7 @@
{
"name": "anon_struct_2",
"byte_size": 4,
+ "align": 4,
"fields": [
{
"name": "y",
@@ -258,6 +434,7 @@
{
"name": "anon_struct_3",
"byte_size": 8,
+ "align": 8,
"is_union": true,
"fields": [
{
@@ -287,6 +464,7 @@
{
"name": "anon_struct_a",
"byte_size": 4,
+ "align": 4,
"fields": [
{
"name": "x",
@@ -304,6 +482,7 @@
{
"name": "anon_struct_array",
"byte_size": 8,
+ "align": 4,
"fields": [
{
"name": "a",
@@ -330,11 +509,13 @@
]
},
{
- "name": "anon_struct_b"
+ "name": "anon_struct_b",
+ "align": 1
},
{
"name": "anon_struct_ptr",
"byte_size": 8,
+ "align": 4,
"fields": [
{
"name": "a",
@@ -363,6 +544,7 @@
{
"name": "anon_struct_ptr_array",
"byte_size": 8,
+ "align": 4,
"fields": [
{
"name": "a",
@@ -391,6 +573,7 @@
{
"name": "anon_t",
"byte_size": 4,
+ "align": 4,
"fields": [
{
"name": "f",
@@ -409,6 +592,7 @@
"name": "bitfields",
"byte_size": 32,
"align": 32,
+ "align_attr": 32,
"fields": [
{
"name": "a",
@@ -512,13 +696,41 @@
]
},
{
- "name": "empty_struct"
+ "name": "empty_struct",
+ "align": 1
+ },
+ {
+ "name": "large_struct",
+ "byte_size": 80,
+ "align": 8,
+ "fields": [
+ {
+ "name": "foo",
+ "counted_by": -1,
+ "type": {
+ "array": {
+ "elem": {
+ "int": {
+ "byte_size": 8,
+ "name": "long",
+ "base": "long"
+ }
+ },
+ "min_size": 10,
+ "max_size": 10,
+ "align": 8,
+ "is_const_size": true
+ }
+ }
+ }
+ ]
},
{
"name": "packed_t",
"byte_size": 32,
- "is_packed": true,
"align": 32,
+ "is_packed": true,
+ "align_attr": 32,
"fields": [
{
"name": "x",
@@ -546,20 +758,12 @@
},
{
"name": "recursive",
- "byte_size": 64,
- "fields": [
- {
- "name": "various",
- "counted_by": -1,
- "type": {
- "struct": "various"
- }
- }
- ]
+ "align": 1
},
{
"name": "various",
"byte_size": 64,
+ "align": 32,
"fields": [
{
"name": "recursive",
@@ -595,6 +799,56 @@
],
"syscalls": [
{
+ "func": "__do_sys_align_syscall",
+ "args": [
+ {
+ "name": "a1",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "align1"
+ }
+ }
+ }
+ },
+ {
+ "name": "a2",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "align2"
+ }
+ }
+ }
+ },
+ {
+ "name": "a3",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "align3"
+ }
+ }
+ }
+ },
+ {
+ "name": "a4",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "align4"
+ }
+ }
+ }
+ }
+ ],
+ "source_file": "types.c"
+ },
+ {
"func": "__do_sys_types_syscall",
"args": [
{
diff --git a/tools/syz-declextract/testdata/types.c.txt b/tools/syz-declextract/testdata/types.c.txt
index 04a160771..ea95b4bb5 100644
--- a/tools/syz-declextract/testdata/types.c.txt
+++ b/tools/syz-declextract/testdata/types.c.txt
@@ -4,13 +4,42 @@ meta automatic
type auto_todo int8
+type auto_aligner[N] {
+ void void
+} [align[N]]
+
bitfield_enum$auto = a, b, c
-types_syscall$auto(p ptr[inout, anon_struct$auto], y ptr[inout, void], b ptr[inout, bitfields$auto], pid pid, f int32, v ptr[inout, various$auto])
+align_syscall$auto(a1 ptr[inout, align1$auto], a2 ptr[inout, align2$auto], a3 ptr[inout, align3$auto], a4 ptr[inout, align4$auto])
+types_syscall$auto(p ptr[inout, anon_struct$auto], y ptr[inout, auto_aligner[1]], b ptr[inout, bitfields$auto], pid pid, f int32, v ptr[inout, various$auto])
+
+align1$auto {
+ f1 int8
+ aligner auto_aligner[8]
+ f2 int8
+}
+
+align2$auto {
+ f1 int8
+ aligner auto_aligner[1]
+ f2 int8
+}
+
+align3$auto {
+ f1 int8
+ aligner auto_aligner[8]
+ f2 int8
+}
+
+align4$auto {
+ f1 int8
+ aligner auto_aligner[8]
+ f2 int8
+}
anon_struct$auto {
a anon_struct_a$auto
- b void
+ b auto_aligner[1]
anon_struct_2 anon_struct_2$auto
anon_struct_3 anon_struct_3$auto
foo anon_t$auto
@@ -68,13 +97,9 @@ packed_t$auto {
y int32
} [packed, align[32]]
-recursive$auto {
- various various$auto
-}
-
various$auto {
recursive ptr[inout, various$auto, opt]
- next ptr[inout, recursive$auto, opt]
+ next ptr[inout, auto_aligner[1], opt]
packed packed_t$auto
}