aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-12-02 10:57:36 +0100
committerDmitry Vyukov <dvyukov@google.com>2024-12-11 15:22:17 +0000
commitb2c5a234aeb69e981c6e7ad120b49d37a86c6cae (patch)
treed2e575e4c5dd3f044d43a71231c50c1b1459e35a
parentbfb4b3275371a3b53cd6562fa06e5a9dfb5627b7 (diff)
tools/syz-declextract: rewrite
syz-declextract accumulated a bunch of code health problems so that now it's hard to change/extend it, lots of new features can only be added in in hacky ways and cause lots of code duplication. It's also completly untested. Rewrite the tool to: - move as much code as possible to Go (working with the clang tool is painful for a number of reasons) - allow testing and add unit tests (first layer of tests test what information is produced by the clang tool, second layer of tests test how that information is transformed to descriptions) - allow extending the clang tool output to export arbitrary info in non-hacky way (now it produces arbitrary JSON instead of a mix of incomplete descriptions and interfaces) - remove code duplication in the clang tool and provide common infrastructure to add new analysis w/o causing more duplication - provide more convinient primitives in the clang tool - improve code style consistency and stick to the LLVM code style (in particular, variable names must start with a capital letter, single-statement blocks are not surrounded with {}) - remove intermixing of code that works on different levels (currently we have AST analysis + busness logic + printfs all intermixed with each other) - provide several helper Go packages for better code structuring (e.g. pkg/clangtool just runs the tool on source files in parallel and returns results, this already separates a bunch of low-level logic from the rest of the code under a simple abstraction) I've tried to make the output match the current output as much as possible so that the diff is managable (in some cases at the cost of code quality, this should be fixed in future commits). There are still some differences, but hopefully they are managable for review (more includes/defines, reordered some netlink attributes). Fixed minor bugs are fixed along the way, but mostly NFC: 1. Some unions were incorrectly emitted as [varlen] (C unions are never varlen). 2. Only a of [packed], [align[N]] attributes was emitted for struct (both couldn't be emitted).
-rw-r--r--Makefile3
-rw-r--r--pkg/clangtool/clangtool.go165
-rw-r--r--pkg/declextract/declextract.go394
-rw-r--r--pkg/declextract/entity.go175
-rw-r--r--pkg/declextract/interface.go46
-rw-r--r--pkg/declextract/netlink.go236
-rw-r--r--pkg/declextract/serialization.go113
-rw-r--r--sys/linux/auto.txt524
-rw-r--r--sys/linux/auto.txt.const32
-rw-r--r--sys/linux/auto.txt.info18
-rw-r--r--tools/syz-declextract/README.md12
-rw-r--r--tools/syz-declextract/clangtool/.clang-format (renamed from tools/syz-declextract/.clang-format)2
-rw-r--r--tools/syz-declextract/clangtool/declextract.cpp545
-rw-r--r--tools/syz-declextract/clangtool/json.h78
-rw-r--r--tools/syz-declextract/clangtool/output.h292
-rw-r--r--tools/syz-declextract/declextract.go260
-rw-r--r--tools/syz-declextract/declextract_test.go154
-rw-r--r--tools/syz-declextract/run.go546
-rw-r--r--tools/syz-declextract/syz-declextract.cpp1016
-rw-r--r--tools/syz-declextract/testdata/README.md28
-rw-r--r--tools/syz-declextract/testdata/arch/arm/syscalls.tbl0
-rw-r--r--tools/syz-declextract/testdata/arch/arm64/syscalls.tbl0
-rw-r--r--tools/syz-declextract/testdata/arch/mips/syscalls.tbl0
-rw-r--r--tools/syz-declextract/testdata/arch/powerpc/syscalls.tbl0
-rw-r--r--tools/syz-declextract/testdata/arch/riscv/syscalls.tbl0
-rw-r--r--tools/syz-declextract/testdata/arch/s390/syscalls.tbl0
-rw-r--r--tools/syz-declextract/testdata/arch/x86/syscalls.tbl6
-rw-r--r--tools/syz-declextract/testdata/include/netlink.h86
-rw-r--r--tools/syz-declextract/testdata/include/syscall.h7
-rw-r--r--tools/syz-declextract/testdata/include/types.h13
-rw-r--r--tools/syz-declextract/testdata/include/uapi/io_uring.h9
-rw-r--r--tools/syz-declextract/testdata/include/uapi/netlink_family.h27
-rw-r--r--tools/syz-declextract/testdata/io_uring.c36
-rw-r--r--tools/syz-declextract/testdata/io_uring.c.info3
-rw-r--r--tools/syz-declextract/testdata/io_uring.c.json22
-rw-r--r--tools/syz-declextract/testdata/io_uring.c.txt10
-rw-r--r--tools/syz-declextract/testdata/manual.txt50
-rw-r--r--tools/syz-declextract/testdata/netlink.c87
-rw-r--r--tools/syz-declextract/testdata/netlink.c.info3
-rw-r--r--tools/syz-declextract/testdata/netlink.c.json197
-rw-r--r--tools/syz-declextract/testdata/netlink.c.txt51
-rw-r--r--tools/syz-declextract/testdata/syscall.c12
-rw-r--r--tools/syz-declextract/testdata/syscall.c.info2
-rw-r--r--tools/syz-declextract/testdata/syscall.c.json77
-rw-r--r--tools/syz-declextract/testdata/syscall.c.txt12
-rw-r--r--tools/syz-declextract/testdata/types.c55
-rw-r--r--tools/syz-declextract/testdata/types.c.info1
-rw-r--r--tools/syz-declextract/testdata/types.c.json555
-rw-r--r--tools/syz-declextract/testdata/types.c.txt87
49 files changed, 4336 insertions, 1711 deletions
diff --git a/Makefile b/Makefile
index becb95317..39e0804bf 100644
--- a/Makefile
+++ b/Makefile
@@ -262,7 +262,8 @@ format_go:
format_cpp:
clang-format --style=file -i executor/*.cc executor/*.h \
executor/android/android_seccomp.h \
- tools/kcovtrace/*.c tools/kcovfuzzer/*.c tools/fops_probe/*.cc tools/syz-declextract/syz-declextract.cpp
+ tools/kcovtrace/*.c tools/kcovfuzzer/*.c tools/fops_probe/*.cc \
+ tools/syz-declextract/clangtool/*.cpp tools/syz-declextract/clangtool/*.h
format_sys: bin/syz-fmt
bin/syz-fmt all
diff --git a/pkg/clangtool/clangtool.go b/pkg/clangtool/clangtool.go
new file mode 100644
index 000000000..4e2605a48
--- /dev/null
+++ b/pkg/clangtool/clangtool.go
@@ -0,0 +1,165 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package clangtool
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/rand"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "slices"
+ "strings"
+ "time"
+
+ "github.com/google/syzkaller/pkg/declextract"
+ "github.com/google/syzkaller/pkg/osutil"
+)
+
+type Config struct {
+ ToolBin string
+ KernelSrc string
+ KernelObj string
+ CacheDir string
+ ReuseCache bool
+}
+
+// Run runs the clang tool on all files in the compilation database
+// in the kernel build dir and returns combined output for all files.
+// It always caches results, and optionally reuses previously cached results.
+func Run(cfg *Config) (*declextract.Output, error) {
+ dbFile := filepath.Join(cfg.KernelObj, "compile_commands.json")
+ cmds, err := loadCompileCommands(dbFile)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load compile commands: %w", err)
+ }
+
+ type result struct {
+ out *declextract.Output
+ err error
+ }
+ results := make(chan *result, 10)
+ files := make(chan string, len(cmds))
+ for w := 0; w < runtime.NumCPU(); w++ {
+ go func() {
+ for file := range files {
+ out, err := runTool(cfg, dbFile, file)
+ results <- &result{out, err}
+ }
+ }()
+ }
+ for _, cmd := range cmds {
+ files <- cmd.File
+ }
+ close(files)
+
+ out := new(declextract.Output)
+ for range cmds {
+ res := <-results
+ if res.err != nil {
+ return nil, res.err
+ }
+ out.Merge(res.out)
+ }
+ out.SortAndDedup()
+ return out, nil
+}
+
+func runTool(cfg *Config, dbFile, file string) (*declextract.Output, error) {
+ relFile := strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(filepath.Clean(file),
+ cfg.KernelSrc), cfg.KernelObj), "/")
+ cacheFile := filepath.Join(cfg.CacheDir, relFile+".json")
+ if cfg.ReuseCache {
+ data, err := os.ReadFile(cacheFile)
+ if err == nil {
+ out, err := unmarshal(data)
+ if err == nil {
+ return out, nil
+ }
+ }
+ }
+ // Suppress warning since we may build the tool on a different clang
+ // version that produces more warnings.
+ data, err := exec.Command(cfg.ToolBin, "-p", dbFile, "--extra-arg=-w", file).Output()
+ if err != nil {
+ var exitErr *exec.ExitError
+ if errors.As(err, &exitErr) {
+ err = fmt.Errorf("%v: %w\n%s", relFile, err, exitErr.Stderr)
+ }
+ return nil, err
+ }
+ out, err := unmarshal(data)
+ if err != nil {
+ return nil, err
+ }
+ fixupFileNames(cfg, out, relFile)
+ normalized, err := json.MarshalIndent(out, "", "\t")
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal output data: %w", err)
+ }
+ osutil.MkdirAll(filepath.Dir(cacheFile))
+ if err := osutil.WriteFile(cacheFile, normalized); err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func unmarshal(data []byte) (*declextract.Output, error) {
+ dec := json.NewDecoder(bytes.NewReader(data))
+ dec.DisallowUnknownFields()
+ out := new(declextract.Output)
+ if err := dec.Decode(out); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal clang tool output: %w\n%s", err, data)
+ }
+ return out, nil
+}
+
+func fixupFileNames(cfg *Config, out *declextract.Output, file string) {
+ // All includes in the tool output are relative to the build dir.
+ // Make them relative to the source dir.
+ for i, inc := range out.Includes {
+ if file, err := filepath.Rel(cfg.KernelSrc, filepath.Join(cfg.KernelObj, inc)); err == nil {
+ out.Includes[i] = file
+ }
+ }
+ out.SetSourceFile(file)
+}
+
+type compileCommand struct {
+ Command string
+ Directory string
+ File string
+}
+
+func loadCompileCommands(dbFile string) ([]compileCommand, error) {
+ data, err := os.ReadFile(dbFile)
+ if err != nil {
+ return nil, err
+ }
+ var cmds []compileCommand
+ if err := json.Unmarshal(data, &cmds); err != nil {
+ return nil, err
+ }
+ // Remove commands that don't relate to the kernel build
+ // (probably some host tools, etc).
+ cmds = slices.DeleteFunc(cmds, func(cmd compileCommand) bool {
+ return !strings.HasSuffix(cmd.File, ".c") ||
+ // Files compiled with gcc are not a part of the kernel
+ // (assuming compile commands were generated with make CC=clang).
+ // They are probably a part of some host tool.
+ strings.HasPrefix(cmd.Command, "gcc") ||
+ // KBUILD should add this define all kernel files.
+ !strings.Contains(cmd.Command, "-DKBUILD_BASENAME")
+ })
+ // Shuffle the order to detect any non-determinism caused by the order early.
+ // The result should be the same regardless.
+ rand.New(rand.NewSource(time.Now().UnixNano())).Shuffle(len(cmds), func(i, j int) {
+ cmds[i], cmds[j] = cmds[j], cmds[i]
+ })
+ return cmds, nil
+}
diff --git a/pkg/declextract/declextract.go b/pkg/declextract/declextract.go
new file mode 100644
index 000000000..dd54bf79c
--- /dev/null
+++ b/pkg/declextract/declextract.go
@@ -0,0 +1,394 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package declextract
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "slices"
+ "strings"
+)
+
+func Run(out *Output, syscallRename map[string][]string) ([]byte, []*Interface, error) {
+ ctx := &context{
+ Output: out,
+ syscallRename: syscallRename,
+ structs: make(map[string]*Struct),
+ }
+ ctx.processIncludes()
+ ctx.processEnums()
+ ctx.processStructs()
+ ctx.processSyscalls()
+ ctx.processIouring()
+ ctx.fabricateNetlinkPolicies()
+
+ ctx.serialize()
+ ctx.finishInterfaces()
+ return ctx.descriptions.Bytes(), ctx.interfaces, errors.Join(ctx.errs...)
+}
+
+type context struct {
+ *Output
+ syscallRename map[string][]string // syscall function -> syscall names
+ structs map[string]*Struct
+ interfaces []*Interface
+ descriptions *bytes.Buffer
+ errs []error
+}
+
+func (ctx *context) error(msg string, args ...any) {
+ ctx.errs = append(ctx.errs, fmt.Errorf(msg, args...))
+}
+
+func (ctx *context) processIncludes() {
+ // These additional includes must be at the top, because other kernel headers
+ // are broken and won't compile without these additional ones included first.
+ ctx.Includes = append([]string{
+ "vdso/bits.h",
+ "linux/types.h",
+ "net/netlink.h",
+ }, ctx.Includes...)
+ replaces := map[string]string{
+ // Arches may use some includes from asm-generic and some from arch/arm.
+ // If the arch used for extract used asm-generic for a header,
+ // other arches may need arch/asm version of the header. So switch to
+ // a more generic file name that should resolve correctly for all arches.
+ "include/uapi/asm-generic/ioctls.h": "asm/ioctls.h",
+ "include/uapi/asm-generic/sockios.h": "asm/sockios.h",
+ }
+ for i, inc := range ctx.Includes {
+ if replace := replaces[inc]; replace != "" {
+ ctx.Includes[i] = replace
+ }
+ }
+}
+
+func (ctx *context) processEnums() {
+ for _, enum := range ctx.Enums {
+ // TODO: change for consistency:
+ // enum.Name += autoSuffix
+ enum.Name = "auto_" + enum.Name
+ }
+}
+
+func (ctx *context) processSyscalls() {
+ var syscalls []*Syscall
+ for _, call := range ctx.Syscalls {
+ ctx.processFields(call.Args, "", false)
+ fn := strings.TrimPrefix(call.Func, "__do_sys_")
+ for _, name := range ctx.syscallRename[fn] {
+ ctx.noteInterface(&Interface{
+ Type: IfaceSyscall,
+ Name: name,
+ IdentifyingConst: "__NR_" + name,
+ Files: []string{call.SourceFile},
+ Func: call.Func,
+ AutoDescriptions: true,
+ })
+ newCall := *call
+ newCall.Func = name + autoSuffix
+ syscalls = append(syscalls, &newCall)
+ }
+ }
+ ctx.Syscalls = sortAndDedupSlice(syscalls)
+}
+
+func (ctx *context) processIouring() {
+ for _, op := range ctx.IouringOps {
+ ctx.noteInterface(&Interface{
+ Type: IfaceIouring,
+ Name: op.Name,
+ IdentifyingConst: op.Name,
+ Files: []string{op.SourceFile},
+ Func: op.Func,
+ Access: AccessUser,
+ })
+ }
+}
+
+func (ctx *context) processStructs() {
+ for _, str := range ctx.Structs {
+ // TODO: change for consistency:
+ // str.Name += autoSuffix
+ str.Name += "$auto_record"
+ ctx.structs[str.Name] = str
+ }
+ 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)
+ }
+}
+
+func (ctx *context) processFields(fields []*Field, parent string, needBase bool) {
+ counts := make([]*Field, len(fields))
+ for _, f := range fields {
+ f.Name = fixIdentifier(f.Name)
+ if f.CountedBy != -1 {
+ counts[f.CountedBy] = f
+ }
+ }
+ for i, f := range fields {
+ f.syzType = ctx.fieldType(f, counts[i], parent, needBase)
+ }
+}
+
+func (ctx *context) fieldType(f, counts *Field, parent string, needBase bool) string {
+ if f.BitWidth != 0 && !needBase {
+ ctx.error("syscall arg %v is a bitfield", f.Name)
+ }
+ if f.BitWidth != 0 && f.Type.Int == nil {
+ ctx.error("non-int field %v is a bitfield", f.Name)
+ }
+ if counts != nil && f.Type.Int == nil && f.Type.Ptr == nil {
+ ctx.error("non-int/ptr field %v counts field %v", f.Name, counts.Name)
+ }
+ f.Name = strings.ToLower(f.Name)
+ switch {
+ case f.Type.Int != nil:
+ return ctx.fieldTypeInt(f, counts, needBase)
+ case f.Type.Ptr != nil:
+ return ctx.fieldTypePtr(f, counts, parent)
+ case f.Type.Array != nil:
+ return ctx.fieldTypeArray(f, parent)
+ case f.Type.Buffer != nil:
+ return ctx.fieldTypeBuffer(f)
+ case f.Type.Struct != "":
+ return ctx.fieldTypeStruct(f)
+ }
+ ctx.error("field %v does not have type", f.Name)
+ return ""
+}
+
+func (ctx *context) fieldTypeInt(f, counts *Field, needBase bool) string {
+ t := f.Type.Int
+ switch t.ByteSize {
+ case 1, 2, 4, 8:
+ default:
+ ctx.error("field %v has unsupported size %v", f.Name, t.ByteSize)
+ }
+ if t.Enum != "" && counts != nil {
+ ctx.error("field %v is both enum %v and counts field %v", f.Name, t.Enum, counts.Name)
+ }
+ baseType := fmt.Sprintf("int%v", t.ByteSize*8)
+ // Note: we make all 8-byte syscall arguments intptr b/c for 64-bit arches it does not matter,
+ // but for 32-bit arches int64 as syscall argument won't work. IIUC the ABI is that these
+ // are split into 2 32-bit arguments.
+ intptr := t.ByteSize == 8 && (!needBase || strings.Contains(t.Base, "long") &&
+ !strings.Contains(t.Base, "long long"))
+ if intptr {
+ baseType = "intptr"
+ }
+ if t.isBigEndian && t.ByteSize != 1 {
+ baseType += "be"
+ }
+ if f.BitWidth == t.ByteSize*8 {
+ f.BitWidth = 0
+ }
+ if f.BitWidth != 0 {
+ baseType += fmt.Sprintf(":%v", f.BitWidth)
+ }
+ unusedType := fmt.Sprintf("const[0 %v]", maybeBaseType(baseType, needBase))
+ if f.IsAnonymous {
+ return unusedType
+ }
+ if t.Enum != "" {
+ // TODO: change for consistency:
+ // t.Enum += autoSuffix
+ t.Enum = "auto_" + t.Enum
+ return fmt.Sprintf("flags[%v %v]", t.Enum, maybeBaseType(baseType, needBase))
+ }
+ if counts != nil {
+ return fmt.Sprintf("len[%v %v]", counts.Name, maybeBaseType(baseType, needBase))
+ }
+ if t.Name == "TODO" {
+ return todoType
+ }
+ special := ""
+ switch t.ByteSize {
+ case 2:
+ special = ctx.specialInt2(f.Name, t.Name, needBase)
+ case 4:
+ special = ctx.specialInt4(f.Name, t.Name, needBase)
+ case 8:
+ if intptr {
+ special = ctx.specialIntptr(f.Name, t.Name, needBase)
+ }
+ }
+ if special != "" {
+ if f.BitWidth != 0 {
+ // We don't have syntax to express this.
+ ctx.error("field %v is both special %v and a bitfield", f.Name, special)
+ }
+ return special
+ }
+ if strings.HasSuffix(f.Name, "enabled") || strings.HasSuffix(f.Name, "enable") {
+ return "bool" + strings.TrimPrefix(baseType, "int")
+ }
+ if strings.Contains(f.Name, "pad") || strings.Contains(f.Name, "unused") ||
+ strings.Contains(f.Name, "_reserved") {
+ return unusedType
+ }
+ return baseType
+}
+
+func (ctx *context) specialInt2(field, typ string, needBase bool) string {
+ switch {
+ case strings.Contains(field, "port"):
+ return "sock_port"
+ }
+ return ""
+}
+
+func (ctx *context) specialInt4(field, typ string, needBase bool) string {
+ switch {
+ case strings.Contains(field, "ipv4"):
+ return "ipv4_addr"
+ case strings.HasSuffix(field, "_pid") || strings.HasSuffix(field, "_tid") ||
+ strings.HasSuffix(field, "_pgid") || strings.HasSuffix(field, "_tgid") ||
+ field == "pid" || field == "tid" || field == "pgid" || field == "tgid":
+ return "pid"
+ case strings.HasSuffix(field, "dfd") && !strings.HasSuffix(field, "oldfd") && !strings.HasSuffix(field, "pidfd"):
+ return "fd_dir"
+ case strings.HasSuffix(field, "ns_fd"):
+ return "fd_namespace"
+ case strings.HasSuffix(field, "_uid") || field == "uid" || field == "user" ||
+ field == "ruid" || field == "euid" || field == "suid":
+ return "uid"
+ case strings.HasSuffix(field, "_gid") || field == "gid" || field == "group" ||
+ field == "rgid" || field == "egid" || field == "sgid":
+ return "gid"
+ case strings.HasSuffix(field, "fd") || strings.HasPrefix(field, "fd_") ||
+ strings.Contains(field, "fildes") || field == "fdin" || field == "fdout":
+ return "fd"
+ case strings.Contains(field, "ifindex") || strings.Contains(field, "dev_index"):
+ return "ifindex"
+ }
+ return ""
+}
+
+func (ctx *context) specialIntptr(field, typ string, needBase bool) string {
+ switch {
+ case field == "sigsetsize":
+ return fmt.Sprintf("const[8 %v]", maybeBaseType("intptr", needBase))
+ }
+ return ""
+}
+
+func (ctx *context) fieldTypePtr(f, counts *Field, parent string) string {
+ t := f.Type.Ptr
+ dir := "inout"
+ if t.IsConst {
+ dir = "in"
+ }
+ opt := ""
+ // Use an opt pointer if the direct parent is the same as this node, or if the field name is next.
+ // Looking at the field name is a hack, but it's enough to avoid some recursion cases,
+ // e.g. for struct adf_user_cfg_section.
+ if f.Name == "next" || parent != "" && parent == t.Elem.Struct+"$auto_record" {
+ opt = ", opt"
+ }
+ elem := &Field{
+ Name: f.Name,
+ Type: t.Elem,
+ }
+ return fmt.Sprintf("ptr[%v, %v %v]", dir, ctx.fieldType(elem, counts, parent, true), opt)
+}
+
+func (ctx *context) fieldTypeArray(f *Field, parent string) string {
+ t := f.Type.Array
+ elem := &Field{
+ Name: f.Name,
+ Type: t.Elem,
+ }
+ elemType := ctx.fieldType(elem, nil, parent, true)
+ if t.MinSize == 1 && t.MaxSize == 1 {
+ return elemType
+ }
+ bounds := ctx.bounds(f.Name, t.MinSize, t.MaxSize)
+ return fmt.Sprintf("array[%v%v]", elemType, bounds)
+}
+
+func (ctx *context) fieldTypeBuffer(f *Field) string {
+ t := f.Type.Buffer
+ bounds := ctx.bounds(f.Name, t.MinSize, t.MaxSize)
+ baseType := "string"
+ if t.IsNonTerminated {
+ baseType = "stringnoz"
+ }
+ switch {
+ case !t.IsString:
+ return fmt.Sprintf("array[int8 %v]", bounds)
+ case strings.Contains(f.Name, "ifname") || strings.HasSuffix(f.Name, "dev_name"):
+ return "devname"
+ case strings.Contains(f.Name, "filename") || strings.Contains(f.Name, "pathname") ||
+ strings.Contains(f.Name, "dir_name") || f.Name == "oldname" ||
+ f.Name == "newname" || f.Name == "path":
+ if !t.IsNonTerminated && bounds == "" {
+ return "filename" // alias that is easier to read
+ }
+ return fmt.Sprintf("%v[filename %v]", baseType, bounds)
+ }
+ return baseType
+}
+
+func (ctx *context) fieldTypeStruct(f *Field) string {
+ // TODO: change for consistency:
+ // f.Type.Struct += autoSuffix
+ f.Type.Struct += "$auto_record"
+ if ctx.structs[f.Type.Struct].ByteSize == 0 {
+ return "void"
+ }
+ return f.Type.Struct
+}
+
+func (ctx *context) bounds(name string, min, max int) string {
+ if min < 0 || min > max {
+ ctx.error("field %v has bad bounds %v:%v", name, min, max)
+ }
+ if max > min {
+ return fmt.Sprintf(", %v:%v", min, max)
+ }
+ if max != 0 {
+ return fmt.Sprintf(", %v", max)
+ }
+ return ""
+}
+
+const (
+ autoSuffix = "$auto"
+ todoType = "auto_todo"
+)
+
+func fixIdentifier(name string) string {
+ switch name {
+ case "resource", "include", "define", "incdir", "syscall", "parent":
+ return "_" + name
+ }
+ return name
+}
+
+func stringIdentifier(name string) string {
+ // TODO: make the identifier lower case.
+ for _, bad := range []string{" ", ".", "-"} {
+ name = strings.ReplaceAll(name, bad, "_")
+ }
+ return name
+}
+
+func maybeBaseType(baseType string, needBase bool) string {
+ if needBase {
+ return ", " + baseType
+ }
+ return ""
+}
+
+func comma(i int) string {
+ if i == 0 {
+ return ""
+ }
+ return ", "
+}
diff --git a/pkg/declextract/entity.go b/pkg/declextract/entity.go
new file mode 100644
index 000000000..57e589e40
--- /dev/null
+++ b/pkg/declextract/entity.go
@@ -0,0 +1,175 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package declextract
+
+import (
+ "bytes"
+ "encoding/json"
+ "slices"
+)
+
+type Output struct {
+ Includes []string `json:"includes,omitempty"`
+ Defines []*Define `json:"defines,omitempty"`
+ Enums []*Enum `json:"enums,omitempty"`
+ Structs []*Struct `json:"structs,omitempty"`
+ Syscalls []*Syscall `json:"syscalls,omitempty"`
+ IouringOps []*IouringOp `json:"iouring_ops,omitempty"`
+ NetlinkFamilies []*NetlinkFamily `json:"netlink_families,omitempty"`
+ NetlinkPolicies []*NetlinkPolicy `json:"netlink_policies,omitempty"`
+}
+
+type Define struct {
+ Name string `json:"name,omitempty"`
+ Value string `json:"value,omitempty"`
+}
+
+type Field struct {
+ Name string `json:"name,omitempty"`
+ IsAnonymous bool `json:"is_anonymous,omitempty"`
+ BitWidth int `json:"bit_width,omitempty"`
+ CountedBy int `json:"counted_by,omitempty"`
+ Type *Type `json:"type,omitempty"`
+
+ syzType string
+}
+
+type Syscall struct {
+ Func string `json:"func,omitempty"`
+ Args []*Field `json:"args,omitempty"`
+ SourceFile string `json:"source_file,omitempty"`
+}
+
+type IouringOp struct {
+ Name string `json:"name,omitempty"`
+ Func string `json:"func,omitempty"`
+ SourceFile string `json:"source_file,omitempty"`
+}
+
+type NetlinkFamily struct {
+ Name string `json:"name,omitempty"`
+ Ops []*NetlinkOp `json:"ops,omitempty"`
+ SourceFile string `json:"source_file,omitempty"`
+}
+
+type NetlinkPolicy struct {
+ Name string `json:"name,omitempty"`
+ Attrs []*NetlinkAttr `json:"attrs,omitempty"`
+}
+
+type NetlinkOp struct {
+ Name string `json:"name,omitempty"`
+ Func string `json:"func,omitempty"`
+ Access string `json:"access,omitempty"`
+ Policy string `json:"policy,omitempty"`
+}
+
+type NetlinkAttr struct {
+ Name string `json:"name,omitempty"`
+ Kind string `json:"kind,omitempty"`
+ MaxSize int `json:"max_size,omitempty"`
+ NestedPolicy string `json:"nested_policy,omitempty"`
+ Elem *Type `json:"elem,omitempty"`
+}
+
+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"`
+
+ // TODO: remove me.
+ isVarlen bool
+}
+
+type Enum struct {
+ Name string `json:"name,omitempty"`
+ Values []string `json:"values,omitempty"`
+}
+
+type Type struct {
+ Int *IntType `json:"int,omitempty"`
+ Ptr *PtrType `json:"ptr,omitempty"`
+ Array *ArrayType `json:"array,omitempty"`
+ Buffer *BufferType `json:"buffer,omitempty"`
+ Struct string `json:"struct,omitempty"`
+}
+
+type IntType struct {
+ ByteSize int `json:"byte_size,omitempty"`
+ Name string `json:"name,omitempty"`
+ Base string `json:"base,omitempty"`
+ Enum string `json:"enum,omitempty"`
+
+ isBigEndian bool
+}
+
+type PtrType struct {
+ Elem *Type `json:"elem,omitempty"`
+ IsConst bool `json:"is_const,omitempty"`
+}
+
+type ArrayType struct {
+ Elem *Type `json:"elem,omitempty"`
+ MinSize int `json:"min_size,omitempty"`
+ MaxSize int `json:"max_size,omitempty"`
+}
+
+type BufferType struct {
+ MinSize int `json:"min_size,omitempty"`
+ MaxSize int `json:"max_size,omitempty"`
+ IsString bool `json:"is_string,omitempty"`
+ IsNonTerminated bool `json:"is_non_terminated,omitempty"`
+}
+
+func (out *Output) Merge(other *Output) {
+ out.Includes = append(out.Includes, other.Includes...)
+ out.Defines = append(out.Defines, other.Defines...)
+ out.Enums = append(out.Enums, other.Enums...)
+ out.Structs = append(out.Structs, other.Structs...)
+ out.Syscalls = append(out.Syscalls, other.Syscalls...)
+ out.IouringOps = append(out.IouringOps, other.IouringOps...)
+ out.NetlinkFamilies = append(out.NetlinkFamilies, other.NetlinkFamilies...)
+ out.NetlinkPolicies = append(out.NetlinkPolicies, other.NetlinkPolicies...)
+}
+
+func (out *Output) SortAndDedup() {
+ out.Includes = sortAndDedupSlice(out.Includes)
+ out.Defines = sortAndDedupSlice(out.Defines)
+ out.Enums = sortAndDedupSlice(out.Enums)
+ out.Structs = sortAndDedupSlice(out.Structs)
+ out.Syscalls = sortAndDedupSlice(out.Syscalls)
+ out.IouringOps = sortAndDedupSlice(out.IouringOps)
+ out.NetlinkFamilies = sortAndDedupSlice(out.NetlinkFamilies)
+ out.NetlinkPolicies = sortAndDedupSlice(out.NetlinkPolicies)
+}
+
+// SetSoureFile attaches the source file to the entities that need it.
+// The clang tool could do it, but it looks easier to do it here.
+func (out *Output) SetSourceFile(file string) {
+ for _, call := range out.Syscalls {
+ call.SourceFile = file
+ }
+ for _, fam := range out.NetlinkFamilies {
+ fam.SourceFile = file
+ }
+ for _, op := range out.IouringOps {
+ op.SourceFile = file
+ }
+}
+
+func sortAndDedupSlice[Slice ~[]E, E any](s Slice) Slice {
+ slices.SortFunc(s, func(a, b E) int {
+ aa, _ := json.Marshal(a)
+ bb, _ := json.Marshal(b)
+ return bytes.Compare(aa, bb)
+ })
+ return slices.CompactFunc(s, func(a, b E) bool {
+ aa, _ := json.Marshal(a)
+ bb, _ := json.Marshal(b)
+ return bytes.Equal(aa, bb)
+ })
+}
diff --git a/pkg/declextract/interface.go b/pkg/declextract/interface.go
new file mode 100644
index 000000000..dfb223d16
--- /dev/null
+++ b/pkg/declextract/interface.go
@@ -0,0 +1,46 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package declextract
+
+import (
+ "slices"
+)
+
+type Interface struct {
+ Type string
+ Name string
+ IdentifyingConst string
+ Files []string
+ Func string
+ Access string
+ Subsystems []string
+ ManualDescriptions bool
+ AutoDescriptions bool
+}
+
+const (
+ IfaceSyscall = "SYSCALL"
+ IfaceNetlinkOp = "NETLINK"
+ IfaceIouring = "IOURING"
+
+ AccessUnknown = "unknown"
+ AccessUser = "user"
+ AccessNsAdmin = "ns_admin"
+ AccessAdmin = "admin"
+)
+
+func (ctx *context) noteInterface(iface *Interface) {
+ ctx.interfaces = append(ctx.interfaces, iface)
+}
+
+func (ctx *context) finishInterfaces() {
+ for _, iface := range ctx.interfaces {
+ slices.Sort(iface.Files)
+ iface.Files = slices.Compact(iface.Files)
+ if iface.Access == "" {
+ iface.Access = AccessUnknown
+ }
+ }
+ ctx.interfaces = sortAndDedupSlice(ctx.interfaces)
+}
diff --git a/pkg/declextract/netlink.go b/pkg/declextract/netlink.go
new file mode 100644
index 000000000..de60ae30e
--- /dev/null
+++ b/pkg/declextract/netlink.go
@@ -0,0 +1,236 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package declextract
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+func (ctx *context) fabricateNetlinkPolicies() {
+ for _, pol := range ctx.NetlinkPolicies {
+ if len(pol.Attrs) == 0 {
+ continue
+ }
+ str := &Struct{
+ Name: pol.Name,
+ IsUnion: true,
+ isVarlen: true,
+ }
+ for _, attr := range pol.Attrs {
+ str.Fields = append(str.Fields, &Field{
+ Name: attr.Name,
+ syzType: ctx.nlattrType(attr),
+ })
+ }
+ ctx.Structs = append(ctx.Structs, str)
+ }
+ ctx.Structs = sortAndDedupSlice(ctx.Structs)
+}
+
+func (ctx *context) emitNetlinkTypes() {
+ for _, fam := range ctx.NetlinkFamilies {
+ if isEmptyFamily(fam) {
+ continue
+ }
+ id := stringIdentifier(fam.Name)
+ ctx.fmt("resource genl_%v_family_id_auto[int16]\n", id)
+ }
+ for _, fam := range ctx.NetlinkFamilies {
+ if isEmptyFamily(fam) {
+ continue
+ }
+ id := stringIdentifier(fam.Name)
+ ctx.fmt("type msghdr_%v_auto[CMD, POLICY] msghdr_netlink[netlink_msg_t"+
+ "[genl_%v_family_id_auto, genlmsghdr_t[CMD], POLICY]]\n", id, id)
+ }
+ for _, pol := range ctx.NetlinkPolicies {
+ if len(pol.Attrs) == 0 {
+ ctx.fmt("type %v auto_todo\n", pol.Name)
+ }
+ }
+}
+
+func (ctx *context) emitNetlinkGetFamily() {
+ for _, fam := range ctx.NetlinkFamilies {
+ if isEmptyFamily(fam) {
+ continue
+ }
+ id := stringIdentifier(fam.Name)
+ ctx.fmt("syz_genetlink_get_family_id$auto_%v(name ptr[in, string[\"%v\"]],"+
+ " fd sock_nl_generic) genl_%v_family_id_auto\n", id, fam.Name, id)
+ }
+}
+
+func (ctx *context) emitNetlinkSendmsgs() {
+ var syscalls []string
+ for _, fam := range ctx.NetlinkFamilies {
+ id := stringIdentifier(fam.Name)
+ dedup := make(map[string]bool)
+ for _, op := range fam.Ops {
+ // TODO: emit these as well, these are dump commands w/o input arguments.
+ if op.Policy == "" {
+ continue
+ }
+ // TODO: emit all of these with unique names, these should be doit/dump variants.
+ // They may have different policies.
+ if dedup[op.Name] {
+ continue
+ }
+ dedup[op.Name] = true
+ syscalls = append(syscalls, fmt.Sprintf("sendmsg$auto_%v(fd sock_nl_generic,"+
+ " msg ptr[in, msghdr_%v_auto[%v, %v]], f flags[send_flags])\n",
+ op.Name, id, op.Name, op.Policy))
+
+ ctx.noteInterface(&Interface{
+ Type: IfaceNetlinkOp,
+ Name: op.Name,
+ IdentifyingConst: op.Name,
+ Files: []string{fam.SourceFile},
+ Func: op.Func,
+ Access: op.Access,
+ AutoDescriptions: true,
+ })
+ }
+ }
+ sort.Strings(syscalls)
+ for _, call := range syscalls {
+ ctx.fmt("%s", call)
+ }
+}
+
+func isEmptyFamily(fam *NetlinkFamily) bool {
+ for _, op := range fam.Ops {
+ if op.Policy != "" {
+ return false
+ }
+ }
+ return true
+}
+
+func (ctx *context) nlattrType(attr *NetlinkAttr) string {
+ nlattr, typ := "nlattr", ""
+ switch attr.Kind {
+ case "NLA_BITFIELD32":
+ // TODO: Extract values from NLA_POLICY_BITFIELD32 macro.
+ typ = "int32"
+ case "NLA_MSECS":
+ typ = "int64"
+ case "NLA_FLAG":
+ typ = "void"
+ case "NLA_NESTED", "NLA_NESTED_ARRAY":
+ nlattr = "nlnest"
+ policy := "nl_generic_attr"
+ if attr.NestedPolicy != "" {
+ policy = attr.NestedPolicy
+ }
+ typ = fmt.Sprintf("array[%v]", policy)
+ if attr.Kind == "NLA_NESTED_ARRAY" {
+ typ = fmt.Sprintf("array[nlnest[0, %v]]", typ)
+ }
+ case "NLA_BINARY", "NLA_UNSPEC", "":
+ // TODO: also handle size 6 for MAC addresses.
+ if attr.Elem == nil && (attr.MaxSize == 16 || attr.MaxSize == 0) &&
+ strings.Contains(attr.Name, "IPV6") {
+ typ = "ipv6_addr"
+ break
+ }
+ fallthrough
+ default:
+ field := &Field{
+ Name: attr.Name,
+ Type: ctx.netlinkType(attr),
+ }
+ typ = ctx.fieldType(field, nil, "", true)
+ }
+ return fmt.Sprintf("%v[%v, %v]", nlattr, attr.Name, typ)
+}
+
+func (ctx *context) netlinkType(attr *NetlinkAttr) *Type {
+ switch attr.Kind {
+ case "NLA_STRING", "NLA_NUL_STRING":
+ return &Type{
+ Buffer: &BufferType{
+ MaxSize: attr.MaxSize,
+ IsString: true,
+ IsNonTerminated: attr.Kind == "NLA_STRING",
+ },
+ }
+ case "NLA_BINARY", "NLA_UNSPEC", "":
+ if attr.Elem == nil {
+ switch attr.MaxSize {
+ case 1, 2, 4, 8:
+ attr.Kind = fmt.Sprintf("NLA_U%v", attr.MaxSize*8)
+ return ctx.netlinkTypeInt(attr)
+ }
+ minSize := 0
+ if attr.Kind != "NLA_BINARY" {
+ minSize = attr.MaxSize
+ }
+ return &Type{
+ Buffer: &BufferType{
+ MaxSize: attr.MaxSize,
+ MinSize: minSize,
+ },
+ }
+ }
+ elemSize := 1
+ switch {
+ case attr.Elem.Int != nil:
+ elemSize = attr.Elem.Int.ByteSize
+ case attr.Elem.Struct != "":
+ if str := ctx.structs[attr.Elem.Struct+"$auto_record"]; str != nil {
+ elemSize = str.ByteSize
+ } else {
+ ctx.error("binary nlattr %v referenced non-existing struct %v",
+ attr.Name, attr.Elem.Struct)
+ }
+ default:
+ ctx.error("binary nlattr %v has unsupported elem type", attr.Name)
+ }
+ if attr.MaxSize%elemSize != 0 {
+ ctx.error("binary nlattr %v has odd size: %v, elem size %v",
+ attr.Name, attr.MaxSize, elemSize)
+ }
+ numElems := attr.MaxSize / elemSize
+ if numElems == 1 {
+ return attr.Elem
+ }
+ return &Type{
+ Array: &ArrayType{
+ Elem: attr.Elem,
+ MaxSize: numElems,
+ },
+ }
+ default:
+ return ctx.netlinkTypeInt(attr)
+ }
+}
+
+func (ctx *context) netlinkTypeInt(attr *NetlinkAttr) *Type {
+ size, be := 0, false
+ switch attr.Kind {
+ case "NLA_U8", "NLA_S8":
+ size = 1
+ case "NLA_U16", "NLA_S16":
+ size = 2
+ case "NLA_U32", "NLA_S32":
+ size = 4
+ case "NLA_U64", "NLA_S64", "NLA_SINT", "NLA_UINT":
+ size = 8
+ case "NLA_BE16":
+ size, be = 2, true
+ case "NLA_BE32":
+ size, be = 4, true
+ default:
+ panic(fmt.Sprintf("unhandled netlink attribute kind %v", attr.Kind))
+ }
+ return &Type{
+ Int: &IntType{
+ ByteSize: size,
+ isBigEndian: be,
+ },
+ }
+}
diff --git a/pkg/declextract/serialization.go b/pkg/declextract/serialization.go
new file mode 100644
index 000000000..c737a675c
--- /dev/null
+++ b/pkg/declextract/serialization.go
@@ -0,0 +1,113 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package declextract
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+)
+
+func (ctx *context) serialize() {
+ ctx.descriptions = new(bytes.Buffer)
+ ctx.fmt(header)
+ ctx.serializeIncludes()
+ ctx.serializeEnums()
+ ctx.emitNetlinkTypes()
+ ctx.serializeSyscalls()
+ ctx.serializeStructs()
+ ctx.serializeDefines()
+}
+
+const header = `# Code generated by syz-declextract. DO NOT EDIT.
+
+meta automatic
+
+type auto_todo intptr
+
+`
+
+func (ctx *context) fmt(msg string, args ...any) {
+ fmt.Fprintf(ctx.descriptions, msg, args...)
+}
+
+func (ctx *context) serializeIncludes() {
+ for _, inc := range ctx.Includes {
+ ctx.fmt("include <%s>\n", inc)
+ }
+ ctx.fmt("\n")
+}
+
+func (ctx *context) serializeDefines() {
+ for _, def := range ctx.Defines {
+ ctx.fmt("define %v %v\n", def.Name, def.Value)
+ }
+ ctx.fmt("\n")
+}
+
+func (ctx *context) serializeSyscalls() {
+ printedGetFamily, printedSendmsg := false, false
+ for _, call := range ctx.Syscalls {
+ ctx.fmt("%v(", call.Func)
+ for i, arg := range call.Args {
+ ctx.fmt("%v%v %v", comma(i), arg.Name, arg.syzType)
+ }
+ ctx.fmt(")\n")
+
+ if call.Func == "syslog$auto" {
+ printedGetFamily = true
+ ctx.emitNetlinkGetFamily()
+ }
+ if call.Func == "sendmsg$auto" {
+ printedSendmsg = true
+ ctx.emitNetlinkSendmsgs()
+ }
+ }
+ if !printedGetFamily {
+ ctx.emitNetlinkGetFamily()
+ }
+ if !printedSendmsg {
+ ctx.emitNetlinkSendmsgs()
+ }
+ ctx.fmt("\n")
+}
+
+func (ctx *context) serializeEnums() {
+ for _, enum := range ctx.Enums {
+ ctx.fmt("%v = ", enum.Name)
+ for i, val := range enum.Values {
+ ctx.fmt("%v %v", comma(i), val)
+ }
+ ctx.fmt("\n")
+ }
+ ctx.fmt("\n")
+}
+
+func (ctx *context) serializeStructs() {
+ for _, str := range ctx.Structs {
+ delims := "{}"
+ if str.IsUnion {
+ delims = "[]"
+ }
+ ctx.fmt("%v %c\n", str.Name, delims[0])
+ for _, f := range str.Fields {
+ ctx.fmt("%v %v\n", f.Name, f.syzType)
+ }
+ ctx.fmt("%c", delims[1])
+ var attrs []string
+ if str.IsPacked {
+ attrs = append(attrs, "packed")
+ }
+ if str.Align != 0 {
+ attrs = append(attrs, fmt.Sprintf("align[%v]", str.Align))
+ }
+ if str.isVarlen {
+ attrs = append(attrs, "varlen")
+ }
+ if len(attrs) != 0 {
+ ctx.fmt(" [%v]", strings.Join(attrs, ", "))
+ }
+ ctx.fmt("\n\n")
+ }
+}
diff --git a/sys/linux/auto.txt b/sys/linux/auto.txt
index 6ba83a8a4..61a29c951 100644
--- a/sys/linux/auto.txt
+++ b/sys/linux/auto.txt
@@ -2,12 +2,11 @@
meta automatic
-include <include/vdso/bits.h>
-include <include/linux/types.h>
-include <drivers/net/ieee802154/mac802154_hwsim.h>
-include <drivers/net/wireless/virtual/mac80211_hwsim.h>
-include <include/linux/nl802154.h>
-include <include/net/nl802154.h>
+type auto_todo intptr
+
+include <vdso/bits.h>
+include <linux/types.h>
+include <net/netlink.h>
include <include/uapi/linux/batman_adv.h>
include <include/uapi/linux/caif/if_caif.h>
include <include/uapi/linux/can/gw.h>
@@ -33,6 +32,7 @@ include <include/uapi/linux/if_macsec.h>
include <include/uapi/linux/if_team.h>
include <include/uapi/linux/if_tunnel.h>
include <include/uapi/linux/ila.h>
+include <include/uapi/linux/io_uring.h>
include <include/uapi/linux/ioam6_genl.h>
include <include/uapi/linux/ip_vs.h>
include <include/uapi/linux/l2tp.h>
@@ -66,6 +66,7 @@ include <include/uapi/linux/nl80211.h>
include <include/uapi/linux/openvswitch.h>
include <include/uapi/linux/pkt_cls.h>
include <include/uapi/linux/pkt_sched.h>
+include <include/uapi/linux/psample.h>
include <include/uapi/linux/rpl_iptunnel.h>
include <include/uapi/linux/rtnetlink.h>
include <include/uapi/linux/seg6_genl.h>
@@ -102,11 +103,9 @@ include <include/uapi/linux/wireguard.h>
include <include/uapi/linux/wwan.h>
include <include/uapi/linux/xfrm.h>
include <include/uapi/rdma/rdma_netlink.h>
-include <net/netlabel/netlabel_calipso.h>
-include <net/netlabel/netlabel_cipso_v4.h>
-include <net/netlabel/netlabel_mgmt.h>
-include <net/netlabel/netlabel_unlabeled.h>
+
auto_landlock_rule_type = LANDLOCK_RULE_PATH_BENEATH, LANDLOCK_RULE_NET_PORT
+
resource genl_802_15_4_MAC_family_id_auto[int16]
resource genl_HSR_family_id_auto[int16]
resource genl_IOAM6_family_id_auto[int16]
@@ -150,7 +149,6 @@ resource genl_tcp_metrics_family_id_auto[int16]
resource genl_thermal_family_id_auto[int16]
resource genl_vdpa_family_id_auto[int16]
resource genl_wireguard_family_id_auto[int16]
-type auto_todo intptr
type msghdr_802_15_4_MAC_auto[CMD, POLICY] msghdr_netlink[netlink_msg_t[genl_802_15_4_MAC_family_id_auto, genlmsghdr_t[CMD], POLICY]]
type msghdr_HSR_auto[CMD, POLICY] msghdr_netlink[netlink_msg_t[genl_HSR_family_id_auto, genlmsghdr_t[CMD], POLICY]]
type msghdr_IOAM6_auto[CMD, POLICY] msghdr_netlink[netlink_msg_t[genl_IOAM6_family_id_auto, genlmsghdr_t[CMD], POLICY]]
@@ -665,22 +663,18 @@ sendmsg$auto_NCSI_CMD_SET_PACKAGE_MASK(fd sock_nl_generic, msg ptr[in, msghdr_NC
sendmsg$auto_NETDEV_CMD_BIND_RX(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_BIND_RX, netdev_bind_rx_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
sendmsg$auto_NETDEV_CMD_DEV_GET(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_DEV_GET, netdev_dev_get_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
sendmsg$auto_NETDEV_CMD_NAPI_GET(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_NAPI_GET, netdev_napi_get_do_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
-sendmsg$auto_NETDEV_CMD_NAPI_GET0(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_NAPI_GET, netdev_napi_get_dump_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
sendmsg$auto_NETDEV_CMD_NAPI_SET(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_NAPI_SET, netdev_napi_set_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
sendmsg$auto_NETDEV_CMD_PAGE_POOL_GET(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_PAGE_POOL_GET, netdev_page_pool_get_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
sendmsg$auto_NETDEV_CMD_QSTATS_GET(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_QSTATS_GET, netdev_qstats_get_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
sendmsg$auto_NETDEV_CMD_QUEUE_GET(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_QUEUE_GET, netdev_queue_get_do_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
-sendmsg$auto_NETDEV_CMD_QUEUE_GET0(fd sock_nl_generic, msg ptr[in, msghdr_netdev_auto[NETDEV_CMD_QUEUE_GET, netdev_queue_get_dump_nl_policy$auto_netdev_genl_gen]], f flags[send_flags])
sendmsg$auto_NET_DM_CMD_CONFIG(fd sock_nl_generic, msg ptr[in, msghdr_NET_DM_auto[NET_DM_CMD_CONFIG, net_dm_nl_policy$auto_drop_monitor]], f flags[send_flags])
sendmsg$auto_NET_DM_CMD_CONFIG_GET(fd sock_nl_generic, msg ptr[in, msghdr_NET_DM_auto[NET_DM_CMD_CONFIG_GET, net_dm_nl_policy$auto_drop_monitor]], f flags[send_flags])
sendmsg$auto_NET_DM_CMD_START(fd sock_nl_generic, msg ptr[in, msghdr_NET_DM_auto[NET_DM_CMD_START, net_dm_nl_policy$auto_drop_monitor]], f flags[send_flags])
sendmsg$auto_NET_DM_CMD_STATS_GET(fd sock_nl_generic, msg ptr[in, msghdr_NET_DM_auto[NET_DM_CMD_STATS_GET, net_dm_nl_policy$auto_drop_monitor]], f flags[send_flags])
sendmsg$auto_NET_DM_CMD_STOP(fd sock_nl_generic, msg ptr[in, msghdr_NET_DM_auto[NET_DM_CMD_STOP, net_dm_nl_policy$auto_drop_monitor]], f flags[send_flags])
sendmsg$auto_NET_SHAPER_CMD_CAP_GET(fd sock_nl_generic, msg ptr[in, msghdr_net_shaper_auto[NET_SHAPER_CMD_CAP_GET, net_shaper_cap_get_do_nl_policy$auto_shaper_nl_gen]], f flags[send_flags])
-sendmsg$auto_NET_SHAPER_CMD_CAP_GET0(fd sock_nl_generic, msg ptr[in, msghdr_net_shaper_auto[NET_SHAPER_CMD_CAP_GET, net_shaper_cap_get_dump_nl_policy$auto_shaper_nl_gen]], f flags[send_flags])
sendmsg$auto_NET_SHAPER_CMD_DELETE(fd sock_nl_generic, msg ptr[in, msghdr_net_shaper_auto[NET_SHAPER_CMD_DELETE, net_shaper_delete_nl_policy$auto_shaper_nl_gen]], f flags[send_flags])
sendmsg$auto_NET_SHAPER_CMD_GET(fd sock_nl_generic, msg ptr[in, msghdr_net_shaper_auto[NET_SHAPER_CMD_GET, net_shaper_get_do_nl_policy$auto_shaper_nl_gen]], f flags[send_flags])
-sendmsg$auto_NET_SHAPER_CMD_GET0(fd sock_nl_generic, msg ptr[in, msghdr_net_shaper_auto[NET_SHAPER_CMD_GET, net_shaper_get_dump_nl_policy$auto_shaper_nl_gen]], f flags[send_flags])
sendmsg$auto_NET_SHAPER_CMD_GROUP(fd sock_nl_generic, msg ptr[in, msghdr_net_shaper_auto[NET_SHAPER_CMD_GROUP, net_shaper_group_nl_policy$auto_shaper_nl_gen]], f flags[send_flags])
sendmsg$auto_NET_SHAPER_CMD_SET(fd sock_nl_generic, msg ptr[in, msghdr_net_shaper_auto[NET_SHAPER_CMD_SET, net_shaper_set_nl_policy$auto_shaper_nl_gen]], f flags[send_flags])
sendmsg$auto_NFC_CMD_ACTIVATE_TARGET(fd sock_nl_generic, msg ptr[in, msghdr_nfc_auto[NFC_CMD_ACTIVATE_TARGET, nfc_genl_policy$auto_netlink]], f flags[send_flags])
@@ -1133,6 +1127,10 @@ __kernel_fd_set$auto_record {
fds_bits array[intptr, 16]
}
+__kernel_fsid_t$auto_record {
+ val array[int32, 2]
+}
+
__kernel_itimerspec$auto_record {
it_interval __kernel_timespec$auto_record
it_value __kernel_timespec$auto_record
@@ -1366,11 +1364,11 @@ batadv_netlink_policy$auto_netlink [
BATADV_ATTR_HOP_PENALTY nlattr[BATADV_ATTR_HOP_PENALTY, int8]
BATADV_ATTR_LOG_LEVEL nlattr[BATADV_ATTR_LOG_LEVEL, int32]
BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED nlattr[BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED, bool8]
+ BATADV_ATTR_MULTICAST_FANOUT nlattr[BATADV_ATTR_MULTICAST_FANOUT, int32]
BATADV_ATTR_NETWORK_CODING_ENABLED nlattr[BATADV_ATTR_NETWORK_CODING_ENABLED, bool8]
BATADV_ATTR_ORIG_INTERVAL nlattr[BATADV_ATTR_ORIG_INTERVAL, int32]
BATADV_ATTR_ELP_INTERVAL nlattr[BATADV_ATTR_ELP_INTERVAL, int32]
BATADV_ATTR_THROUGHPUT_OVERRIDE nlattr[BATADV_ATTR_THROUGHPUT_OVERRIDE, int32]
- BATADV_ATTR_MULTICAST_FANOUT nlattr[BATADV_ATTR_MULTICAST_FANOUT, int32]
] [varlen]
bpf_attr$auto_record [
@@ -1976,11 +1974,11 @@ ethnl_mm_get_policy$auto_netlink [
ethnl_mm_set_policy$auto_netlink [
ETHTOOL_A_MM_HEADER nlnest[ETHTOOL_A_MM_HEADER, array[ethnl_header_policy$auto_netlink]]
- ETHTOOL_A_MM_PMAC_ENABLED nlattr[ETHTOOL_A_MM_PMAC_ENABLED, bool8]
- ETHTOOL_A_MM_TX_ENABLED nlattr[ETHTOOL_A_MM_TX_ENABLED, bool8]
- ETHTOOL_A_MM_TX_MIN_FRAG_SIZE nlattr[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, int32]
ETHTOOL_A_MM_VERIFY_ENABLED nlattr[ETHTOOL_A_MM_VERIFY_ENABLED, bool8]
ETHTOOL_A_MM_VERIFY_TIME nlattr[ETHTOOL_A_MM_VERIFY_TIME, int32]
+ ETHTOOL_A_MM_TX_ENABLED nlattr[ETHTOOL_A_MM_TX_ENABLED, bool8]
+ ETHTOOL_A_MM_PMAC_ENABLED nlattr[ETHTOOL_A_MM_PMAC_ENABLED, bool8]
+ ETHTOOL_A_MM_TX_MIN_FRAG_SIZE nlattr[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, int32]
] [varlen]
ethnl_module_eeprom_get_policy$auto_netlink [
@@ -2038,8 +2036,8 @@ ethnl_plca_get_status_policy$auto_netlink [
ethnl_plca_set_cfg_policy$auto_netlink [
ETHTOOL_A_PLCA_HEADER nlnest[ETHTOOL_A_PLCA_HEADER, array[ethnl_header_policy_phy$auto_netlink]]
ETHTOOL_A_PLCA_ENABLED nlattr[ETHTOOL_A_PLCA_ENABLED, bool8]
- ETHTOOL_A_PLCA_NODE_CNT nlattr[ETHTOOL_A_PLCA_NODE_CNT, int32]
ETHTOOL_A_PLCA_NODE_ID nlattr[ETHTOOL_A_PLCA_NODE_ID, int32]
+ ETHTOOL_A_PLCA_NODE_CNT nlattr[ETHTOOL_A_PLCA_NODE_CNT, int32]
ETHTOOL_A_PLCA_TO_TMR nlattr[ETHTOOL_A_PLCA_TO_TMR, int32]
ETHTOOL_A_PLCA_BURST_CNT nlattr[ETHTOOL_A_PLCA_BURST_CNT, int32]
ETHTOOL_A_PLCA_BURST_TMR nlattr[ETHTOOL_A_PLCA_BURST_TMR, int32]
@@ -2127,9 +2125,9 @@ file_handle$auto_record {
flow_policy$auto_datapath [
OVS_FLOW_ATTR_KEY nlnest[OVS_FLOW_ATTR_KEY, array[nl_generic_attr]]
+ OVS_FLOW_ATTR_MASK nlnest[OVS_FLOW_ATTR_MASK, array[nl_generic_attr]]
OVS_FLOW_ATTR_ACTIONS nlnest[OVS_FLOW_ATTR_ACTIONS, array[nl_generic_attr]]
OVS_FLOW_ATTR_CLEAR nlattr[OVS_FLOW_ATTR_CLEAR, void]
- OVS_FLOW_ATTR_MASK nlnest[OVS_FLOW_ATTR_MASK, array[nl_generic_attr]]
OVS_FLOW_ATTR_PROBE nlattr[OVS_FLOW_ATTR_PROBE, void]
OVS_FLOW_ATTR_UFID nlattr[OVS_FLOW_ATTR_UFID, int8]
OVS_FLOW_ATTR_UFID_FLAGS nlattr[OVS_FLOW_ATTR_UFID_FLAGS, int32]
@@ -2188,10 +2186,10 @@ he_obss_pd_policy$auto_nl80211 [
hsr_genl_policy$auto_hsr_netlink [
HSR_A_NODE_ADDR nlattr[HSR_A_NODE_ADDR, array[int8, 6]]
+ HSR_A_NODE_ADDR_B nlattr[HSR_A_NODE_ADDR_B, array[int8, 6]]
HSR_A_IFINDEX nlattr[HSR_A_IFINDEX, ifindex]
HSR_A_IF1_AGE nlattr[HSR_A_IF1_AGE, int32]
HSR_A_IF2_AGE nlattr[HSR_A_IF2_AGE, int32]
- HSR_A_NODE_ADDR_B nlattr[HSR_A_NODE_ADDR_B, array[int8, 6]]
HSR_A_IF1_SEQ nlattr[HSR_A_IF1_SEQ, int16]
HSR_A_IF2_SEQ nlattr[HSR_A_IF2_SEQ, int16]
] [varlen]
@@ -2289,11 +2287,11 @@ hwsim_pmsr_peers_result_policy$auto_mac80211_hwsim [
] [varlen]
hwsim_pmsr_resp_policy$auto_mac80211_hwsim [
- NL80211_PMSR_RESP_ATTR_DATA nlnest[NL80211_PMSR_RESP_ATTR_DATA, array[hwsim_pmsr_resp_type_policy$auto_mac80211_hwsim]]
NL80211_PMSR_RESP_ATTR_STATUS nlattr[NL80211_PMSR_RESP_ATTR_STATUS, int32]
NL80211_PMSR_RESP_ATTR_HOST_TIME nlattr[NL80211_PMSR_RESP_ATTR_HOST_TIME, int64]
NL80211_PMSR_RESP_ATTR_AP_TSF nlattr[NL80211_PMSR_RESP_ATTR_AP_TSF, int64]
NL80211_PMSR_RESP_ATTR_FINAL nlattr[NL80211_PMSR_RESP_ATTR_FINAL, void]
+ NL80211_PMSR_RESP_ATTR_DATA nlnest[NL80211_PMSR_RESP_ATTR_DATA, array[hwsim_pmsr_resp_type_policy$auto_mac80211_hwsim]]
] [varlen]
hwsim_pmsr_resp_type_policy$auto_mac80211_hwsim [
@@ -2322,11 +2320,19 @@ hwsim_tx_rate$auto_record {
ieee802154_policy$auto_nl802154 [
IEEE802154_ATTR_DEV_NAME nlattr[IEEE802154_ATTR_DEV_NAME, devname]
IEEE802154_ATTR_DEV_INDEX nlattr[IEEE802154_ATTR_DEV_INDEX, ifindex]
+ IEEE802154_ATTR_PHY_NAME nlattr[IEEE802154_ATTR_PHY_NAME, stringnoz]
IEEE802154_ATTR_STATUS nlattr[IEEE802154_ATTR_STATUS, int8]
IEEE802154_ATTR_SHORT_ADDR nlattr[IEEE802154_ATTR_SHORT_ADDR, int16]
IEEE802154_ATTR_HW_ADDR nlattr[IEEE802154_ATTR_HW_ADDR, int64]
IEEE802154_ATTR_PAN_ID nlattr[IEEE802154_ATTR_PAN_ID, int16]
IEEE802154_ATTR_CHANNEL nlattr[IEEE802154_ATTR_CHANNEL, int8]
+ IEEE802154_ATTR_BCN_ORD nlattr[IEEE802154_ATTR_BCN_ORD, int8]
+ IEEE802154_ATTR_SF_ORD nlattr[IEEE802154_ATTR_SF_ORD, int8]
+ IEEE802154_ATTR_PAN_COORD nlattr[IEEE802154_ATTR_PAN_COORD, int8]
+ IEEE802154_ATTR_BAT_EXT nlattr[IEEE802154_ATTR_BAT_EXT, int8]
+ IEEE802154_ATTR_COORD_REALIGN nlattr[IEEE802154_ATTR_COORD_REALIGN, int8]
+ IEEE802154_ATTR_PAGE nlattr[IEEE802154_ATTR_PAGE, int8]
+ IEEE802154_ATTR_DEV_TYPE nlattr[IEEE802154_ATTR_DEV_TYPE, int8]
IEEE802154_ATTR_COORD_SHORT_ADDR nlattr[IEEE802154_ATTR_COORD_SHORT_ADDR, int16]
IEEE802154_ATTR_COORD_HW_ADDR nlattr[IEEE802154_ATTR_COORD_HW_ADDR, int64]
IEEE802154_ATTR_COORD_PAN_ID nlattr[IEEE802154_ATTR_COORD_PAN_ID, int16]
@@ -2342,15 +2348,7 @@ ieee802154_policy$auto_nl802154 [
IEEE802154_ATTR_CHANNELS nlattr[IEEE802154_ATTR_CHANNELS, int32]
IEEE802154_ATTR_DURATION nlattr[IEEE802154_ATTR_DURATION, int8]
IEEE802154_ATTR_ED_LIST nlattr[IEEE802154_ATTR_ED_LIST, array[int8, 27]]
- IEEE802154_ATTR_BCN_ORD nlattr[IEEE802154_ATTR_BCN_ORD, int8]
- IEEE802154_ATTR_SF_ORD nlattr[IEEE802154_ATTR_SF_ORD, int8]
- IEEE802154_ATTR_PAN_COORD nlattr[IEEE802154_ATTR_PAN_COORD, int8]
- IEEE802154_ATTR_BAT_EXT nlattr[IEEE802154_ATTR_BAT_EXT, int8]
- IEEE802154_ATTR_COORD_REALIGN nlattr[IEEE802154_ATTR_COORD_REALIGN, int8]
- IEEE802154_ATTR_PAGE nlattr[IEEE802154_ATTR_PAGE, int8]
IEEE802154_ATTR_CHANNEL_PAGE_LIST nlattr[IEEE802154_ATTR_CHANNEL_PAGE_LIST, array[int8, 128]]
- IEEE802154_ATTR_PHY_NAME nlattr[IEEE802154_ATTR_PHY_NAME, stringnoz]
- IEEE802154_ATTR_DEV_TYPE nlattr[IEEE802154_ATTR_DEV_TYPE, int8]
IEEE802154_ATTR_TXPOWER nlattr[IEEE802154_ATTR_TXPOWER, int8]
IEEE802154_ATTR_LBT_ENABLED nlattr[IEEE802154_ATTR_LBT_ENABLED, bool8]
IEEE802154_ATTR_CCA_MODE nlattr[IEEE802154_ATTR_CCA_MODE, int8]
@@ -2527,15 +2525,12 @@ l2tp_nl_policy$auto_l2tp_netlink [
L2TP_ATTR_L2SPEC_TYPE nlattr[L2TP_ATTR_L2SPEC_TYPE, int8]
L2TP_ATTR_L2SPEC_LEN nlattr[L2TP_ATTR_L2SPEC_LEN, int8]
L2TP_ATTR_PROTO_VERSION nlattr[L2TP_ATTR_PROTO_VERSION, int8]
- L2TP_ATTR_IFNAME nlattr[L2TP_ATTR_IFNAME, devname]
L2TP_ATTR_CONN_ID nlattr[L2TP_ATTR_CONN_ID, int32]
L2TP_ATTR_PEER_CONN_ID nlattr[L2TP_ATTR_PEER_CONN_ID, int32]
L2TP_ATTR_SESSION_ID nlattr[L2TP_ATTR_SESSION_ID, int32]
L2TP_ATTR_PEER_SESSION_ID nlattr[L2TP_ATTR_PEER_SESSION_ID, int32]
L2TP_ATTR_UDP_CSUM nlattr[L2TP_ATTR_UDP_CSUM, int8]
L2TP_ATTR_VLAN_ID nlattr[L2TP_ATTR_VLAN_ID, int16]
- L2TP_ATTR_COOKIE nlattr[L2TP_ATTR_COOKIE, int64]
- L2TP_ATTR_PEER_COOKIE nlattr[L2TP_ATTR_PEER_COOKIE, int64]
L2TP_ATTR_DEBUG nlattr[L2TP_ATTR_DEBUG, int32]
L2TP_ATTR_RECV_SEQ nlattr[L2TP_ATTR_RECV_SEQ, int8]
L2TP_ATTR_SEND_SEQ nlattr[L2TP_ATTR_SEND_SEQ, int8]
@@ -2552,6 +2547,9 @@ l2tp_nl_policy$auto_l2tp_netlink [
L2TP_ATTR_STATS nlnest[L2TP_ATTR_STATS, array[nl_generic_attr]]
L2TP_ATTR_IP6_SADDR nlattr[L2TP_ATTR_IP6_SADDR, in6_addr$auto_record]
L2TP_ATTR_IP6_DADDR nlattr[L2TP_ATTR_IP6_DADDR, in6_addr$auto_record]
+ L2TP_ATTR_IFNAME nlattr[L2TP_ATTR_IFNAME, devname]
+ L2TP_ATTR_COOKIE nlattr[L2TP_ATTR_COOKIE, int64]
+ L2TP_ATTR_PEER_COOKIE nlattr[L2TP_ATTR_PEER_COOKIE, int64]
] [varlen]
landlock_ruleset_attr$auto_record {
@@ -2688,25 +2686,19 @@ net_shaper_cap_get_do_nl_policy$auto_shaper_nl_gen [
NET_SHAPER_A_CAPS_SCOPE nlattr[NET_SHAPER_A_CAPS_SCOPE, int32]
] [varlen]
-net_shaper_cap_get_dump_nl_policy$auto_shaper_nl_gen [
- NET_SHAPER_A_CAPS_IFINDEX nlattr[NET_SHAPER_A_CAPS_IFINDEX, ifindex]
-] [varlen]
-
net_shaper_delete_nl_policy$auto_shaper_nl_gen [
- NET_SHAPER_A_HANDLE nlnest[NET_SHAPER_A_HANDLE, array[net_shaper_handle_nl_policy$auto_shaper_nl_gen]]
NET_SHAPER_A_IFINDEX nlattr[NET_SHAPER_A_IFINDEX, ifindex]
-] [varlen]
-
-net_shaper_get_do_nl_policy$auto_shaper_nl_gen [
NET_SHAPER_A_HANDLE nlnest[NET_SHAPER_A_HANDLE, array[net_shaper_handle_nl_policy$auto_shaper_nl_gen]]
- NET_SHAPER_A_IFINDEX nlattr[NET_SHAPER_A_IFINDEX, ifindex]
] [varlen]
-net_shaper_get_dump_nl_policy$auto_shaper_nl_gen [
+net_shaper_get_do_nl_policy$auto_shaper_nl_gen [
NET_SHAPER_A_IFINDEX nlattr[NET_SHAPER_A_IFINDEX, ifindex]
+ NET_SHAPER_A_HANDLE nlnest[NET_SHAPER_A_HANDLE, array[net_shaper_handle_nl_policy$auto_shaper_nl_gen]]
] [varlen]
net_shaper_group_nl_policy$auto_shaper_nl_gen [
+ NET_SHAPER_A_IFINDEX nlattr[NET_SHAPER_A_IFINDEX, ifindex]
+ NET_SHAPER_A_PARENT nlnest[NET_SHAPER_A_PARENT, array[net_shaper_handle_nl_policy$auto_shaper_nl_gen]]
NET_SHAPER_A_HANDLE nlnest[NET_SHAPER_A_HANDLE, array[net_shaper_handle_nl_policy$auto_shaper_nl_gen]]
NET_SHAPER_A_METRIC nlattr[NET_SHAPER_A_METRIC, int32]
NET_SHAPER_A_BW_MIN nlattr[NET_SHAPER_A_BW_MIN, int64]
@@ -2714,8 +2706,6 @@ net_shaper_group_nl_policy$auto_shaper_nl_gen [
NET_SHAPER_A_BURST nlattr[NET_SHAPER_A_BURST, int64]
NET_SHAPER_A_PRIORITY nlattr[NET_SHAPER_A_PRIORITY, int32]
NET_SHAPER_A_WEIGHT nlattr[NET_SHAPER_A_WEIGHT, int32]
- NET_SHAPER_A_IFINDEX nlattr[NET_SHAPER_A_IFINDEX, ifindex]
- NET_SHAPER_A_PARENT nlnest[NET_SHAPER_A_PARENT, array[net_shaper_handle_nl_policy$auto_shaper_nl_gen]]
NET_SHAPER_A_LEAVES nlnest[NET_SHAPER_A_LEAVES, array[net_shaper_leaf_info_nl_policy$auto_shaper_nl_gen]]
] [varlen]
@@ -2731,6 +2721,7 @@ net_shaper_leaf_info_nl_policy$auto_shaper_nl_gen [
] [varlen]
net_shaper_set_nl_policy$auto_shaper_nl_gen [
+ NET_SHAPER_A_IFINDEX nlattr[NET_SHAPER_A_IFINDEX, ifindex]
NET_SHAPER_A_HANDLE nlnest[NET_SHAPER_A_HANDLE, array[net_shaper_handle_nl_policy$auto_shaper_nl_gen]]
NET_SHAPER_A_METRIC nlattr[NET_SHAPER_A_METRIC, int32]
NET_SHAPER_A_BW_MIN nlattr[NET_SHAPER_A_BW_MIN, int64]
@@ -2738,13 +2729,12 @@ net_shaper_set_nl_policy$auto_shaper_nl_gen [
NET_SHAPER_A_BURST nlattr[NET_SHAPER_A_BURST, int64]
NET_SHAPER_A_PRIORITY nlattr[NET_SHAPER_A_PRIORITY, int32]
NET_SHAPER_A_WEIGHT nlattr[NET_SHAPER_A_WEIGHT, int32]
- NET_SHAPER_A_IFINDEX nlattr[NET_SHAPER_A_IFINDEX, ifindex]
] [varlen]
netdev_bind_rx_nl_policy$auto_netdev_genl_gen [
NETDEV_A_DMABUF_IFINDEX nlattr[NETDEV_A_DMABUF_IFINDEX, ifindex]
- NETDEV_A_DMABUF_QUEUES nlnest[NETDEV_A_DMABUF_QUEUES, array[netdev_queue_id_nl_policy$auto_netdev_genl_gen]]
NETDEV_A_DMABUF_FD nlattr[NETDEV_A_DMABUF_FD, fd]
+ NETDEV_A_DMABUF_QUEUES nlnest[NETDEV_A_DMABUF_QUEUES, array[netdev_queue_id_nl_policy$auto_netdev_genl_gen]]
] [varlen]
netdev_dev_get_nl_policy$auto_netdev_genl_gen [
@@ -2755,10 +2745,6 @@ netdev_napi_get_do_nl_policy$auto_netdev_genl_gen [
NETDEV_A_NAPI_ID nlattr[NETDEV_A_NAPI_ID, int32]
] [varlen]
-netdev_napi_get_dump_nl_policy$auto_netdev_genl_gen [
- NETDEV_A_NAPI_IFINDEX nlattr[NETDEV_A_NAPI_IFINDEX, ifindex]
-] [varlen]
-
netdev_napi_set_nl_policy$auto_netdev_genl_gen [
NETDEV_A_NAPI_ID nlattr[NETDEV_A_NAPI_ID, int32]
NETDEV_A_NAPI_DEFER_HARD_IRQS nlattr[NETDEV_A_NAPI_DEFER_HARD_IRQS, int32]
@@ -2776,13 +2762,9 @@ netdev_qstats_get_nl_policy$auto_netdev_genl_gen [
] [varlen]
netdev_queue_get_do_nl_policy$auto_netdev_genl_gen [
- NETDEV_A_QUEUE_ID nlattr[NETDEV_A_QUEUE_ID, int32]
NETDEV_A_QUEUE_IFINDEX nlattr[NETDEV_A_QUEUE_IFINDEX, ifindex]
NETDEV_A_QUEUE_TYPE nlattr[NETDEV_A_QUEUE_TYPE, int32]
-] [varlen]
-
-netdev_queue_get_dump_nl_policy$auto_netdev_genl_gen [
- NETDEV_A_QUEUE_IFINDEX nlattr[NETDEV_A_QUEUE_IFINDEX, ifindex]
+ NETDEV_A_QUEUE_ID nlattr[NETDEV_A_QUEUE_ID, int32]
] [varlen]
netdev_queue_id_nl_policy$auto_netdev_genl_gen [
@@ -2948,15 +2930,33 @@ nl80211_pmsr_req_data_policy$auto_nl80211 [
nl80211_policy$auto_nl80211 [
NL80211_ATTR_WIPHY nlattr[NL80211_ATTR_WIPHY, int32]
NL80211_ATTR_WIPHY_NAME nlattr[NL80211_ATTR_WIPHY_NAME, string]
+ NL80211_ATTR_WIPHY_TXQ_PARAMS nlnest[NL80211_ATTR_WIPHY_TXQ_PARAMS, array[nl_generic_attr]]
+ NL80211_ATTR_WIPHY_FREQ nlattr[NL80211_ATTR_WIPHY_FREQ, int32]
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE nlattr[NL80211_ATTR_WIPHY_CHANNEL_TYPE, int32]
+ NL80211_ATTR_WIPHY_EDMG_CHANNELS nlattr[NL80211_ATTR_WIPHY_EDMG_CHANNELS, int8]
+ NL80211_ATTR_WIPHY_EDMG_BW_CONFIG nlattr[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG, int8]
+ NL80211_ATTR_CHANNEL_WIDTH nlattr[NL80211_ATTR_CHANNEL_WIDTH, int32]
+ NL80211_ATTR_CENTER_FREQ1 nlattr[NL80211_ATTR_CENTER_FREQ1, int32]
+ NL80211_ATTR_CENTER_FREQ1_OFFSET nlattr[NL80211_ATTR_CENTER_FREQ1_OFFSET, int32]
+ NL80211_ATTR_CENTER_FREQ2 nlattr[NL80211_ATTR_CENTER_FREQ2, int32]
+ NL80211_ATTR_WIPHY_RETRY_SHORT nlattr[NL80211_ATTR_WIPHY_RETRY_SHORT, int8]
+ NL80211_ATTR_WIPHY_RETRY_LONG nlattr[NL80211_ATTR_WIPHY_RETRY_LONG, int8]
+ NL80211_ATTR_WIPHY_FRAG_THRESHOLD nlattr[NL80211_ATTR_WIPHY_FRAG_THRESHOLD, int32]
+ NL80211_ATTR_WIPHY_RTS_THRESHOLD nlattr[NL80211_ATTR_WIPHY_RTS_THRESHOLD, int32]
+ NL80211_ATTR_WIPHY_COVERAGE_CLASS nlattr[NL80211_ATTR_WIPHY_COVERAGE_CLASS, int8]
+ NL80211_ATTR_WIPHY_DYN_ACK nlattr[NL80211_ATTR_WIPHY_DYN_ACK, void]
+ NL80211_ATTR_IFTYPE nlattr[NL80211_ATTR_IFTYPE, int32]
NL80211_ATTR_IFINDEX nlattr[NL80211_ATTR_IFINDEX, ifindex]
NL80211_ATTR_IFNAME nlattr[NL80211_ATTR_IFNAME, devname]
- NL80211_ATTR_IFTYPE nlattr[NL80211_ATTR_IFTYPE, int32]
NL80211_ATTR_MAC nlattr[NL80211_ATTR_MAC, array[int8]]
+ NL80211_ATTR_PREV_BSSID nlattr[NL80211_ATTR_PREV_BSSID, array[int8]]
+ NL80211_ATTR_KEY nlnest[NL80211_ATTR_KEY, array[nl_generic_attr]]
NL80211_ATTR_KEY_DATA nlattr[NL80211_ATTR_KEY_DATA, array[int8, 0:32]]
NL80211_ATTR_KEY_IDX nlattr[NL80211_ATTR_KEY_IDX, int8]
NL80211_ATTR_KEY_CIPHER nlattr[NL80211_ATTR_KEY_CIPHER, int32]
- NL80211_ATTR_KEY_SEQ nlattr[NL80211_ATTR_KEY_SEQ, array[int8, 0:16]]
NL80211_ATTR_KEY_DEFAULT nlattr[NL80211_ATTR_KEY_DEFAULT, void]
+ NL80211_ATTR_KEY_SEQ nlattr[NL80211_ATTR_KEY_SEQ, array[int8, 0:16]]
+ NL80211_ATTR_KEY_TYPE nlattr[NL80211_ATTR_KEY_TYPE, int32]
NL80211_ATTR_BEACON_INTERVAL nlattr[NL80211_ATTR_BEACON_INTERVAL, int32]
NL80211_ATTR_DTIM_PERIOD nlattr[NL80211_ATTR_DTIM_PERIOD, int32]
NL80211_ATTR_BEACON_HEAD nlattr[NL80211_ATTR_BEACON_HEAD, array[int8, 0:2304]]
@@ -2965,53 +2965,49 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_STA_FLAGS nlnest[NL80211_ATTR_STA_FLAGS, array[nl_generic_attr]]
NL80211_ATTR_STA_LISTEN_INTERVAL nlattr[NL80211_ATTR_STA_LISTEN_INTERVAL, int16]
NL80211_ATTR_STA_SUPPORTED_RATES nlattr[NL80211_ATTR_STA_SUPPORTED_RATES, array[int8, 0:32]]
+ NL80211_ATTR_STA_PLINK_ACTION nlattr[NL80211_ATTR_STA_PLINK_ACTION, int8]
+ NL80211_ATTR_STA_TX_POWER_SETTING nlattr[NL80211_ATTR_STA_TX_POWER_SETTING, int8]
+ NL80211_ATTR_STA_TX_POWER nlattr[NL80211_ATTR_STA_TX_POWER, int16]
NL80211_ATTR_STA_VLAN nlattr[NL80211_ATTR_STA_VLAN, int32]
NL80211_ATTR_MNTR_FLAGS nlattr[NL80211_ATTR_MNTR_FLAGS, array[int8]]
NL80211_ATTR_MESH_ID nlattr[NL80211_ATTR_MESH_ID, array[int8, 0:32]]
- NL80211_ATTR_STA_PLINK_ACTION nlattr[NL80211_ATTR_STA_PLINK_ACTION, int8]
NL80211_ATTR_MPATH_NEXT_HOP nlattr[NL80211_ATTR_MPATH_NEXT_HOP, array[int8]]
+ NL80211_ATTR_REG_ALPHA2 nlattr[NL80211_ATTR_REG_ALPHA2, array[int8]]
+ NL80211_ATTR_REG_RULES nlnest[NL80211_ATTR_REG_RULES, array[nl_generic_attr]]
NL80211_ATTR_BSS_CTS_PROT nlattr[NL80211_ATTR_BSS_CTS_PROT, int8]
NL80211_ATTR_BSS_SHORT_PREAMBLE nlattr[NL80211_ATTR_BSS_SHORT_PREAMBLE, int8]
NL80211_ATTR_BSS_SHORT_SLOT_TIME nlattr[NL80211_ATTR_BSS_SHORT_SLOT_TIME, int8]
- NL80211_ATTR_HT_CAPABILITY nlattr[NL80211_ATTR_HT_CAPABILITY, array[int8]]
- NL80211_ATTR_REG_ALPHA2 nlattr[NL80211_ATTR_REG_ALPHA2, array[int8]]
- NL80211_ATTR_REG_RULES nlnest[NL80211_ATTR_REG_RULES, array[nl_generic_attr]]
- NL80211_ATTR_MESH_CONFIG nlnest[NL80211_ATTR_MESH_CONFIG, array[nl_generic_attr]]
NL80211_ATTR_BSS_BASIC_RATES nlattr[NL80211_ATTR_BSS_BASIC_RATES, array[int8, 0:32]]
- NL80211_ATTR_WIPHY_TXQ_PARAMS nlnest[NL80211_ATTR_WIPHY_TXQ_PARAMS, array[nl_generic_attr]]
- NL80211_ATTR_WIPHY_FREQ nlattr[NL80211_ATTR_WIPHY_FREQ, int32]
- NL80211_ATTR_WIPHY_CHANNEL_TYPE nlattr[NL80211_ATTR_WIPHY_CHANNEL_TYPE, int32]
+ NL80211_ATTR_BSS_HT_OPMODE nlattr[NL80211_ATTR_BSS_HT_OPMODE, int16]
+ NL80211_ATTR_MESH_CONFIG nlnest[NL80211_ATTR_MESH_CONFIG, array[nl_generic_attr]]
+ NL80211_ATTR_SUPPORT_MESH_AUTH nlattr[NL80211_ATTR_SUPPORT_MESH_AUTH, void]
+ NL80211_ATTR_HT_CAPABILITY nlattr[NL80211_ATTR_HT_CAPABILITY, array[int8]]
NL80211_ATTR_MGMT_SUBTYPE nlattr[NL80211_ATTR_MGMT_SUBTYPE, int8]
NL80211_ATTR_IE nlattr[NL80211_ATTR_IE, array[int8, 0:2304]]
NL80211_ATTR_SCAN_FREQUENCIES nlnest[NL80211_ATTR_SCAN_FREQUENCIES, array[nl_generic_attr]]
NL80211_ATTR_SCAN_SSIDS nlnest[NL80211_ATTR_SCAN_SSIDS, array[nl_generic_attr]]
- NL80211_ATTR_FRAME nlattr[NL80211_ATTR_FRAME, array[int8, 0:2304]]
NL80211_ATTR_SSID nlattr[NL80211_ATTR_SSID, array[int8, 0:32]]
NL80211_ATTR_AUTH_TYPE nlattr[NL80211_ATTR_AUTH_TYPE, int32]
NL80211_ATTR_REASON_CODE nlattr[NL80211_ATTR_REASON_CODE, int16]
- NL80211_ATTR_KEY_TYPE nlattr[NL80211_ATTR_KEY_TYPE, int32]
NL80211_ATTR_FREQ_FIXED nlattr[NL80211_ATTR_FREQ_FIXED, void]
- NL80211_ATTR_WIPHY_RETRY_SHORT nlattr[NL80211_ATTR_WIPHY_RETRY_SHORT, int8]
- NL80211_ATTR_WIPHY_RETRY_LONG nlattr[NL80211_ATTR_WIPHY_RETRY_LONG, int8]
- NL80211_ATTR_WIPHY_FRAG_THRESHOLD nlattr[NL80211_ATTR_WIPHY_FRAG_THRESHOLD, int32]
- NL80211_ATTR_WIPHY_RTS_THRESHOLD nlattr[NL80211_ATTR_WIPHY_RTS_THRESHOLD, int32]
NL80211_ATTR_TIMED_OUT nlattr[NL80211_ATTR_TIMED_OUT, void]
NL80211_ATTR_USE_MFP nlattr[NL80211_ATTR_USE_MFP, int32]
NL80211_ATTR_STA_FLAGS2 nlattr[NL80211_ATTR_STA_FLAGS2, array[int8]]
NL80211_ATTR_CONTROL_PORT nlattr[NL80211_ATTR_CONTROL_PORT, void]
+ NL80211_ATTR_CONTROL_PORT_ETHERTYPE nlattr[NL80211_ATTR_CONTROL_PORT_ETHERTYPE, sock_port]
+ NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT nlattr[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, void]
+ NL80211_ATTR_CONTROL_PORT_OVER_NL80211 nlattr[NL80211_ATTR_CONTROL_PORT_OVER_NL80211, void]
NL80211_ATTR_PRIVACY nlattr[NL80211_ATTR_PRIVACY, void]
NL80211_ATTR_STATUS_CODE nlattr[NL80211_ATTR_STATUS_CODE, int16]
NL80211_ATTR_CIPHER_SUITE_GROUP nlattr[NL80211_ATTR_CIPHER_SUITE_GROUP, int32]
NL80211_ATTR_WPA_VERSIONS nlattr[NL80211_ATTR_WPA_VERSIONS, int32]
- NL80211_ATTR_PREV_BSSID nlattr[NL80211_ATTR_PREV_BSSID, array[int8]]
- NL80211_ATTR_KEY nlnest[NL80211_ATTR_KEY, array[nl_generic_attr]]
NL80211_ATTR_PID nlattr[NL80211_ATTR_PID, pid]
NL80211_ATTR_4ADDR nlattr[NL80211_ATTR_4ADDR, int8]
NL80211_ATTR_PMKID nlattr[NL80211_ATTR_PMKID, array[int8]]
NL80211_ATTR_DURATION nlattr[NL80211_ATTR_DURATION, int32]
NL80211_ATTR_COOKIE nlattr[NL80211_ATTR_COOKIE, int64]
- NL80211_ATTR_WIPHY_COVERAGE_CLASS nlattr[NL80211_ATTR_WIPHY_COVERAGE_CLASS, int8]
NL80211_ATTR_TX_RATES nlnest[NL80211_ATTR_TX_RATES, array[nl_generic_attr]]
+ NL80211_ATTR_FRAME nlattr[NL80211_ATTR_FRAME, array[int8, 0:2304]]
NL80211_ATTR_FRAME_MATCH nlattr[NL80211_ATTR_FRAME_MATCH, array[int8]]
NL80211_ATTR_PS_STATE nlattr[NL80211_ATTR_PS_STATE, int32]
NL80211_ATTR_CQM nlnest[NL80211_ATTR_CQM, array[nl_generic_attr]]
@@ -3020,25 +3016,24 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_WIPHY_TX_POWER_SETTING nlattr[NL80211_ATTR_WIPHY_TX_POWER_SETTING, int32]
NL80211_ATTR_WIPHY_TX_POWER_LEVEL nlattr[NL80211_ATTR_WIPHY_TX_POWER_LEVEL, int32]
NL80211_ATTR_FRAME_TYPE nlattr[NL80211_ATTR_FRAME_TYPE, int16]
- NL80211_ATTR_CONTROL_PORT_ETHERTYPE nlattr[NL80211_ATTR_CONTROL_PORT_ETHERTYPE, sock_port]
- NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT nlattr[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, void]
NL80211_ATTR_WIPHY_ANTENNA_TX nlattr[NL80211_ATTR_WIPHY_ANTENNA_TX, int32]
NL80211_ATTR_WIPHY_ANTENNA_RX nlattr[NL80211_ATTR_WIPHY_ANTENNA_RX, int32]
NL80211_ATTR_MCAST_RATE nlattr[NL80211_ATTR_MCAST_RATE, int32]
NL80211_ATTR_OFFCHANNEL_TX_OK nlattr[NL80211_ATTR_OFFCHANNEL_TX_OK, void]
- NL80211_ATTR_BSS_HT_OPMODE nlattr[NL80211_ATTR_BSS_HT_OPMODE, int16]
NL80211_ATTR_KEY_DEFAULT_TYPES nlnest[NL80211_ATTR_KEY_DEFAULT_TYPES, array[nl_generic_attr]]
- NL80211_ATTR_SUPPORT_MESH_AUTH nlattr[NL80211_ATTR_SUPPORT_MESH_AUTH, void]
- NL80211_ATTR_STA_PLINK_STATE nlattr[NL80211_ATTR_STA_PLINK_STATE, int8]
NL80211_ATTR_WOWLAN_TRIGGERS nlnest[NL80211_ATTR_WOWLAN_TRIGGERS, array[nl_generic_attr]]
+ NL80211_ATTR_STA_PLINK_STATE nlattr[NL80211_ATTR_STA_PLINK_STATE, int8]
+ NL80211_ATTR_MEASUREMENT_DURATION nlattr[NL80211_ATTR_MEASUREMENT_DURATION, int16]
+ NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY nlattr[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, void]
+ NL80211_ATTR_MESH_PEER_AID nlattr[NL80211_ATTR_MESH_PEER_AID, int16]
NL80211_ATTR_SCHED_SCAN_INTERVAL nlattr[NL80211_ATTR_SCHED_SCAN_INTERVAL, int32]
NL80211_ATTR_REKEY_DATA nlnest[NL80211_ATTR_REKEY_DATA, array[nl_generic_attr]]
NL80211_ATTR_SCAN_SUPP_RATES nlnest[NL80211_ATTR_SCAN_SUPP_RATES, array[nl_generic_attr]]
NL80211_ATTR_HIDDEN_SSID nlattr[NL80211_ATTR_HIDDEN_SSID, int32]
NL80211_ATTR_IE_PROBE_RESP nlattr[NL80211_ATTR_IE_PROBE_RESP, array[int8, 0:2304]]
NL80211_ATTR_IE_ASSOC_RESP nlattr[NL80211_ATTR_IE_ASSOC_RESP, array[int8, 0:2304]]
- NL80211_ATTR_STA_WME nlnest[NL80211_ATTR_STA_WME, array[nl80211_sta_wme_policy$auto_nl80211]]
NL80211_ATTR_ROAM_SUPPORT nlattr[NL80211_ATTR_ROAM_SUPPORT, void]
+ NL80211_ATTR_STA_WME nlnest[NL80211_ATTR_STA_WME, array[nl80211_sta_wme_policy$auto_nl80211]]
NL80211_ATTR_SCHED_SCAN_MATCH nlnest[NL80211_ATTR_SCHED_SCAN_MATCH, array[nl_generic_attr]]
NL80211_ATTR_TX_NO_CCK_RATE nlattr[NL80211_ATTR_TX_NO_CCK_RATE, void]
NL80211_ATTR_TDLS_ACTION nlattr[NL80211_ATTR_TDLS_ACTION, int8]
@@ -3046,6 +3041,7 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_TDLS_OPERATION nlattr[NL80211_ATTR_TDLS_OPERATION, int8]
NL80211_ATTR_TDLS_SUPPORT nlattr[NL80211_ATTR_TDLS_SUPPORT, void]
NL80211_ATTR_TDLS_EXTERNAL_SETUP nlattr[NL80211_ATTR_TDLS_EXTERNAL_SETUP, void]
+ NL80211_ATTR_TDLS_INITIATOR nlattr[NL80211_ATTR_TDLS_INITIATOR, void]
NL80211_ATTR_DONT_WAIT_FOR_ACK nlattr[NL80211_ATTR_DONT_WAIT_FOR_ACK, void]
NL80211_ATTR_PROBE_RESP nlattr[NL80211_ATTR_PROBE_RESP, array[int8, 0:2304]]
NL80211_ATTR_DFS_REGION nlattr[NL80211_ATTR_DFS_REGION, int8]
@@ -3059,9 +3055,6 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_AUTH_DATA nlattr[NL80211_ATTR_AUTH_DATA, array[int8]]
NL80211_ATTR_VHT_CAPABILITY nlattr[NL80211_ATTR_VHT_CAPABILITY, array[int8]]
NL80211_ATTR_SCAN_FLAGS nlattr[NL80211_ATTR_SCAN_FLAGS, int32]
- NL80211_ATTR_CHANNEL_WIDTH nlattr[NL80211_ATTR_CHANNEL_WIDTH, int32]
- NL80211_ATTR_CENTER_FREQ1 nlattr[NL80211_ATTR_CENTER_FREQ1, int32]
- NL80211_ATTR_CENTER_FREQ2 nlattr[NL80211_ATTR_CENTER_FREQ2, int32]
NL80211_ATTR_P2P_CTWINDOW nlattr[NL80211_ATTR_P2P_CTWINDOW, int8]
NL80211_ATTR_P2P_OPPPS nlattr[NL80211_ATTR_P2P_OPPPS, int8]
NL80211_ATTR_LOCAL_MESH_POWER_MODE nlattr[NL80211_ATTR_LOCAL_MESH_POWER_MODE, int32]
@@ -3095,9 +3088,7 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_TDLS_PEER_CAPABILITY nlattr[NL80211_ATTR_TDLS_PEER_CAPABILITY, int32]
NL80211_ATTR_SOCKET_OWNER nlattr[NL80211_ATTR_SOCKET_OWNER, void]
NL80211_ATTR_CSA_C_OFFSETS_TX nlattr[NL80211_ATTR_CSA_C_OFFSETS_TX, array[int8]]
- NL80211_ATTR_TDLS_INITIATOR nlattr[NL80211_ATTR_TDLS_INITIATOR, void]
NL80211_ATTR_USE_RRM nlattr[NL80211_ATTR_USE_RRM, void]
- NL80211_ATTR_WIPHY_DYN_ACK nlattr[NL80211_ATTR_WIPHY_DYN_ACK, void]
NL80211_ATTR_TSID nlattr[NL80211_ATTR_TSID, int8]
NL80211_ATTR_USER_PRIO nlattr[NL80211_ATTR_USER_PRIO, int8]
NL80211_ATTR_ADMITTED_TIME nlattr[NL80211_ATTR_ADMITTED_TIME, int16]
@@ -3113,9 +3104,6 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_STA_SUPPORT_P2P_PS nlattr[NL80211_ATTR_STA_SUPPORT_P2P_PS, int8]
NL80211_ATTR_MU_MIMO_GROUP_DATA nlattr[NL80211_ATTR_MU_MIMO_GROUP_DATA, array[int8, 24]]
NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR nlattr[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, array[int8]]
- NL80211_ATTR_MEASUREMENT_DURATION nlattr[NL80211_ATTR_MEASUREMENT_DURATION, int16]
- NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY nlattr[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, void]
- NL80211_ATTR_MESH_PEER_AID nlattr[NL80211_ATTR_MESH_PEER_AID, int16]
NL80211_ATTR_NAN_MASTER_PREF nlattr[NL80211_ATTR_NAN_MASTER_PREF, int8]
NL80211_ATTR_BANDS nlattr[NL80211_ATTR_BANDS, int32]
NL80211_ATTR_NAN_FUNC nlnest[NL80211_ATTR_NAN_FUNC, array[nl_generic_attr]]
@@ -3132,10 +3120,9 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_FILS_ERP_RRK nlattr[NL80211_ATTR_FILS_ERP_RRK, array[int8, 0:64]]
NL80211_ATTR_FILS_CACHE_ID nlattr[NL80211_ATTR_FILS_CACHE_ID, array[int8]]
NL80211_ATTR_PMK nlattr[NL80211_ATTR_PMK, array[int8, 0:64]]
- NL80211_ATTR_SCHED_SCAN_MULTI nlattr[NL80211_ATTR_SCHED_SCAN_MULTI, void]
NL80211_ATTR_PMKR0_NAME nlattr[NL80211_ATTR_PMKR0_NAME, array[int8]]
+ NL80211_ATTR_SCHED_SCAN_MULTI nlattr[NL80211_ATTR_SCHED_SCAN_MULTI, void]
NL80211_ATTR_EXTERNAL_AUTH_SUPPORT nlattr[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, void]
- NL80211_ATTR_CONTROL_PORT_OVER_NL80211 nlattr[NL80211_ATTR_CONTROL_PORT_OVER_NL80211, void]
NL80211_ATTR_TXQ_LIMIT nlattr[NL80211_ATTR_TXQ_LIMIT, int32]
NL80211_ATTR_TXQ_MEMORY_LIMIT nlattr[NL80211_ATTR_TXQ_MEMORY_LIMIT, int32]
NL80211_ATTR_TXQ_QUANTUM nlattr[NL80211_ATTR_TXQ_QUANTUM, int32]
@@ -3144,13 +3131,9 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_TIMEOUT nlattr[NL80211_ATTR_TIMEOUT, int32]
NL80211_ATTR_PEER_MEASUREMENTS nlnest[NL80211_ATTR_PEER_MEASUREMENTS, array[nl80211_pmsr_attr_policy$auto_nl80211]]
NL80211_ATTR_AIRTIME_WEIGHT nlattr[NL80211_ATTR_AIRTIME_WEIGHT, int16]
- NL80211_ATTR_STA_TX_POWER_SETTING nlattr[NL80211_ATTR_STA_TX_POWER_SETTING, int8]
- NL80211_ATTR_STA_TX_POWER nlattr[NL80211_ATTR_STA_TX_POWER, int16]
NL80211_ATTR_SAE_PASSWORD nlattr[NL80211_ATTR_SAE_PASSWORD, array[int8, 0:128]]
NL80211_ATTR_TWT_RESPONDER nlattr[NL80211_ATTR_TWT_RESPONDER, void]
NL80211_ATTR_HE_OBSS_PD nlnest[NL80211_ATTR_HE_OBSS_PD, array[he_obss_pd_policy$auto_nl80211]]
- NL80211_ATTR_WIPHY_EDMG_CHANNELS nlattr[NL80211_ATTR_WIPHY_EDMG_CHANNELS, int8]
- NL80211_ATTR_WIPHY_EDMG_BW_CONFIG nlattr[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG, int8]
NL80211_ATTR_VLAN_ID nlattr[NL80211_ATTR_VLAN_ID, int16]
NL80211_ATTR_HE_BSS_COLOR nlnest[NL80211_ATTR_HE_BSS_COLOR, array[he_bss_color_policy$auto_nl80211]]
NL80211_ATTR_TID_CONFIG nlnest[NL80211_ATTR_TID_CONFIG, array[nlnest[0, array[nl80211_tid_config_attr_policy$auto_nl80211]]]]
@@ -3159,7 +3142,6 @@ nl80211_policy$auto_nl80211 [
NL80211_ATTR_PMK_REAUTH_THRESHOLD nlattr[NL80211_ATTR_PMK_REAUTH_THRESHOLD, int8]
NL80211_ATTR_RECEIVE_MULTICAST nlattr[NL80211_ATTR_RECEIVE_MULTICAST, void]
NL80211_ATTR_WIPHY_FREQ_OFFSET nlattr[NL80211_ATTR_WIPHY_FREQ_OFFSET, int32]
- NL80211_ATTR_CENTER_FREQ1_OFFSET nlattr[NL80211_ATTR_CENTER_FREQ1_OFFSET, int32]
NL80211_ATTR_SCAN_FREQ_KHZ nlnest[NL80211_ATTR_SCAN_FREQ_KHZ, array[nl_generic_attr]]
NL80211_ATTR_HE_6GHZ_CAPABILITY nlattr[NL80211_ATTR_HE_6GHZ_CAPABILITY, array[int8]]
NL80211_ATTR_FILS_DISCOVERY nlnest[NL80211_ATTR_FILS_DISCOVERY, array[nl80211_fils_discovery_policy$auto_nl80211]]
@@ -3234,24 +3216,24 @@ nl802154_policy$auto_nl802154 [
NL802154_ATTR_WPAN_PHY nlattr[NL802154_ATTR_WPAN_PHY, int32]
NL802154_ATTR_WPAN_PHY_NAME nlattr[NL802154_ATTR_WPAN_PHY_NAME, string]
NL802154_ATTR_IFINDEX nlattr[NL802154_ATTR_IFINDEX, ifindex]
- NL802154_ATTR_IFNAME nlattr[NL802154_ATTR_IFNAME, devname]
NL802154_ATTR_IFTYPE nlattr[NL802154_ATTR_IFTYPE, int32]
+ NL802154_ATTR_IFNAME nlattr[NL802154_ATTR_IFNAME, devname]
NL802154_ATTR_WPAN_DEV nlattr[NL802154_ATTR_WPAN_DEV, int64]
NL802154_ATTR_PAGE nlattr[NL802154_ATTR_PAGE, int8]
NL802154_ATTR_CHANNEL nlattr[NL802154_ATTR_CHANNEL, int8]
- NL802154_ATTR_PAN_ID nlattr[NL802154_ATTR_PAN_ID, int16]
- NL802154_ATTR_SHORT_ADDR nlattr[NL802154_ATTR_SHORT_ADDR, int16]
NL802154_ATTR_TX_POWER nlattr[NL802154_ATTR_TX_POWER, int32]
NL802154_ATTR_CCA_MODE nlattr[NL802154_ATTR_CCA_MODE, int32]
NL802154_ATTR_CCA_OPT nlattr[NL802154_ATTR_CCA_OPT, int32]
NL802154_ATTR_CCA_ED_LEVEL nlattr[NL802154_ATTR_CCA_ED_LEVEL, int32]
- NL802154_ATTR_MAX_FRAME_RETRIES nlattr[NL802154_ATTR_MAX_FRAME_RETRIES, int8]
- NL802154_ATTR_MAX_BE nlattr[NL802154_ATTR_MAX_BE, int8]
+ NL802154_ATTR_SUPPORTED_CHANNEL nlattr[NL802154_ATTR_SUPPORTED_CHANNEL, int32]
+ NL802154_ATTR_PAN_ID nlattr[NL802154_ATTR_PAN_ID, int16]
+ NL802154_ATTR_EXTENDED_ADDR nlattr[NL802154_ATTR_EXTENDED_ADDR, int64]
+ NL802154_ATTR_SHORT_ADDR nlattr[NL802154_ATTR_SHORT_ADDR, int16]
NL802154_ATTR_MIN_BE nlattr[NL802154_ATTR_MIN_BE, int8]
+ NL802154_ATTR_MAX_BE nlattr[NL802154_ATTR_MAX_BE, int8]
NL802154_ATTR_MAX_CSMA_BACKOFFS nlattr[NL802154_ATTR_MAX_CSMA_BACKOFFS, int8]
+ NL802154_ATTR_MAX_FRAME_RETRIES nlattr[NL802154_ATTR_MAX_FRAME_RETRIES, int8]
NL802154_ATTR_LBT_MODE nlattr[NL802154_ATTR_LBT_MODE, int8]
- NL802154_ATTR_SUPPORTED_CHANNEL nlattr[NL802154_ATTR_SUPPORTED_CHANNEL, int32]
- NL802154_ATTR_EXTENDED_ADDR nlattr[NL802154_ATTR_EXTENDED_ADDR, int64]
NL802154_ATTR_WPAN_PHY_CAPS nlnest[NL802154_ATTR_WPAN_PHY_CAPS, array[nl_generic_attr]]
NL802154_ATTR_SUPPORTED_COMMANDS nlnest[NL802154_ATTR_SUPPORTED_COMMANDS, array[nl_generic_attr]]
NL802154_ATTR_ACKREQ_DEFAULT nlattr[NL802154_ATTR_ACKREQ_DEFAULT, int8]
@@ -3544,11 +3526,7 @@ sigaction$auto_record {
sa_handler ptr[inout, ptr[in, auto_todo]]
sa_flags intptr
sa_restorer ptr[inout, ptr[in, auto_todo]]
- sa_mask sigaction_sa_mask$auto_record
-}
-
-sigaction_sa_mask$auto_record {
- sig intptr
+ sa_mask sigset_t$auto_record
}
sigaltstack$auto_record {
@@ -3619,12 +3597,8 @@ sockaddr$auto_record {
sockaddr_1$auto_record [
sa_data_min array[int8, 14]
- sockaddr_1_1 sockaddr_1_1$auto_record
-] [varlen]
-
-sockaddr_1_1$auto_record {
- sa_data array[int8]
-}
+ sockaddr_1_1 void
+]
stat$auto_record {
st_dev intptr
@@ -3655,7 +3629,7 @@ statfs$auto_record {
f_bavail intptr
f_files intptr
f_ffree intptr
- f_fsid statfs_f_fsid$auto_record
+ f_fsid __kernel_fsid_t$auto_record
f_namelen intptr
f_frsize intptr
f_flags intptr
@@ -3670,21 +3644,13 @@ statfs64$auto_record {
f_bavail int64
f_files int64
f_ffree int64
- f_fsid statfs64_f_fsid$auto_record
+ f_fsid __kernel_fsid_t$auto_record
f_namelen intptr
f_frsize intptr
f_flags intptr
f_spare array[intptr, 4]
}
-statfs64_f_fsid$auto_record {
- val array[int32, 2]
-}
-
-statfs_f_fsid$auto_record {
- val array[int32, 2]
-}
-
statmount$auto_record {
size int32
mnt_opts int32
@@ -3780,9 +3746,9 @@ taskstats_cmd_get_policy$auto_taskstats [
tcp_metrics_nl_policy$auto_tcp_metrics [
TCP_METRICS_ATTR_ADDR_IPV4 nlattr[TCP_METRICS_ATTR_ADDR_IPV4, ipv4_addr]
- TCP_METRICS_ATTR_ADDR_IPV6 nlattr[TCP_METRICS_ATTR_ADDR_IPV6, array[int8]]
+ TCP_METRICS_ATTR_ADDR_IPV6 nlattr[TCP_METRICS_ATTR_ADDR_IPV6, ipv6_addr]
TCP_METRICS_ATTR_SADDR_IPV4 nlattr[TCP_METRICS_ATTR_SADDR_IPV4, ipv4_addr]
- TCP_METRICS_ATTR_SADDR_IPV6 nlattr[TCP_METRICS_ATTR_SADDR_IPV6, array[int8]]
+ TCP_METRICS_ATTR_SADDR_IPV6 nlattr[TCP_METRICS_ATTR_SADDR_IPV6, ipv6_addr]
] [varlen]
thermal_genl_policy$auto_thermal_netlink [
@@ -3791,12 +3757,12 @@ thermal_genl_policy$auto_thermal_netlink [
THERMAL_GENL_ATTR_TZ_TEMP nlattr[THERMAL_GENL_ATTR_TZ_TEMP, int32]
THERMAL_GENL_ATTR_TZ_TRIP nlnest[THERMAL_GENL_ATTR_TZ_TRIP, array[nl_generic_attr]]
THERMAL_GENL_ATTR_TZ_TRIP_ID nlattr[THERMAL_GENL_ATTR_TZ_TRIP_ID, int32]
- THERMAL_GENL_ATTR_TZ_TRIP_TYPE nlattr[THERMAL_GENL_ATTR_TZ_TRIP_TYPE, int32]
THERMAL_GENL_ATTR_TZ_TRIP_TEMP nlattr[THERMAL_GENL_ATTR_TZ_TRIP_TEMP, int32]
+ THERMAL_GENL_ATTR_TZ_TRIP_TYPE nlattr[THERMAL_GENL_ATTR_TZ_TRIP_TYPE, int32]
THERMAL_GENL_ATTR_TZ_TRIP_HYST nlattr[THERMAL_GENL_ATTR_TZ_TRIP_HYST, int32]
THERMAL_GENL_ATTR_TZ_MODE nlattr[THERMAL_GENL_ATTR_TZ_MODE, int32]
- THERMAL_GENL_ATTR_TZ_NAME nlattr[THERMAL_GENL_ATTR_TZ_NAME, stringnoz]
THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT nlattr[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT, int32]
+ THERMAL_GENL_ATTR_TZ_NAME nlattr[THERMAL_GENL_ATTR_TZ_NAME, stringnoz]
THERMAL_GENL_ATTR_TZ_GOV nlnest[THERMAL_GENL_ATTR_TZ_GOV, array[nl_generic_attr]]
THERMAL_GENL_ATTR_TZ_GOV_NAME nlattr[THERMAL_GENL_ATTR_TZ_GOV_NAME, stringnoz]
THERMAL_GENL_ATTR_CDEV nlnest[THERMAL_GENL_ATTR_CDEV, array[nl_generic_attr]]
@@ -3885,12 +3851,12 @@ vdpa_nl_policy$auto_vdpa [
] [varlen]
vport_policy$auto_datapath [
+ OVS_VPORT_ATTR_NAME nlattr[OVS_VPORT_ATTR_NAME, string]
+ OVS_VPORT_ATTR_STATS nlattr[OVS_VPORT_ATTR_STATS, ovs_vport_stats$auto_record]
OVS_VPORT_ATTR_PORT_NO nlattr[OVS_VPORT_ATTR_PORT_NO, int32]
OVS_VPORT_ATTR_TYPE nlattr[OVS_VPORT_ATTR_TYPE, int32]
- OVS_VPORT_ATTR_NAME nlattr[OVS_VPORT_ATTR_NAME, string]
- OVS_VPORT_ATTR_OPTIONS nlnest[OVS_VPORT_ATTR_OPTIONS, array[nl_generic_attr]]
OVS_VPORT_ATTR_UPCALL_PID nlattr[OVS_VPORT_ATTR_UPCALL_PID, array[int8]]
- OVS_VPORT_ATTR_STATS nlattr[OVS_VPORT_ATTR_STATS, ovs_vport_stats$auto_record]
+ OVS_VPORT_ATTR_OPTIONS nlnest[OVS_VPORT_ATTR_OPTIONS, array[nl_generic_attr]]
OVS_VPORT_ATTR_IFINDEX nlattr[OVS_VPORT_ATTR_IFINDEX, ifindex]
OVS_VPORT_ATTR_NETNSID nlattr[OVS_VPORT_ATTR_NETNSID, int32]
OVS_VPORT_ATTR_UPCALL_STATS nlnest[OVS_VPORT_ATTR_UPCALL_STATS, array[nl_generic_attr]]
@@ -3901,3 +3867,297 @@ xattr_args$auto_record {
size int32
flags int32
}
+
+define HWSIM_ATTR_ADDR_RECEIVER 1
+define HWSIM_ATTR_ADDR_TRANSMITTER 2
+define HWSIM_ATTR_CHANNELS 9
+define HWSIM_ATTR_CIPHER_SUPPORT 24
+define HWSIM_ATTR_COOKIE 8
+define HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE 16
+define HWSIM_ATTR_FLAGS 4
+define HWSIM_ATTR_FRAME 3
+define HWSIM_ATTR_FREQ 19
+define HWSIM_ATTR_IFTYPE_SUPPORT 23
+define HWSIM_ATTR_MLO_SUPPORT 25
+define HWSIM_ATTR_MULTI_RADIO 29
+define HWSIM_ATTR_NO_VIF 18
+define HWSIM_ATTR_PERM_ADDR 22
+define HWSIM_ATTR_PMSR_RESULT 28
+define HWSIM_ATTR_PMSR_SUPPORT 26
+define HWSIM_ATTR_RADIO_ID 10
+define HWSIM_ATTR_RADIO_NAME 17
+define HWSIM_ATTR_REG_CUSTOM_REG 12
+define HWSIM_ATTR_REG_HINT_ALPHA2 11
+define HWSIM_ATTR_REG_STRICT_REG 13
+define HWSIM_ATTR_RX_RATE 5
+define HWSIM_ATTR_SIGNAL 6
+define HWSIM_ATTR_SUPPORT_P2P_DEVICE 14
+define HWSIM_ATTR_TX_INFO 7
+define HWSIM_ATTR_TX_INFO_FLAGS 21
+define HWSIM_ATTR_USE_CHANCTX 15
+define HWSIM_CMD_DEL_RADIO 5
+define HWSIM_CMD_FRAME 2
+define HWSIM_CMD_GET_RADIO 6
+define HWSIM_CMD_NEW_RADIO 4
+define HWSIM_CMD_REGISTER 1
+define HWSIM_CMD_REPORT_PMSR 11
+define HWSIM_CMD_TX_INFO_FRAME 3
+define HWSIM_RATE_INFO_ATTR_BW 5
+define HWSIM_RATE_INFO_ATTR_EHT_GI 10
+define HWSIM_RATE_INFO_ATTR_EHT_RU_ALLOC 11
+define HWSIM_RATE_INFO_ATTR_FLAGS 1
+define HWSIM_RATE_INFO_ATTR_HE_DCM 7
+define HWSIM_RATE_INFO_ATTR_HE_GI 6
+define HWSIM_RATE_INFO_ATTR_HE_RU_ALLOC 8
+define HWSIM_RATE_INFO_ATTR_LEGACY 3
+define HWSIM_RATE_INFO_ATTR_MCS 2
+define HWSIM_RATE_INFO_ATTR_NSS 4
+define HWSIM_RATE_INFO_ATTR_N_BOUNDED_CH 9
+define IEEE802154_ADD_IFACE 33
+define IEEE802154_ASSOCIATE_REQ 1
+define IEEE802154_ASSOCIATE_RESP 19
+define IEEE802154_ATTR_BAT_EXT 26
+define IEEE802154_ATTR_BCN_ORD 23
+define IEEE802154_ATTR_CAPABILITY 17
+define IEEE802154_ATTR_CCA_ED_LEVEL 36
+define IEEE802154_ATTR_CCA_MODE 35
+define IEEE802154_ATTR_CHANNEL 7
+define IEEE802154_ATTR_CHANNELS 20
+define IEEE802154_ATTR_CHANNEL_PAGE_LIST 30
+define IEEE802154_ATTR_COORD_HW_ADDR 9
+define IEEE802154_ATTR_COORD_PAN_ID 10
+define IEEE802154_ATTR_COORD_REALIGN 27
+define IEEE802154_ATTR_COORD_SHORT_ADDR 8
+define IEEE802154_ATTR_CSMA_MAX_BE 39
+define IEEE802154_ATTR_CSMA_MIN_BE 38
+define IEEE802154_ATTR_CSMA_RETRIES 37
+define IEEE802154_ATTR_DEST_HW_ADDR 15
+define IEEE802154_ATTR_DEST_PAN_ID 16
+define IEEE802154_ATTR_DEST_SHORT_ADDR 14
+define IEEE802154_ATTR_DEV_INDEX 2
+define IEEE802154_ATTR_DEV_NAME 1
+define IEEE802154_ATTR_DEV_TYPE 32
+define IEEE802154_ATTR_DURATION 21
+define IEEE802154_ATTR_ED_LIST 22
+define IEEE802154_ATTR_FRAME_RETRIES 40
+define IEEE802154_ATTR_HW_ADDR 5
+define IEEE802154_ATTR_LBT_ENABLED 34
+define IEEE802154_ATTR_LLSEC_CMD_FRAME_ID 52
+define IEEE802154_ATTR_LLSEC_DEV_KEY_MODE 55
+define IEEE802154_ATTR_LLSEC_DEV_OVERRIDE 54
+define IEEE802154_ATTR_LLSEC_ENABLED 41
+define IEEE802154_ATTR_LLSEC_FRAME_COUNTER 47
+define IEEE802154_ATTR_LLSEC_FRAME_TYPE 51
+define IEEE802154_ATTR_LLSEC_KEY_BYTES 48
+define IEEE802154_ATTR_LLSEC_KEY_ID 46
+define IEEE802154_ATTR_LLSEC_KEY_MODE 43
+define IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED 45
+define IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT 44
+define IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS 50
+define IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES 49
+define IEEE802154_ATTR_LLSEC_SECLEVEL 42
+define IEEE802154_ATTR_LLSEC_SECLEVELS 53
+define IEEE802154_ATTR_PAGE 29
+define IEEE802154_ATTR_PAN_COORD 25
+define IEEE802154_ATTR_PAN_ID 6
+define IEEE802154_ATTR_PHY_NAME 31
+define IEEE802154_ATTR_REASON 18
+define IEEE802154_ATTR_SCAN_TYPE 19
+define IEEE802154_ATTR_SF_ORD 24
+define IEEE802154_ATTR_SHORT_ADDR 4
+define IEEE802154_ATTR_SRC_HW_ADDR 12
+define IEEE802154_ATTR_SRC_PAN_ID 13
+define IEEE802154_ATTR_SRC_SHORT_ADDR 11
+define IEEE802154_ATTR_STATUS 3
+define IEEE802154_ATTR_TXPOWER 33
+define IEEE802154_DEL_IFACE 34
+define IEEE802154_DISASSOCIATE_REQ 3
+define IEEE802154_LIST_IFACE 31
+define IEEE802154_LIST_PHY 32
+define IEEE802154_LLSEC_ADD_DEV 42
+define IEEE802154_LLSEC_ADD_DEVKEY 45
+define IEEE802154_LLSEC_ADD_KEY 39
+define IEEE802154_LLSEC_ADD_SECLEVEL 48
+define IEEE802154_LLSEC_DEL_DEV 43
+define IEEE802154_LLSEC_DEL_DEVKEY 46
+define IEEE802154_LLSEC_DEL_KEY 40
+define IEEE802154_LLSEC_DEL_SECLEVEL 49
+define IEEE802154_LLSEC_GETPARAMS 36
+define IEEE802154_LLSEC_LIST_DEV 41
+define IEEE802154_LLSEC_LIST_DEVKEY 44
+define IEEE802154_LLSEC_LIST_KEY 38
+define IEEE802154_LLSEC_LIST_SECLEVEL 47
+define IEEE802154_LLSEC_SETPARAMS 37
+define IEEE802154_SCAN_REQ 9
+define IEEE802154_SET_MACPARAMS 35
+define IEEE802154_START_REQ 13
+define MAC802154_HWSIM_ATTR_RADIO_EDGE 2
+define MAC802154_HWSIM_ATTR_RADIO_EDGES 3
+define MAC802154_HWSIM_ATTR_RADIO_ID 1
+define MAC802154_HWSIM_CMD_DEL_EDGE 7
+define MAC802154_HWSIM_CMD_DEL_RADIO 4
+define MAC802154_HWSIM_CMD_GET_RADIO 1
+define MAC802154_HWSIM_CMD_NEW_EDGE 8
+define MAC802154_HWSIM_CMD_NEW_RADIO 3
+define MAC802154_HWSIM_CMD_SET_EDGE 6
+define MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID 1
+define MAC802154_HWSIM_EDGE_ATTR_LQI 2
+define NL802154_ATTR_ACKREQ_DEFAULT 26
+define NL802154_ATTR_BEACON_INTERVAL 38
+define NL802154_ATTR_CCA_ED_LEVEL 14
+define NL802154_ATTR_CCA_MODE 12
+define NL802154_ATTR_CCA_OPT 13
+define NL802154_ATTR_CHANNEL 8
+define NL802154_ATTR_COORDINATOR 30
+define NL802154_ATTR_EXTENDED_ADDR 23
+define NL802154_ATTR_IFINDEX 3
+define NL802154_ATTR_IFNAME 4
+define NL802154_ATTR_IFTYPE 5
+define NL802154_ATTR_LBT_MODE 19
+define NL802154_ATTR_MAX_ASSOCIATIONS 39
+define NL802154_ATTR_MAX_BE 16
+define NL802154_ATTR_MAX_CSMA_BACKOFFS 18
+define NL802154_ATTR_MAX_FRAME_RETRIES 15
+define NL802154_ATTR_MIN_BE 17
+define NL802154_ATTR_NETNS_FD 29
+define NL802154_ATTR_PAGE 7
+define NL802154_ATTR_PAN_ID 9
+define NL802154_ATTR_PEER 40
+define NL802154_ATTR_PID 28
+define NL802154_ATTR_SCAN_CHANNELS 33
+define NL802154_ATTR_SCAN_DONE_REASON 37
+define NL802154_ATTR_SCAN_DURATION 36
+define NL802154_ATTR_SCAN_MEAN_PRF 35
+define NL802154_ATTR_SCAN_PREAMBLE_CODES 34
+define NL802154_ATTR_SCAN_TYPE 31
+define NL802154_ATTR_SEC_DEVICE 46
+define NL802154_ATTR_SEC_DEVKEY 47
+define NL802154_ATTR_SEC_ENABLED 41
+define NL802154_ATTR_SEC_FRAME_COUNTER 44
+define NL802154_ATTR_SEC_KEY 48
+define NL802154_ATTR_SEC_LEVEL 45
+define NL802154_ATTR_SEC_OUT_KEY_ID 43
+define NL802154_ATTR_SEC_OUT_LEVEL 42
+define NL802154_ATTR_SHORT_ADDR 10
+define NL802154_ATTR_SUPPORTED_CHANNEL 22
+define NL802154_ATTR_SUPPORTED_COMMANDS 25
+define NL802154_ATTR_TX_POWER 11
+define NL802154_ATTR_WPAN_DEV 6
+define NL802154_ATTR_WPAN_PHY 1
+define NL802154_ATTR_WPAN_PHY_CAPS 24
+define NL802154_ATTR_WPAN_PHY_NAME 2
+define NL802154_CMD_ABORT_SCAN 36
+define NL802154_CMD_ASSOCIATE 40
+define NL802154_CMD_DEL_INTERFACE 8
+define NL802154_CMD_DEL_SEC_DEV 27
+define NL802154_CMD_DEL_SEC_DEVKEY 30
+define NL802154_CMD_DEL_SEC_KEY 24
+define NL802154_CMD_DEL_SEC_LEVEL 33
+define NL802154_CMD_DISASSOCIATE 41
+define NL802154_CMD_GET_INTERFACE 5
+define NL802154_CMD_GET_SEC_DEV 25
+define NL802154_CMD_GET_SEC_DEVKEY 28
+define NL802154_CMD_GET_SEC_KEY 22
+define NL802154_CMD_GET_SEC_LEVEL 31
+define NL802154_CMD_GET_WPAN_PHY 1
+define NL802154_CMD_LIST_ASSOCIATIONS 43
+define NL802154_CMD_NEW_INTERFACE 7
+define NL802154_CMD_NEW_SEC_DEV 26
+define NL802154_CMD_NEW_SEC_DEVKEY 29
+define NL802154_CMD_NEW_SEC_KEY 23
+define NL802154_CMD_NEW_SEC_LEVEL 32
+define NL802154_CMD_SEND_BEACONS 38
+define NL802154_CMD_SET_ACKREQ_DEFAULT 19
+define NL802154_CMD_SET_BACKOFF_EXPONENT 16
+define NL802154_CMD_SET_CCA_ED_LEVEL 14
+define NL802154_CMD_SET_CCA_MODE 13
+define NL802154_CMD_SET_CHANNEL 9
+define NL802154_CMD_SET_LBT_MODE 18
+define NL802154_CMD_SET_MAX_ASSOCIATIONS 42
+define NL802154_CMD_SET_MAX_CSMA_BACKOFFS 17
+define NL802154_CMD_SET_MAX_FRAME_RETRIES 15
+define NL802154_CMD_SET_PAN_ID 10
+define NL802154_CMD_SET_SEC_PARAMS 21
+define NL802154_CMD_SET_SHORT_ADDR 11
+define NL802154_CMD_SET_TX_POWER 12
+define NL802154_CMD_SET_WPAN_PHY_NETNS 20
+define NL802154_CMD_STOP_BEACONS 39
+define NL802154_CMD_TRIGGER_SCAN 35
+define NL802154_DEVKEY_ATTR_EXTENDED_ADDR 2
+define NL802154_DEVKEY_ATTR_FRAME_COUNTER 1
+define NL802154_DEVKEY_ATTR_ID 3
+define NL802154_DEV_ADDR_ATTR_EXTENDED 4
+define NL802154_DEV_ADDR_ATTR_MODE 2
+define NL802154_DEV_ADDR_ATTR_PAN_ID 1
+define NL802154_DEV_ADDR_ATTR_SHORT 3
+define NL802154_DEV_ATTR_EXTENDED_ADDR 4
+define NL802154_DEV_ATTR_FRAME_COUNTER 1
+define NL802154_DEV_ATTR_KEY_MODE 6
+define NL802154_DEV_ATTR_PAN_ID 2
+define NL802154_DEV_ATTR_SECLEVEL_EXEMPT 5
+define NL802154_DEV_ATTR_SHORT_ADDR 3
+define NL802154_KEY_ATTR_BYTES 4
+define NL802154_KEY_ATTR_ID 1
+define NL802154_KEY_ATTR_USAGE_CMDS 3
+define NL802154_KEY_ATTR_USAGE_FRAMES 2
+define NL802154_KEY_ID_ATTR_IMPLICIT 3
+define NL802154_KEY_ID_ATTR_INDEX 2
+define NL802154_KEY_ID_ATTR_MODE 1
+define NL802154_KEY_ID_ATTR_SOURCE_EXTENDED 5
+define NL802154_KEY_ID_ATTR_SOURCE_SHORT 4
+define NL802154_SECLEVEL_ATTR_CMD_FRAME 3
+define NL802154_SECLEVEL_ATTR_DEV_OVERRIDE 4
+define NL802154_SECLEVEL_ATTR_FRAME 2
+define NL802154_SECLEVEL_ATTR_LEVELS 1
+define NLBL_CALIPSO_A_DOI 1
+define NLBL_CALIPSO_A_MTYPE 2
+define NLBL_CALIPSO_C_ADD 1
+define NLBL_CALIPSO_C_LIST 3
+define NLBL_CALIPSO_C_LISTALL 4
+define NLBL_CALIPSO_C_REMOVE 2
+define NLBL_CIPSOV4_A_DOI 1
+define NLBL_CIPSOV4_A_MLSCAT 11
+define NLBL_CIPSOV4_A_MLSCATLOC 9
+define NLBL_CIPSOV4_A_MLSCATLST 12
+define NLBL_CIPSOV4_A_MLSCATREM 10
+define NLBL_CIPSOV4_A_MLSLVL 7
+define NLBL_CIPSOV4_A_MLSLVLLOC 5
+define NLBL_CIPSOV4_A_MLSLVLLST 8
+define NLBL_CIPSOV4_A_MLSLVLREM 6
+define NLBL_CIPSOV4_A_MTYPE 2
+define NLBL_CIPSOV4_A_TAG 3
+define NLBL_CIPSOV4_A_TAGLST 4
+define NLBL_CIPSOV4_C_ADD 1
+define NLBL_CIPSOV4_C_LIST 3
+define NLBL_CIPSOV4_C_LISTALL 4
+define NLBL_CIPSOV4_C_REMOVE 2
+define NLBL_MGMT_A_CLPDOI 12
+define NLBL_MGMT_A_CV4DOI 4
+define NLBL_MGMT_A_DOMAIN 1
+define NLBL_MGMT_A_FAMILY 11
+define NLBL_MGMT_A_PROTOCOL 2
+define NLBL_MGMT_A_VERSION 3
+define NLBL_MGMT_C_ADD 1
+define NLBL_MGMT_C_ADDDEF 4
+define NLBL_MGMT_C_LISTALL 3
+define NLBL_MGMT_C_LISTDEF 6
+define NLBL_MGMT_C_PROTOCOLS 7
+define NLBL_MGMT_C_REMOVE 2
+define NLBL_MGMT_C_REMOVEDEF 5
+define NLBL_MGMT_C_VERSION 8
+define NLBL_UNLABEL_A_ACPTFLG 1
+define NLBL_UNLABEL_A_IFACE 6
+define NLBL_UNLABEL_A_IPV4ADDR 4
+define NLBL_UNLABEL_A_IPV4MASK 5
+define NLBL_UNLABEL_A_IPV6ADDR 2
+define NLBL_UNLABEL_A_IPV6MASK 3
+define NLBL_UNLABEL_A_SECCTX 7
+define NLBL_UNLABEL_C_ACCEPT 1
+define NLBL_UNLABEL_C_LIST 2
+define NLBL_UNLABEL_C_STATICADD 3
+define NLBL_UNLABEL_C_STATICADDDEF 6
+define NLBL_UNLABEL_C_STATICLIST 5
+define NLBL_UNLABEL_C_STATICLISTDEF 8
+define NLBL_UNLABEL_C_STATICREMOVE 4
+define NLBL_UNLABEL_C_STATICREMOVEDEF 7
+define QCA_WLAN_VENDOR_ATTR_MAX 8
diff --git a/sys/linux/auto.txt.const b/sys/linux/auto.txt.const
index 84b04aab3..8fc43268a 100644
--- a/sys/linux/auto.txt.const
+++ b/sys/linux/auto.txt.const
@@ -526,6 +526,8 @@ MAC802154_HWSIM_CMD_GET_RADIO = 1
MAC802154_HWSIM_CMD_NEW_EDGE = 8
MAC802154_HWSIM_CMD_NEW_RADIO = 3
MAC802154_HWSIM_CMD_SET_EDGE = 6
+MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID = 1
+MAC802154_HWSIM_EDGE_ATTR_LQI = 2
MACSEC_ATTR_IFINDEX = 1
MACSEC_ATTR_OFFLOAD = 9
MACSEC_ATTR_RXSC_CONFIG = 2
@@ -576,7 +578,6 @@ NETDEV_A_DMABUF_QUEUES = 2
NETDEV_A_NAPI_DEFER_HARD_IRQS = 5
NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT = 6
NETDEV_A_NAPI_ID = 2
-NETDEV_A_NAPI_IFINDEX = 1
NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT = 7
NETDEV_A_PAGE_POOL_ID = 1
NETDEV_A_QSTATS_IFINDEX = 1
@@ -1169,6 +1170,8 @@ NL802154_ATTR_PID = 28
NL802154_ATTR_SCAN_CHANNELS = 33
NL802154_ATTR_SCAN_DONE_REASON = 37
NL802154_ATTR_SCAN_DURATION = 36
+NL802154_ATTR_SCAN_MEAN_PRF = 35
+NL802154_ATTR_SCAN_PREAMBLE_CODES = 34
NL802154_ATTR_SCAN_TYPE = 31
NL802154_ATTR_SEC_DEVICE = 46
NL802154_ATTR_SEC_DEVKEY = 47
@@ -1223,6 +1226,32 @@ NL802154_CMD_SET_TX_POWER = 12
NL802154_CMD_SET_WPAN_PHY_NETNS = 20
NL802154_CMD_STOP_BEACONS = 39
NL802154_CMD_TRIGGER_SCAN = 35
+NL802154_DEVKEY_ATTR_EXTENDED_ADDR = 2
+NL802154_DEVKEY_ATTR_FRAME_COUNTER = 1
+NL802154_DEVKEY_ATTR_ID = 3
+NL802154_DEV_ADDR_ATTR_EXTENDED = 4
+NL802154_DEV_ADDR_ATTR_MODE = 2
+NL802154_DEV_ADDR_ATTR_PAN_ID = 1
+NL802154_DEV_ADDR_ATTR_SHORT = 3
+NL802154_DEV_ATTR_EXTENDED_ADDR = 4
+NL802154_DEV_ATTR_FRAME_COUNTER = 1
+NL802154_DEV_ATTR_KEY_MODE = 6
+NL802154_DEV_ATTR_PAN_ID = 2
+NL802154_DEV_ATTR_SECLEVEL_EXEMPT = 5
+NL802154_DEV_ATTR_SHORT_ADDR = 3
+NL802154_KEY_ATTR_BYTES = 4
+NL802154_KEY_ATTR_ID = 1
+NL802154_KEY_ATTR_USAGE_CMDS = 3
+NL802154_KEY_ATTR_USAGE_FRAMES = 2
+NL802154_KEY_ID_ATTR_IMPLICIT = 3
+NL802154_KEY_ID_ATTR_INDEX = 2
+NL802154_KEY_ID_ATTR_MODE = 1
+NL802154_KEY_ID_ATTR_SOURCE_EXTENDED = 5
+NL802154_KEY_ID_ATTR_SOURCE_SHORT = 4
+NL802154_SECLEVEL_ATTR_CMD_FRAME = 3
+NL802154_SECLEVEL_ATTR_DEV_OVERRIDE = 4
+NL802154_SECLEVEL_ATTR_FRAME = 2
+NL802154_SECLEVEL_ATTR_LEVELS = 1
NLBL_CALIPSO_A_DOI = 1
NLBL_CALIPSO_A_MTYPE = 2
NLBL_CALIPSO_C_ADD = 1
@@ -1330,6 +1359,7 @@ OVS_VPORT_CMD_DEL = 2
OVS_VPORT_CMD_GET = 3
OVS_VPORT_CMD_NEW = 1
OVS_VPORT_CMD_SET = 4
+QCA_WLAN_VENDOR_ATTR_MAX = 8
SEG6_ATTR_ALGID = 6
SEG6_ATTR_DST = 1
SEG6_ATTR_DSTLEN = 2
diff --git a/sys/linux/auto.txt.info b/sys/linux/auto.txt.info
index 813f809b3..3ee32e87f 100644
--- a/sys/linux/auto.txt.info
+++ b/sys/linux/auto.txt.info
@@ -76,8 +76,8 @@ NETLINK BATADV_CMD_TP_METER func:batadv_netlink_tp_meter_start access:ns_admin m
NETLINK BATADV_CMD_TP_METER_CANCEL func:batadv_netlink_tp_meter_cancel access:ns_admin manual_desc:true auto_desc:true file:net/batman-adv/netlink.c subsystem:batman
NETLINK CGROUPSTATS_CMD_GET func:cgroupstats_user_cmd access:user manual_desc:false auto_desc:true file:kernel/taskstats.c subsystem:kernel
NETLINK CIFS_GENL_CMD_SWN_NOTIFY func:cifs_swn_notify access:user manual_desc:false auto_desc:true file:fs/smb/client/netlink.c subsystem:cifs
-NETLINK CTRL_CMD_GETFAMILY func:ctrl_dumpfamily access:user manual_desc:false auto_desc:true file:net/netlink/genetlink.c subsystem:net
-NETLINK CTRL_CMD_GETPOLICY func:ctrl_dumppolicy_done access:user manual_desc:false auto_desc:true file:net/netlink/genetlink.c subsystem:net
+NETLINK CTRL_CMD_GETFAMILY func:ctrl_getfamily access:user manual_desc:false auto_desc:true file:net/netlink/genetlink.c subsystem:net
+NETLINK CTRL_CMD_GETPOLICY func:ctrl_dumppolicy_start access:user manual_desc:false auto_desc:true file:net/netlink/genetlink.c subsystem:net
NETLINK ETHTOOL_MSG_CABLE_TEST_ACT func:ethnl_act_cable_test access:ns_admin manual_desc:false auto_desc:true file:net/ethtool/netlink.c subsystem:net
NETLINK ETHTOOL_MSG_CABLE_TEST_TDR_ACT func:ethnl_act_cable_test_tdr access:ns_admin manual_desc:false auto_desc:true file:net/ethtool/netlink.c subsystem:net
NETLINK ETHTOOL_MSG_CHANNELS_GET func:ethnl_default_doit access:user manual_desc:true auto_desc:true file:net/ethtool/netlink.c subsystem:net
@@ -225,21 +225,21 @@ NETLINK NCSI_CMD_SET_INTERFACE func:ncsi_set_interface_nl access:admin manual_de
NETLINK NCSI_CMD_SET_PACKAGE_MASK func:ncsi_set_package_mask_nl access:admin manual_desc:false auto_desc:true file:net/ncsi/ncsi-netlink.c subsystem:net
NETLINK NETDEV_CMD_BIND_RX func:netdev_nl_bind_rx_doit access:admin manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
NETLINK NETDEV_CMD_DEV_GET func:netdev_nl_dev_get_doit access:user manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
-NETLINK NETDEV_CMD_NAPI_GET func:netdev_nl_napi_get_dumpit access:user manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
+NETLINK NETDEV_CMD_NAPI_GET func:netdev_nl_napi_get_doit access:user manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
NETLINK NETDEV_CMD_NAPI_SET func:netdev_nl_napi_set_doit access:admin manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
NETLINK NETDEV_CMD_PAGE_POOL_GET func:netdev_nl_page_pool_get_doit access:user manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
NETLINK NETDEV_CMD_QSTATS_GET func:netdev_nl_qstats_get_dumpit access:user manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
-NETLINK NETDEV_CMD_QUEUE_GET func:netdev_nl_queue_get_dumpit access:user manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
+NETLINK NETDEV_CMD_QUEUE_GET func:netdev_nl_queue_get_doit access:user manual_desc:false auto_desc:true file:net/core/netdev-genl-gen.c subsystem:net
NETLINK NET_DM_CMD_CONFIG func:net_dm_cmd_config access:admin manual_desc:false auto_desc:true file:net/core/drop_monitor.c subsystem:net
NETLINK NET_DM_CMD_CONFIG_GET func:net_dm_cmd_config_get access:user manual_desc:false auto_desc:true file:net/core/drop_monitor.c subsystem:net
NETLINK NET_DM_CMD_START func:net_dm_cmd_trace access:admin manual_desc:true auto_desc:true file:net/core/drop_monitor.c subsystem:net
NETLINK NET_DM_CMD_STATS_GET func:net_dm_cmd_stats_get access:user manual_desc:false auto_desc:true file:net/core/drop_monitor.c subsystem:net
NETLINK NET_DM_CMD_STOP func:net_dm_cmd_trace access:admin manual_desc:true auto_desc:true file:net/core/drop_monitor.c subsystem:net
-NETLINK NET_SHAPER_CMD_CAP_GET func:net_shaper_nl_cap_post_dumpit access:user manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
-NETLINK NET_SHAPER_CMD_DELETE func:net_shaper_nl_post_doit access:admin manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
-NETLINK NET_SHAPER_CMD_GET func:net_shaper_nl_post_dumpit access:user manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
-NETLINK NET_SHAPER_CMD_GROUP func:net_shaper_nl_post_doit access:admin manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
-NETLINK NET_SHAPER_CMD_SET func:net_shaper_nl_post_doit access:admin manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
+NETLINK NET_SHAPER_CMD_CAP_GET func:net_shaper_nl_cap_pre_doit access:user manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
+NETLINK NET_SHAPER_CMD_DELETE func:net_shaper_nl_pre_doit access:admin manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
+NETLINK NET_SHAPER_CMD_GET func:net_shaper_nl_pre_doit access:user manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
+NETLINK NET_SHAPER_CMD_GROUP func:net_shaper_nl_pre_doit access:admin manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
+NETLINK NET_SHAPER_CMD_SET func:net_shaper_nl_pre_doit access:admin manual_desc:false auto_desc:true file:net/shaper/shaper_nl_gen.c subsystem:net
NETLINK NFC_CMD_ACTIVATE_TARGET func:nfc_genl_activate_target access:admin manual_desc:true auto_desc:true file:net/nfc/netlink.c subsystem:net subsystem:nfc
NETLINK NFC_CMD_DEACTIVATE_TARGET func:nfc_genl_deactivate_target access:admin manual_desc:true auto_desc:true file:net/nfc/netlink.c subsystem:net subsystem:nfc
NETLINK NFC_CMD_DEP_LINK_DOWN func:nfc_genl_dep_link_down access:admin manual_desc:true auto_desc:true file:net/nfc/netlink.c subsystem:net subsystem:nfc
diff --git a/tools/syz-declextract/README.md b/tools/syz-declextract/README.md
index fefc157a4..cab397192 100644
--- a/tools/syz-declextract/README.md
+++ b/tools/syz-declextract/README.md
@@ -17,8 +17,8 @@ make CC=clang -j`nproc` # kernel has to be built at least once for the script to
LLVM=$PWD/llvm-project
git clone https://github.com/llvm/llvm-project.git $LLVM
cd $LLVM
-git checkout 0f231567719c99caa99164d8f91bad50883dab03 # In case of any breaking changes, this commit works
-echo 'add_clang_executable(syz-declextract syz-declextract/syz-declextract.cpp)
+git checkout 3a31427224d4fa49d7ef737b21f6027dc4928ecf # In case of any breaking changes, this commit works
+echo 'add_clang_executable(syz-declextract syz-declextract/declextract.cpp)
target_link_libraries(syz-declextract PRIVATE clangTooling)' >> $LLVM/clang/CMakeLists.txt
```
@@ -26,12 +26,12 @@ target_link_libraries(syz-declextract PRIVATE clangTooling)' >> $LLVM/clang/CMak
```
mkdir $LLVM/clang/syz-declextract
```
-Download `syz-declextract.cpp` file and add it to `$LLVM/clang/syz-declextract` directory
+Copy `tools/syz-declextract/clangtool/*.{cpp,h}` files to `$LLVM/clang/syz-declextract/` directory.
```
LLVM_BUILD=$PWD/syz
mkdir $LLVM_BUILD && cd $LLVM_BUILD
-cmake -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
--DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ $LLVM/llvm
+cmake -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=On \
+-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -GNinja $LLVM/llvm
make -j`nproc` syz-declextract
```
@@ -42,6 +42,6 @@ make -j`nproc` syz-declextract
## Running on the whole kernel
```
-go run tools/syz-declextract/run.go -binary=$LLVM_BUILD/bin/syz-declextract -sourcedir=$KERNEL
+go run tools/syz-declextract -binary=$LLVM_BUILD/bin/syz-declextract -config=manager.cfg
syz-env make extract SOURCEDIR=$KERNEL
```
diff --git a/tools/syz-declextract/.clang-format b/tools/syz-declextract/clangtool/.clang-format
index 5d1fbe84f..15868add4 100644
--- a/tools/syz-declextract/.clang-format
+++ b/tools/syz-declextract/clangtool/.clang-format
@@ -1,3 +1,5 @@
BasedOnStyle: LLVM
ColumnLimit: 120
+DerivePointerAlignment: false
+PointerAlignment: Left
CommentPragmas: '^[^ ]'
diff --git a/tools/syz-declextract/clangtool/declextract.cpp b/tools/syz-declextract/clangtool/declextract.cpp
new file mode 100644
index 000000000..8e8632c12
--- /dev/null
+++ b/tools/syz-declextract/clangtool/declextract.cpp
@@ -0,0 +1,545 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#include "json.h"
+#include "output.h"
+
+#include "clang/AST/APValue.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Attrs.inc"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TypeTraits.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <filesystem>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+class Extractor : public MatchFinder {
+public:
+ Extractor() {
+ match(&Extractor::matchSyscall,
+ functionDecl(isExpandedFromMacro("SYSCALL_DEFINEx"), matchesName("__do_sys_.*")).bind("syscall"));
+
+ match(&Extractor::matchIouring,
+ translationUnitDecl(forEachDescendant(
+ varDecl(hasType(constantArrayType(hasElementType(hasDeclaration(recordDecl(hasName("io_issue_def")))))),
+ isDefinition())
+ .bind("io_issue_defs"))));
+
+ match(&Extractor::matchNetlinkPolicy,
+ translationUnitDecl(forEachDescendant(
+ varDecl(hasType(constantArrayType(hasElementType(hasDeclaration(recordDecl(hasName("nla_policy")))))),
+ isDefinition())
+ .bind("netlink_policy"))));
+
+ match(&Extractor::matchNetlinkFamily, varDecl(hasType(recordDecl(hasName("genl_family")).bind("genl_family")),
+ has(initListExpr().bind("genl_family_init"))));
+ }
+
+ void print() const { Output.print(); }
+
+private:
+ using MatchFunc = void (Extractor::*)();
+ // Thunk that redirects MatchCallback::run method to one of the methods of the Extractor class.
+ struct MatchCallbackThunk : MatchFinder::MatchCallback {
+ Extractor& Ex;
+ MatchFunc Action;
+ MatchCallbackThunk(Extractor& Ex, MatchFunc Action) : Ex(Ex), Action(Action) {}
+ void run(const MatchFinder::MatchResult& Result) override { Ex.run(Result, Action); }
+ };
+ std::vector<std::unique_ptr<MatchCallbackThunk>> Matchers;
+
+ // These set to point to the Result of the current match (to avoid passing them through all methods).
+ const BoundNodes* Nodes = nullptr;
+ ASTContext* Context = nullptr;
+ SourceManager* SourceManager = nullptr;
+
+ Output Output;
+ std::unordered_map<std::string, bool> EnumDedup;
+ std::unordered_map<std::string, bool> StructDedup;
+
+ void matchSyscall();
+ void matchIouring();
+ void matchNetlinkPolicy();
+ void matchNetlinkFamily();
+ template <typename M> void match(MatchFunc Action, const M& Matcher);
+ void run(const MatchFinder::MatchResult& Result, MatchFunc Action);
+ template <typename T> const T* getResult(StringRef ID) const;
+ FieldType extractRecord(QualType QT, const RecordType* Typ, const std::string& BackupName);
+ std::string extractEnum(const EnumDecl* Decl);
+ void noteConstUse(const std::string& Name, int64_t Val, const SourceRange& Range);
+ std::string getDeclName(const Expr* Expr);
+ const ValueDecl* getValueDecl(const Expr* Expr);
+ std::string getDeclFileID(const Decl* Decl);
+ std::string policyName(const ValueDecl* Decl);
+ std::vector<std::pair<int, std::string>> extractDesignatedInitConsts(const VarDecl& ArrayDecl);
+ FieldType genType(QualType Typ, const std::string& BackupName = "");
+ std::unordered_map<std::string, unsigned> structFieldIndexes(const RecordDecl* Decl);
+ template <typename T = int64_t> T evaluate(const Expr* E);
+ template <typename T, typename Node, typename Condition>
+ std::vector<const T*> findAllMatches(const Node* Expr, const Condition& Cond);
+ template <typename T, typename Node, typename Condition>
+ const T* findFirstMatch(const Node* Expr, const Condition& Cond);
+ std::optional<QualType> getSizeofType(const Expr* E);
+ int sizeofType(const Type* T);
+};
+
+template <typename M> void Extractor::match(MatchFunc Action, const M& Matcher) {
+ Matchers.emplace_back(new MatchCallbackThunk(*this, Action));
+ addMatcher(Matcher, Matchers.back().get());
+}
+
+void Extractor::run(const MatchFinder::MatchResult& Result, MatchFunc Action) {
+ Nodes = &Result.Nodes;
+ Context = Result.Context;
+ SourceManager = Result.SourceManager;
+ (this->*Action)();
+}
+
+template <typename T> const T* Extractor::getResult(StringRef ID) const { return Nodes->getNodeAs<T>(ID); }
+
+// Top function that converts any clang type QT to our output type.
+FieldType Extractor::genType(QualType QT, const std::string& BackupName) {
+ const Type* T = QT.IgnoreParens().getUnqualifiedType().getDesugaredType(*Context).getTypePtr();
+ if (auto* Typ = llvm::dyn_cast<BuiltinType>(T)) {
+ return IntType{.ByteSize = sizeofType(T), .Name = QT.getAsString(), .Base = QualType(T, 0).getAsString()};
+ }
+ if (auto* Typ = llvm::dyn_cast<EnumType>(T)) {
+ return IntType{.ByteSize = sizeofType(T), .Enum = extractEnum(Typ->getDecl())};
+ }
+ if (auto* Typ = llvm::dyn_cast<FunctionProtoType>(T)) {
+ return PtrType{.Elem = TodoType(), .IsConst = true};
+ }
+ if (auto* Typ = llvm::dyn_cast<IncompleteArrayType>(T)) {
+ return ArrType{.Elem = genType(Typ->getElementType(), BackupName)};
+ }
+ if (auto* Typ = llvm::dyn_cast<RecordType>(T)) {
+ return extractRecord(QT, Typ, BackupName);
+ }
+ if (auto* Typ = llvm::dyn_cast<ConstantArrayType>(T)) {
+ int Size = Typ->getSize().getZExtValue();
+ return ArrType{
+ .Elem = genType(Typ->getElementType(), BackupName),
+ .MinSize = Size,
+ .MaxSize = Size,
+ };
+ }
+ if (auto* Typ = llvm::dyn_cast<PointerType>(T)) {
+ FieldType Elem;
+ const QualType& Pointee = Typ->getPointeeType();
+ if (Pointee->isAnyCharacterType())
+ Elem = BufferType{.IsString = true};
+ else if (Pointee->isVoidType())
+ Elem = ArrType{.Elem = TodoType()};
+ else
+ Elem = genType(Pointee, BackupName); // note: it may be an array as well
+ return PtrType{
+ .Elem = std::move(Elem),
+ .IsConst = Pointee.isConstQualified(),
+ };
+ }
+ QT.dump();
+ llvm::report_fatal_error("unhandled type");
+}
+
+FieldType Extractor::extractRecord(QualType QT, const RecordType* Typ, const std::string& BackupName) {
+ auto* Decl = Typ->getDecl()->getDefinition();
+ if (!Decl)
+ return TodoType(); // definition is in a different TU
+ std::string Name = Decl->getDeclName().getAsString();
+ // If it's a typedef of anon struct, we want to use the typedef name:
+ // typedef struct {...} foo_t;
+ if (Name.empty() && QT->isTypedefNameType())
+ Name = QualType(Typ, 0).getAsString();
+ // If no other names, fallback to the parent-struct-based name.
+ if (Name.empty()) {
+ assert(!BackupName.empty());
+ // The BackupName is supposed to be unique.
+ assert(!StructDedup[BackupName]);
+ Name = BackupName;
+ }
+ if (StructDedup[Name])
+ return Name;
+ StructDedup[Name] = true;
+ std::vector<Field> Fields;
+ for (const FieldDecl* F : Decl->fields()) {
+ std::string FieldName = F->getNameAsString();
+ std::string BackupFieldName = Name + "_" + FieldName;
+ bool IsAnonymous = false;
+ if (FieldName.empty()) {
+ BackupFieldName = Name + "_" + std::to_string(F->getFieldIndex());
+ FieldName = BackupFieldName;
+ IsAnonymous = true;
+ }
+ FieldType FieldType = genType(F->getType(), BackupFieldName);
+ int BitWidth = F->isBitField() ? F->getBitWidthValue(*Context) : 0;
+ int CountedBy = F->getType()->isCountAttributedType()
+ ? llvm::dyn_cast<FieldDecl>(
+ F->getType()->getAs<CountAttributedType>()->getCountExpr()->getReferencedDeclOfCallee())
+ ->getFieldIndex()
+ : -1;
+ Fields.push_back(Field{
+ .Name = FieldName,
+ .IsAnonymous = IsAnonymous,
+ .BitWidth = BitWidth,
+ .CountedBy = CountedBy,
+ .Type = std::move(FieldType),
+ });
+ }
+ int Align = 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;
+ else if (llvm::isa<PackedAttr>(A))
+ Packed = true;
+ }
+ }
+ Output.emit(Struct{
+ .Name = Name,
+ .ByteSize = sizeofType(Typ),
+ .IsUnion = Decl->isUnion(),
+ .IsPacked = Packed,
+ .Align = Align,
+ .Fields = std::move(Fields),
+ });
+ return Name;
+}
+
+std::string Extractor::extractEnum(const EnumDecl* Decl) {
+ const std::string& Name = Decl->getNameAsString();
+ if (EnumDedup[Name])
+ return Name;
+ EnumDedup[Name] = true;
+ std::vector<std::string> Values;
+ for (const auto* Enumerator : Decl->enumerators()) {
+ const std::string& Name = Enumerator->getNameAsString();
+ noteConstUse(Name, Enumerator->getInitVal().getExtValue(), Decl->getSourceRange());
+ Values.push_back(Name);
+ }
+ Output.emit(Enum{
+ .Name = Name,
+ .Values = Values,
+ });
+ return Name;
+}
+
+void Extractor::noteConstUse(const std::string& Name, int64_t Val, const SourceRange& Range) {
+ const std::string& Filename = std::filesystem::relative(SourceManager->getFilename(Range.getBegin()).str());
+ // Include only uapi headers. Some ioctl commands defined in internal headers, or even in .c files.
+ // They have high chances of breaking compilation during const extract.
+ // If it's not defined in uapi, emit define with concrete value.
+ // Note: the value may be wrong for other arches.
+ if (Filename.find("/uapi/") != std::string::npos && Filename.back() == 'h') {
+ Output.emit(Include{Filename});
+ return;
+ }
+ Output.emit(Define{
+ .Name = Name,
+ .Value = std::to_string(Val),
+ });
+}
+
+// Returns base part of the source file containing the canonical declaration.
+// If the passed declaration is also a definition, then it will look for a preceeding declaration.
+// This is used to generate unique names for static definitions that may have duplicate names
+// across different TUs. We assume that the base part of the source file is enough
+// to make them unique.
+std::string Extractor::getDeclFileID(const Decl* Decl) {
+ std::string file =
+ std::filesystem::path(SourceManager->getFilename(Decl->getCanonicalDecl()->getSourceRange().getBegin()).str())
+ .filename()
+ .stem()
+ .string();
+ std::replace(file.begin(), file.end(), '-', '_');
+ return file;
+}
+
+template <typename Node> void matchHelper(MatchFinder& Finder, ASTContext* Context, const Node* Expr) {
+ Finder.match(*Expr, *Context);
+}
+
+void matchHelper(MatchFinder& Finder, ASTContext* Context, const ASTContext* Expr) {
+ assert(Context == Expr);
+ Finder.matchAST(*Context);
+}
+
+// Returns all matches of Cond named "res" in Expr and returns them casted to T.
+// Expr can point to Context for a global match.
+template <typename T, typename Node, typename Condition>
+std::vector<const T*> Extractor::findAllMatches(const Node* Expr, const Condition& Cond) {
+ if (!Expr)
+ return {};
+ struct Matcher : MatchFinder::MatchCallback {
+ std::vector<const T*> Matches;
+ void run(const MatchFinder::MatchResult& Result) override {
+ if (const T* M = Result.Nodes.getNodeAs<T>("res"))
+ Matches.push_back(M);
+ }
+ };
+ MatchFinder Finder;
+ Matcher Matcher;
+ Finder.addMatcher(Cond, &Matcher);
+ matchHelper(Finder, Context, Expr);
+ return std::move(Matcher.Matches);
+}
+
+// Returns the first match of Cond named "res" in Expr and returns it casted to T.
+// If no match is found, returns nullptr.
+template <typename T, typename Node, typename Condition>
+const T* Extractor::findFirstMatch(const Node* Expr, const Condition& Cond) {
+ const auto& Matches = findAllMatches<T>(Expr, Cond);
+ return Matches.empty() ? nullptr : Matches[0];
+}
+
+// If expression refers to some identifier, returns the identifier name.
+// Otherwise returns an empty string.
+// For example, if the expression is `function_name`, returns "function_name" string.
+// If AppendFile, then it also appends per-file suffix.
+std::string Extractor::getDeclName(const Expr* Expr) {
+ // The expression can be complex and include casts and e.g. InitListExpr,
+ // to remove all of these we match the first/any DeclRefExpr.
+ auto* Decl = getValueDecl(Expr);
+ return Decl ? Decl->getNameAsString() : "";
+}
+
+// Returns the first ValueDecl in the expression.
+const ValueDecl* Extractor::getValueDecl(const Expr* Expr) {
+ // The expression can be complex and include casts and e.g. InitListExpr,
+ // to remove all of these we match the first/any DeclRefExpr.
+ auto* Decl = findFirstMatch<DeclRefExpr>(Expr, stmt(forEachDescendant(declRefExpr().bind("res"))));
+ return Decl ? Decl->getDecl() : nullptr;
+}
+
+// Recursively finds first sizeof in the expression and return the type passed to sizeof.
+std::optional<QualType> Extractor::getSizeofType(const Expr* E) {
+ auto* Res = findFirstMatch<UnaryExprOrTypeTraitExpr>(
+ E, stmt(forEachDescendant(unaryExprOrTypeTraitExpr(ofKind(UETT_SizeOf)).bind("res"))));
+ if (!Res)
+ return {};
+ if (Res->isArgumentType())
+ return Res->getArgumentType();
+ return Res->getArgumentExpr()->getType();
+}
+
+// Returns map of field name -> field index.
+std::unordered_map<std::string, unsigned> Extractor::structFieldIndexes(const RecordDecl* Decl) {
+ // TODO: this is wrong for structs that contain unions and anonymous sub-structs (e.g. genl_split_ops).
+ // To handle these we would need to look at InitListExpr::getInitializedFieldInUnion, and recurse
+ // into anonymous structs.
+ std::unordered_map<std::string, unsigned> Indexes;
+ for (const auto& F : Decl->fields())
+ Indexes[F->getNameAsString()] = F->getFieldIndex();
+ return Indexes;
+}
+
+// Extracts enum info from array variable designated initialization.
+// For example, for the following code:
+//
+// enum Foo {
+// FooA = 11,
+// FooB = 42,
+// };
+//
+// struct Bar bars[] = {
+// [FooA] = {...},
+// [FooB] = {...},
+// };
+//
+// it returns the following vector: {{11, "FooA"}, {42, "FooB"}}.
+std::vector<std::pair<int, std::string>> Extractor::extractDesignatedInitConsts(const VarDecl& ArrayDecl) {
+ const auto& Matches = findAllMatches<ConstantExpr>(
+ &ArrayDecl,
+ decl(forEachDescendant(designatedInitExpr(optionally(has(constantExpr(has(declRefExpr())).bind("res")))))));
+ std::vector<std::pair<int, std::string>> Inits;
+ for (auto* Match : Matches) {
+ const int64_t Val = *Match->getAPValueResult().getInt().getRawData();
+ const auto& Name = Match->getEnumConstantDecl()->getNameAsString();
+ const auto& SR = Match->getEnumConstantDecl()->getSourceRange();
+ noteConstUse(Name, Val, SR);
+ Inits.emplace_back(Val, Name);
+ }
+ return Inits;
+}
+
+int Extractor::sizeofType(const Type* T) { return static_cast<int>(Context->getTypeInfo(T).Width) / 8; }
+
+template <typename T> T Extractor::evaluate(const Expr* E) {
+ Expr::EvalResult Res;
+ E->EvaluateAsConstantExpr(Res, *Context);
+ return static_cast<T>(Res.Val.getInt().getExtValue());
+}
+
+void Extractor::matchNetlinkPolicy() {
+ const auto* PolicyArray = getResult<VarDecl>("netlink_policy");
+ const auto* Init = llvm::dyn_cast_if_present<InitListExpr>(PolicyArray->getInit());
+ if (!Init)
+ return;
+ const auto& InitConsts = extractDesignatedInitConsts(*PolicyArray);
+ auto Fields = structFieldIndexes(Init->getInit(0)->getType()->getAsRecordDecl());
+ std::vector<NetlinkAttr> Attrs;
+ for (const auto& [I, Name] : InitConsts) {
+ const auto* AttrInit = llvm::dyn_cast<InitListExpr>(Init->getInit(I));
+ const std::string& AttrKind = getDeclName(AttrInit->getInit(Fields["type"]));
+ if (AttrKind == "NLA_REJECT")
+ continue;
+ auto* LenExpr = AttrInit->getInit(Fields["len"]);
+ int MaxSize = 0;
+ std::string NestedPolicy;
+ std::unique_ptr<FieldType> Elem;
+ if (AttrKind == "NLA_NESTED" || AttrKind == "NLA_NESTED_ARRAY") {
+ if (const auto* NestedDecl = getValueDecl(AttrInit->getInit(2)))
+ NestedPolicy = policyName(NestedDecl);
+ } else {
+ MaxSize = evaluate<int>(LenExpr);
+ if (auto SizeofType = getSizeofType(LenExpr))
+ Elem = std::make_unique<FieldType>(genType(*SizeofType));
+ }
+ Attrs.push_back(NetlinkAttr{
+ .Name = Name,
+ .Kind = AttrKind,
+ .MaxSize = MaxSize,
+ .NestedPolicy = NestedPolicy,
+ .Elem = std::move(Elem),
+ });
+ }
+ Output.emit(NetlinkPolicy{
+ .Name = policyName(PolicyArray),
+ .Attrs = std::move(Attrs),
+ });
+}
+
+void Extractor::matchNetlinkFamily() {
+ const auto* FamilyInit = getResult<InitListExpr>("genl_family_init");
+ auto Fields = structFieldIndexes(getResult<RecordDecl>("genl_family"));
+ const std::string& FamilyName = llvm::dyn_cast<StringLiteral>(FamilyInit->getInit(Fields["name"]))->getString().str();
+ std::string DefaultPolicy;
+ if (const auto* PolicyDecl = FamilyInit->getInit(Fields["policy"])->getAsBuiltinConstantDeclRef(*Context))
+ DefaultPolicy = policyName(PolicyDecl);
+ std::vector<NetlinkOp> Ops;
+ for (const auto& OpsName : {"ops", "small_ops", "split_ops"}) {
+ const auto* OpsDecl =
+ llvm::dyn_cast_if_present<VarDecl>(FamilyInit->getInit(Fields[OpsName])->getAsBuiltinConstantDeclRef(*Context));
+ const auto NumOps = FamilyInit->getInit(Fields[std::string("n_") + OpsName])->getIntegerConstantExpr(*Context);
+ // The ops variable may be defined in another TU.
+ // TODO: extract variables from another TUs.
+ if (!OpsDecl || !OpsDecl->getInit() || !NumOps)
+ continue;
+ const auto* OpsInit = llvm::dyn_cast<InitListExpr>(OpsDecl->getInit());
+ auto OpsFields = structFieldIndexes(OpsInit->getInit(0)->getType()->getAsRecordDecl());
+ for (int I = 0; I < *NumOps; I++) {
+ const auto* OpInit = llvm::dyn_cast<InitListExpr>(OpsInit->getInit(I));
+ const auto* CmdInit = OpInit->getInit(OpsFields["cmd"])->getEnumConstantDecl();
+ if (!CmdInit)
+ continue;
+ const std::string& OpName = CmdInit->getNameAsString();
+ noteConstUse(OpName, CmdInit->getInitVal().getExtValue(), CmdInit->getSourceRange());
+ std::string Policy;
+ if (OpsFields.count("policy") != 0) {
+ if (const auto* PolicyDecl = OpInit->getInit(OpsFields["policy"])->getAsBuiltinConstantDeclRef(*Context))
+ Policy = policyName(PolicyDecl);
+ }
+ if (Policy.empty())
+ Policy = DefaultPolicy;
+ std::string Func = getDeclName(OpInit->getInit(OpsFields["doit"]));
+ if (Func.empty())
+ Func = getDeclName(OpInit->getInit(OpsFields["dumpit"]));
+ int Flags = evaluate(OpInit->getInit(OpsFields["flags"]));
+ const char* Access = AccessUser;
+ constexpr int GENL_ADMIN_PERM = 0x01;
+ constexpr int GENL_UNS_ADMIN_PERM = 0x10;
+ if (Flags & GENL_ADMIN_PERM)
+ Access = AccessAdmin;
+ else if (Flags & GENL_UNS_ADMIN_PERM)
+ Access = AccessNsAdmin;
+ Ops.push_back(NetlinkOp{
+ .Name = OpName,
+ .Func = Func,
+ .Access = Access,
+ .Policy = Policy,
+ });
+ }
+ }
+ Output.emit(NetlinkFamily{
+ .Name = FamilyName,
+ .Ops = std::move(Ops),
+ });
+}
+
+std::string Extractor::policyName(const ValueDecl* Decl) {
+ // TODO: remove appending of $ sign here.
+ return Decl->getNameAsString() + "$auto_" + getDeclFileID(Decl);
+}
+
+void Extractor::matchSyscall() {
+ const auto* Func = getResult<FunctionDecl>("syscall");
+ std::vector<Field> Args;
+ for (const auto& Param : Func->parameters()) {
+ Args.push_back(Field{
+ .Name = Param->getNameAsString(),
+ .Type = genType(Param->getType()),
+ });
+ }
+ Output.emit(Syscall{
+ .Func = Func->getNameAsString(),
+ .Args = std::move(Args),
+ });
+}
+
+void Extractor::matchIouring() {
+ const auto* IssueDefs = getResult<VarDecl>("io_issue_defs");
+ const auto& InitConsts = extractDesignatedInitConsts(*IssueDefs);
+ const auto* InitList = llvm::dyn_cast<InitListExpr>(IssueDefs->getInit());
+ auto Fields = structFieldIndexes(InitList->getInit(0)->getType()->getAsRecordDecl());
+ for (const auto& [I, Name] : InitConsts) {
+ const auto& Init = llvm::dyn_cast<InitListExpr>(InitList->getInit(I));
+ std::string Prep = getDeclName(Init->getInit(Fields["prep"]));
+ if (Prep == "io_eopnotsupp_prep")
+ continue;
+ Output.emit(IouringOp{
+ .Name = Name,
+ .Func = getDeclName(Init->getInit(Fields["issue"])),
+ });
+ }
+}
+
+int main(int argc, const char** argv) {
+ llvm::cl::OptionCategory Options("syz-declextract options");
+ auto OptionsParser = tooling::CommonOptionsParser::create(argc, argv, Options);
+ if (!OptionsParser) {
+ llvm::errs() << OptionsParser.takeError();
+ return 1;
+ }
+ Extractor Ex;
+ tooling::ClangTool Tool(OptionsParser->getCompilations(), OptionsParser->getSourcePathList());
+ if (Tool.run(tooling::newFrontendActionFactory(&Ex).get()))
+ return 1;
+ Ex.print();
+ return 0;
+}
diff --git a/tools/syz-declextract/clangtool/json.h b/tools/syz-declextract/clangtool/json.h
new file mode 100644
index 000000000..fbbcc12a1
--- /dev/null
+++ b/tools/syz-declextract/clangtool/json.h
@@ -0,0 +1,78 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#ifndef SYZ_DECLEXTRACT_JSON_H
+#define SYZ_DECLEXTRACT_JSON_H
+
+#include <cassert>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <vector>
+
+class JSONPrinter {
+public:
+ JSONPrinter() : Top(*this) {}
+
+ template <typename T> void Field(const char* Name, const T& V, bool Last = false) {
+ printf("%s\"%s\": ", Indent(), Name);
+ print(*this, V);
+ printf("%s\n", Last ? "" : ",");
+ }
+
+ const char* Indent() const {
+ static std::string Indents;
+ while (Indents.size() < Nesting)
+ Indents.push_back('\t');
+ return Indents.c_str() + Indents.size() - Nesting;
+ }
+
+ class Scope {
+ public:
+ Scope(JSONPrinter& Printer, bool Array = false) : Printer(Printer), Array(Array) {
+ printf("%c\n", "{["[Array]);
+ Printer.Nesting++;
+ assert(Printer.Nesting < 1000);
+ }
+
+ ~Scope() {
+ assert(Printer.Nesting > 0);
+ Printer.Nesting--;
+ printf("%s%c", Printer.Indent(), "}]"[Array]);
+ }
+
+ private:
+ JSONPrinter& Printer;
+ const bool Array;
+ };
+
+private:
+ friend class Scope;
+ size_t Nesting = 0;
+ Scope Top;
+};
+
+inline void print(JSONPrinter& Printer, int V) { printf("%d", V); }
+inline void print(JSONPrinter& Printer, int64_t V) { printf("%ld", V); }
+inline void print(JSONPrinter& Printer, bool V) { printf("%s", V ? "true" : "false"); }
+inline void print(JSONPrinter& Printer, const char* V) { printf("\"%s\"", V ? V : ""); }
+inline void print(JSONPrinter& Printer, const std::string& V) { print(Printer, V.c_str()); }
+
+template <typename E> void print(JSONPrinter& Printer, const std::unique_ptr<E>& V) {
+ if (!V)
+ printf("null");
+ else
+ print(Printer, *V);
+}
+
+template <typename E> void print(JSONPrinter& Printer, const std::vector<E>& V) {
+ JSONPrinter::Scope Scope(Printer, true);
+ size_t i = 0;
+ for (const auto& Elem : V) {
+ printf("%s", Printer.Indent());
+ print(Printer, Elem);
+ printf("%s\n", ++i == V.size() ? "" : ",");
+ }
+}
+
+#endif
diff --git a/tools/syz-declextract/clangtool/output.h b/tools/syz-declextract/clangtool/output.h
new file mode 100644
index 000000000..df3f290b6
--- /dev/null
+++ b/tools/syz-declextract/clangtool/output.h
@@ -0,0 +1,292 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#ifndef SYZ_DECLEXTRACT_OUTPUT_H
+#define SYZ_DECLEXTRACT_OUTPUT_H
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+const char* const AccessUnknown = "";
+const char* const AccessUser = "user";
+const char* const AccessNsAdmin = "ns_admin";
+const char* const AccessAdmin = "admin";
+
+struct IntType;
+struct PtrType;
+struct ArrType;
+struct BufferType;
+
+struct FieldType {
+ std::unique_ptr<IntType> Int;
+ std::unique_ptr<PtrType> Ptr;
+ std::unique_ptr<ArrType> Array;
+ std::unique_ptr<BufferType> Buffer;
+ std::unique_ptr<std::string> Struct;
+
+ FieldType() = default;
+ FieldType(IntType&& Typ) : Int(std::make_unique<IntType>(std::move(Typ))) {}
+ FieldType(PtrType&& Typ) : Ptr(std::make_unique<PtrType>(std::move(Typ))) {}
+ FieldType(ArrType&& Typ) : Array(std::make_unique<ArrType>(std::move(Typ))) {}
+ FieldType(BufferType&& Typ) : Buffer(std::make_unique<BufferType>(std::move(Typ))) {}
+ FieldType(const std::string& Typ) : Struct(std::make_unique<std::string>(std::move(Typ))) {}
+};
+
+struct IntType {
+ int ByteSize = 0;
+ std::string Name;
+ std::string Base;
+ std::string Enum;
+};
+
+struct PtrType {
+ FieldType Elem;
+ bool IsConst = false;
+};
+
+struct ArrType {
+ FieldType Elem;
+ int MinSize = 0;
+ int MaxSize = 0;
+};
+
+struct BufferType {
+ int MinSize = 0;
+ int MaxSize = 0;
+ bool IsString = false;
+ bool IsNonTerminated = false;
+};
+
+struct Include {
+ std::string Filename;
+};
+
+struct Define {
+ std::string Name;
+ std::string Value;
+};
+
+struct Field {
+ std::string Name;
+ bool IsAnonymous = false;
+ int BitWidth = 0;
+ int CountedBy = -1;
+ FieldType Type;
+};
+
+struct Struct {
+ std::string Name;
+ int ByteSize = 0;
+ bool IsUnion = false;
+ bool IsPacked = false;
+ int Align = 0;
+ std::vector<Field> Fields;
+};
+
+struct Enum {
+ std::string Name;
+ std::vector<std::string> Values;
+};
+
+struct Syscall {
+ std::string Func;
+ std::vector<Field> Args;
+};
+
+struct IouringOp {
+ std::string Name;
+ std::string Func;
+};
+
+struct NetlinkOp {
+ std::string Name;
+ std::string Func;
+ const char* Access;
+ std::string Policy;
+};
+
+struct NetlinkFamily {
+ std::string Name;
+ std::vector<NetlinkOp> Ops;
+};
+
+struct NetlinkAttr {
+ std::string Name;
+ std::string Kind;
+ int MaxSize = 0;
+ std::string NestedPolicy;
+ std::unique_ptr<FieldType> Elem;
+};
+
+struct NetlinkPolicy {
+ std::string Name;
+ std::vector<NetlinkAttr> Attrs;
+};
+
+inline void print(JSONPrinter& Printer, const Define& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ Printer.Field("value", V.Value, true);
+}
+
+inline void print(JSONPrinter& Printer, const Field& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ Printer.Field("is_anonymous", V.IsAnonymous);
+ Printer.Field("bit_width", V.BitWidth);
+ Printer.Field("counted_by", V.CountedBy);
+ Printer.Field("type", V.Type, true);
+}
+
+inline void print(JSONPrinter& Printer, const Struct& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ 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("fields", V.Fields, true);
+}
+
+inline void print(JSONPrinter& Printer, const Enum& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ Printer.Field("values", V.Values, true);
+}
+
+inline void print(JSONPrinter& Printer, const FieldType& V) {
+ JSONPrinter::Scope Scope(Printer);
+ if (V.Int)
+ Printer.Field("int", *V.Int, true);
+ else if (V.Ptr)
+ Printer.Field("ptr", *V.Ptr, true);
+ else if (V.Array)
+ Printer.Field("array", *V.Array, true);
+ else if (V.Buffer)
+ Printer.Field("buffer", *V.Buffer, true);
+ else
+ Printer.Field("struct", *V.Struct, true);
+}
+
+inline void print(JSONPrinter& Printer, const IntType& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("byte_size", V.ByteSize);
+ Printer.Field("name", V.Name);
+ Printer.Field("base", V.Base);
+ Printer.Field("enum", V.Enum, true);
+}
+
+inline void print(JSONPrinter& Printer, const PtrType& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("elem", V.Elem);
+ Printer.Field("is_const", V.IsConst, true);
+}
+
+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);
+}
+
+inline void print(JSONPrinter& Printer, const BufferType& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("min_size", V.MinSize);
+ Printer.Field("max_size", V.MaxSize);
+ Printer.Field("is_string", V.IsString);
+ Printer.Field("is_non_terminated", V.IsNonTerminated, true);
+}
+
+inline void print(JSONPrinter& Printer, const Syscall& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("func", V.Func);
+ Printer.Field("args", V.Args, true);
+}
+
+inline void print(JSONPrinter& Printer, const IouringOp& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ Printer.Field("func", V.Func, true);
+}
+
+inline void print(JSONPrinter& Printer, const NetlinkOp& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ Printer.Field("func", V.Func);
+ Printer.Field("access", V.Access);
+ Printer.Field("policy", V.Policy, true);
+}
+
+inline void print(JSONPrinter& Printer, const NetlinkFamily& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ Printer.Field("ops", V.Ops, true);
+}
+
+inline void print(JSONPrinter& Printer, const NetlinkAttr& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ Printer.Field("kind", V.Kind);
+ Printer.Field("max_size", V.MaxSize);
+ Printer.Field("nested_policy", V.NestedPolicy);
+ Printer.Field("elem", V.Elem, true);
+}
+
+inline void print(JSONPrinter& Printer, const NetlinkPolicy& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name);
+ Printer.Field("attrs", V.Attrs, true);
+}
+
+// This type is used when we can't figure out the right type, but need some type to use.
+inline FieldType TodoType() {
+ return IntType{
+ // TODO: use size 1, then arrays will be lowered to buffers.
+ .ByteSize = 8,
+ .Name = "TODO",
+ .Base = "long",
+ };
+}
+
+class Output {
+public:
+ void emit(Include&& Inc) {
+ if (IncludesDedup.insert(Inc.Filename).second)
+ Includes.push_back(Inc.Filename);
+ }
+
+ void emit(Define&& V) { Defines.push_back(std::move(V)); }
+ void emit(Struct&& V) { Structs.push_back(std::move(V)); }
+ void emit(Enum&& V) { Enums.push_back(std::move(V)); }
+ void emit(Syscall&& V) { Syscalls.push_back(std::move(V)); }
+ void emit(IouringOp&& V) { IouringOps.push_back(std::move(V)); }
+ void emit(NetlinkFamily&& V) { NetlinkFamilies.push_back(std::move(V)); }
+ void emit(NetlinkPolicy&& V) { NetlinkPolicies.push_back(std::move(V)); }
+
+ void print() const {
+ JSONPrinter Printer;
+ Printer.Field("includes", Includes);
+ Printer.Field("defines", Defines);
+ Printer.Field("enums", Enums);
+ Printer.Field("structs", Structs);
+ Printer.Field("syscalls", Syscalls);
+ Printer.Field("iouring_ops", IouringOps);
+ Printer.Field("netlink_families", NetlinkFamilies);
+ Printer.Field("netlink_policies", NetlinkPolicies, true);
+ }
+
+private:
+ std::vector<std::string> Includes;
+ std::unordered_set<std::string> IncludesDedup;
+ std::vector<Define> Defines;
+ std::vector<Enum> Enums;
+ std::vector<Struct> Structs;
+ std::vector<Syscall> Syscalls;
+ std::vector<IouringOp> IouringOps;
+ std::vector<NetlinkFamily> NetlinkFamilies;
+ std::vector<NetlinkPolicy> NetlinkPolicies;
+};
+
+#endif
diff --git a/tools/syz-declextract/declextract.go b/tools/syz-declextract/declextract.go
new file mode 100644
index 000000000..7bf6d6081
--- /dev/null
+++ b/tools/syz-declextract/declextract.go
@@ -0,0 +1,260 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "flag"
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "slices"
+ "strings"
+
+ "github.com/google/syzkaller/pkg/ast"
+ "github.com/google/syzkaller/pkg/clangtool"
+ "github.com/google/syzkaller/pkg/compiler"
+ "github.com/google/syzkaller/pkg/declextract"
+ "github.com/google/syzkaller/pkg/mgrconfig"
+ "github.com/google/syzkaller/pkg/osutil"
+ "github.com/google/syzkaller/pkg/subsystem"
+ _ "github.com/google/syzkaller/pkg/subsystem/lists"
+ "github.com/google/syzkaller/pkg/tool"
+ "github.com/google/syzkaller/sys/targets"
+)
+
+// The target we currently assume for extracted descriptions.
+var target = targets.Get(targets.Linux, targets.AMD64)
+
+func main() {
+ var (
+ flagConfig = flag.String("config", "", "manager config file")
+ flagBinary = flag.String("binary", "syz-declextract", "path to syz-declextract binary")
+ flagCacheExtract = flag.Bool("cache-extract", false, "use cached extract results if present"+
+ " (cached in manager.workdir/declextract.cache)")
+ )
+ defer tool.Init()()
+ cfg, err := mgrconfig.LoadFile(*flagConfig)
+ if err != nil {
+ tool.Fail(err)
+ }
+ if err := run(filepath.FromSlash("sys/linux/auto.txt"), &clangtool.Config{
+ ToolBin: *flagBinary,
+ KernelSrc: cfg.KernelSrc,
+ KernelObj: cfg.KernelObj,
+ CacheDir: filepath.Join(cfg.Workdir, "declextract.cache"),
+ ReuseCache: *flagCacheExtract,
+ }); err != nil {
+ tool.Fail(err)
+ }
+}
+
+func run(autoFile string, cfg *clangtool.Config) error {
+ syscallRename, err := buildSyscallRenameMap(cfg.KernelSrc)
+ if err != nil {
+ return fmt.Errorf("failed to build syscall rename map: %w", err)
+ }
+ out, err := clangtool.Run(cfg)
+ if err != nil {
+ return err
+ }
+ descriptions, interfaces, err := declextract.Run(out, syscallRename)
+ if err != nil {
+ return err
+ }
+ if err := osutil.WriteFile(autoFile, descriptions); err != nil {
+ return err
+ }
+ if err := osutil.WriteFile(autoFile+".info", serialize(interfaces)); err != nil {
+ return 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
+ // by manual descriptions (compiler.CollectUnused requires complete descriptions).
+ // This also canonicalizes them b/c new lines are added during parsing.
+ 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())
+ }
+ // 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())
+ }
+ consts := compiler.ExtractConsts(desc.Clone(), target, eh)
+ if consts == nil {
+ return 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
+ }
+ unused := make(map[string]bool)
+ for _, n := range unusedNodes {
+ _, typ, name := n.Info()
+ unused[typ+name] = true
+ }
+ desc.Nodes = slices.DeleteFunc(desc.Nodes, func(n ast.Node) bool {
+ pos, typ, name := n.Info()
+ return pos.File != autoFile || unused[typ+name]
+ })
+ // 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)
+}
+
+func errorHandler() (func(pos ast.Pos, msg string), *bytes.Buffer) {
+ errors := new(bytes.Buffer)
+ eh := func(pos ast.Pos, msg string) {
+ pos.File = filepath.Base(pos.File)
+ fmt.Fprintf(errors, "%v: %v\n", pos, msg)
+ }
+ return eh, errors
+}
+
+func serialize(interfaces []*declextract.Interface) []byte {
+ w := new(bytes.Buffer)
+ for _, iface := range interfaces {
+ fmt.Fprintf(w, "%v\t%v\tfunc:%v\taccess:%v\tmanual_desc:%v\tauto_desc:%v",
+ iface.Type, iface.Name, iface.Func, iface.Access,
+ iface.ManualDescriptions, iface.AutoDescriptions)
+ for _, file := range iface.Files {
+ fmt.Fprintf(w, "\tfile:%v", file)
+ }
+ for _, subsys := range iface.Subsystems {
+ fmt.Fprintf(w, "\tsubsystem:%v", subsys)
+ }
+ fmt.Fprintf(w, "\n")
+ }
+ return w.Bytes()
+}
+
+func finishInterfaces(interfaces []*declextract.Interface, consts map[string]*compiler.ConstInfo, autoFile string) {
+ manual := make(map[string]bool)
+ for file, desc := range consts {
+ for _, c := range desc.Consts {
+ if file != autoFile {
+ manual[c.Name] = true
+ }
+ }
+ }
+ extractor := subsystem.MakeExtractor(subsystem.GetList(target.OS))
+ for _, iface := range interfaces {
+ iface.ManualDescriptions = manual[iface.IdentifyingConst]
+ var crashes []*subsystem.Crash
+ for _, file := range iface.Files {
+ crashes = append(crashes, &subsystem.Crash{GuiltyPath: file})
+ }
+ for _, s := range extractor.Extract(crashes) {
+ iface.Subsystems = append(iface.Subsystems, s.Name)
+ }
+ slices.Sort(iface.Subsystems)
+ }
+}
+
+func buildSyscallRenameMap(sourceDir string) (map[string][]string, error) {
+ // Some syscalls have different names and entry points and thus need to be renamed.
+ // e.g. SYSCALL_DEFINE1(setuid16, old_uid_t, uid) is referred to in the .tbl file with setuid.
+ // Parse *.tbl files that map functions defined with SYSCALL_DEFINE macros to actual syscall names.
+ // Lines in the files look as follows:
+ // 288 common accept4 sys_accept4
+ // Total mapping is many-to-many, so we give preference to x86 arch, then to 64-bit syscalls,
+ // and then just order arches by name to have deterministic result.
+ // Note: some syscalls may have no record in the tables for the architectures we support.
+ syscalls := make(map[string][]tblSyscall)
+ tblFiles, err := findTblFiles(sourceDir)
+ if err != nil {
+ return nil, err
+ }
+ if len(tblFiles) == 0 {
+ return nil, fmt.Errorf("found no *.tbl files in the kernel dir %v", sourceDir)
+ }
+ for file, arches := range tblFiles {
+ for _, arch := range arches {
+ data, err := os.ReadFile(file)
+ if err != nil {
+ return nil, err
+ }
+ parseTblFile(data, arch, syscalls)
+ }
+ }
+ rename := make(map[string][]string)
+ for syscall, descs := range syscalls {
+ slices.SortFunc(descs, func(a, b tblSyscall) int {
+ if (a.arch == target.Arch) != (b.arch == target.Arch) {
+ if a.arch == target.Arch {
+ return -1
+ }
+ return 1
+ }
+ if a.is64bit != b.is64bit {
+ if a.is64bit {
+ return -1
+ }
+ return 1
+ }
+ return strings.Compare(a.arch, b.arch)
+ })
+ fn := descs[0].fn
+ rename[fn] = append(rename[fn], syscall)
+ }
+ return rename, nil
+}
+
+type tblSyscall struct {
+ fn string
+ arch string
+ is64bit bool
+}
+
+func parseTblFile(data []byte, arch string, syscalls map[string][]tblSyscall) {
+ for s := bufio.NewScanner(bytes.NewReader(data)); s.Scan(); {
+ fields := strings.Fields(s.Text())
+ if len(fields) < 4 || fields[0] == "#" {
+ continue
+ }
+ group := fields[1]
+ syscall := fields[2]
+ fn := strings.TrimPrefix(fields[3], "sys_")
+ if strings.HasPrefix(syscall, "unused") || fn == "-" ||
+ // Powerpc spu group defines some syscalls (utimesat)
+ // that are not present on any of our arches.
+ group == "spu" ||
+ // llseek does not exist, it comes from:
+ // arch/arm64/tools/syscall_64.tbl -> scripts/syscall.tbl
+ // 62 32 llseek sys_llseek
+ // So scripts/syscall.tbl is pulled for 64-bit arch, but the syscall
+ // is defined only for 32-bit arch in that file.
+ syscall == "llseek" ||
+ // Don't want to test it (but see issue 5308).
+ syscall == "reboot" {
+ continue
+ }
+ syscalls[syscall] = append(syscalls[syscall], tblSyscall{
+ fn: fn,
+ arch: arch,
+ is64bit: group == "common" || strings.Contains(group, "64"),
+ })
+ }
+}
+
+func findTblFiles(sourceDir string) (map[string][]string, error) {
+ files := make(map[string][]string)
+ for _, arch := range targets.List[target.OS] {
+ err := filepath.Walk(filepath.Join(sourceDir, "arch", arch.KernelHeaderArch),
+ func(file string, info fs.FileInfo, err error) error {
+ if err == nil && strings.HasSuffix(file, ".tbl") {
+ files[file] = append(files[file], arch.VMArch)
+ }
+ return err
+ })
+ if err != nil {
+ return nil, err
+ }
+ }
+ return files, nil
+}
diff --git a/tools/syz-declextract/declextract_test.go b/tools/syz-declextract/declextract_test.go
new file mode 100644
index 000000000..b93a6dcbf
--- /dev/null
+++ b/tools/syz-declextract/declextract_test.go
@@ -0,0 +1,154 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/syzkaller/pkg/ast"
+ "github.com/google/syzkaller/pkg/clangtool"
+ "github.com/google/syzkaller/pkg/compiler"
+ "github.com/google/syzkaller/pkg/osutil"
+)
+
+var (
+ flagBin = flag.String("bin", "", "path to syz-declextract binary to use")
+ flagUpdate = flag.Bool("update", false, "update golden files")
+)
+
+func TestClangTool(t *testing.T) {
+ if *flagBin == "" {
+ t.Skipf("syz-declextract path is not specified, run with -bin=syz-declextract flag")
+ }
+ testEachFile(t, func(t *testing.T, cfg *clangtool.Config, file string) {
+ out, err := clangtool.Run(cfg)
+ if err != nil {
+ t.Fatal(err)
+ }
+ got, err := json.MarshalIndent(out, "", "\t")
+ if err != nil {
+ t.Fatal(err)
+ }
+ compareGoldenData(t, file+".json", got)
+ })
+}
+
+func TestDeclextract(t *testing.T) {
+ testEachFile(t, func(t *testing.T, cfg *clangtool.Config, file string) {
+ // Created cache file to avoid running the clang tool.
+ goldenFile := file + ".json"
+ cacheFile := filepath.Join(cfg.KernelObj, filepath.Base(goldenFile))
+ if err := os.Symlink(goldenFile, cacheFile); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.Symlink(filepath.Join(cfg.KernelSrc, "manual.txt"),
+ filepath.Join(cfg.KernelObj, "manual.txt")); err != nil {
+ t.Fatal(err)
+ }
+ cfg.ToolBin = "this-is-not-supposed-to-run"
+ autoFile := filepath.Join(cfg.KernelObj, filepath.Base(file)+".txt")
+ if err := run(autoFile, cfg); err != nil {
+ if *flagUpdate {
+ osutil.CopyFile(autoFile, file+".txt")
+ osutil.CopyFile(autoFile+".info", file+".info")
+ }
+ t.Fatal(err)
+ }
+
+ // Check that descriptions compile.
+ eh, errors := errorHandler()
+ full := ast.ParseGlob(filepath.Join(cfg.KernelObj, "*.txt"), eh)
+ if full == nil {
+ t.Fatalf("failed to parse full descriptions:\n%s", errors)
+ }
+ constInfo := compiler.ExtractConsts(full, target, eh)
+ if constInfo == nil {
+ t.Fatalf("failed to compile full descriptions:\n%s", errors)
+ }
+ // Fabricate consts.
+ consts := make(map[string]uint64)
+ for _, info := range constInfo {
+ for i, c := range info.Consts {
+ consts[c.Name] = uint64(i + 1)
+ }
+ }
+ res := compiler.Compile(full, consts, target, eh)
+ if res == nil {
+ t.Fatalf("failed to compile full descriptions:\n%s", errors)
+ }
+
+ // TODO: Ensure that none of the syscalls will be disabled by TransitivelyEnabledCalls.
+
+ compareGoldenFile(t, file+".txt", autoFile)
+ compareGoldenFile(t, file+".info", autoFile+".info")
+ })
+}
+
+func testEachFile(t *testing.T, fn func(t *testing.T, cfg *clangtool.Config, file string)) {
+ testdata, err := filepath.Abs("testdata")
+ if err != nil {
+ t.Fatal(err)
+ }
+ files, err := filepath.Glob(filepath.Join(testdata, "*.c"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(files) == 0 {
+ t.Fatal("found no source files")
+ }
+ for _, file := range files {
+ file := file
+ t.Run(filepath.Base(file), func(t *testing.T) {
+ t.Parallel()
+ buildDir := t.TempDir()
+ commands := fmt.Sprintf(`[{
+ "file": "%s",
+ "directory": "%s",
+ "command": "clang -c %s -DKBUILD_BASENAME=foo"
+ }]`,
+ file, buildDir, file)
+ dbFile := filepath.Join(buildDir, "compile_commands.json")
+ if err := os.WriteFile(dbFile, []byte(commands), 0600); err != nil {
+ t.Fatal(err)
+ }
+ cfg := &clangtool.Config{
+ ToolBin: *flagBin,
+ KernelSrc: testdata,
+ KernelObj: buildDir,
+ CacheDir: buildDir,
+ ReuseCache: true,
+ }
+ fn(t, cfg, file)
+ })
+ }
+}
+
+func compareGoldenFile(t *testing.T, goldenFile, gotFile string) {
+ got, err := os.ReadFile(gotFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ compareGoldenData(t, goldenFile, got)
+}
+
+func compareGoldenData(t *testing.T, goldenFile string, got []byte) {
+ if *flagUpdate {
+ if err := os.WriteFile(goldenFile, got, 0644); err != nil {
+ t.Fatal(err)
+ }
+ }
+ want, err := os.ReadFile(goldenFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Fatal(diff)
+ }
+}
diff --git a/tools/syz-declextract/run.go b/tools/syz-declextract/run.go
deleted file mode 100644
index e93723c95..000000000
--- a/tools/syz-declextract/run.go
+++ /dev/null
@@ -1,546 +0,0 @@
-// Copyright 2024 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package main
-
-import (
- "bufio"
- "bytes"
- "encoding/json"
- "errors"
- "flag"
- "fmt"
- "io/fs"
- "math/rand"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "slices"
- "strconv"
- "strings"
- "time"
-
- "github.com/google/syzkaller/pkg/ast"
- "github.com/google/syzkaller/pkg/compiler"
- "github.com/google/syzkaller/pkg/mgrconfig"
- "github.com/google/syzkaller/pkg/osutil"
- "github.com/google/syzkaller/pkg/subsystem"
- _ "github.com/google/syzkaller/pkg/subsystem/lists"
- "github.com/google/syzkaller/pkg/tool"
- "github.com/google/syzkaller/sys/targets"
-)
-
-var (
- autoFile = filepath.FromSlash("sys/linux/auto.txt")
- target = targets.Get(targets.Linux, targets.AMD64)
-)
-
-func main() {
- var (
- flagConfig = flag.String("config", "", "manager config file")
- flagBinary = flag.String("binary", "syz-declextract", "path to syz-declextract binary")
- flagCacheExtract = flag.Bool("cache-extract", false, "use cached extract results if present"+
- " (cached in manager.workdir/declextract.cache)")
- )
- defer tool.Init()()
- cfg, err := mgrconfig.LoadFile(*flagConfig)
- if err != nil {
- tool.Failf("failed to load manager config: %v", err)
- }
-
- compilationDatabase := filepath.Join(cfg.KernelObj, "compile_commands.json")
- cmds, err := loadCompileCommands(compilationDatabase)
- if err != nil {
- tool.Failf("failed to load compile commands: %v", err)
- }
-
- ctx := &context{
- cfg: cfg,
- clangTool: *flagBinary,
- compilationDatabase: compilationDatabase,
- compileCommands: cmds,
- extractor: subsystem.MakeExtractor(subsystem.GetList(target.OS)),
- syscallNameMap: readSyscallMap(cfg.KernelSrc),
- interfaces: make(map[string]Interface),
- }
-
- outputs := make(chan *output, len(cmds))
- files := make(chan string, len(cmds))
- for w := 0; w < runtime.NumCPU(); w++ {
- go ctx.worker(outputs, files, *flagCacheExtract)
- }
-
- for _, cmd := range cmds {
- files <- cmd.File
- }
- close(files)
-
- for range cmds {
- out := <-outputs
- if out == nil {
- continue
- }
- file, err := filepath.Rel(cfg.KernelSrc, out.file)
- if err != nil {
- tool.Fail(err)
- }
- if out.err != nil {
- tool.Failf("%v: %v", file, out.err)
- }
- parse := ast.Parse(out.output, "", nil)
- if parse == nil {
- tool.Failf("%v: parsing error:\n%s", file, out.output)
- }
- ctx.appendNodes(parse.Nodes, file)
- }
- ctx.finishDescriptions()
-
- desc := &ast.Description{
- Nodes: ctx.nodes,
- }
- writeDescriptions(desc)
- // 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
- // by manual descriptions (compiler.CollectUnused requires complete descriptions).
- removeUnused(desc)
- writeDescriptions(desc)
-
- ifaces := ctx.finishInterfaces()
- ifacesData := serializeInterfaces(ifaces)
- if err := osutil.WriteFile(autoFile+".info", ifacesData); err != nil {
- tool.Fail(err)
- }
-}
-
-type context struct {
- cfg *mgrconfig.Config
- clangTool string
- compilationDatabase string
- compileCommands []compileCommand
- extractor *subsystem.Extractor
- syscallNameMap map[string][]string
- interfaces map[string]Interface
- nodes []ast.Node
-}
-
-type compileCommand struct {
- Command string
- Directory string
- File string
-}
-
-func loadCompileCommands(file string) ([]compileCommand, error) {
- data, err := os.ReadFile(file)
- if err != nil {
- return nil, err
- }
- var cmds []compileCommand
- if err := json.Unmarshal(data, &cmds); err != nil {
- return nil, err
- }
- // Remove commands that don't relate to the kernel build
- // (probably some host tools, etc).
- cmds = slices.DeleteFunc(cmds, func(cmd compileCommand) bool {
- return !strings.HasSuffix(cmd.File, ".c") ||
- // Files compiled with gcc are not a part of the kernel
- // (assuming compile commands were generated with make CC=clang).
- // They are probably a part of some host tool.
- strings.HasPrefix(cmd.Command, "gcc") ||
- // KBUILD should add this define all kernel files.
- !strings.Contains(cmd.Command, "-DKBUILD_BASENAME")
- })
- // Shuffle the order to detect any non-determinism caused by the order early.
- // The result should be the same regardless.
- rand.New(rand.NewSource(time.Now().UnixNano())).Shuffle(len(cmds), func(i, j int) {
- cmds[i], cmds[j] = cmds[j], cmds[i]
- })
- return cmds, nil
-}
-
-type output struct {
- file string
- output []byte
- err error
-}
-
-type Interface struct {
- Type string
- Name string
- Files []string
- Func string
- Access string
- Subsystems []string
- ManualDescriptions bool
- AutoDescriptions bool
-
- identifyingConst string
-}
-
-func (iface *Interface) ID() string {
- return fmt.Sprintf("%v/%v", iface.Type, iface.Name)
-}
-
-func serializeInterfaces(ifaces []Interface) []byte {
- w := new(bytes.Buffer)
- for _, iface := range ifaces {
- fmt.Fprintf(w, "%v\t%v\tfunc:%v\taccess:%v\tmanual_desc:%v\tauto_desc:%v",
- iface.Type, iface.Name, iface.Func, iface.Access,
- iface.ManualDescriptions, iface.AutoDescriptions)
- for _, file := range iface.Files {
- fmt.Fprintf(w, "\tfile:%v", file)
- }
- for _, subsys := range iface.Subsystems {
- fmt.Fprintf(w, "\tsubsystem:%v", subsys)
- }
- fmt.Fprintf(w, "\n")
- }
- return w.Bytes()
-}
-
-func (ctx *context) finishInterfaces() []Interface {
- var interfaces []Interface
- for _, iface := range ctx.interfaces {
- slices.Sort(iface.Files)
- iface.Files = slices.Compact(iface.Files)
- var crashes []*subsystem.Crash
- for _, file := range iface.Files {
- crashes = append(crashes, &subsystem.Crash{GuiltyPath: file})
- }
- for _, s := range ctx.extractor.Extract(crashes) {
- iface.Subsystems = append(iface.Subsystems, s.Name)
- }
- slices.Sort(iface.Subsystems)
- if iface.Access == "" {
- iface.Access = "unknown"
- }
- interfaces = append(interfaces, iface)
- }
- slices.SortFunc(interfaces, func(a, b Interface) int {
- return strings.Compare(a.ID(), b.ID())
- })
- checkDescriptionPresence(interfaces, autoFile)
- return interfaces
-}
-
-func (ctx *context) mergeInterface(iface Interface) {
- prev, ok := ctx.interfaces[iface.ID()]
- if ok {
- if iface.identifyingConst != prev.identifyingConst {
- tool.Failf("interface %v has different identifying consts: %v vs %v",
- iface.ID(), iface.identifyingConst, prev.identifyingConst)
- }
- iface.Files = append(iface.Files, prev.Files...)
- }
- ctx.interfaces[iface.ID()] = iface
-}
-
-func checkDescriptionPresence(interfaces []Interface, autoFile string) {
- desc := ast.ParseGlob(filepath.Join("sys", target.OS, "*.txt"), nil)
- if desc == nil {
- tool.Failf("failed to parse descriptions")
- }
- consts := compiler.ExtractConsts(desc, target, nil)
- auto := make(map[string]bool)
- manual := make(map[string]bool)
- for file, desc := range consts {
- for _, c := range desc.Consts {
- if file == autoFile {
- auto[c.Name] = true
- } else {
- manual[c.Name] = true
- }
- }
- }
- for i := range interfaces {
- iface := &interfaces[i]
- if auto[iface.identifyingConst] {
- iface.AutoDescriptions = true
- }
- if manual[iface.identifyingConst] {
- iface.ManualDescriptions = true
- }
- }
-}
-
-func writeDescriptions(desc *ast.Description) {
- // New lines are added in the parsing step. This is why we need to Format (serialize the description),
- // Parse, then Format again.
- output := ast.Format(ast.Parse(ast.Format(desc), "", ast.LoggingHandler))
- if err := osutil.WriteFile(autoFile, output); err != nil {
- tool.Fail(err)
- }
-}
-
-func (ctx *context) finishDescriptions() {
- slices.SortFunc(ctx.nodes, func(a, b ast.Node) int {
- return strings.Compare(ast.SerializeNode(a), ast.SerializeNode(b))
- })
- ctx.nodes = slices.CompactFunc(ctx.nodes, func(a, b ast.Node) bool {
- return ast.SerializeNode(a) == ast.SerializeNode(b)
- })
- slices.SortStableFunc(ctx.nodes, func(a, b ast.Node) int {
- return getTypeOrder(a) - getTypeOrder(b)
- })
-
- prevCall, prevCallIndex := "", 0
- for _, node := range ctx.nodes {
- switch n := node.(type) {
- case *ast.Call:
- if n.Name.Name == prevCall {
- n.Name.Name += strconv.Itoa(prevCallIndex)
- prevCallIndex++
- } else {
- prevCall = n.Name.Name
- prevCallIndex = 0
- }
- }
- }
-
- // These additional includes must be at the top (added after sorting), because other kernel headers
- // are broken and won't compile without these additional ones included first.
- header := `# Code generated by syz-declextract. DO NOT EDIT.
-
-include <include/vdso/bits.h>
-include <include/linux/types.h>
-`
- desc := ast.Parse([]byte(header), "", nil)
- ctx.nodes = append(desc.Nodes, ctx.nodes...)
-}
-
-func removeUnused(desc *ast.Description) {
- all := ast.ParseGlob(filepath.Join("sys", target.OS, "*.txt"), nil)
- if all == nil {
- tool.Failf("failed to parse descriptions")
- }
- unusedNodes, err := compiler.CollectUnused(all, target, nil)
- if err != nil {
- tool.Failf("failed to typecheck descriptions: %v", err)
- }
- unused := make(map[string]bool)
- for _, n := range unusedNodes {
- if pos, typ, name := n.Info(); pos.File == autoFile {
- unused[fmt.Sprintf("%v/%v", typ, name)] = true
- }
- }
- desc.Nodes = slices.DeleteFunc(desc.Nodes, func(n ast.Node) bool {
- _, typ, name := n.Info()
- return unused[fmt.Sprintf("%v/%v", typ, name)]
- })
-}
-
-func (ctx *context) worker(outputs chan *output, files chan string, cache bool) {
- for file := range files {
- cacheFile := filepath.Join(ctx.cfg.Workdir, "declextract.cache",
- strings.TrimPrefix(strings.TrimPrefix(filepath.Clean(file),
- ctx.cfg.KernelSrc), ctx.cfg.KernelObj))
- if cache {
- out, err := os.ReadFile(cacheFile)
- if err == nil {
- outputs <- &output{file, out, nil}
- continue
- }
- }
- // Suppress warning since we may build the tool on a different clang
- // version that produces more warnings.
- out, err := exec.Command(ctx.clangTool, "-p", ctx.compilationDatabase, file, "--extra-arg=-w").Output()
- var exitErr *exec.ExitError
- if err != nil && errors.As(err, &exitErr) && len(exitErr.Stderr) != 0 {
- err = fmt.Errorf("%s", exitErr.Stderr)
- }
- if err == nil {
- osutil.MkdirAll(filepath.Dir(cacheFile))
- osutil.WriteFile(cacheFile, out)
- }
- outputs <- &output{file, out, err}
- }
-}
-
-func (ctx *context) renameSyscall(syscall *ast.Call) []ast.Node {
- names := ctx.syscallNameMap[syscall.CallName]
- if len(names) == 0 {
- // Syscall has no record in the tables for the architectures we support.
- return nil
- }
- variant := strings.TrimPrefix(syscall.Name.Name, syscall.CallName)
- if variant == "" {
- variant = "$auto"
- }
- var renamed []ast.Node
- for _, name := range names {
- newCall := syscall.Clone().(*ast.Call)
- newCall.Name.Name = name + variant
- newCall.CallName = name // Not required but avoids mistakenly treating CallName as the part before the $.
- renamed = append(renamed, newCall)
- }
-
- return renamed
-}
-
-func readSyscallMap(sourceDir string) map[string][]string {
- // Parse arch/*/*.tbl files that map functions defined with SYSCALL_DEFINE macros to actual syscall names.
- // Lines in the files look as follows:
- // 288 common accept4 sys_accept4
- // Total mapping is many-to-many, so we give preference to x86 arch, then to 64-bit syscalls,
- // and then just order arches by name to have deterministic result.
- type desc struct {
- fn string
- arch string
- is64bit bool
- }
- syscalls := make(map[string][]desc)
- for _, arch := range targets.List[target.OS] {
- filepath.Walk(filepath.Join(sourceDir, "arch", arch.KernelHeaderArch),
- func(path string, info fs.FileInfo, err error) error {
- if err != nil || !strings.HasSuffix(path, ".tbl") {
- return err
- }
- f, err := os.Open(path)
- if err != nil {
- tool.Fail(err)
- }
- defer f.Close()
- for s := bufio.NewScanner(f); s.Scan(); {
- fields := strings.Fields(s.Text())
- if len(fields) < 4 || fields[0] == "#" {
- continue
- }
- group := fields[1]
- syscall := fields[2]
- fn := strings.TrimPrefix(fields[3], "sys_")
- if strings.HasPrefix(syscall, "unused") || fn == "-" ||
- // Powerpc spu group defines some syscalls (utimesat)
- // that are not present on any of our arches.
- group == "spu" ||
- // llseek does not exist, it comes from:
- // arch/arm64/tools/syscall_64.tbl -> scripts/syscall.tbl
- // 62 32 llseek sys_llseek
- // So scripts/syscall.tbl is pulled for 64-bit arch, but the syscall
- // is defined only for 32-bit arch in that file.
- syscall == "llseek" ||
- // Don't want to test it (see issue 5308).
- syscall == "reboot" {
- continue
- }
- syscalls[syscall] = append(syscalls[syscall], desc{
- fn: fn,
- arch: arch.VMArch,
- is64bit: group == "common" || strings.Contains(group, "64"),
- })
- }
- return nil
- })
- }
-
- rename := map[string][]string{
- "syz_genetlink_get_family_id": {"syz_genetlink_get_family_id"},
- }
- for syscall, descs := range syscalls {
- slices.SortFunc(descs, func(a, b desc) int {
- if (a.arch == target.Arch) != (b.arch == target.Arch) {
- if a.arch == target.Arch {
- return -1
- }
- return 1
- }
- if a.is64bit != b.is64bit {
- if a.is64bit {
- return -1
- }
- return 1
- }
- return strings.Compare(a.arch, b.arch)
- })
- fn := descs[0].fn
- rename[fn] = append(rename[fn], syscall)
- }
- return rename
-}
-
-func (ctx *context) appendNodes(nodes []ast.Node, file string) {
- for _, node := range nodes {
- switch node := node.(type) {
- case *ast.Call:
- // Some syscalls have different names and entry points and thus need to be renamed.
- // e.g. SYSCALL_DEFINE1(setuid16, old_uid_t, uid) is referred to in the .tbl file with setuid.
- ctx.nodes = append(ctx.nodes, ctx.renameSyscall(node)...)
- case *ast.Include:
- if file, err := filepath.Rel(ctx.cfg.KernelSrc, filepath.Join(ctx.cfg.KernelObj, node.File.Value)); err == nil {
- node.File.Value = file
- }
- if replace := includeReplaces[node.File.Value]; replace != "" {
- node.File.Value = replace
- }
- ctx.nodes = append(ctx.nodes, node)
- case *ast.Comment:
- switch {
- case strings.HasPrefix(node.Text, "INTERFACE:"):
- fields := strings.Fields(node.Text)
- if len(fields) != 6 {
- tool.Failf("%q has wrong number of fields", node.Text)
- }
- for i := range fields {
- if fields[i] == "-" {
- fields[i] = ""
- }
- }
- iface := Interface{
- Type: fields[1],
- Name: fields[2],
- Files: []string{file},
- identifyingConst: fields[3],
- Func: fields[4],
- Access: fields[5],
- }
- if iface.Type == "SYSCALL" {
- for _, name := range ctx.syscallNameMap[iface.Name] {
- iface.Name = name
- iface.identifyingConst = "__NR_" + name
- ctx.mergeInterface(iface)
- }
- } else {
- ctx.mergeInterface(iface)
- }
- default:
- ctx.nodes = append(ctx.nodes, node)
- }
- default:
- ctx.nodes = append(ctx.nodes, node)
- }
- }
-}
-
-// Replace these includes in the tool output.
-var includeReplaces = map[string]string{
- // Arches may use some includes from asm-generic and some from arch/arm.
- // If the arch used for extract used asm-generic for a header,
- // other arches may need arch/asm version of the header. So switch to
- // a more generic file name that should resolve correctly for all arches.
- "include/uapi/asm-generic/ioctls.h": "asm/ioctls.h",
- "include/uapi/asm-generic/sockios.h": "asm/sockios.h",
-}
-
-func getTypeOrder(a ast.Node) int {
- switch a.(type) {
- case *ast.Comment:
- return 0
- case *ast.Include:
- return 1
- case *ast.Define:
- return 2
- case *ast.IntFlags:
- return 3
- case *ast.Resource:
- return 4
- case *ast.TypeDef:
- return 5
- case *ast.Call:
- return 6
- case *ast.Struct:
- return 7
- case *ast.NewLine:
- return 8
- default:
- panic(fmt.Sprintf("unhandled type %T", a))
- }
-}
diff --git a/tools/syz-declextract/syz-declextract.cpp b/tools/syz-declextract/syz-declextract.cpp
deleted file mode 100644
index c348eb16d..000000000
--- a/tools/syz-declextract/syz-declextract.cpp
+++ /dev/null
@@ -1,1016 +0,0 @@
-// Copyright 2024 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-//go:build ignore
-
-#include "clang/AST/APValue.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Attr.h"
-#include "clang/AST/Attrs.inc"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclarationName.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/PrettyPrinter.h"
-#include "clang/AST/Stmt.h"
-#include "clang/AST/Type.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/ASTMatchers/ASTMatchersInternal.h"
-#include "clang/Basic/CharInfo.h"
-#include "clang/Basic/LLVM.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/TypeTraits.h"
-#include "clang/Lex/Lexer.h"
-#include "clang/Sema/Ownership.h"
-#include "clang/Tooling/CommonOptionsParser.h"
-#include "clang/Tooling/Tooling.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Frontend/OpenMP/OMP.h.inc"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/raw_ostream.h"
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <filesystem>
-#include <optional>
-#include <stdio.h>
-#include <string>
-#include <string_view>
-#include <unordered_map>
-#include <vector>
-
-using namespace clang;
-using namespace clang::ast_matchers;
-
-const char *const AccessUnknown = "-";
-const char *const AccessUser = "user";
-const char *const AccessNsAdmin = "ns_admin";
-const char *const AccessAdmin = "admin";
-
-struct Param {
- std::string type;
- std::string name;
-};
-
-struct NetlinkOps {
- std::string cmd;
- std::string func;
- const char *access;
- std::string policy;
-};
-
-struct NetlinkType {
- RecordDecl *decl;
- int64_t len;
-};
-
-struct StructMember {
- std::string type;
- std::string name;
- unsigned int countedBy;
-};
-
-void emitInterface(const char *type, std::string_view name, std::string_view identifying_const,
- std::string_view entry_func = "", const char *access = AccessUnknown) {
- if (entry_func.empty())
- entry_func = "-";
- printf("\n#INTERFACE: %s %s %s %s %s\n\n", type, std::string(name).c_str(), std::string(identifying_const).c_str(),
- std::string(entry_func).c_str(), access);
-}
-
-std::string toIdentifier(std::string name) {
- if (name == "resource" || name == "include" || name == "define" || name == "incdir" || name == "syscall" ||
- name == "parent") {
- return "_" + name;
- }
- std::replace(name.begin(), name.end(), '.', '_');
- std::replace(name.begin(), name.end(), ' ', '_');
- std::replace(name.begin(), name.end(), '-', '_');
- return name;
-}
-
-struct SyzRecordDecl {
- std::string name;
- std::vector<StructMember> members;
- std::string attr;
- bool isUnion;
- bool isVarlen;
- bool operator==(const SyzRecordDecl &decl) { return name == decl.name; }
- bool operator<(const SyzRecordDecl &decl) { return name < decl.name; }
- void print() const {
- if (name.empty()) {
- return;
- }
- const char openBracket = isUnion ? '[' : '{';
- const char closeBracket = isUnion ? ']' : '}';
- printf("%s %c\n", name.c_str(), openBracket);
- for (const auto &member : members) {
- printf("\t%s %s\n", toIdentifier(member.name).c_str(), member.type.c_str());
- }
- putchar(closeBracket);
- if (isUnion && isVarlen) {
- printf("[%s]", "varlen");
- } else if (!isUnion && !attr.empty()) {
- printf("[%s]", attr.c_str());
- }
- puts("");
- }
-};
-
-std::string getDeclFilename(const SourceManager *SM, const Decl *decl) {
- return toIdentifier(
- std::filesystem::path(SM->getFilename(decl->getCanonicalDecl()->getSourceRange().getBegin()).str())
- .filename()
- .stem()
- .string());
-}
-
-// If expression refers to some identifier, returns the identifier name.
-// Otherwise returns an empty string.
-// For example, if the expression is `function_name`, returns "function_name" string.
-// If SM is passed, then it also appends per-file suffix.
-std::string getDeclName(ASTContext &context, const clang::Expr *expr, const SourceManager *SM = nullptr) {
- if (!expr) {
- return "";
- }
- // The expression can be complex and include casts and e.g. InitListExpr,
- // to remove all of these we match the first/any DeclRefExpr.
- struct Matcher : MatchFinder::MatchCallback {
- const DeclRefExpr *decl = nullptr;
- void run(const MatchFinder::MatchResult &Result) override { decl = Result.Nodes.getNodeAs<DeclRefExpr>("decl"); }
- };
- MatchFinder finder;
- Matcher matcher;
- finder.addMatcher(stmt(forEachDescendant(declRefExpr().bind("decl"))), &matcher);
- finder.match(*expr, context);
- if (!matcher.decl) {
- return "";
- }
- std::string name = matcher.decl->getDecl()->getNameAsString();
- if (SM) {
- name += "$auto_" + getDeclFilename(SM, matcher.decl->getDecl());
- }
- return name;
-}
-
-bool endsWith(const std::string_view &str, const std::string_view end) {
- size_t substrBegin = str.rfind(end);
- return substrBegin != std::string::npos && str.substr(substrBegin) == end;
-}
-
-bool beginsWith(const std::string_view &str, const std::string_view begin) {
- size_t substrBegin = str.find(begin);
- return substrBegin != std::string::npos && str.substr(0, begin.size()) == begin;
-}
-
-bool contains(const std::string_view &str, const std::string_view sub) { return str.find(sub) != std::string::npos; }
-
-std::string makeArray(const std::string &type, const size_t min = 0, const size_t max = -1) {
- if (max != size_t(-1)) {
- return "array[" + type + ", " + std::to_string(min) + ":" + std::to_string(max) + "]";
- }
- if (min == 1) {
- return type;
- }
- if (min) {
- return "array[" + type + ", " + std::to_string(min) + "]";
- }
- return "array[" + type + "]";
-}
-
-std::string makePtr(const std::string &dir, const std::string &type, bool isOpt = false) {
- std::string ptr = "ptr[" + dir + ", " + type;
- if (isOpt) {
- return ptr + ", opt]";
- }
- return ptr + "]";
-}
-
-std::string makeConst(bool isSyscallArg, const std::string &type, const std::string &val) {
- if (isSyscallArg) {
- return "const[" + val + "]";
- }
- return "const[" + val + ", " + type + "]";
-}
-
-std::string makeFlags(bool isSyscallArg, const std::string &type, const std::string &flags) {
- if (isSyscallArg) {
- return "flags[" + flags + "]";
- }
- return "flags[" + flags + ", " + type + "]";
-}
-
-std::string int8Subtype(const std::string &name, const bool isSyscallParam) { return "int8"; }
-
-std::string int16Subtype(const std::string &name, const bool isSyscallParam) {
- if (contains(name, "port")) {
- return "sock_port";
- }
- return "int16";
-}
-
-std::string int32Subtype(const std::string &name, const bool isSyscallParam) {
- if (contains(name, "ipv4")) {
- return "ipv4_addr";
- }
- if (endsWith(name, "_pid") || endsWith(name, "_tid") || endsWith(name, "_pgid") || endsWith(name, "_tgid") ||
- name == "pid" || name == "tid" || name == "pgid" || name == "tgid") {
- return "pid";
- }
- if (endsWith(name, "dfd") && !endsWith(name, "oldfd") && !endsWith(name, "pidfd")) {
- return "fd_dir";
- }
- if (endsWith(name, "ns_fd")) {
- return "fd_namespace";
- }
- if (endsWith(name, "_uid") || name == "uid" || name == "user" || name == "ruid" || name == "euid" || name == "suid") {
- return "uid";
- }
- if (endsWith(name, "_gid") || name == "gid" || name == "group" || name == "rgid" || name == "egid" ||
- name == "sgid") {
- return "gid";
- }
- if (endsWith(name, "fd") || beginsWith(name, "fd_") || contains(name, "fildes") || name == "fdin" ||
- name == "fdout") {
- return "fd";
- }
- if (contains(name, "ifindex") || contains(name, "dev_index")) {
- return "ifindex";
- }
- return "int32";
-}
-
-std::string int64Subtype(const std::string &name, const bool isSyscallParam) { return "int64"; }
-
-std::string intptrSubtype(const std::string &name, const bool isSyscallParam) {
- if (name == "sigsetsize") {
- return makeConst(isSyscallParam, "intptr", "8");
- }
- return "intptr";
-}
-
-std::string stringSubtype(const std::string &name, const char *defaultName = "string") {
- if (contains(name, "ifname") || endsWith(name, "dev_name")) {
- return "devname";
- }
- if (contains(name, "filename") || contains(name, "pathname") || contains(name, "dir_name") || name == "oldname" ||
- name == "newname" || name == "path") {
- return "filename";
- }
- return defaultName;
-}
-
-enum IntType {
- INVALID_INT = 0,
- INT_8 = 1,
- INT_16 = 2,
- INT_32 = 4,
- INT_64 = 8,
- INT_PTR,
-};
-
-IntType getIntType(const std::string &ctype, const bool isSyscallParam) {
- // TODO: Handle arm32 passing 64bit arguments
- if (!isSyscallParam && (contains(ctype, "long long") || contains(ctype, "64"))) {
- return INT_64;
- }
- if (contains(ctype, "16") || contains(ctype, "short")) {
- return INT_16;
- }
- if (contains(ctype, "8") || contains(ctype, "char") || ctype == "_Bool") {
- return INT_8;
- }
- if (contains(ctype, "32") || contains(ctype, "int")) {
- return INT_32;
- }
- if (contains(ctype, "long")) {
- return INT_PTR;
- }
- fprintf(stderr, "Unhandled int length for type: %s\n", ctype.c_str());
- exit(1);
-}
-
-const std::string intNSubtype(const std::string &name, const IntType len, const bool isSyscallParam) {
- switch (len) {
- case INT_8:
- return int8Subtype(name, isSyscallParam);
- case INT_16:
- return int16Subtype(name, isSyscallParam);
- case INT_32:
- return int32Subtype(name, isSyscallParam);
- case INT_64:
- return int64Subtype(name, isSyscallParam);
- case INT_PTR:
- return intptrSubtype(name, isSyscallParam);
- default:
- fprintf(stderr, "invalid int type: %d\n", static_cast<int>(len));
- exit(1);
- }
-}
-
-bool isIntN(const std::string &type) {
- return (!type.compare(0, 3, "int") && std::all_of(type.begin() + 3, type.end(), ::isDigit)) || (type == "intptr");
-}
-
-const std::string intSubtype(const std::string &name, const IntType len, const bool isSyscallParam = false) {
- if (len == INVALID_INT) {
- fprintf(stderr, "Invalid int type\n");
- exit(1);
- }
-
- const std::string subType = intNSubtype(name, len, isSyscallParam);
- if (!isIntN(subType)) {
- return subType;
- }
- if (endsWith(name, "enabled") || endsWith(name, "enable")) {
- // Replace "int" with "bool".
- return "bool" + subType.substr(3);
- }
- return subType;
-}
-
-const std::string getSyzType(const std::string &ctype, std::string name, const bool isSyscallParam,
- const int bitFieldWidth = 0) {
- std::transform(name.begin(), name.end(), name.begin(), ::tolower);
- IntType len = getIntType(ctype, isSyscallParam);
- const int byteLen = len * 8;
- if (INT_8 <= len && len <= INT_64 && contains(ctype, "be")) {
- return "int" + std::to_string(byteLen) + "be";
- }
-
- std::string type;
- const bool isBitField = bitFieldWidth;
- if (isBitField) {
- type = "int" + std::to_string(byteLen);
- if (byteLen != bitFieldWidth) {
- type += ":" + std::to_string(bitFieldWidth);
- }
- } else {
- type = intSubtype(name, len, isSyscallParam);
- }
-
- if (isBitField || isIntN(type)) {
- if (name.empty() || contains(name, "pad") || contains(name, "unused") || contains(name, "_reserved")) {
- return makeConst(isSyscallParam, type, "0");
- }
- }
-
- return type;
-}
-
-class RecordExtractor {
-private:
- const SourceManager *const SM;
- std::vector<std::string> includes;
- std::vector<std::string> flags;
- std::unordered_map<std::string, SyzRecordDecl> extractedRecords;
- const std::string emptyStructType = "empty struct";
- const std::string autoTodo = "auto_todo";
-
- unsigned int getCountedBy(const FieldDecl *const &field) {
- return field->getType()->isCountAttributedType()
- ? llvm::dyn_cast<FieldDecl>(
- field->getType()->getAs<CountAttributedType>()->getCountExpr()->getReferencedDeclOfCallee())
- ->getFieldIndex()
- : UINT_MAX;
- }
-
- bool isFieldVarlen(const QualType &fieldType) {
- return fieldType->isIncompleteArrayType() ||
- (fieldType->isConstantArrayType() && llvm::dyn_cast<ConstantArrayType>(fieldType)->getSize().isZero());
- }
-
- std::string getStructAttr(const RecordDecl *const recordDecl, ASTContext *context) {
- if (recordDecl->isStruct() && recordDecl->hasAttrs()) {
- for (const auto &item : recordDecl->getAttrs()) {
- if (item->getKind() == clang::attr::Aligned) {
- return "align[" + std::to_string(llvm::dyn_cast<AlignedAttr>(item)->getAlignment(*context) / 8) + "]";
- } else if (item->getKind() == clang::attr::Packed) {
- return "packed";
- }
- }
- }
- return "";
- }
-
-public:
- RecordExtractor(const SourceManager *const SM) : SM(SM){};
- std::string getFieldType(const QualType &fieldType, ASTContext *context, const std::string &fieldName,
- const std::string &parent = "", bool isSyscallParam = false,
- const std::string &fieldTypeName = "") {
- const auto &field = fieldType.IgnoreParens().getUnqualifiedType().getDesugaredType(*context);
- switch (fieldType.IgnoreParens()->getTypeClass()) {
- case clang::Type::Record: {
- std::string backupName;
- if (!parent.empty()) {
- backupName = parent + "_" + fieldName;
- } else if (!fieldTypeName.empty()) {
- backupName = fieldTypeName;
- } else {
- backupName = fieldName;
- }
- return extractRecord(field->getAsRecordDecl(), context, backupName);
- }
- case clang::Type::IncompleteArray: // Defined as type[]
- return makeArray(getFieldType(llvm::dyn_cast<IncompleteArrayType>(field)->getElementType(), context, fieldName));
- case clang::Type::ConstantArray: {
- const auto &array = llvm::dyn_cast<ConstantArrayType>(field);
- return makeArray(getFieldType(array->getElementType(), context, fieldName), array->getSize().getZExtValue());
- }
- case clang::Type::Pointer: {
- const auto &pointerType = llvm::dyn_cast<PointerType>(field);
- const auto &pointeeType = pointerType->getPointeeType();
- std::string fieldType;
- if (pointeeType->isAnyCharacterType()) {
- fieldType = stringSubtype(fieldName);
- } else if (pointeeType->isVoidType()) {
- fieldType = makeArray(autoTodo);
- } else {
- fieldType = getFieldType(pointeeType, context, fieldName);
- }
- const auto &ptrDir = pointeeType.isConstQualified() ? "in" : "inout"; // TODO: Infer direction of non-const.
- return makePtr(ptrDir, fieldType,
- parent + "$auto_record" == fieldType); // Checks if the direct parent is the same as the node.
- }
- case clang::Type::Builtin:
- return getSyzType(field.getAsString(), fieldName, isSyscallParam);
- case clang::Type::CountAttributed: // Has the attribute counted_by. Handled by getCountedBy
- case clang::Type::BTFTagAttributed: // Currently Unused
- case clang::Type::Typedef:
- return getFieldType(field, context, fieldName, parent, isSyscallParam, field.getAsString());
- case clang::Type::Elaborated:
- return getFieldType(llvm::dyn_cast<ElaboratedType>(fieldType)->desugar(), context, fieldName, parent,
- isSyscallParam); // NOTE: The fieldType contains information we need, don't use field instead.
- case clang::Type::Enum: {
- const auto &enumDecl = llvm::dyn_cast<EnumType>(field)->getDecl();
- auto name = "auto_" + enumDecl->getNameAsString();
- flags.push_back(name);
- includes.push_back(std::filesystem::relative(SM->getFilename(enumDecl->getSourceRange().getBegin()).str()));
- const char *sep = " = ";
- for (const auto &enumerator : enumDecl->enumerators()) {
- flags.back() += sep + enumerator->getNameAsString();
- sep = ", ";
- }
- std::string baseType = "int" + std::to_string(context->getTypeInfo(field).Width);
- return makeFlags(isSyscallParam, baseType, name);
- }
- case clang::Type::FunctionProto:
- return makePtr("in", autoTodo);
- default:
- field->dump();
- fprintf(stderr, "Unhandled field type %s\n", field->getTypeClassName());
- exit(1);
- }
- }
-
- std::string extractRecord(const RecordDecl *recordDecl, ASTContext *context, const std::string &backupName) {
- recordDecl = recordDecl->getDefinition();
- if (!recordDecl) { // When the definition is in a different translation unit.
- return autoTodo;
- }
- const auto &name = (recordDecl->getNameAsString().empty() ? backupName : recordDecl->getNameAsString());
- const auto &recordName = name + "$auto_record";
- if (extractedRecords.find(name) != extractedRecords.end()) { // Don't extract the same record twice.
- return recordName;
- }
- extractedRecords[name];
- bool isVarlen = false;
- std::vector<StructMember> members;
- for (const auto &field : recordDecl->fields()) {
- std::string fieldName;
- if (field->getName().empty()) {
- fieldName = name + "_" + std::to_string(field->getFieldIndex());
- } else if (field->isAnonymousStructOrUnion()) {
- fieldName = name;
- } else {
- fieldName = field->getNameAsString();
- }
- const std::string &parentName = field->isAnonymousStructOrUnion() ? "" : name;
- const std::string &fieldType =
- field->isBitField() ? getSyzType(field->getType().getAsString(), field->isUnnamedBitField() ? "" : fieldName,
- false, field->getBitWidthValue(*context))
- : getFieldType(field->getType(), context, fieldName, parentName);
- if (fieldType == emptyStructType) {
- continue;
- }
- isVarlen |= isFieldVarlen(field->getType()) ||
- (extractedRecords.find(fieldName) != extractedRecords.end() &&
- !extractedRecords[fieldName].name.empty() && extractedRecords[fieldName].isVarlen);
- members.push_back({fieldType, fieldName, getCountedBy(field)});
- }
- if (members.empty()) { // Empty structs are not allowed in Syzlang.
- return emptyStructType;
- }
- extractedRecords[name] = {recordName, std::move(members), getStructAttr(recordDecl, context), recordDecl->isUnion(),
- isVarlen};
- return recordName;
- }
-
- void print() {
- puts("type auto_todo intptr");
- for (const auto &inc : includes) {
- printf("include<%s>\n", inc.c_str());
- }
- for (const auto &flag : flags) {
- puts(flag.c_str());
- }
- for (auto &[_, decl] : extractedRecords) {
- for (auto &member : decl.members) {
- if (member.countedBy != UINT_MAX) {
- auto &type = decl.members[member.countedBy].type;
- type = "len[" + member.name + ", " + type + "]";
- }
- }
- }
- for (const auto &[_, decl] : extractedRecords) {
- decl.print();
- }
- }
-};
-
-struct EnumData {
- std::string name;
- unsigned long long value;
- std::string file;
-};
-
-// Extracts enum info from array variable designated initialization.
-// For example, for the following code:
-//
-// enum Foo {
-// FooA = 11,
-// FooB = 42,
-// };
-//
-// struct Bar bars[] = {
-// [FooA] = {...},
-// [FooB] = {...},
-// };
-//
-// it returns the following map:
-// 11: {"FooA", 11, file.c},
-// 42: {"FooB", 42, file.c},
-std::map<int, EnumData> extractDesignatedInitConsts(ASTContext &context, const VarDecl &arrayDecl) {
- struct DesignatedInitMatcher : MatchFinder::MatchCallback {
- std::vector<EnumData> Inits;
-
- DesignatedInitMatcher(MatchFinder &Finder) {
- Finder.addMatcher(
- decl(forEachDescendant(designatedInitExpr(optionally(has(constantExpr(has(declRefExpr())).bind("init")))))),
- this);
- }
-
- void run(const MatchFinder::MatchResult &Result) override {
- const auto *init = Result.Nodes.getNodeAs<ConstantExpr>("init");
- if (!init) {
- return;
- }
- const auto &name = init->getEnumConstantDecl()->getNameAsString();
- const auto value = *init->getAPValueResult().getInt().getRawData();
- const auto &path = std::filesystem::relative(
- Result.SourceManager->getFilename(init->getEnumConstantDecl()->getSourceRange().getBegin()).str());
- Inits.push_back({std::move(name), value, std::move(path)});
- }
- };
-
- MatchFinder finder;
- DesignatedInitMatcher matcher(finder);
- finder.match(arrayDecl, context);
- std::map<int, EnumData> ordered;
- for (auto &init : matcher.Inits) {
- ordered[init.value] = init;
- }
- return ordered;
-}
-
-class SyscallMatcher : public MatchFinder::MatchCallback {
-public:
- SyscallMatcher(MatchFinder &Finder) {
- Finder.addMatcher(functionDecl(isExpandedFromMacro("SYSCALL_DEFINEx"), matchesName("__do_sys_.*")).bind("syscall"),
- this);
- }
-
-private:
- void run(const MatchFinder::MatchResult &Result) override {
- ASTContext *context = Result.Context;
- const auto *syscall = Result.Nodes.getNodeAs<FunctionDecl>("syscall");
- RecordExtractor recordExtractor(Result.SourceManager);
-
- const char *sep = "";
- const auto func = syscall->getNameAsString();
- const auto &name = func.substr(9); // Remove "__do_sys_" prefix.
- emitInterface("SYSCALL", name, "__NR_" + name, func);
- printf("%s(", name.c_str());
- for (const auto &param : syscall->parameters()) {
- const auto &type = recordExtractor.getFieldType(param->getType(), context, param->getNameAsString(), "", true);
- const auto &name = param->getNameAsString();
- printf("%s%s %s", sep, toIdentifier(name).c_str(), type.c_str());
- sep = ", ";
- }
- printf(") (automatic)\n");
- recordExtractor.print();
- }
-};
-
-class NetlinkPolicyMatcher : public MatchFinder::MatchCallback {
-public:
- NetlinkPolicyMatcher(MatchFinder &Finder) {
- Finder.addMatcher(
- translationUnitDecl(
- hasDescendant(enumDecl(has(enumConstantDecl(hasName("__NLA_TYPE_MAX")))).bind("NLA_ENUM")),
- forEachDescendant(
- varDecl(hasType(constantArrayType(hasElementType(hasDeclaration(
- recordDecl(hasName("nla_policy")).bind("nla_policy"))))
- .bind("nla_policy_array")),
- isDefinition())
- .bind("netlink"))),
- this);
- Finder.addMatcher(varDecl(hasType(recordDecl(hasName("genl_family")).bind("genl_family")),
- has(initListExpr().bind("genl_family_init")))
- .bind("genl_family_decl"),
- this);
- }
-
-private:
- void run(const MatchFinder::MatchResult &Result) override {
- nlaEnum(Result); // NOTE: Must be executed first, as it generates maps that are used in the following methods.
- netlink(Result);
- genlFamily(Result);
- }
-
- // u8ToNlaEnum stores the Enum values to string conversions. This is later used to transfer types from an unnamed
- // integer to a readable form. E.g. 1 -> NLA_U8
- // See: https://elixir.bootlin.com/linux/v6.10/source/include/net/netlink.h#L172
- std::unordered_map<uint8_t, std::string> u8ToNlaEnum;
- void nlaEnum(const MatchFinder::MatchResult &Result) {
- const auto &num = Result.Nodes.getNodeAs<EnumDecl>("NLA_ENUM");
- if (!num || !u8ToNlaEnum.empty()) { // Don't evaluate the Enum twice
- return;
- }
- for (const auto &enumerator : num->enumerators()) {
- const auto &name = enumerator->getNameAsString();
- const auto val = uint8_t(enumerator->getValue().getZExtValue());
- u8ToNlaEnum[val] = name.substr(4); // Remove NLA_ prefix
- }
- }
-
- const std::string nlaArraySubtype(const std::string &name, const std::string &type, const size_t len,
- const std::string &typeOfLen) {
- if (!typeOfLen.empty()) {
- return len == 0 ? typeOfLen : makeArray(typeOfLen, 0, len);
- }
- switch (len) {
- case 0:
- return makeArray("int8");
- case 1:
- case 2:
- case 4:
- case 8:
- return intSubtype(name, IntType(len));
- default:
- if (contains(name, "IPV6")) {
- return "ipv6_addr";
- }
- if (type == "BINARY") {
- return makeArray("int8", 0, len);
- }
- return makeArray("int8", len);
- }
- }
-
- const std::string nlaToSyz(std::string name, const std::string &type, const size_t len,
- const std::string &typeOfLen) {
- std::transform(name.begin(), name.end(), name.begin(), ::tolower);
- // TODO:Gather information from other defined fields to better specify a type.
- // Loosely based on https://elixir.bootlin.com/linux/v6.10/source/lib/nlattr.c
- if (type == "U8" || type == "S8") {
- return intSubtype(name, INT_8);
- }
- if (type == "U16" || type == "S16") {
- return intSubtype(name, INT_16);
- }
- if (type == "U32" || type == "S32") {
- return intSubtype(name, INT_32);
- }
- if (type == "U64" || type == "S64" || type == "SINT" || type == "UINT" || type == "MSECS") {
- return intSubtype(name, INT_64);
- }
- if (type == "BINARY" || type == "UNSPEC") {
- return nlaArraySubtype(name, type, len, typeOfLen);
- }
- if (type == "BE16") {
- return "int16be";
- }
- if (type == "BE32") {
- return "int32be";
- }
- if (type == "FLAG") {
- return "void";
- }
- if (type == "STRING") {
- return stringSubtype(name, "stringnoz");
- }
- if (type == "NUL_STRING") {
- return stringSubtype(name);
- }
- if (type == "BITFIELD32") { // TODO:Extract valued values from NLA_POLICY_BITFIELD32 macro.
- return "int32";
- }
- if (type == "NESTED") {
- return makeArray(typeOfLen.empty() ? "nl_generic_attr" : typeOfLen);
- }
- if (type == "NESTED_ARRAY") {
- return "array[nlnest[0, array[" + (typeOfLen.empty() ? "nl_generic_attr" : typeOfLen) + "]]]";
- }
- fprintf(stderr, "Unsupported netlink type %s\n", type.c_str());
- exit(1);
- }
-
- RecordDecl *getStructFromSizeof(UnaryExprOrTypeTraitExpr *stmt) {
- if (!stmt || stmt->getKind() != clang::UETT_SizeOf) {
- return NULL;
- }
- return stmt->getTypeOfArgument()->getAsRecordDecl();
- }
-
- NetlinkType getStructAndLenFromBinary(BinaryOperator *stmt, ASTContext *context) {
- const auto &lhs = stmt->getLHS();
- const auto &rhs = stmt->getRHS();
-
- // NOTE: Usually happens in case of NESTED_POLICY which is not handled currently.
- // TODO: Handle NESTED_POLICY
- if (lhs->getStmtClass() == clang::Stmt::BinaryOperatorClass ||
- rhs->getStmtClass() == clang::Stmt::BinaryOperatorClass) {
- return {NULL, 0};
- }
- auto decl = getStructFromSizeof(llvm::dyn_cast<UnaryExprOrTypeTraitExpr>(lhs));
- Expr::EvalResult len;
- if (!decl) {
- decl = getStructFromSizeof(llvm::dyn_cast<UnaryExprOrTypeTraitExpr>(rhs));
- lhs->EvaluateAsConstantExpr(len, *context);
- } else {
- rhs->EvaluateAsConstantExpr(len, *context);
- }
- return NetlinkType{decl, len.Val.getInt().getExtValue()};
- }
-
- // Returns the struct type from .len field.
- // e.g. if .len = sizeof(struct x * LEN), returns the declaration of struct x and LEN
- NetlinkType getNetlinkStruct(clang::Expr *stmt, ASTContext *context) {
- stmt = stmt->IgnoreParens();
- Expr::EvalResult len;
- stmt->EvaluateAsConstantExpr(len, *context);
- switch (stmt->getStmtClass()) {
- case clang::Stmt::ImplicitValueInitExprClass:
- return NetlinkType{NULL, 0};
- case clang::Stmt::BinaryOperatorClass:
- return getStructAndLenFromBinary(llvm::dyn_cast<BinaryOperator>(stmt), context);
- case clang::Stmt::UnaryExprOrTypeTraitExprClass:
- return NetlinkType{getStructFromSizeof(llvm::dyn_cast<UnaryExprOrTypeTraitExpr>(stmt)), 0};
- case clang::Stmt::UnaryOperatorClass:
- case clang::Stmt::DeclRefExprClass:
- case clang::Stmt::CStyleCastExprClass:
- case clang::Stmt::IntegerLiteralClass:
- return NetlinkType{NULL, len.Val.getInt().getExtValue()};
- default:
- fprintf(stderr, "Unhandled .len case %s\n", stmt->getStmtClassName());
- exit(1);
- }
- }
-
- void netlink(const MatchFinder::MatchResult &Result) {
- ASTContext *context = Result.Context;
- const auto *netlinkDecl = Result.Nodes.getNodeAs<VarDecl>("netlink");
- if (!netlinkDecl) {
- return;
- }
-
- const auto *init = netlinkDecl->getInit();
- if (!init) {
- return;
- }
- std::vector<std::vector<Expr *>> fields;
- for (const auto &policy : *llvm::dyn_cast<InitListExpr>(init)) {
- fields.push_back({});
- for (const auto &member : policy->children()) {
- fields.back().push_back(llvm::dyn_cast<Expr>(member));
- }
- }
-
- auto enumData = extractDesignatedInitConsts(*context, *netlinkDecl);
- if (enumData.empty()) {
- // We need to emit at least some type for it.
- // Ideally it should be void, but typedef to void currently does not work.
- printf("type %s auto_todo\n", getPolicyName(Result, netlinkDecl).c_str());
- return;
- }
- for (const auto &[_, item] : enumData) {
- if (!endsWith(item.file, ".h")) {
- continue;
- }
- printf("include <%s>\n", item.file.c_str());
- }
-
- RecordExtractor recordExtractor(Result.SourceManager);
- printf("%s [\n", getPolicyName(Result, netlinkDecl).c_str());
- for (size_t i = 0; i < fields.size(); ++i) {
- // The array could have an implicitly initialized policy (i.e. empty) or an unnamed attribute
- if (fields[i].empty() || enumData[i].name.empty()) {
- continue;
- }
-
- Expr::EvalResult evalResult;
- fields[i][0]->EvaluateAsConstantExpr(evalResult, *context); // This contains the NLA Enum type
- const auto &nlaEnum = u8ToNlaEnum[evalResult.Val.getInt().getZExtValue()];
- if (nlaEnum == "REJECT") {
- continue;
- }
- auto [structDecl, len] = getNetlinkStruct(fields[i][2]->IgnoreCasts(), context);
- std::string netlinkStruct;
- if (structDecl) {
- netlinkStruct = recordExtractor.extractRecord(structDecl, context, enumData[i].name);
- } else {
- fields[i][2]->EvaluateAsConstantExpr(evalResult, *context);
- len = evalResult.Val.getInt().getExtValue();
- }
- const char *nlattr = "nlattr";
- if (nlaEnum == "NESTED" || nlaEnum == "NESTED_ARRAY") {
- nlattr = "nlnest";
- netlinkStruct = getDeclName(*context, fields[i][3], Result.SourceManager);
- }
- printf("\t%s %s[%s, %s]\n", enumData[i].name.c_str(), nlattr, enumData[i].name.c_str(),
- nlaToSyz(enumData[i].name, nlaEnum, len, netlinkStruct).c_str());
- }
- puts("] [varlen]");
- recordExtractor.print();
- }
-
- std::map<std::string, unsigned> genlFamilyMember;
-
- std::string getPolicyName(const MatchFinder::MatchResult &Result, const ValueDecl *decl) {
- if (!decl) {
- return "";
- }
- // Filename is added to address ambiguity when multiple policies
- // are named the same but have different definitions.
- return decl->getNameAsString() + "$auto_" + getDeclFilename(Result.SourceManager, decl);
- }
-
- std::vector<NetlinkOps> getOps(const MatchFinder::MatchResult &Result, const std::string &opsName,
- const InitListExpr *init) {
- ASTContext *context = Result.Context;
- const auto n_ops = init->getInit(genlFamilyMember["n_" + opsName])->getIntegerConstantExpr(*context);
- const auto &opsRef = init->getInit(genlFamilyMember[opsName])->getAsBuiltinConstantDeclRef(*context);
- if (!n_ops || !opsRef) {
- return {};
- }
- const auto *opsDecl = llvm::dyn_cast<VarDecl>(opsRef);
- if (!opsDecl->getInit()) {
- // NOTE: This usually happens when the ops is defined as an extern variable
- // TODO: Extract extern variables
- return {};
- }
- const auto *opsInit = llvm::dyn_cast<InitListExpr>(opsDecl->getInit());
- std::map<std::string, unsigned> opsMember;
- for (const auto &field : opsInit->getInit(0)->getType()->getAsRecordDecl()->fields()) {
- opsMember[field->getNameAsString()] = field->getFieldIndex();
- }
- std::vector<NetlinkOps> ops;
- for (int i = 0; i < n_ops; ++i) {
- const auto &init = llvm::dyn_cast<InitListExpr>(opsInit->getInit(i));
- const auto &cmdInit = init->getInit(opsMember["cmd"])->getEnumConstantDecl();
- if (!cmdInit) {
- continue;
- }
- const auto &cmd = cmdInit->getNameAsString();
- const ValueDecl *policyDecl = nullptr;
- if (opsName != "small_ops") {
- policyDecl = init->getInit(opsMember["policy"])->getAsBuiltinConstantDeclRef(*context);
- }
- std::string func = getDeclName(*context, init->getInit(opsMember["doit"]));
- if (func.empty())
- func = getDeclName(*context, init->getInit(opsMember["dumpit"]));
- const Expr *flagsDecl = init->getInit(opsMember["flags"]);
- Expr::EvalResult flags;
- flagsDecl->EvaluateAsConstantExpr(flags, *context);
- auto flagsVal = flags.Val.getInt().getExtValue();
- const char *access = AccessUser;
- constexpr int GENL_ADMIN_PERM = 0x01;
- constexpr int GENL_UNS_ADMIN_PERM = 0x10;
- if (flagsVal & GENL_ADMIN_PERM)
- access = AccessAdmin;
- else if (flagsVal & GENL_UNS_ADMIN_PERM)
- access = AccessNsAdmin;
- ops.push_back({std::move(cmd), func, access, getPolicyName(Result, policyDecl)});
- }
- return ops;
- }
-
- void genlFamily(const MatchFinder::MatchResult &Result) {
- ASTContext *context = Result.Context;
- const auto *genlFamilyInit = Result.Nodes.getNodeAs<InitListExpr>("genl_family_init");
- if (!genlFamilyInit) {
- return;
- }
- if (genlFamilyMember.empty()) {
- const auto *genlFamily = Result.Nodes.getNodeAs<RecordDecl>("genl_family");
- for (const auto &field : genlFamily->fields()) {
- genlFamilyMember[field->getNameAsString()] = field->getFieldIndex();
- }
- }
-
- const auto &globalPolicyName =
- genlFamilyInit->getInit(genlFamilyMember["policy"])->getAsBuiltinConstantDeclRef(*context);
-
- std::string familyPolicyName;
- if (globalPolicyName) {
- familyPolicyName = getPolicyName(Result, globalPolicyName);
- }
-
- std::string familyName =
- llvm::dyn_cast<StringLiteral>(genlFamilyInit->getInit(genlFamilyMember["name"]))->getString().str();
- std::string identifierName = toIdentifier(familyName);
- std::string msghdr = "msghdr_" + identifierName + "_auto";
- bool printedCmds = false;
- for (const auto &opsType : {"ops", "small_ops", "split_ops"}) {
- for (auto &ops : getOps(Result, opsType, genlFamilyInit)) {
- const char *policyName;
- if (!ops.policy.empty()) {
- policyName = ops.policy.c_str();
- } else if (globalPolicyName) {
- policyName = familyPolicyName.c_str();
- } else {
- continue;
- }
- emitInterface("NETLINK", ops.cmd, ops.cmd, ops.func, ops.access);
- printf("sendmsg$auto_%s(fd sock_nl_generic, msg ptr[in, %s[%s, %s]], f flags[send_flags]) (automatic)\n",
- ops.cmd.c_str(), msghdr.c_str(), ops.cmd.c_str(), policyName);
- printedCmds = true;
- }
- }
- if (!printedCmds) { // Do not print resources and types if they're not used in any cmds
- return;
- }
- std::string resourceName = "genl_" + identifierName + "_family_id_auto";
- printf("resource %s[int16]\n", resourceName.c_str());
- printf("type %s[CMD, POLICY] msghdr_netlink[netlink_msg_t[%s, genlmsghdr_t[CMD], POLICY]]\n", msghdr.c_str(),
- resourceName.c_str());
- printf("syz_genetlink_get_family_id$auto_%s(name ptr[in, string[\"%s\"]], fd sock_nl_generic) %s (automatic)\n",
- identifierName.c_str(), familyName.c_str(), resourceName.c_str());
- }
-};
-
-class IouringMatcher : public MatchFinder::MatchCallback {
-public:
- IouringMatcher(MatchFinder &Finder) {
- Finder.addMatcher(
- translationUnitDecl(forEachDescendant(
- varDecl(hasType(constantArrayType(hasElementType(hasDeclaration(recordDecl(hasName("io_issue_def")))))),
- isDefinition())
- .bind("io_issue_defs"))),
- this);
- }
-
-private:
- void run(const MatchFinder::MatchResult &Result) override {
- ASTContext *context = Result.Context;
- const auto *ioIssueDefs = Result.Nodes.getNodeAs<VarDecl>("io_issue_defs");
- if (!ioIssueDefs) {
- return;
- }
- auto elements = extractDesignatedInitConsts(*Result.Context, *ioIssueDefs);
- const auto *initList = llvm::dyn_cast<InitListExpr>(ioIssueDefs->getInit());
- std::map<std::string, unsigned> fields;
- for (const auto &field : initList->getInit(0)->getType()->getAsRecordDecl()->fields()) {
- fields[field->getNameAsString()] = field->getFieldIndex();
- }
- for (const auto &[i, op] : elements) {
- const auto &init = llvm::dyn_cast<InitListExpr>(initList->getInit(i));
- std::string prep = getDeclName(*context, init->getInit(fields["prep"]));
- if (prep == "io_eopnotsupp_prep") {
- continue;
- }
- std::string issue = getDeclName(*context, init->getInit(fields["issue"]));
- emitInterface("IOURING", op.name, op.name, issue, AccessUser);
- }
- }
-};
-
-int main(int argc, const char **argv) {
- llvm::cl::OptionCategory SyzDeclExtractOptionCategory("syz-declextract options");
- auto ExpectedParser = clang::tooling::CommonOptionsParser::create(argc, argv, SyzDeclExtractOptionCategory);
- if (!ExpectedParser) {
- llvm::errs() << ExpectedParser.takeError();
- return 1;
- }
-
- MatchFinder Finder;
- SyscallMatcher SyscallMatcher(Finder);
- NetlinkPolicyMatcher NetlinkPolicyMatcher(Finder);
- IouringMatcher IouringMatcher(Finder);
-
- clang::tooling::CommonOptionsParser &OptionsParser = ExpectedParser.get();
- clang::tooling::ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());
- return Tool.run(clang::tooling::newFrontendActionFactory(&Finder).get());
-}
diff --git a/tools/syz-declextract/testdata/README.md b/tools/syz-declextract/testdata/README.md
new file mode 100644
index 000000000..494ddac6a
--- /dev/null
+++ b/tools/syz-declextract/testdata/README.md
@@ -0,0 +1,28 @@
+This dir contains sources of a fake kernel that resembles Linux for testing of the `syz-declextract` tool.
+
+For each `*.c` file there 3 golden files:
+ - `*.c.json` with the expected output of the clang tool
+ - `*.c.txt` with the expected syzlang descriptions
+ - `*.c.info` with the expected kernel interface list
+
+Testing is done by `tools/syz-declextract` tests.
+
+`TestClangTool` invokes the clang tool and verifies `*.c.json` contents.
+The test requires the clang tool binary specified in the `-bin`, otherwise it skips testing.
+You also want to run it with `-count=1` flag since the Go tool does not detect changes in the tool binary,
+and will cache and reuse test results:
+```
+go test -count=1 ./tools/syz-declextract -bin=llvm/build/bin/syz-declextract
+```
+
+`TestDeclextract` verifies `*.c.txt` and `*.c.info` using `*.c.json` files as inputs
+(it does not involve the clang tool and runs always).
+
+All tests also support `-update` flag, which updates the golden files.
+Generally you don't need to update them manually.
+
+Since the test covers multiple packages, it's useful to run coverage as follows:
+```
+go test -count=1 -coverprofile=/tmp/cover -coverpkg="github.com/google/syzkaller/tools/syz-declextract,github.com/google/syzkaller/pkg/declextract,github.com/google/syzkaller/pkg/clangtool" ./tools/syz-declextract -bin=llvm/build/bin/syz-declextract
+go tool cover -html /tmp/cover
+```
diff --git a/tools/syz-declextract/testdata/arch/arm/syscalls.tbl b/tools/syz-declextract/testdata/arch/arm/syscalls.tbl
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/syz-declextract/testdata/arch/arm/syscalls.tbl
diff --git a/tools/syz-declextract/testdata/arch/arm64/syscalls.tbl b/tools/syz-declextract/testdata/arch/arm64/syscalls.tbl
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/syz-declextract/testdata/arch/arm64/syscalls.tbl
diff --git a/tools/syz-declextract/testdata/arch/mips/syscalls.tbl b/tools/syz-declextract/testdata/arch/mips/syscalls.tbl
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/syz-declextract/testdata/arch/mips/syscalls.tbl
diff --git a/tools/syz-declextract/testdata/arch/powerpc/syscalls.tbl b/tools/syz-declextract/testdata/arch/powerpc/syscalls.tbl
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/syz-declextract/testdata/arch/powerpc/syscalls.tbl
diff --git a/tools/syz-declextract/testdata/arch/riscv/syscalls.tbl b/tools/syz-declextract/testdata/arch/riscv/syscalls.tbl
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/syz-declextract/testdata/arch/riscv/syscalls.tbl
diff --git a/tools/syz-declextract/testdata/arch/s390/syscalls.tbl b/tools/syz-declextract/testdata/arch/s390/syscalls.tbl
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/syz-declextract/testdata/arch/s390/syscalls.tbl
diff --git a/tools/syz-declextract/testdata/arch/x86/syscalls.tbl b/tools/syz-declextract/testdata/arch/x86/syscalls.tbl
new file mode 100644
index 000000000..19309c4ef
--- /dev/null
+++ b/tools/syz-declextract/testdata/arch/x86/syscalls.tbl
@@ -0,0 +1,6 @@
+# Copyright 2024 syzkaller project authors. All rights reserved.
+# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+1 64 open sys_open
+2 64 chmod sys_chmod
+3 64 types_syscall sys_types_syscall
diff --git a/tools/syz-declextract/testdata/include/netlink.h b/tools/syz-declextract/testdata/include/netlink.h
new file mode 100644
index 000000000..ee8091290
--- /dev/null
+++ b/tools/syz-declextract/testdata/include/netlink.h
@@ -0,0 +1,86 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#include "types.h"
+
+enum {
+ NLA_UNSPEC,
+ NLA_U8,
+ NLA_U16,
+ NLA_U32,
+ NLA_U64,
+ NLA_STRING,
+ NLA_FLAG,
+ NLA_MSECS,
+ NLA_NESTED,
+ NLA_NESTED_ARRAY,
+ NLA_NUL_STRING,
+ NLA_BINARY,
+ NLA_S8,
+ NLA_S16,
+ NLA_S32,
+ NLA_S64,
+ NLA_BITFIELD32,
+ NLA_REJECT,
+ NLA_BE16,
+ NLA_BE32,
+ NLA_SINT,
+ NLA_UINT,
+ __NLA_TYPE_MAX,
+};
+
+struct nla_policy {
+ u8 type;
+ u8 validation_type;
+ u16 len;
+ union {
+ const u32 bitfield32_valid;
+ const u32 mask;
+ const struct nla_policy *nested_policy;
+ struct { s16 min, max; };
+ };
+};
+
+#define NLA_POLICY_NESTED(policy) { .type = NLA_NESTED, .nested_policy = policy, .len = sizeof(policy)/sizeof(policy[0]) }
+
+#define GENL_ADMIN_PERM 0x01
+#define GENL_UNS_ADMIN_PERM 0x10
+
+struct genl_ops {
+ u8 cmd;
+ u8 flags;
+ const struct nla_policy* policy;
+ void (*doit)(void);
+ void (*dumpit)(void);
+};
+
+struct genl_split_ops {
+ u8 cmd;
+ u8 flags;
+ const struct nla_policy *policy;
+ union {
+ struct {
+ void (*pre_doit)(void);
+ void (*doit)(void);
+ void (*post_doit)(void);
+ };
+ struct {
+ void (*start)(void);
+ void (*dumpit)(void);
+ void (*done)(void);
+ };
+ };
+};
+
+struct genl_small_ops {};
+
+struct genl_family {
+ char name[64];
+ u8 n_ops;
+ u8 n_small_ops;
+ u8 n_split_ops;
+ const struct nla_policy* policy;
+ const struct genl_ops* ops;
+ const struct genl_small_ops* mall_ops;
+ const struct genl_split_ops* split_ops;
+};
diff --git a/tools/syz-declextract/testdata/include/syscall.h b/tools/syz-declextract/testdata/include/syscall.h
new file mode 100644
index 000000000..601480cb4
--- /dev/null
+++ b/tools/syz-declextract/testdata/include/syscall.h
@@ -0,0 +1,7 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#define SYSCALL_DEFINE1(NAME, ...) SYSCALL_DEFINEx(1, NAME, __VA_ARGS__)
+#define SYSCALL_DEFINE2(NAME, ...) SYSCALL_DEFINEx(2, NAME, __VA_ARGS__)
+#define SYSCALL_DEFINEx(NARGS, NAME, ...) long __do_sys_##NAME(__VA_ARGS__); \
+long __do_sys_##NAME(__VA_ARGS__)
diff --git a/tools/syz-declextract/testdata/include/types.h b/tools/syz-declextract/testdata/include/types.h
new file mode 100644
index 000000000..5b1d6303c
--- /dev/null
+++ b/tools/syz-declextract/testdata/include/types.h
@@ -0,0 +1,13 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+typedef signed char s8;
+typedef short s16;
+typedef int s32;
+typedef long long s64;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
diff --git a/tools/syz-declextract/testdata/include/uapi/io_uring.h b/tools/syz-declextract/testdata/include/uapi/io_uring.h
new file mode 100644
index 000000000..cdc5a372a
--- /dev/null
+++ b/tools/syz-declextract/testdata/include/uapi/io_uring.h
@@ -0,0 +1,9 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+enum io_uring_op {
+ IORING_OP_NOP,
+ IORING_OP_READV,
+ IORING_OP_WRITEV,
+ IORING_OP_NOT_SUPPORTED,
+};
diff --git a/tools/syz-declextract/testdata/include/uapi/netlink_family.h b/tools/syz-declextract/testdata/include/uapi/netlink_family.h
new file mode 100644
index 000000000..ffaf66bf7
--- /dev/null
+++ b/tools/syz-declextract/testdata/include/uapi/netlink_family.h
@@ -0,0 +1,27 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+// Description of some hypothetic netlink family.
+
+enum netlink_foo_cmds {
+ NETLINK_FOO_CMD_FOO,
+ NETLINK_FOO_CMD_BAR,
+};
+
+enum netlink_foo_attrs {
+ NETLINK_FOO_ATTR1,
+ NETLINK_FOO_ATTR2,
+ NETLINK_FOO_ATTR3 = NETLINK_FOO_ATTR2 + 3, // make them non-dense
+ NETLINK_FOO_ATTR4,
+ NETLINK_FOO_ATTR5,
+ NETLINK_FOO_ATTR6,
+ NETLINK_FOO_ATTR7,
+};
+
+struct netlink_foo_struct1 {
+ int a, b, c;
+};
+
+typedef struct {
+ double a, b, c;
+} netlink_foo_struct2;
diff --git a/tools/syz-declextract/testdata/io_uring.c b/tools/syz-declextract/testdata/io_uring.c
new file mode 100644
index 000000000..20f85f0e5
--- /dev/null
+++ b/tools/syz-declextract/testdata/io_uring.c
@@ -0,0 +1,36 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#include "include/uapi/io_uring.h"
+
+struct io_issue_def {
+ void (*prep)(void);
+ void (*issue)(void);
+};
+
+void io_eopnotsupp_prep() {}
+void io_nop_prep() {}
+void io_nop() {}
+void io_readv_prep() {}
+void io_read() {}
+void io_writev_prep() {}
+void io_write() {}
+
+const struct io_issue_def ops[] = {
+ [IORING_OP_NOP] = {
+ .prep = io_nop_prep,
+ .issue = io_nop,
+ },
+ [IORING_OP_READV] = {
+ .prep = io_readv_prep,
+ .issue = io_read,
+ },
+ [IORING_OP_WRITEV] = {
+ .prep = io_writev_prep,
+ .issue = io_write,
+ },
+ [IORING_OP_NOT_SUPPORTED] = {
+ .prep = io_eopnotsupp_prep,
+ .issue = io_write,
+ },
+};
diff --git a/tools/syz-declextract/testdata/io_uring.c.info b/tools/syz-declextract/testdata/io_uring.c.info
new file mode 100644
index 000000000..6757eca74
--- /dev/null
+++ b/tools/syz-declextract/testdata/io_uring.c.info
@@ -0,0 +1,3 @@
+IOURING IORING_OP_NOP func:io_nop access:user manual_desc:false auto_desc:false file:io_uring.c subsystem:kernel
+IOURING IORING_OP_READV func:io_read access:user manual_desc:false auto_desc:false file:io_uring.c subsystem:kernel
+IOURING IORING_OP_WRITEV func:io_write access:user manual_desc:false auto_desc:false file:io_uring.c subsystem:kernel
diff --git a/tools/syz-declextract/testdata/io_uring.c.json b/tools/syz-declextract/testdata/io_uring.c.json
new file mode 100644
index 000000000..927adfe26
--- /dev/null
+++ b/tools/syz-declextract/testdata/io_uring.c.json
@@ -0,0 +1,22 @@
+{
+ "includes": [
+ "include/uapi/io_uring.h"
+ ],
+ "iouring_ops": [
+ {
+ "name": "IORING_OP_NOP",
+ "func": "io_nop",
+ "source_file": "io_uring.c"
+ },
+ {
+ "name": "IORING_OP_READV",
+ "func": "io_read",
+ "source_file": "io_uring.c"
+ },
+ {
+ "name": "IORING_OP_WRITEV",
+ "func": "io_write",
+ "source_file": "io_uring.c"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tools/syz-declextract/testdata/io_uring.c.txt b/tools/syz-declextract/testdata/io_uring.c.txt
new file mode 100644
index 000000000..3ddbbcf40
--- /dev/null
+++ b/tools/syz-declextract/testdata/io_uring.c.txt
@@ -0,0 +1,10 @@
+# Code generated by syz-declextract. DO NOT EDIT.
+
+meta automatic
+
+type auto_todo intptr
+
+include <vdso/bits.h>
+include <linux/types.h>
+include <net/netlink.h>
+include <include/uapi/io_uring.h>
diff --git a/tools/syz-declextract/testdata/manual.txt b/tools/syz-declextract/testdata/manual.txt
new file mode 100644
index 000000000..31ab63c9c
--- /dev/null
+++ b/tools/syz-declextract/testdata/manual.txt
@@ -0,0 +1,50 @@
+# Copyright 2024 syzkaller project authors. All rights reserved.
+# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+# This file contains manual descriptions that are required to compile auto-generated descriptions.
+
+resource fd[int32]
+resource sock_nl_generic[fd]
+resource pid[int32]
+
+type ifindex int32
+
+create$pid() pid (automatic_helper)
+create$sock_nl_generic() sock_nl_generic (automatic_helper)
+sendmsg$netlink(fd sock_nl_generic, data ptr[in, msghdr_netlink[netlink_msg_t[int32, genlmsghdr_t[1], int32]]], flags flags[send_flags])
+
+use(a ptr[in, use])
+use {
+ f0 pid
+ f1 ifindex
+ f2 auto_todo
+ f3 nlattr[1, int32]
+ f4 nlnest[1, int32]
+ f5 nl_generic_attr
+
+}
+
+type msghdr_netlink[MSG] {
+ vec ptr[in, MSG]
+}
+
+type netlink_msg_t[TYPE, PAYLOAD, ATTRS] {
+ type TYPE
+ payload PAYLOAD
+ attrs array[ATTRS]
+}
+
+type genlmsghdr_t[CMD] {
+ cmd const[CMD, int8]
+}
+
+type nlattr[ATTR, TYPE] {
+ attr const[ATTR, int32]
+ data TYPE
+}
+
+type nlnest[ATTR, TYPE] nlattr[ATTR, TYPE]
+
+type nl_generic_attr int32
+
+send_flags = 1, 2
diff --git a/tools/syz-declextract/testdata/netlink.c b/tools/syz-declextract/testdata/netlink.c
new file mode 100644
index 000000000..355b84f1f
--- /dev/null
+++ b/tools/syz-declextract/testdata/netlink.c
@@ -0,0 +1,87 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#include "include/netlink.h"
+#include "include/uapi/netlink_family.h"
+
+// These consts are defined not in uapi .h, so the descriptions should contain
+// values for them rather than includes.
+enum netlink_foo_nested_attrs {
+ NETLINK_FOO_NESTED_ATTR1,
+ NETLINK_FOO_NESTED_ATTR2,
+};
+
+static netlink_foo_struct2 var;
+
+const struct nla_policy foo_genl_nested_policy[] = {
+ [NETLINK_FOO_NESTED_ATTR1] = { .type = NLA_U32 },
+ [NETLINK_FOO_NESTED_ATTR2] = { .type = NLA_U32 },
+};
+
+const struct nla_policy foo_genl_policy[] = {
+ [NETLINK_FOO_ATTR1] = { .type = NLA_U32 },
+ [NETLINK_FOO_ATTR2] = { .type = NLA_STRING, .len = 10 },
+ [NETLINK_FOO_ATTR3] = { .type = NLA_NESTED },
+ [NETLINK_FOO_ATTR4] = NLA_POLICY_NESTED(foo_genl_nested_policy),
+ [NETLINK_FOO_ATTR5] = { .len = sizeof(struct netlink_foo_struct1) },
+ [NETLINK_FOO_ATTR6] = { .len = sizeof(netlink_foo_struct2) * 10 },
+ [NETLINK_FOO_ATTR7] = { .len = sizeof(var) },
+};
+
+const struct nla_policy genl_policy_reject_all[] = {
+ { .type = NLA_REJECT },
+ { .type = NLA_REJECT },
+};
+
+const struct nla_policy policy_forward_decl[10];
+
+static void foo_cmd() {}
+static void bar_cmd() {}
+
+static const struct genl_ops foo_ops[] = {
+ {
+ .cmd = NETLINK_FOO_CMD_FOO,
+ .flags = GENL_ADMIN_PERM,
+ .doit = foo_cmd,
+ },
+ {
+ .cmd = NETLINK_FOO_CMD_BAR,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .dumpit = bar_cmd,
+ },
+};
+
+static struct genl_family foo_family = {
+ .ops = foo_ops,
+ .n_ops = ARRAY_SIZE(foo_ops),
+ .name = "foo family",
+ .policy = foo_genl_policy,
+};
+
+enum {
+ NETLINK_BAR_CMD_FOO,
+};
+
+static void bar_pre_doit() {}
+static void bar_doit() {}
+static void bar_post_doit() {}
+
+static const struct genl_split_ops bar_ops[] = {
+ {
+ .cmd = NETLINK_BAR_CMD_FOO,
+ .pre_doit = bar_pre_doit,
+ .doit = bar_doit,
+ .post_doit = bar_post_doit,
+ },
+};
+
+struct genl_family bar_family = {
+ .split_ops = bar_ops,
+ .n_split_ops = ARRAY_SIZE(bar_ops),
+ .name = "BAR",
+ .policy = foo_genl_policy,
+};
+
+struct genl_family noops_family = {
+ .name = "NOOPS",
+};
diff --git a/tools/syz-declextract/testdata/netlink.c.info b/tools/syz-declextract/testdata/netlink.c.info
new file mode 100644
index 000000000..e15a8e738
--- /dev/null
+++ b/tools/syz-declextract/testdata/netlink.c.info
@@ -0,0 +1,3 @@
+NETLINK NETLINK_BAR_CMD_FOO func:NETLINK_BAR_CMD_FOO access:user manual_desc:false auto_desc:true file:netlink.c subsystem:kernel
+NETLINK NETLINK_FOO_CMD_BAR func:bar_cmd access:ns_admin manual_desc:false auto_desc:true file:netlink.c subsystem:kernel
+NETLINK NETLINK_FOO_CMD_FOO func:foo_cmd access:admin manual_desc:false auto_desc:true file:netlink.c subsystem:kernel
diff --git a/tools/syz-declextract/testdata/netlink.c.json b/tools/syz-declextract/testdata/netlink.c.json
new file mode 100644
index 000000000..e4e485182
--- /dev/null
+++ b/tools/syz-declextract/testdata/netlink.c.json
@@ -0,0 +1,197 @@
+{
+ "includes": [
+ "include/uapi/netlink_family.h"
+ ],
+ "defines": [
+ {
+ "name": "NETLINK_BAR_CMD_FOO",
+ "value": "0"
+ },
+ {
+ "name": "NETLINK_FOO_NESTED_ATTR1",
+ "value": "0"
+ },
+ {
+ "name": "NETLINK_FOO_NESTED_ATTR2",
+ "value": "1"
+ }
+ ],
+ "structs": [
+ {
+ "name": "netlink_foo_struct1",
+ "byte_size": 12,
+ "fields": [
+ {
+ "name": "a",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "b",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "c",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "netlink_foo_struct2",
+ "byte_size": 24,
+ "fields": [
+ {
+ "name": "a",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "double",
+ "base": "double"
+ }
+ }
+ },
+ {
+ "name": "b",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "double",
+ "base": "double"
+ }
+ }
+ },
+ {
+ "name": "c",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "double",
+ "base": "double"
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "netlink_families": [
+ {
+ "name": "BAR",
+ "ops": [
+ {
+ "name": "NETLINK_BAR_CMD_FOO",
+ "func": "NETLINK_BAR_CMD_FOO",
+ "access": "user",
+ "policy": "foo_genl_policy$auto_netlink"
+ }
+ ],
+ "source_file": "netlink.c"
+ },
+ {
+ "name": "NOOPS",
+ "source_file": "netlink.c"
+ },
+ {
+ "name": "foo family",
+ "ops": [
+ {
+ "name": "NETLINK_FOO_CMD_FOO",
+ "func": "foo_cmd",
+ "access": "admin",
+ "policy": "foo_genl_policy$auto_netlink"
+ },
+ {
+ "name": "NETLINK_FOO_CMD_BAR",
+ "func": "bar_cmd",
+ "access": "ns_admin",
+ "policy": "foo_genl_policy$auto_netlink"
+ }
+ ],
+ "source_file": "netlink.c"
+ }
+ ],
+ "netlink_policies": [
+ {
+ "name": "foo_genl_nested_policy$auto_netlink",
+ "attrs": [
+ {
+ "name": "NETLINK_FOO_NESTED_ATTR1",
+ "kind": "NLA_U32"
+ },
+ {
+ "name": "NETLINK_FOO_NESTED_ATTR2",
+ "kind": "NLA_U32"
+ }
+ ]
+ },
+ {
+ "name": "foo_genl_policy$auto_netlink",
+ "attrs": [
+ {
+ "name": "NETLINK_FOO_ATTR1",
+ "kind": "NLA_U32"
+ },
+ {
+ "name": "NETLINK_FOO_ATTR2",
+ "kind": "NLA_STRING",
+ "max_size": 10
+ },
+ {
+ "name": "NETLINK_FOO_ATTR3",
+ "kind": "NLA_NESTED"
+ },
+ {
+ "name": "NETLINK_FOO_ATTR4",
+ "kind": "NLA_NESTED",
+ "nested_policy": "foo_genl_nested_policy$auto_netlink"
+ },
+ {
+ "name": "NETLINK_FOO_ATTR5",
+ "max_size": 12,
+ "elem": {
+ "struct": "netlink_foo_struct1"
+ }
+ },
+ {
+ "name": "NETLINK_FOO_ATTR6",
+ "max_size": 240,
+ "elem": {
+ "struct": "netlink_foo_struct2"
+ }
+ },
+ {
+ "name": "NETLINK_FOO_ATTR7",
+ "max_size": 24,
+ "elem": {
+ "struct": "netlink_foo_struct2"
+ }
+ }
+ ]
+ },
+ {
+ "name": "genl_policy_reject_all$auto_netlink"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tools/syz-declextract/testdata/netlink.c.txt b/tools/syz-declextract/testdata/netlink.c.txt
new file mode 100644
index 000000000..4b67a444d
--- /dev/null
+++ b/tools/syz-declextract/testdata/netlink.c.txt
@@ -0,0 +1,51 @@
+# Code generated by syz-declextract. DO NOT EDIT.
+
+meta automatic
+
+type auto_todo intptr
+
+include <vdso/bits.h>
+include <linux/types.h>
+include <net/netlink.h>
+include <include/uapi/netlink_family.h>
+
+resource genl_BAR_family_id_auto[int16]
+resource genl_foo_family_family_id_auto[int16]
+type msghdr_BAR_auto[CMD, POLICY] msghdr_netlink[netlink_msg_t[genl_BAR_family_id_auto, genlmsghdr_t[CMD], POLICY]]
+type msghdr_foo_family_auto[CMD, POLICY] msghdr_netlink[netlink_msg_t[genl_foo_family_family_id_auto, genlmsghdr_t[CMD], POLICY]]
+syz_genetlink_get_family_id$auto_BAR(name ptr[in, string["BAR"]], fd sock_nl_generic) genl_BAR_family_id_auto
+syz_genetlink_get_family_id$auto_foo_family(name ptr[in, string["foo family"]], fd sock_nl_generic) genl_foo_family_family_id_auto
+sendmsg$auto_NETLINK_BAR_CMD_FOO(fd sock_nl_generic, msg ptr[in, msghdr_BAR_auto[NETLINK_BAR_CMD_FOO, foo_genl_policy$auto_netlink]], f flags[send_flags])
+sendmsg$auto_NETLINK_FOO_CMD_BAR(fd sock_nl_generic, msg ptr[in, msghdr_foo_family_auto[NETLINK_FOO_CMD_BAR, foo_genl_policy$auto_netlink]], f flags[send_flags])
+sendmsg$auto_NETLINK_FOO_CMD_FOO(fd sock_nl_generic, msg ptr[in, msghdr_foo_family_auto[NETLINK_FOO_CMD_FOO, foo_genl_policy$auto_netlink]], f flags[send_flags])
+
+foo_genl_nested_policy$auto_netlink [
+ NETLINK_FOO_NESTED_ATTR1 nlattr[NETLINK_FOO_NESTED_ATTR1, int32]
+ NETLINK_FOO_NESTED_ATTR2 nlattr[NETLINK_FOO_NESTED_ATTR2, int32]
+] [varlen]
+
+foo_genl_policy$auto_netlink [
+ NETLINK_FOO_ATTR1 nlattr[NETLINK_FOO_ATTR1, int32]
+ NETLINK_FOO_ATTR2 nlattr[NETLINK_FOO_ATTR2, stringnoz]
+ NETLINK_FOO_ATTR3 nlnest[NETLINK_FOO_ATTR3, array[nl_generic_attr]]
+ NETLINK_FOO_ATTR4 nlnest[NETLINK_FOO_ATTR4, array[foo_genl_nested_policy$auto_netlink]]
+ NETLINK_FOO_ATTR5 nlattr[NETLINK_FOO_ATTR5, netlink_foo_struct1$auto_record]
+ NETLINK_FOO_ATTR6 nlattr[NETLINK_FOO_ATTR6, array[netlink_foo_struct2$auto_record, 0:10]]
+ NETLINK_FOO_ATTR7 nlattr[NETLINK_FOO_ATTR7, netlink_foo_struct2$auto_record]
+] [varlen]
+
+netlink_foo_struct1$auto_record {
+ a int32
+ b int32
+ c int32
+}
+
+netlink_foo_struct2$auto_record {
+ a int64
+ b int64
+ c int64
+}
+
+define NETLINK_BAR_CMD_FOO 0
+define NETLINK_FOO_NESTED_ATTR1 0
+define NETLINK_FOO_NESTED_ATTR2 1
diff --git a/tools/syz-declextract/testdata/syscall.c b/tools/syz-declextract/testdata/syscall.c
new file mode 100644
index 000000000..be247151b
--- /dev/null
+++ b/tools/syz-declextract/testdata/syscall.c
@@ -0,0 +1,12 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#include "include/syscall.h"
+
+SYSCALL_DEFINE1(open, const char* filename, int flags, int mode) {
+ return 0;
+}
+
+SYSCALL_DEFINE1(chmod, const char* filename, int mode) {
+ return 0;
+}
diff --git a/tools/syz-declextract/testdata/syscall.c.info b/tools/syz-declextract/testdata/syscall.c.info
new file mode 100644
index 000000000..5d204a68e
--- /dev/null
+++ b/tools/syz-declextract/testdata/syscall.c.info
@@ -0,0 +1,2 @@
+SYSCALL chmod func:__do_sys_chmod access:unknown manual_desc:false auto_desc:true file:syscall.c subsystem:kernel
+SYSCALL open func:__do_sys_open access:unknown manual_desc:false auto_desc:true file:syscall.c subsystem:kernel
diff --git a/tools/syz-declextract/testdata/syscall.c.json b/tools/syz-declextract/testdata/syscall.c.json
new file mode 100644
index 000000000..6e52fd57e
--- /dev/null
+++ b/tools/syz-declextract/testdata/syscall.c.json
@@ -0,0 +1,77 @@
+{
+ "syscalls": [
+ {
+ "func": "__do_sys_chmod",
+ "args": [
+ {
+ "name": "filename",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "buffer": {
+ "is_string": true
+ }
+ },
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "mode",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ],
+ "source_file": "syscall.c"
+ },
+ {
+ "func": "__do_sys_open",
+ "args": [
+ {
+ "name": "filename",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "buffer": {
+ "is_string": true
+ }
+ },
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "flags",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "mode",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ],
+ "source_file": "syscall.c"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tools/syz-declextract/testdata/syscall.c.txt b/tools/syz-declextract/testdata/syscall.c.txt
new file mode 100644
index 000000000..b55b077c3
--- /dev/null
+++ b/tools/syz-declextract/testdata/syscall.c.txt
@@ -0,0 +1,12 @@
+# Code generated by syz-declextract. DO NOT EDIT.
+
+meta automatic
+
+type auto_todo intptr
+
+include <vdso/bits.h>
+include <linux/types.h>
+include <net/netlink.h>
+
+chmod$auto(filename ptr[in, filename], mode int32)
+open$auto(filename ptr[in, filename], flags int32, mode int32)
diff --git a/tools/syz-declextract/testdata/types.c b/tools/syz-declextract/testdata/types.c
new file mode 100644
index 000000000..8fc67aeb9
--- /dev/null
+++ b/tools/syz-declextract/testdata/types.c
@@ -0,0 +1,55 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#include "include/syscall.h"
+
+typedef struct { float f; } anon_t;
+struct empty_struct {};
+typedef int fd_t;
+typedef struct forward forward_t;
+
+struct anon_struct {
+ // Various tricky anon cases.
+ struct { int x; } a;
+ struct {} b;
+ struct { int y; };
+ union { int q; long w; };
+ anon_t foo;
+ forward_t* forward;
+ struct { int a; int b; } array[4];
+ struct { int a; int b; } *ptr;
+ struct { int a; int b; } *ptr_array[4];
+};
+
+enum bitfield_enum { a, b, c };
+
+struct bitfields {
+ int a : 1;
+ int : 2;
+ int b : 3;
+ long d : 2;
+ long pad : 3;
+ enum bitfield_enum e : 10;
+ int l : 10;
+ int* p __attribute__((counted_by(l)));
+} __attribute__((aligned(32)));
+
+struct packed_t {
+ char x;
+ int y;
+} __attribute__((packed, aligned(32)));
+
+struct various {
+ struct various* recursive;
+ struct recursive* next;
+ struct packed_t packed;
+};
+
+struct recursive {
+ struct various various;
+};
+
+SYSCALL_DEFINE1(types_syscall, struct anon_struct* p, struct empty_struct* y,
+ struct bitfields* b, int pid, fd_t f, struct various* v) {
+ return 0;
+}
diff --git a/tools/syz-declextract/testdata/types.c.info b/tools/syz-declextract/testdata/types.c.info
new file mode 100644
index 000000000..4e1bdf314
--- /dev/null
+++ b/tools/syz-declextract/testdata/types.c.info
@@ -0,0 +1 @@
+SYSCALL types_syscall func:__do_sys_types_syscall 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
new file mode 100644
index 000000000..df19ab4a4
--- /dev/null
+++ b/tools/syz-declextract/testdata/types.c.json
@@ -0,0 +1,555 @@
+{
+ "defines": [
+ {
+ "name": "a",
+ "value": "0"
+ },
+ {
+ "name": "b",
+ "value": "1"
+ },
+ {
+ "name": "c",
+ "value": "2"
+ }
+ ],
+ "enums": [
+ {
+ "name": "bitfield_enum",
+ "values": [
+ "a",
+ "b",
+ "c"
+ ]
+ }
+ ],
+ "structs": [
+ {
+ "name": "anon_struct",
+ "byte_size": 104,
+ "fields": [
+ {
+ "name": "a",
+ "counted_by": -1,
+ "type": {
+ "struct": "anon_struct_a"
+ }
+ },
+ {
+ "name": "b",
+ "counted_by": -1,
+ "type": {
+ "struct": "anon_struct_b"
+ }
+ },
+ {
+ "name": "anon_struct_2",
+ "is_anonymous": true,
+ "counted_by": -1,
+ "type": {
+ "struct": "anon_struct_2"
+ }
+ },
+ {
+ "name": "anon_struct_3",
+ "is_anonymous": true,
+ "counted_by": -1,
+ "type": {
+ "struct": "anon_struct_3"
+ }
+ },
+ {
+ "name": "foo",
+ "counted_by": -1,
+ "type": {
+ "struct": "anon_t"
+ }
+ },
+ {
+ "name": "forward",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "int": {
+ "byte_size": 8,
+ "name": "TODO",
+ "base": "long"
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "array",
+ "counted_by": -1,
+ "type": {
+ "array": {
+ "elem": {
+ "struct": "anon_struct_array"
+ },
+ "min_size": 4,
+ "max_size": 4
+ }
+ }
+ },
+ {
+ "name": "ptr",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "anon_struct_ptr"
+ }
+ }
+ }
+ },
+ {
+ "name": "ptr_array",
+ "counted_by": -1,
+ "type": {
+ "array": {
+ "elem": {
+ "ptr": {
+ "elem": {
+ "struct": "anon_struct_ptr_array"
+ }
+ }
+ },
+ "min_size": 4,
+ "max_size": 4
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "anon_struct_2",
+ "byte_size": 4,
+ "fields": [
+ {
+ "name": "y",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "anon_struct_3",
+ "byte_size": 8,
+ "is_union": true,
+ "fields": [
+ {
+ "name": "q",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "w",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "long",
+ "base": "long"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "anon_struct_a",
+ "byte_size": 4,
+ "fields": [
+ {
+ "name": "x",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "anon_struct_array",
+ "byte_size": 8,
+ "fields": [
+ {
+ "name": "a",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "b",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "anon_struct_b"
+ },
+ {
+ "name": "anon_struct_ptr",
+ "byte_size": 8,
+ "fields": [
+ {
+ "name": "a",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "b",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "anon_struct_ptr_array",
+ "byte_size": 8,
+ "fields": [
+ {
+ "name": "a",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "b",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "anon_t",
+ "byte_size": 4,
+ "fields": [
+ {
+ "name": "f",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "float",
+ "base": "float"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "bitfields",
+ "byte_size": 32,
+ "align": 32,
+ "fields": [
+ {
+ "name": "a",
+ "bit_width": 1,
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "bitfields_1",
+ "is_anonymous": true,
+ "bit_width": 2,
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "b",
+ "bit_width": 3,
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "d",
+ "bit_width": 2,
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "long",
+ "base": "long"
+ }
+ }
+ },
+ {
+ "name": "pad",
+ "bit_width": 3,
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "long",
+ "base": "long"
+ }
+ }
+ },
+ {
+ "name": "e",
+ "bit_width": 10,
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "enum": "bitfield_enum"
+ }
+ }
+ },
+ {
+ "name": "l",
+ "bit_width": 10,
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "p",
+ "counted_by": 6,
+ "type": {
+ "ptr": {
+ "elem": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "empty_struct"
+ },
+ {
+ "name": "packed_t",
+ "byte_size": 32,
+ "is_packed": true,
+ "align": 32,
+ "fields": [
+ {
+ "name": "x",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "name": "char",
+ "base": "char"
+ }
+ }
+ },
+ {
+ "name": "y",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "recursive",
+ "byte_size": 64,
+ "fields": [
+ {
+ "name": "various",
+ "counted_by": -1,
+ "type": {
+ "struct": "various"
+ }
+ }
+ ]
+ },
+ {
+ "name": "various",
+ "byte_size": 64,
+ "fields": [
+ {
+ "name": "recursive",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "various"
+ }
+ }
+ }
+ },
+ {
+ "name": "next",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "recursive"
+ }
+ }
+ }
+ },
+ {
+ "name": "packed",
+ "counted_by": -1,
+ "type": {
+ "struct": "packed_t"
+ }
+ }
+ ]
+ }
+ ],
+ "syscalls": [
+ {
+ "func": "__do_sys_types_syscall",
+ "args": [
+ {
+ "name": "p",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "anon_struct"
+ }
+ }
+ }
+ },
+ {
+ "name": "y",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "empty_struct"
+ }
+ }
+ }
+ },
+ {
+ "name": "b",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "bitfields"
+ }
+ }
+ }
+ },
+ {
+ "name": "pid",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "f",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "fd_t",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "v",
+ "counted_by": -1,
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "various"
+ }
+ }
+ }
+ }
+ ],
+ "source_file": "types.c"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tools/syz-declextract/testdata/types.c.txt b/tools/syz-declextract/testdata/types.c.txt
new file mode 100644
index 000000000..d7586effc
--- /dev/null
+++ b/tools/syz-declextract/testdata/types.c.txt
@@ -0,0 +1,87 @@
+# Code generated by syz-declextract. DO NOT EDIT.
+
+meta automatic
+
+type auto_todo intptr
+
+include <vdso/bits.h>
+include <linux/types.h>
+include <net/netlink.h>
+
+auto_bitfield_enum = a, b, c
+
+types_syscall$auto(p ptr[inout, anon_struct$auto_record], y ptr[inout, void], b ptr[inout, bitfields$auto_record], pid pid, f int32, v ptr[inout, various$auto_record])
+
+anon_struct$auto_record {
+ a anon_struct_a$auto_record
+ b void
+ anon_struct_2 anon_struct_2$auto_record
+ anon_struct_3 anon_struct_3$auto_record
+ foo anon_t$auto_record
+ forward ptr[inout, auto_todo]
+ array array[anon_struct_array$auto_record, 4]
+ ptr ptr[inout, anon_struct_ptr$auto_record]
+ ptr_array array[ptr[inout, anon_struct_ptr_array$auto_record], 4]
+}
+
+anon_struct_2$auto_record {
+ y int32
+}
+
+anon_struct_3$auto_record [
+ q int32
+ w intptr
+]
+
+anon_struct_a$auto_record {
+ x int32
+}
+
+anon_struct_array$auto_record {
+ a int32
+ b int32
+}
+
+anon_struct_ptr$auto_record {
+ a int32
+ b int32
+}
+
+anon_struct_ptr_array$auto_record {
+ a int32
+ b int32
+}
+
+anon_t$auto_record {
+ f int32
+}
+
+bitfields$auto_record {
+ a int32:1
+ bitfields_1 const[0, int32:2]
+ b int32:3
+ d intptr:2
+ pad const[0, intptr:3]
+ e flags[auto_bitfield_enum, int32:10]
+ l len[p, int32:10]
+ p ptr[inout, int32]
+} [align[32]]
+
+packed_t$auto_record {
+ x int8
+ y int32
+} [packed, align[32]]
+
+recursive$auto_record {
+ various various$auto_record
+}
+
+various$auto_record {
+ recursive ptr[inout, various$auto_record, opt]
+ next ptr[inout, recursive$auto_record, opt]
+ packed packed_t$auto_record
+}
+
+define a 0
+define b 1
+define c 2