aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-12-13 15:15:49 +0100
committerDmitry Vyukov <dvyukov@google.com>2024-12-17 13:44:24 +0000
commitc8c15bb214509bafc8fe1a1e3abb8ccf90b3306e (patch)
treeca722a71aff5a1566389f178d9c95d7d7e8caeed
parentbc1a1b50f942408a9139887b914f745d9fa02adc (diff)
tools/syz-declextract: infer argument/field types
Use data flow analysis to infer syscall argument, return value, and struct field types. See the comment in pkg/declextract/typing.go for more details.
-rw-r--r--pkg/clangtool/clangtool.go10
-rw-r--r--pkg/declextract/declextract.go25
-rw-r--r--pkg/declextract/entity.go49
-rw-r--r--pkg/declextract/serialization.go7
-rw-r--r--pkg/declextract/typing.go268
-rw-r--r--pkg/testutil/testutil.go9
-rw-r--r--sys/linux/auto.txt193
-rw-r--r--tools/syz-declextract/clangtool/declextract.cpp189
-rw-r--r--tools/syz-declextract/clangtool/json.h1
-rw-r--r--tools/syz-declextract/clangtool/output.h110
-rw-r--r--tools/syz-declextract/declextract.go11
-rw-r--r--tools/syz-declextract/declextract_test.go10
-rw-r--r--tools/syz-declextract/testdata/functions.c45
-rw-r--r--tools/syz-declextract/testdata/functions.c.info2
-rw-r--r--tools/syz-declextract/testdata/functions.c.json231
-rw-r--r--tools/syz-declextract/testdata/functions.c.txt2
-rw-r--r--tools/syz-declextract/testdata/types.c11
-rw-r--r--tools/syz-declextract/testdata/types.c.json105
18 files changed, 1134 insertions, 144 deletions
diff --git a/pkg/clangtool/clangtool.go b/pkg/clangtool/clangtool.go
index 528f80c57..d15cc51a7 100644
--- a/pkg/clangtool/clangtool.go
+++ b/pkg/clangtool/clangtool.go
@@ -8,6 +8,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "io"
"math/rand"
"os"
"os/exec"
@@ -22,10 +23,11 @@ import (
)
type Config struct {
- ToolBin string
- KernelSrc string
- KernelObj string
- CacheFile string
+ ToolBin string
+ KernelSrc string
+ KernelObj string
+ CacheFile string
+ DebugTrace io.Writer
}
// Run runs the clang tool on all files in the compilation database
diff --git a/pkg/declextract/declextract.go b/pkg/declextract/declextract.go
index 4edb6c867..1f4592523 100644
--- a/pkg/declextract/declextract.go
+++ b/pkg/declextract/declextract.go
@@ -7,6 +7,7 @@ import (
"bytes"
"errors"
"fmt"
+ "io"
"os"
"slices"
"strings"
@@ -14,16 +15,20 @@ import (
"github.com/google/syzkaller/pkg/ifaceprobe"
)
-func Run(out *Output, probe *ifaceprobe.Info, syscallRename map[string][]string) ([]byte, []*Interface, error) {
+func Run(out *Output, probe *ifaceprobe.Info, syscallRename map[string][]string, trace io.Writer) (
+ []byte, []*Interface, error) {
ctx := &context{
Output: out,
probe: probe,
syscallRename: syscallRename,
structs: make(map[string]*Struct),
funcs: make(map[string]*Function),
+ facts: make(map[string]*typingNode),
uniqualizer: make(map[string]int),
+ debugTrace: trace,
}
ctx.processFunctions()
+ ctx.processTypingFacts()
ctx.processIncludes()
ctx.processEnums()
ctx.processStructs()
@@ -41,9 +46,11 @@ type context struct {
syscallRename map[string][]string // syscall function -> syscall names
structs map[string]*Struct
funcs map[string]*Function
+ facts map[string]*typingNode
uniqualizer map[string]int
interfaces []*Interface
descriptions *bytes.Buffer
+ debugTrace io.Writer
errs []error
}
@@ -55,6 +62,12 @@ func (ctx *context) warn(msg string, args ...any) {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
}
+func (ctx *context) trace(msg string, args ...any) {
+ if ctx.debugTrace != nil {
+ fmt.Fprintf(ctx.debugTrace, msg+"\n", 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.
@@ -88,6 +101,11 @@ func (ctx *context) processSyscalls() {
var syscalls []*Syscall
for _, call := range ctx.Syscalls {
ctx.processFields(call.Args, "", false)
+ call.returnType = ctx.inferReturnType(call.Func, call.SourceFile)
+ for i, arg := range call.Args {
+ typ := ctx.inferArgType(call.Func, call.SourceFile, i)
+ refineFieldType(arg, typ, false)
+ }
fn := strings.TrimPrefix(call.Func, "__do_sys_")
for _, name := range ctx.syscallRename[fn] {
ctx.noteInterface(&Interface{
@@ -129,6 +147,11 @@ func (ctx *context) processStructs() {
})
for _, str := range ctx.Structs {
ctx.processFields(str.Fields, str.Name, true)
+ name := strings.TrimSuffix(str.Name, autoSuffix)
+ for _, f := range str.Fields {
+ typ := ctx.inferFieldType(name, f.Name)
+ refineFieldType(f, typ, true)
+ }
}
}
diff --git a/pkg/declextract/entity.go b/pkg/declextract/entity.go
index ba45cc51c..266647ed8 100644
--- a/pkg/declextract/entity.go
+++ b/pkg/declextract/entity.go
@@ -24,14 +24,16 @@ type Output struct {
}
type Function struct {
- Name string `json:"name,omitempty"`
- File string `json:"file,omitempty"`
- IsStatic bool `json:"is_static,omitempty"`
- LOC int `json:"loc,omitempty"`
- Calls []string `json:"calls,omitempty"`
+ Name string `json:"name,omitempty"`
+ File string `json:"file,omitempty"`
+ IsStatic bool `json:"is_static,omitempty"`
+ LOC int `json:"loc,omitempty"`
+ Calls []string `json:"calls,omitempty"`
+ Facts []*TypingFact `json:"facts,omitempty"`
callers int
calls []*Function
+ facts map[string]*typingNode
}
type Define struct {
@@ -53,6 +55,8 @@ type Syscall struct {
Func string `json:"func,omitempty"`
Args []*Field `json:"args,omitempty"`
SourceFile string `json:"source_file,omitempty"`
+
+ returnType string
}
// FileOps describes one file_operations variable.
@@ -158,6 +162,41 @@ type BufferType struct {
IsNonTerminated bool `json:"is_non_terminated,omitempty"`
}
+type TypingFact struct {
+ Src *TypingEntity `json:"src,omitempty"`
+ Dst *TypingEntity `json:"dst,omitempty"`
+}
+
+type TypingEntity struct {
+ Return *EntityReturn `json:"return,omitempty"`
+ Argument *EntityArgument `json:"argument,omitempty"`
+ Field *EntityField `json:"field,omitempty"`
+ Local *EntityLocal `json:"local,omitempty"`
+ GlobalAddr *EntityGlobalAddr `json:"global_addr,omitempty"`
+}
+
+type EntityReturn struct {
+ Func string `json:"func,omitempty"`
+}
+
+type EntityArgument struct {
+ Func string `json:"func,omitempty"`
+ Arg int `json:"arg"`
+}
+
+type EntityField struct {
+ Struct string `json:"struct"`
+ Field string `json:"field"`
+}
+
+type EntityLocal struct {
+ Name string `json:"name"`
+}
+
+type EntityGlobalAddr struct {
+ Name string
+}
+
func (out *Output) Merge(other *Output) {
out.Functions = append(out.Functions, other.Functions...)
out.Includes = append(out.Includes, other.Includes...)
diff --git a/pkg/declextract/serialization.go b/pkg/declextract/serialization.go
index 6d27d2a13..d69358679 100644
--- a/pkg/declextract/serialization.go
+++ b/pkg/declextract/serialization.go
@@ -27,6 +27,11 @@ meta automatic
type auto_todo int8
+type auto_union[INFERRED, RAW] [
+ inferred INFERRED
+ raw RAW
+]
+
`
func (ctx *context) fmt(msg string, args ...any) {
@@ -53,7 +58,7 @@ func (ctx *context) serializeSyscalls() {
for i, arg := range call.Args {
ctx.fmt("%v%v %v", comma(i), arg.Name, arg.syzType)
}
- ctx.fmt(")\n")
+ ctx.fmt(") %v\n", call.returnType)
}
ctx.fmt("\n")
}
diff --git a/pkg/declextract/typing.go b/pkg/declextract/typing.go
new file mode 100644
index 000000000..7de22474d
--- /dev/null
+++ b/pkg/declextract/typing.go
@@ -0,0 +1,268 @@
+// 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"
+ "slices"
+ "strings"
+)
+
+// Argument/field type inference based on data flow analysis.
+//
+// First, the clang tool produces data flow summary for each function.
+// The summary describes how data flows between function arguments, return values, local variables, and struct fields.
+// Then, the logic in this file tracks global data flow in the kernel to infer types for syscall arguments,
+// return values, and struct fields.
+// If data transitively flows from an argument to a known function that accepts a resource of a particular type
+// (e.g. __fget_light for file descriptors), then we infer that the original argument is an fd.
+// Similarly, if data flows from a known function that creates a resource (e.g. alloc_fd for file descriptors)
+// to a syscall return value, then we infer that the syscall returns an fd.
+// For struct fields we track data flow in both direction (to/from) to infer their types.
+//
+// If the inference produces multiple resources, currently we pick the one with the shortest flow path
+// (and then additionally pick lexicographically first among them for determinism). Potentially we could
+// use a more elaborate strategy that would somehow rank candidates and/or produce multiple candidates
+// (that we will then use as a union).
+//
+// Other potential improvements:
+// - Add more functions that consume/produce resources.
+// - Refine enum types. If we see an argument is used in bitops with an enum, it has that enum type.
+// - Infer pointer types when they flow to copy_from_user (sometimes they are declared as uint64).
+// - Infer that pointers are file names (they should flow to some known function for path resolution).
+// - Use SSA analysis to track flow via local variables better. Potentiall we can just rename on every next use
+// and ignore backwards edges (it's unlikely that backwards edges are required for type inference).
+// - Infer ioctl commands in transitively called functions using data flow.
+// - Infer file_operations associated with an fd by tracking flow to alloc_file_pseudo and friends.
+// - Add context-sensitivity at least on switched arguments (ioctl commands).
+// - Infer other switched arguments besides ioctl commands.
+// - Infer netlink arg types by tracking flow from genl_info::attrs[ATTR_FOO].
+// - Infer simple constraints on arguments, e.g. "if (arg != 0) return -EINVAL".
+// - Use kernel typedefs for typing (e.g. pid_t). We can use them for uapi structs, but also for kernel
+// structs and function arguments during dataflow tracking (e.g. if int flows to a pid_t argument, it's a pid).
+// - Track side flows. E.g. dup2 argument newfd flows to the return value, and newfd can be inferred to be an fd,
+// but currently we don't infer that the return value is an fd. Potentially we could infer that.
+// - Detect cases where returned value is actually an error rather than a resource.
+// For example, these cases lead to false inference of fd type for returned value:
+// https://elixir.bootlin.com/linux/v6.13-rc2/source/net/core/sock.c#L1870
+// https://elixir.bootlin.com/linux/v6.13-rc2/source/net/socket.c#L1742
+
+var (
+ // Refines types based on data flows...
+ flowResources = [2]map[string]string{
+ // ...to function arguments.
+ {
+ "__fget_light:arg0": "fd",
+ "__fget_files_rcu:arg1": "fd",
+ "make_kuid:arg1": "uid",
+ "make_kgid:arg1": "gid",
+ "find_pid_ns:arg0": "pid",
+ "pidfd_get_pid:arg0": "fd_pidfd",
+ "__dev_get_by_index:arg1": "ifindex",
+ },
+ // ...from function return value.
+ {
+ "alloc_fd:ret": "fd",
+ "pid_nr_ns:ret": "pid",
+ "from_kuid:ret": "uid",
+ "from_kgid:ret": "gid",
+ },
+ }
+ // These functions/structs/files provide very high false connectivity between unrelated nodes.
+ flowIgnoreFuncs = map[string]bool{
+ "ptr_to_compat": true,
+ "compat_ptr": true,
+ }
+ flowIgnoreStructs = map[string]bool{
+ "pt_regs": true,
+ "io_cqe": true,
+ "inode": true,
+ }
+ flowIgnoreFiles = map[string]bool{
+ "include/linux/err.h": true, // PTR_ERR/ERR_PTR/ERR_CAST
+ "include/linux/byteorder": true, // ntohl/etc
+ "include/linux/uaccess.h": true, // copy_to/from_user
+ "fs/befs/endian.h": true, // cpu_to_fs32/etc
+ "fs/ufs/swab.h": true,
+ }
+)
+
+// Limit on the flow graph traversal depth to avoid false positives due to false weird connections.
+const maxTraversalDepth = 18
+
+type typingNode struct {
+ id string
+ flows [2]map[*typingNode]bool
+}
+
+const (
+ flowTo = iota
+ flowFrom
+)
+
+func (ctx *context) processTypingFacts() {
+ for _, fn := range ctx.Functions {
+ for _, fact := range fn.Facts {
+ src := ctx.canonicalNode(fn, fact.Src)
+ dst := ctx.canonicalNode(fn, fact.Dst)
+ if src == nil || dst == nil {
+ continue
+ }
+ src.flows[flowTo][dst] = true
+ dst.flows[flowFrom][src] = true
+ }
+ }
+}
+
+func (ctx *context) canonicalNode(fn *Function, ent *TypingEntity) *typingNode {
+ scope, id := ent.ID(fn)
+ fullID := id
+ facts := ctx.facts
+ if scope != "" {
+ if scope != fn.Name {
+ fn = ctx.findFunc(scope, fn.File)
+ if fn == nil {
+ return nil
+ }
+ }
+ if flowIgnoreFuncs[fn.Name] || flowIgnoreFiles[fn.File] {
+ return nil
+ }
+ if fn.facts == nil {
+ fn.facts = make(map[string]*typingNode)
+ }
+ facts = fn.facts
+ fullID = fmt.Sprintf("%v:%v", scope, id)
+ } else if ent.Field != nil && flowIgnoreStructs[ent.Field.Struct] {
+ return nil
+ }
+ n := facts[id]
+ if n != nil {
+ return n
+ }
+ n = &typingNode{
+ id: fullID,
+ }
+ for i := range n.flows {
+ n.flows[i] = make(map[*typingNode]bool)
+ }
+ facts[id] = n
+ return n
+}
+
+func (ent *TypingEntity) ID(fn *Function) (string, string) {
+ switch {
+ case ent.Return != nil:
+ return ent.Return.Func, "ret"
+ case ent.Argument != nil:
+ return ent.Argument.Func, fmt.Sprintf("arg%v", ent.Argument.Arg)
+ case ent.Local != nil:
+ return fn.Name, fmt.Sprintf("loc.%v", ent.Local.Name)
+ case ent.Field != nil:
+ return "", fmt.Sprintf("%v.%v", ent.Field.Struct, ent.Field.Field)
+ case ent.GlobalAddr != nil:
+ return "", ent.GlobalAddr.Name
+ default:
+ panic("unhandled type")
+ }
+}
+
+func (ctx *context) inferReturnType(name, file string) string {
+ return ctx.inferFuncNode(name, file, "ret")
+}
+
+func (ctx *context) inferArgType(name, file string, arg int) string {
+ return ctx.inferFuncNode(name, file, fmt.Sprintf("arg%v", arg))
+}
+
+func (ctx *context) inferFuncNode(name, file, node string) string {
+ fn := ctx.findFunc(name, file)
+ if fn == nil {
+ return ""
+ }
+ return ctx.inferNodeType(fn.facts[node], fmt.Sprintf("%v %v", name, node))
+}
+
+func (ctx *context) inferFieldType(structName, field string) string {
+ name := fmt.Sprintf("%v.%v", structName, field)
+ return ctx.inferNodeType(ctx.facts[name], name)
+}
+
+func (ctx *context) inferNodeType(n *typingNode, what string) string {
+ if n == nil {
+ return ""
+ }
+ ic := &inferContext{
+ visited: make(map[*typingNode]bool),
+ flowType: flowFrom,
+ maxDepth: maxTraversalDepth,
+ }
+ ic.walk(n)
+ ic.flowType = flowTo
+ ic.visited = make(map[*typingNode]bool)
+ ic.walk(n)
+ if ic.result != "" {
+ ctx.trace("inferred %v\n %v%v", what, ic.result, flowString(ic.resultPath, ic.resultFlow))
+ }
+ return ic.result
+}
+
+type inferContext struct {
+ path []*typingNode
+ visited map[*typingNode]bool
+ result string
+ resultPath []*typingNode
+ resultFlow int
+ flowType int
+ maxDepth int
+}
+
+func (ic *inferContext) walk(n *typingNode) {
+ if ic.visited[n] {
+ return
+ }
+ ic.visited[n] = true
+ ic.path = append(ic.path, n)
+ if result, ok := flowResources[ic.flowType][n.id]; ok {
+ // Use lexicographical order just to make the result stable.
+ if ic.result == "" || len(ic.path) < ic.maxDepth ||
+ len(ic.path) == ic.maxDepth && strings.Compare(result, ic.result) < 0 {
+ ic.result = result
+ ic.resultPath = slices.Clone(ic.path)
+ ic.resultFlow = ic.flowType
+ ic.maxDepth = len(ic.path)
+ }
+ }
+ if len(ic.path) < ic.maxDepth {
+ for e := range n.flows[ic.flowType] {
+ ic.walk(e)
+ }
+ }
+ ic.path = ic.path[:len(ic.path)-1]
+}
+
+func refineFieldType(f *Field, typ string, preserveSize bool) {
+ // If our manual heuristics have figured out a more precise fd subtype,
+ // don't replace it with generic fd.
+ if typ == "" || typ == f.syzType ||
+ typ == "fd" && (strings.HasPrefix(f.syzType, "fd_") || strings.HasPrefix(f.syzType, "sock")) {
+ return
+ }
+ // For struct fields we need to keep the original size.
+ // Sometimes fd is passed as uint64.
+ if preserveSize {
+ typ = fmt.Sprintf("auto_union[%v, %v]", typ, f.syzType)
+ }
+ f.syzType = typ
+}
+
+func flowString(path []*typingNode, flowType int) string {
+ w := new(bytes.Buffer)
+ dir := [2]string{"->", "<-"}[flowType]
+ for _, e := range path {
+ fmt.Fprintf(w, " %v %v", dir, e.id)
+ }
+ return w.String()
+}
diff --git a/pkg/testutil/testutil.go b/pkg/testutil/testutil.go
index 153584dd5..b2ec06afb 100644
--- a/pkg/testutil/testutil.go
+++ b/pkg/testutil/testutil.go
@@ -90,3 +90,12 @@ func randValue(t *testing.T, rnd *rand.Rand, typ reflect.Type) reflect.Value {
}
return v
}
+
+type Writer struct {
+ testing.TB
+}
+
+func (w *Writer) Write(data []byte) (int, error) {
+ w.TB.Logf("%s", data)
+ return len(data), nil
+}
diff --git a/sys/linux/auto.txt b/sys/linux/auto.txt
index 356e8b27d..f2e97b77d 100644
--- a/sys/linux/auto.txt
+++ b/sys/linux/auto.txt
@@ -4,6 +4,11 @@ meta automatic
type auto_todo int8
+type auto_union[INFERRED, RAW] [
+ inferred INFERRED
+ raw RAW
+]
+
include <vdso/bits.h>
include <linux/types.h>
include <net/netlink.h>
@@ -175,8 +180,8 @@ landlock_rule_type$auto = LANDLOCK_RULE_PATH_BENEATH, LANDLOCK_RULE_NET_PORT
_llseek$auto(fd fd, offset_high intptr, offset_low intptr, result ptr[inout, int64], whence int32)
_newselect$auto(n int32, inp ptr[inout, __kernel_fd_set$auto], outp ptr[inout, __kernel_fd_set$auto], exp ptr[inout, __kernel_fd_set$auto], tvp ptr[inout, __kernel_old_timeval$auto])
-accept$auto(fd fd, upeer_sockaddr ptr[inout, sockaddr], upeer_addrlen ptr[inout, int32])
-accept4$auto(fd fd, upeer_sockaddr ptr[inout, sockaddr], upeer_addrlen ptr[inout, int32], flags int32)
+accept$auto(fd fd, upeer_sockaddr ptr[inout, sockaddr], upeer_addrlen ptr[inout, int32]) fd
+accept4$auto(fd fd, upeer_sockaddr ptr[inout, sockaddr], upeer_addrlen ptr[inout, int32], flags int32) fd
access$auto(filename ptr[in, filename], mode int32)
acct$auto(name ptr[in, string])
add_key$auto(_type ptr[in, string], _description ptr[in, string], _payload ptr[in, array[auto_todo]], plen intptr, ringid int32)
@@ -185,7 +190,7 @@ alarm$auto(seconds int32)
arch_prctl$auto(option int32, arg2 intptr)
arm_sync_file_range$auto(fd fd, flags int32, offset intptr, nbytes intptr)
bind$auto(fd fd, umyaddr ptr[inout, sockaddr], addrlen int32)
-bpf$auto(cmd int32, uattr ptr[inout, bpf_attr$auto], size int32)
+bpf$auto(cmd int32, uattr ptr[inout, bpf_attr$auto], size int32) fd
brk$auto(brk intptr)
cachestat$auto(fd fd, cstat_range ptr[inout, cachestat_range$auto], cstat ptr[inout, cachestat$auto], flags int32)
capget$auto(header ptr[inout, __user_cap_header_struct$auto], dataptr ptr[inout, __user_cap_data_struct$auto])
@@ -205,25 +210,25 @@ clock_nanosleep$auto(which_clock int32, flags int32, rqtp ptr[in, __kernel_times
clock_nanosleep_time64$auto(which_clock int32, flags int32, rqtp ptr[in, __kernel_timespec$auto], rmtp ptr[inout, __kernel_timespec$auto])
clock_settime$auto(which_clock int32, tp ptr[in, __kernel_timespec$auto])
clock_settime64$auto(which_clock int32, tp ptr[in, __kernel_timespec$auto])
-clone$auto(clone_flags intptr, newsp intptr, parent_tidptr ptr[inout, int32], child_tidptr ptr[inout, int32], tls intptr)
-clone3$auto(uargs ptr[inout, clone_args$auto], size intptr)
+clone$auto(clone_flags intptr, newsp intptr, parent_tidptr ptr[inout, int32], child_tidptr ptr[inout, int32], tls intptr) pid
+clone3$auto(uargs ptr[inout, clone_args$auto], size intptr) pid
close$auto(fd fd)
close_range$auto(fd fd, max_fd fd, flags int32)
connect$auto(fd fd, uservaddr ptr[inout, sockaddr], addrlen int32)
copy_file_range$auto(fd_in fd, off_in ptr[inout, int64], fd_out fd, off_out ptr[inout, int64], len intptr, flags int32)
-creat$auto(pathname ptr[in, filename], mode int16)
+creat$auto(pathname ptr[in, filename], mode int16) fd
delete_module$auto(name_user ptr[in, string], flags int32)
-dup$auto(fildes fd)
+dup$auto(fildes fd) fd
dup2$auto(oldfd fd, newfd fd)
dup3$auto(oldfd fd, newfd fd, flags int32)
-epoll_create$auto(size int32)
-epoll_create1$auto(flags int32)
+epoll_create$auto(size int32) fd
+epoll_create1$auto(flags int32) fd
epoll_ctl$auto(epfd fd, op int32, fd fd, event ptr[inout, epoll_event$auto])
epoll_pwait$auto(epfd fd, events ptr[inout, epoll_event$auto], maxevents int32, timeout int32, sigmask ptr[in, sigset_t$auto], sigsetsize const[8])
epoll_pwait2$auto(epfd fd, events ptr[inout, epoll_event$auto], maxevents int32, timeout ptr[in, __kernel_timespec$auto], sigmask ptr[in, sigset_t$auto], sigsetsize const[8])
epoll_wait$auto(epfd fd, events ptr[inout, epoll_event$auto], maxevents int32, timeout int32)
-eventfd$auto(count int32)
-eventfd2$auto(count int32, flags int32)
+eventfd$auto(count int32) fd
+eventfd2$auto(count int32, flags int32) fd
execve$auto(filename ptr[in, filename], argv ptr[in, ptr[in, string]], envp ptr[in, ptr[in, string]])
execveat$auto(fd fd, filename ptr[in, filename], argv ptr[in, ptr[in, string]], envp ptr[in, ptr[in, string]], flags int32)
exit$auto(error_code int32)
@@ -233,7 +238,7 @@ faccessat2$auto(dfd fd_dir, filename ptr[in, filename], mode int32, flags int32)
fadvise64$auto(fd fd, offset intptr, len intptr, advice int32)
fadvise64_64$auto(fd fd, offset_low int32, offset_high int32, len_low int32, len_high int32, advice int32)
fallocate$auto(fd fd, mode int32, offset intptr, len intptr)
-fanotify_init$auto(flags int32, event_f_flags int32)
+fanotify_init$auto(flags int32, event_f_flags int32) fd
fanotify_mark$auto(fanotify_fd fd, flags int32, mask intptr, dfd fd_dir, pathname ptr[in, filename])
fchdir$auto(fd fd)
fchmod$auto(fd fd, mode int16)
@@ -242,18 +247,18 @@ fchmodat2$auto(dfd fd_dir, filename ptr[in, filename], mode int16, flags int32)
fchown$auto(fd fd, user uid, group gid)
fchown32$auto(fd fd, user uid, group gid)
fchownat$auto(dfd fd_dir, filename ptr[in, filename], user uid, group gid, flag int32)
-fcntl$auto(fd fd, cmd int32, arg intptr)
+fcntl$auto(fd fd, cmd int32, arg pid) fd
fdatasync$auto(fd fd)
fgetxattr$auto(fd fd, name ptr[in, string], value ptr[inout, array[auto_todo]], size intptr)
finit_module$auto(fd fd, uargs ptr[in, string], flags int32)
flistxattr$auto(fd fd, list ptr[inout, string], size intptr)
flock$auto(fd fd, cmd int32)
fremovexattr$auto(fd fd, name ptr[in, string])
-fsconfig$auto(fd fd, cmd int32, _key ptr[in, string], _value ptr[in, array[auto_todo]], aux int32)
+fsconfig$auto(fd fd, cmd int32, _key ptr[in, string], _value ptr[in, array[auto_todo]], aux uid)
fsetxattr$auto(fd fd, name ptr[in, string], value ptr[in, array[auto_todo]], size intptr, flags int32)
-fsmount$auto(fs_fd fd, flags int32, attr_flags int32)
-fsopen$auto(_fs_name ptr[in, string], flags int32)
-fspick$auto(dfd fd_dir, path ptr[in, filename], flags int32)
+fsmount$auto(fs_fd fd, flags int32, attr_flags int32) fd
+fsopen$auto(_fs_name ptr[in, string], flags int32) fd
+fspick$auto(dfd fd_dir, path ptr[in, filename], flags int32) fd
fstat$auto(fd fd, statbuf ptr[inout, stat$auto])
fstatfs$auto(fd fd, buf ptr[inout, statfs$auto])
fstatfs64$auto(fd fd, sz intptr, buf ptr[inout, statfs64$auto])
@@ -278,8 +283,8 @@ getgroups$auto(gidsetsize int32, grouplist ptr[inout, int32])
getgroups32$auto(gidsetsize int32, grouplist ptr[inout, int32])
getitimer$auto(which int32, value ptr[inout, __kernel_old_itimerval$auto])
getpeername$auto(fd fd, usockaddr ptr[inout, sockaddr], usockaddr_len ptr[inout, int32])
-getpgid$auto(pid pid)
-getpriority$auto(which int32, who int32)
+getpgid$auto(pid pid) pid
+getpriority$auto(which int32, who uid)
getrandom$auto(ubuf ptr[inout, string], len intptr, flags int32)
getresgid$auto(rgidp ptr[inout, int32], egidp ptr[inout, int32], sgidp ptr[inout, int32])
getresgid32$auto(rgidp ptr[inout, int32], egidp ptr[inout, int32], sgidp ptr[inout, int32])
@@ -287,15 +292,15 @@ getresuid$auto(ruidp ptr[inout, int32], euidp ptr[inout, int32], suidp ptr[inout
getresuid32$auto(ruidp ptr[inout, int32], euidp ptr[inout, int32], suidp ptr[inout, int32])
getrlimit$auto(_resource int32, rlim ptr[inout, rlimit$auto])
getrusage$auto(who int32, ru ptr[inout, rusage$auto])
-getsid$auto(pid pid)
+getsid$auto(pid pid) pid
getsockname$auto(fd fd, usockaddr ptr[inout, sockaddr], usockaddr_len ptr[inout, int32])
-getsockopt$auto(fd fd, level int32, optname int32, optval ptr[inout, string], optlen ptr[inout, int32])
+getsockopt$auto(fd fd, level int32, optname int32, optval ptr[inout, string], optlen ptr[inout, int32]) fd
gettimeofday$auto(tv ptr[inout, __kernel_old_timeval$auto], tz ptr[inout, timezone$auto])
getxattr$auto(pathname ptr[in, filename], name ptr[in, string], value ptr[inout, array[auto_todo]], size intptr)
getxattrat$auto(dfd fd_dir, pathname ptr[in, filename], at_flags int32, name ptr[in, string], uargs ptr[inout, xattr_args$auto], usize intptr)
init_module$auto(umod ptr[inout, array[auto_todo]], len intptr, uargs ptr[in, string])
inotify_add_watch$auto(fd fd, pathname ptr[in, filename], mask int32)
-inotify_init1$auto(flags int32)
+inotify_init1$auto(flags int32) fd
inotify_rm_watch$auto(fd fd, wd int32)
io_cancel$auto(ctx_id intptr, iocb ptr[inout, iocb$auto], result ptr[inout, io_event$auto])
io_destroy$auto(ctx intptr)
@@ -306,18 +311,18 @@ io_setup$auto(nr_events int32, ctxp ptr[inout, intptr])
io_submit$auto(ctx_id intptr, nr intptr, iocbpp ptr[inout, ptr[inout, iocb$auto]])
io_uring_enter$auto(fd fd, to_submit int32, min_complete int32, flags int32, argp ptr[in, array[auto_todo]], argsz intptr)
io_uring_register$auto(fd fd, opcode int32, arg ptr[inout, array[auto_todo]], nr_args int32)
-io_uring_setup$auto(entries int32, params ptr[inout, io_uring_params$auto])
-ioctl$auto(fd fd, cmd int32, arg intptr)
+io_uring_setup$auto(entries int32, params ptr[inout, io_uring_params$auto]) fd
+ioctl$auto(fd fd, cmd int32, arg fd)
ioperm$auto(from intptr, num intptr, turn_on int32)
iopl$auto(level int32)
-ioprio_get$auto(which int32, who int32)
-ioprio_set$auto(which int32, who int32, ioprio int32)
-kcmp$auto(pid1 int32, pid2 int32, type int32, idx1 intptr, idx2 intptr)
+ioprio_get$auto(which int32, who uid)
+ioprio_set$auto(which int32, who uid, ioprio int32)
+kcmp$auto(pid1 pid, pid2 pid, type int32, idx1 fd, idx2 fd)
kexec_load$auto(entry intptr, nr_segments intptr, segments ptr[inout, kexec_segment$auto], flags intptr)
-keyctl$auto(option int32, arg2 intptr, arg3 intptr, arg4 intptr, arg5 intptr)
+keyctl$auto(option int32, arg2 uid, arg3 uid, arg4 gid, arg5 intptr)
kill$auto(pid pid, sig int32)
landlock_add_rule$auto(ruleset_fd fd, rule_type flags[landlock_rule_type$auto], rule_attr ptr[in, array[auto_todo]], flags int32)
-landlock_create_ruleset$auto(attr ptr[in, landlock_ruleset_attr$auto], size intptr, flags int32)
+landlock_create_ruleset$auto(attr ptr[in, landlock_ruleset_attr$auto], size intptr, flags int32) fd
landlock_restrict_self$auto(ruleset_fd fd, flags int32)
lchown$auto(filename ptr[in, filename], user uid, group gid)
lchown32$auto(filename ptr[in, filename], user uid, group gid)
@@ -340,8 +345,8 @@ madvise$auto(start intptr, len_in intptr, behavior int32)
map_shadow_stack$auto(addr intptr, size intptr, flags int32)
mbind$auto(start intptr, len intptr, mode intptr, nmask ptr[in, intptr], maxnode intptr, flags int32)
membarrier$auto(cmd int32, flags int32, cpu_id int32)
-memfd_create$auto(uname ptr[in, string], flags int32)
-memfd_secret$auto(flags int32)
+memfd_create$auto(uname ptr[in, string], flags int32) fd
+memfd_secret$auto(flags int32) fd
migrate_pages$auto(pid pid, maxnode intptr, old_nodes ptr[in, intptr], new_nodes ptr[in, intptr])
mincore$auto(start intptr, len intptr, vec ptr[inout, string])
mkdir$auto(pathname ptr[in, filename], mode int16)
@@ -351,21 +356,21 @@ mknodat$auto(dfd fd_dir, filename ptr[in, filename], mode int16, dev int32)
mlock$auto(start intptr, len intptr)
mlock2$auto(start intptr, len intptr, flags int32)
mlockall$auto(flags int32)
-mmap$auto(addr intptr, len intptr, prot intptr, flags intptr, fd intptr, off intptr)
-mmap2$auto(addr intptr, len intptr, prot intptr, flags intptr, fd intptr, pgoff intptr)
+mmap$auto(addr intptr, len intptr, prot intptr, flags intptr, fd fd, off intptr)
+mmap2$auto(addr intptr, len intptr, prot intptr, flags intptr, fd fd, pgoff intptr)
modify_ldt$auto(func int32, ptr ptr[inout, array[auto_todo]], bytecount intptr)
mount$auto(dev_name ptr[inout, devname], dir_name ptr[inout, filename], type ptr[inout, string], flags intptr, data ptr[inout, array[auto_todo]])
mount_setattr$auto(dfd fd_dir, path ptr[in, filename], flags int32, uattr ptr[inout, mount_attr$auto], usize intptr)
move_mount$auto(from_dfd fd_dir, from_pathname ptr[in, filename], to_dfd fd_dir, to_pathname ptr[in, filename], flags int32)
move_pages$auto(pid pid, nr_pages intptr, pages ptr[inout, ptr[in, array[auto_todo]]], nodes ptr[in, int32], status ptr[inout, int32], flags int32)
mprotect$auto(start intptr, len intptr, prot intptr)
-mq_getsetattr$auto(mqdes int32, u_mqstat ptr[in, mq_attr$auto], u_omqstat ptr[inout, mq_attr$auto])
-mq_notify$auto(mqdes int32, u_notification ptr[in, sigevent$auto])
-mq_open$auto(u_name ptr[in, string], oflag int32, mode int16, u_attr ptr[inout, mq_attr$auto])
-mq_timedreceive$auto(mqdes int32, u_msg_ptr ptr[inout, string], msg_len intptr, u_msg_prio ptr[inout, int32], u_abs_timeout ptr[in, __kernel_timespec$auto])
-mq_timedreceive_time64$auto(mqdes int32, u_msg_ptr ptr[inout, string], msg_len intptr, u_msg_prio ptr[inout, int32], u_abs_timeout ptr[in, __kernel_timespec$auto])
-mq_timedsend$auto(mqdes int32, u_msg_ptr ptr[in, string], msg_len intptr, msg_prio int32, u_abs_timeout ptr[in, __kernel_timespec$auto])
-mq_timedsend_time64$auto(mqdes int32, u_msg_ptr ptr[in, string], msg_len intptr, msg_prio int32, u_abs_timeout ptr[in, __kernel_timespec$auto])
+mq_getsetattr$auto(mqdes fd, u_mqstat ptr[in, mq_attr$auto], u_omqstat ptr[inout, mq_attr$auto])
+mq_notify$auto(mqdes fd, u_notification ptr[in, sigevent$auto])
+mq_open$auto(u_name ptr[in, string], oflag int32, mode int16, u_attr ptr[inout, mq_attr$auto]) fd
+mq_timedreceive$auto(mqdes fd, u_msg_ptr ptr[inout, string], msg_len intptr, u_msg_prio ptr[inout, int32], u_abs_timeout ptr[in, __kernel_timespec$auto])
+mq_timedreceive_time64$auto(mqdes fd, u_msg_ptr ptr[inout, string], msg_len intptr, u_msg_prio ptr[inout, int32], u_abs_timeout ptr[in, __kernel_timespec$auto])
+mq_timedsend$auto(mqdes fd, u_msg_ptr ptr[in, string], msg_len intptr, msg_prio int32, u_abs_timeout ptr[in, __kernel_timespec$auto])
+mq_timedsend_time64$auto(mqdes fd, u_msg_ptr ptr[in, string], msg_len intptr, msg_prio int32, u_abs_timeout ptr[in, __kernel_timespec$auto])
mq_unlink$auto(u_name ptr[in, string])
mremap$auto(addr intptr, old_len intptr, new_len intptr, flags intptr, new_addr intptr)
mseal$auto(start intptr, len intptr, flags intptr)
@@ -385,18 +390,18 @@ oldlstat$auto(filename ptr[in, filename], statbuf ptr[inout, __old_kernel_stat$a
oldolduname$auto(name ptr[inout, oldold_utsname$auto])
oldstat$auto(filename ptr[in, filename], statbuf ptr[inout, __old_kernel_stat$auto])
olduname$auto(name ptr[inout, old_utsname$auto])
-open$auto(filename ptr[in, filename], flags int32, mode int16)
-open_by_handle_at$auto(mountdirfd fd, handle ptr[inout, file_handle$auto], flags int32)
-open_tree$auto(dfd fd_dir, filename ptr[in, filename], flags int32)
-openat$auto(dfd fd_dir, filename ptr[in, filename], flags int32, mode int16)
-openat2$auto(dfd fd_dir, filename ptr[in, filename], how ptr[inout, open_how$auto], usize intptr)
-perf_event_open$auto(attr_uptr ptr[inout, perf_event_attr$auto], pid pid, cpu int32, group_fd fd, flags intptr)
+open$auto(filename ptr[in, filename], flags int32, mode int16) fd
+open_by_handle_at$auto(mountdirfd fd, handle ptr[inout, file_handle$auto], flags int32) fd
+open_tree$auto(dfd fd_dir, filename ptr[in, filename], flags int32) fd
+openat$auto(dfd fd_dir, filename ptr[in, filename], flags int32, mode int16) fd
+openat2$auto(dfd fd_dir, filename ptr[in, filename], how ptr[inout, open_how$auto], usize intptr) fd
+perf_event_open$auto(attr_uptr ptr[inout, perf_event_attr$auto], pid pid, cpu int32, group_fd fd, flags intptr) fd
personality$auto(personality int32)
-pidfd_getfd$auto(pidfd fd, fd fd, flags int32)
-pidfd_open$auto(pid pid, flags int32)
+pidfd_getfd$auto(pidfd fd, fd fd, flags int32) fd
+pidfd_open$auto(pid pid, flags int32) fd
pidfd_send_signal$auto(pidfd fd, sig int32, info ptr[inout, siginfo$auto], flags int32)
-pipe$auto(fildes ptr[inout, fd])
-pipe2$auto(fildes ptr[inout, fd], flags int32)
+pipe$auto(fildes ptr[inout, fd]) fd
+pipe2$auto(fildes ptr[inout, fd], flags int32) fd
pivot_root$auto(new_root ptr[in, string], put_old ptr[in, string])
pkey_alloc$auto(flags intptr, init_val intptr)
pkey_free$auto(pkey int32)
@@ -404,29 +409,29 @@ pkey_mprotect$auto(start intptr, len intptr, prot intptr, pkey int32)
poll$auto(ufds ptr[inout, pollfd$auto], nfds int32, timeout_msecs int32)
ppoll$auto(ufds ptr[inout, pollfd$auto], nfds int32, tsp ptr[inout, __kernel_timespec$auto], sigmask ptr[in, sigset_t$auto], sigsetsize const[8])
ppoll_time64$auto(ufds ptr[inout, pollfd$auto], nfds int32, tsp ptr[inout, __kernel_timespec$auto], sigmask ptr[in, sigset_t$auto], sigsetsize const[8])
-prctl$auto(option int32, arg2 intptr, arg3 intptr, arg4 intptr, arg5 intptr)
+prctl$auto(option int32, arg2 intptr, arg3 pid, arg4 intptr, arg5 intptr) fd
pread64$auto(fd fd, buf ptr[inout, string], count intptr, pos intptr)
-preadv$auto(fd intptr, vec ptr[in, iovec$auto], vlen intptr, pos_l intptr, pos_h intptr)
-preadv2$auto(fd intptr, vec ptr[in, iovec$auto], vlen intptr, pos_l intptr, pos_h intptr, flags int32)
+preadv$auto(fd fd, vec ptr[in, iovec$auto], vlen intptr, pos_l intptr, pos_h intptr)
+preadv2$auto(fd fd, vec ptr[in, iovec$auto], vlen intptr, pos_l intptr, pos_h intptr, flags int32)
prlimit64$auto(pid pid, _resource int32, new_rlim ptr[in, rlimit64$auto], old_rlim ptr[inout, rlimit64$auto])
-process_madvise$auto(pidfd fd, vec ptr[in, iovec$auto], vlen intptr, behavior int32, flags int32)
-process_mrelease$auto(pidfd fd, flags int32)
+process_madvise$auto(pidfd fd_pidfd, vec ptr[in, iovec$auto], vlen intptr, behavior int32, flags int32)
+process_mrelease$auto(pidfd fd_pidfd, flags int32)
process_vm_readv$auto(pid pid, lvec ptr[in, iovec$auto], liovcnt intptr, rvec ptr[in, iovec$auto], riovcnt intptr, flags intptr)
process_vm_writev$auto(pid pid, lvec ptr[in, iovec$auto], liovcnt intptr, rvec ptr[in, iovec$auto], riovcnt intptr, flags intptr)
pselect6$auto(n int32, inp ptr[inout, __kernel_fd_set$auto], outp ptr[inout, __kernel_fd_set$auto], exp ptr[inout, __kernel_fd_set$auto], tsp ptr[inout, __kernel_timespec$auto], sig ptr[inout, array[auto_todo]])
pselect6_time64$auto(n int32, inp ptr[inout, __kernel_fd_set$auto], outp ptr[inout, __kernel_fd_set$auto], exp ptr[inout, __kernel_fd_set$auto], tsp ptr[inout, __kernel_timespec$auto], sig ptr[inout, array[auto_todo]])
-ptrace$auto(request intptr, pid intptr, addr intptr, data intptr)
+ptrace$auto(request intptr, pid pid, addr intptr, data intptr)
pwrite64$auto(fd fd, buf ptr[in, string], count intptr, pos intptr)
-pwritev$auto(fd intptr, vec ptr[in, iovec$auto], vlen intptr, pos_l intptr, pos_h intptr)
-pwritev2$auto(fd intptr, vec ptr[in, iovec$auto], vlen intptr, pos_l intptr, pos_h intptr, flags int32)
-quotactl$auto(cmd int32, special ptr[in, string], id int32, addr ptr[inout, array[auto_todo]])
-quotactl_fd$auto(fd fd, cmd int32, id int32, addr ptr[inout, array[auto_todo]])
+pwritev$auto(fd fd, vec ptr[in, iovec$auto], vlen intptr, pos_l intptr, pos_h intptr)
+pwritev2$auto(fd fd, vec ptr[in, iovec$auto], vlen intptr, pos_l intptr, pos_h intptr, flags int32)
+quotactl$auto(cmd int32, special ptr[in, string], id gid, addr ptr[inout, array[auto_todo]])
+quotactl_fd$auto(fd fd, cmd int32, id gid, addr ptr[inout, array[auto_todo]])
read$auto(fd fd, buf ptr[inout, string], count intptr)
readahead$auto(fd fd, offset intptr, count intptr)
readdir$auto(fd fd, dirent ptr[inout, old_linux_dirent$auto], count int32)
readlink$auto(path ptr[in, filename], buf ptr[inout, string], bufsiz int32)
readlinkat$auto(dfd fd_dir, pathname ptr[in, filename], buf ptr[inout, string], bufsiz int32)
-readv$auto(fd intptr, vec ptr[in, iovec$auto], vlen intptr)
+readv$auto(fd fd, vec ptr[in, iovec$auto], vlen intptr)
recv$auto(fd fd, ubuf ptr[inout, array[auto_todo]], size intptr, flags int32)
recvfrom$auto(fd fd, ubuf ptr[inout, array[auto_todo]], size intptr, flags int32, addr ptr[inout, sockaddr], addr_len ptr[inout, int32])
recvmmsg$auto(fd fd, mmsg ptr[inout, mmsghdr$auto], vlen int32, flags int32, timeout ptr[inout, __kernel_timespec$auto])
@@ -461,9 +466,9 @@ sched_setaffinity$auto(pid pid, len int32, user_mask_ptr ptr[inout, intptr])
sched_setattr$auto(pid pid, uattr ptr[inout, sched_attr$auto], flags int32)
sched_setparam$auto(pid pid, param ptr[inout, sched_param$auto])
sched_setscheduler$auto(pid pid, policy int32, param ptr[inout, sched_param$auto])
-seccomp$auto(op int32, flags int32, uargs ptr[inout, array[auto_todo]])
+seccomp$auto(op int32, flags int32, uargs ptr[inout, array[auto_todo]]) fd
select$auto(n int32, inp ptr[inout, __kernel_fd_set$auto], outp ptr[inout, __kernel_fd_set$auto], exp ptr[inout, __kernel_fd_set$auto], tvp ptr[inout, __kernel_old_timeval$auto])
-semctl$auto(semid int32, semnum int32, cmd int32, arg intptr)
+semctl$auto(semid int32, semnum int32, cmd int32, arg intptr) pid
semget$auto(key int32, nsems int32, semflg int32)
semop$auto(semid int32, tsops ptr[inout, sembuf$auto], nsops int32)
semtimedop$auto(semid int32, tsops ptr[inout, sembuf$auto], nsops int32, timeout ptr[in, __kernel_timespec$auto])
@@ -478,12 +483,12 @@ set_mempolicy$auto(mode int32, nmask ptr[in, intptr], maxnode intptr)
set_mempolicy_home_node$auto(start intptr, len intptr, home_node intptr, flags intptr)
set_robust_list$auto(head ptr[inout, robust_list_head$auto], len intptr)
set_thread_area$auto(u_info ptr[inout, user_desc$auto])
-set_tid_address$auto(tidptr ptr[inout, int32])
+set_tid_address$auto(tidptr ptr[inout, int32]) pid
setdomainname$auto(name ptr[inout, string], len int32)
-setfsgid$auto(gid gid)
-setfsgid32$auto(gid gid)
-setfsuid$auto(uid uid)
-setfsuid32$auto(uid uid)
+setfsgid$auto(gid gid) gid
+setfsgid32$auto(gid gid) gid
+setfsuid$auto(uid uid) uid
+setfsuid32$auto(uid uid) uid
setgid$auto(gid gid)
setgid32$auto(gid gid)
setgroups$auto(gidsetsize int32, grouplist ptr[inout, int32])
@@ -492,7 +497,7 @@ sethostname$auto(name ptr[inout, string], len int32)
setitimer$auto(which int32, value ptr[inout, __kernel_old_itimerval$auto], ovalue ptr[inout, __kernel_old_itimerval$auto])
setns$auto(fd fd, flags int32)
setpgid$auto(pid pid, pgid pid)
-setpriority$auto(which int32, who int32, niceval int32)
+setpriority$auto(which int32, who uid, niceval int32)
setregid$auto(rgid gid, egid gid)
setregid32$auto(rgid gid, egid gid)
setresgid$auto(rgid gid, egid gid, sgid gid)
@@ -515,14 +520,14 @@ shmget$auto(key int32, size intptr, shmflg int32)
shutdown$auto(fd fd, how int32)
sigaltstack$auto(uss ptr[in, sigaltstack$auto], uoss ptr[inout, sigaltstack$auto])
signal$auto(sig int32, handler ptr[inout, ptr[in, auto_todo]])
-signalfd$auto(ufd fd, user_mask ptr[inout, sigset_t$auto], sizemask intptr)
-signalfd4$auto(ufd fd, user_mask ptr[inout, sigset_t$auto], sizemask intptr, flags int32)
+signalfd$auto(ufd fd, user_mask ptr[inout, sigset_t$auto], sizemask intptr) fd
+signalfd4$auto(ufd fd, user_mask ptr[inout, sigset_t$auto], sizemask intptr, flags int32) fd
sigpending$auto(uset ptr[inout, intptr])
sigprocmask$auto(how int32, nset ptr[inout, intptr], oset ptr[inout, intptr])
sigsuspend$auto(unused1 const[0], unused2 const[0], mask intptr)
-socket$auto(family int32, type int32, protocol int32)
-socketcall$auto(call int32, args ptr[inout, intptr])
-socketpair$auto(family int32, type int32, protocol int32, usockvec ptr[inout, int32])
+socket$auto(family int32, type int32, protocol int32) fd
+socketcall$auto(call int32, args ptr[inout, intptr]) fd
+socketpair$auto(family int32, type int32, protocol int32, usockvec ptr[inout, int32]) fd
splice$auto(fd_in fd, off_in ptr[inout, int64], fd_out fd, off_out ptr[inout, int64], len intptr, flags int32)
ssetmask$auto(newmask int32)
stat$auto(filename ptr[in, filename], statbuf ptr[inout, stat$auto])
@@ -551,7 +556,7 @@ timer_gettime$auto(timer_id int32, setting ptr[inout, __kernel_itimerspec$auto])
timer_gettime64$auto(timer_id int32, setting ptr[inout, __kernel_itimerspec$auto])
timer_settime$auto(timer_id int32, flags int32, new_setting ptr[in, __kernel_itimerspec$auto], old_setting ptr[inout, __kernel_itimerspec$auto])
timer_settime64$auto(timer_id int32, flags int32, new_setting ptr[in, __kernel_itimerspec$auto], old_setting ptr[inout, __kernel_itimerspec$auto])
-timerfd_create$auto(clockid int32, flags int32)
+timerfd_create$auto(clockid int32, flags int32) fd
timerfd_gettime$auto(ufd fd, otmr ptr[inout, __kernel_itimerspec$auto])
timerfd_gettime64$auto(ufd fd, otmr ptr[inout, __kernel_itimerspec$auto])
timerfd_settime$auto(ufd fd, flags int32, utmr ptr[in, __kernel_itimerspec$auto], otmr ptr[inout, __kernel_itimerspec$auto])
@@ -568,18 +573,18 @@ uname$auto(name ptr[inout, new_utsname$auto])
unlink$auto(pathname ptr[in, filename])
unlinkat$auto(dfd fd_dir, pathname ptr[in, filename], flag int32)
unshare$auto(unshare_flags intptr)
-userfaultfd$auto(flags int32)
+userfaultfd$auto(flags int32) fd
ustat$auto(dev int32, ubuf ptr[inout, ustat$auto])
utime$auto(filename ptr[inout, filename], times ptr[inout, utimbuf$auto])
utimensat$auto(dfd fd_dir, filename ptr[in, filename], utimes ptr[inout, __kernel_timespec$auto], flags int32)
utimensat_time64$auto(dfd fd_dir, filename ptr[in, filename], utimes ptr[inout, __kernel_timespec$auto], flags int32)
utimes$auto(filename ptr[inout, filename], utimes ptr[inout, __kernel_old_timeval$auto])
vmsplice$auto(fd fd, uiov ptr[in, iovec$auto], nr_segs intptr, flags int32)
-wait4$auto(upid int32, stat_addr ptr[inout, int32], options int32, ru ptr[inout, rusage$auto])
-waitid$auto(which int32, upid int32, infop ptr[inout, siginfo$auto], options int32, ru ptr[inout, rusage$auto])
-waitpid$auto(pid pid, stat_addr ptr[inout, int32], options int32)
+wait4$auto(upid pid, stat_addr ptr[inout, int32], options int32, ru ptr[inout, rusage$auto]) pid
+waitid$auto(which int32, upid fd_pidfd, infop ptr[inout, siginfo$auto], options int32, ru ptr[inout, rusage$auto]) pid
+waitpid$auto(pid pid, stat_addr ptr[inout, int32], options int32) pid
write$auto(fd fd, buf ptr[in, string], count intptr)
-writev$auto(fd intptr, vec ptr[in, iovec$auto], vlen intptr)
+writev$auto(fd fd, vec ptr[in, iovec$auto], vlen intptr)
resource fd__ctl_fops_dm_ioctl[fd]
openat$auto__ctl_fops_dm_ioctl(fd const[AT_FDCWD], file ptr[in, string["/dev/mapper/control"]], flags flags[open_flags], mode const[0]) fd__ctl_fops_dm_ioctl
@@ -5340,7 +5345,7 @@ btrfs_ioctl_feature_flags$auto {
}
btrfs_ioctl_vol_args$auto {
- fd int64
+ fd auto_union[fd, int64]
name array[int8, 4088]
}
@@ -5721,7 +5726,7 @@ mmsghdr$auto {
mnt_id_req$auto {
size int32
- spare int32
+ spare auto_union[fd, int32]
mnt_id int64
param int64
mnt_ns_id int64
@@ -5774,7 +5779,7 @@ mount_attr$auto {
attr_set int64
attr_clr int64
propagation int64
- userns_fd int64
+ userns_fd auto_union[fd, int64]
}
mq_attr$auto {
@@ -5802,8 +5807,8 @@ msqid_ds$auto {
msg_cbytes int16
msg_qnum int16
msg_qbytes int16
- msg_lspid int32
- msg_lrpid int32
+ msg_lspid auto_union[pid, int32]
+ msg_lrpid auto_union[pid, int32]
}
new_utsname$auto {
@@ -6138,8 +6143,8 @@ shmid_ds$auto {
shm_atime intptr
shm_dtime intptr
shm_ctime intptr
- shm_cpid int32
- shm_lpid int32
+ shm_cpid auto_union[pid, int32]
+ shm_lpid auto_union[pid, int32]
shm_nattch int16
shm_unused const[0, int16]
shm_unused2 ptr[inout, array[auto_todo]]
@@ -6161,7 +6166,7 @@ sigaltstack$auto {
sigevent$auto {
sigev_value sigval$auto
- sigev_signo int32
+ sigev_signo auto_union[fd, int32]
sigev_notify int32
_sigev_un sigevent__sigev_un$auto
}
@@ -6222,12 +6227,12 @@ snd_ctl_card_info$auto {
}
snd_ctl_elem_id$auto {
- numid int32
+ numid auto_union[pid, int32]
iface int32
device int32
subdevice int32
name array[int8, 44]
- index int32
+ index auto_union[pid, int32]
}
snd_ctl_elem_info$auto {
@@ -6235,7 +6240,7 @@ snd_ctl_elem_info$auto {
type int32
access int32
count int32
- owner int32
+ owner auto_union[pid, int32]
value snd_ctl_elem_info_value$auto
reserved array[int8, 64]
}
@@ -6271,7 +6276,7 @@ snd_ctl_elem_list$auto {
offset int32
space int32
used int32
- count int32
+ count auto_union[pid, int32]
pids ptr[inout, snd_ctl_elem_id$auto]
reserved array[int8, 50]
}
@@ -6363,7 +6368,7 @@ sock_filter$auto {
code int16
jt int8
jf int8
- k int32
+ k auto_union[fd, int32]
}
sock_fprog$auto {
@@ -6494,7 +6499,7 @@ statx_timestamp$auto {
sw_sync_create_fence_data$auto {
value int32
name array[int8, 32]
- fence int32
+ fence auto_union[fd, int32]
}
sw_sync_get_deadline$auto {
diff --git a/tools/syz-declextract/clangtool/declextract.cpp b/tools/syz-declextract/clangtool/declextract.cpp
index d2b2133b9..c41904d8b 100644
--- a/tools/syz-declextract/clangtool/declextract.cpp
+++ b/tools/syz-declextract/clangtool/declextract.cpp
@@ -117,7 +117,7 @@ private:
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::string getUniqueDeclName(const NamedDecl* 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);
@@ -129,6 +129,12 @@ private:
std::optional<QualType> getSizeofType(const Expr* E);
int sizeofType(const Type* T);
std::vector<IoctlCmd> extractIoctlCommands(const std::string& Ioctl);
+ std::optional<TypingEntity> getTypingEntity(const std::string& CurrentFunc,
+ std::unordered_map<const VarDecl*, int>& LocalVars,
+ std::unordered_map<std::string, int>& LocalSeq, const Expr* E);
+ std::optional<TypingEntity> getDeclTypingEntity(const std::string& CurrentFunc,
+ std::unordered_map<const VarDecl*, int>& LocalVars,
+ std::unordered_map<std::string, int>& LocalSeq, const Decl* Decl);
};
// PPCallbacksTracker records all macro definitions (name/value/source location).
@@ -473,7 +479,7 @@ void Extractor::matchNetlinkPolicy() {
std::unique_ptr<FieldType> Elem;
if (AttrKind == "NLA_NESTED" || AttrKind == "NLA_NESTED_ARRAY") {
if (const auto* NestedDecl = getValueDecl(AttrInit->getInit(2)))
- NestedPolicy = policyName(NestedDecl);
+ NestedPolicy = getUniqueDeclName(NestedDecl);
} else {
MaxSize = evaluate<int>(LenExpr);
if (auto SizeofType = getSizeofType(LenExpr))
@@ -488,7 +494,7 @@ void Extractor::matchNetlinkPolicy() {
});
}
Output.emit(NetlinkPolicy{
- .Name = policyName(PolicyArray),
+ .Name = getUniqueDeclName(PolicyArray),
.Attrs = std::move(Attrs),
});
}
@@ -499,7 +505,7 @@ void Extractor::matchNetlinkFamily() {
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);
+ DefaultPolicy = getUniqueDeclName(PolicyDecl);
std::vector<NetlinkOp> Ops;
for (const auto& OpsName : {"ops", "small_ops", "split_ops"}) {
const auto* OpsDecl =
@@ -521,7 +527,7 @@ void Extractor::matchNetlinkFamily() {
std::string Policy;
if (OpsFields.count("policy") != 0) {
if (const auto* PolicyDecl = OpInit->getInit(OpsFields["policy"])->getAsBuiltinConstantDeclRef(*Context))
- Policy = policyName(PolicyDecl);
+ Policy = getUniqueDeclName(PolicyDecl);
}
if (Policy.empty())
Policy = DefaultPolicy;
@@ -550,42 +556,185 @@ void Extractor::matchNetlinkFamily() {
});
}
-std::string Extractor::policyName(const ValueDecl* Decl) { return Decl->getNameAsString() + "_" + getDeclFileID(Decl); }
+std::string Extractor::getUniqueDeclName(const NamedDecl* Decl) {
+ return Decl->getNameAsString() + "_" + getDeclFileID(Decl);
+}
+
+const Expr* removeCasts(const Expr* E) {
+ for (;;) {
+ if (auto* P = dyn_cast<ParenExpr>(E))
+ E = P->getSubExpr();
+ else if (auto* C = dyn_cast<CastExpr>(E))
+ E = C->getSubExpr();
+ else
+ break;
+ }
+ return E;
+}
+
+bool isInterestingCall(const CallExpr* Call) {
+ auto* CalleeDecl = Call->getDirectCallee();
+ // We don't handle indirect calls yet.
+ if (!CalleeDecl)
+ return false;
+ // Builtins are not interesting and won't have a body.
+ if (CalleeDecl->getBuiltinID() != Builtin::ID::NotBuiltin)
+ return false;
+ const std::string& Callee = CalleeDecl->getNameAsString();
+ // There are too many of these and they should only be called at runtime in broken builds.
+ if (Callee.rfind("__compiletime_assert", 0) == 0 || Callee == "____wrong_branch_error" ||
+ Callee == "__bad_size_call_parameter")
+ return false;
+ return true;
+}
void Extractor::matchFunctionDef() {
const auto* Func = getResult<FunctionDecl>("function");
+ const std::string& CurrentFunc = Func->getNameAsString();
const auto Range = Func->getSourceRange();
const std::string& SourceFile =
std::filesystem::relative(SourceManager->getFilename(SourceManager->getExpansionLoc(Range.getBegin())).str());
const int LOC = std::max<int>(0, SourceManager->getExpansionLineNumber(Range.getEnd()) -
SourceManager->getExpansionLineNumber(Range.getBegin()) - 1);
+ std::vector<TypingFact> Facts;
std::vector<std::string> Callees;
std::unordered_set<std::string> CalleesDedup;
+ std::unordered_map<const VarDecl*, int> LocalVars;
+ std::unordered_map<std::string, int> LocalSeq;
const auto& Calls = findAllMatches<CallExpr>(Func->getBody(), stmt(forEachDescendant(callExpr().bind("res"))));
for (auto* Call : Calls) {
- if (auto* CalleeDecl = Call->getDirectCallee()) {
- // Builtins are not interesting and won't have a body.
- if (CalleeDecl->getBuiltinID() != Builtin::ID::NotBuiltin)
- continue;
- const std::string& Callee = CalleeDecl->getNameAsString();
- // There are too many of these and they should only be called at runtime in broken builds.
- if (Callee.rfind("__compiletime_assert", 0) == 0 || Callee == "____wrong_branch_error" ||
- Callee == "__bad_size_call_parameter")
- continue;
- if (!CalleesDedup.insert(Callee).second)
- continue;
- Callees.push_back(Callee);
+ if (!isInterestingCall(Call))
+ continue;
+ const std::string& Callee = Call->getDirectCallee()->getNameAsString();
+ for (unsigned AI = 0; AI < Call->getNumArgs(); AI++) {
+ if (auto Src = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, Call->getArg(AI))) {
+ Facts.push_back({std::move(*Src), EntityArgument{
+ .Func = Callee,
+ .Arg = AI,
+ }});
+ }
+ }
+ if (!CalleesDedup.insert(Callee).second)
+ continue;
+ Callees.push_back(Callee);
+ }
+ const auto& Assignments = findAllMatches<BinaryOperator>(
+ Func->getBody(), stmt(forEachDescendant(binaryOperator(isAssignmentOperator()).bind("res"))));
+ for (auto* A : Assignments) {
+ auto Src = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, A->getRHS());
+ auto Dst = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, A->getLHS());
+ if (Src && Dst)
+ Facts.push_back({std::move(*Src), std::move(*Dst)});
+ }
+ const auto& VarDecls = findAllMatches<VarDecl>(
+ Func->getBody(), stmt(forEachDescendant(varDecl(hasAutomaticStorageDuration()).bind("res"))));
+ for (auto* D : VarDecls) {
+ auto Src = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, D->getInit());
+ auto Dst = getDeclTypingEntity(CurrentFunc, LocalVars, LocalSeq, D);
+ if (Src && Dst)
+ Facts.push_back({std::move(*Src), std::move(*Dst)});
+ }
+ const auto& Returns = findAllMatches<ReturnStmt>(Func->getBody(), stmt(forEachDescendant(returnStmt().bind("res"))));
+ for (auto* Ret : Returns) {
+ if (auto Src = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, Ret->getRetValue())) {
+ Facts.push_back({std::move(*Src), EntityReturn{
+ .Func = CurrentFunc,
+ }});
}
}
Output.emit(Function{
- .Name = Func->getNameAsString(),
+ .Name = CurrentFunc,
.File = SourceFile,
.IsStatic = Func->isStatic(),
.LOC = LOC,
.Calls = std::move(Callees),
+ .Facts = std::move(Facts),
});
}
+std::optional<TypingEntity> Extractor::getTypingEntity(const std::string& CurrentFunc,
+ std::unordered_map<const VarDecl*, int>& LocalVars,
+ std::unordered_map<std::string, int>& LocalSeq, const Expr* E) {
+ if (!E)
+ return {};
+ E = removeCasts(E);
+ if (auto* DeclRef = dyn_cast<DeclRefExpr>(E)) {
+ return getDeclTypingEntity(CurrentFunc, LocalVars, LocalSeq, DeclRef->getDecl());
+ } else if (auto* Member = dyn_cast<MemberExpr>(E)) {
+ const Type* StructType =
+ Member->getBase()->getType().IgnoreParens().getUnqualifiedType().getDesugaredType(*Context).getTypePtr();
+ if (auto* T = dyn_cast<PointerType>(StructType))
+ StructType = T->getPointeeType().IgnoreParens().getUnqualifiedType().getDesugaredType(*Context).getTypePtr();
+ auto* StructDecl = dyn_cast<RecordType>(StructType)->getDecl();
+ std::string StructName = StructDecl->getNameAsString();
+ if (StructName.empty()) {
+ // The struct may be anonymous, but we need some name.
+ // Ideally we generate the same name we generate in struct definitions, then it will be possible
+ // to match them between each other. However, it does not seem to be easy. We can use DeclContext::getParent
+ // to get declaration of the enclosing struct, but we will also need to figure out the field index
+ // and handle all corner cases. For now we just use the following quick hack: hash declaration file:line.
+ // Note: the hash must be stable across different machines (for test golden files), so we take just
+ // the last part of the file name.
+ const std::string& SourceFile =
+ std::filesystem::path(
+ SourceManager->getFilename(SourceManager->getExpansionLoc(StructDecl->getBeginLoc())).str())
+ .filename()
+ .string();
+ int Line = SourceManager->getExpansionLineNumber(StructDecl->getBeginLoc());
+ StructName = std::to_string(std::hash<std::string>()(SourceFile) + std::hash<int>()(Line));
+ }
+ return EntityField{
+ .Struct = StructName,
+ .Field = Member->getMemberDecl()->getNameAsString(),
+ };
+ } else if (auto* Unary = dyn_cast<UnaryOperator>(E)) {
+ if (Unary->getOpcode() == UnaryOperatorKind::UO_AddrOf) {
+ if (auto* DeclRef = dyn_cast<DeclRefExpr>(removeCasts(Unary->getSubExpr()))) {
+ if (auto* Var = dyn_cast<VarDecl>(DeclRef->getDecl())) {
+ if (Var->hasGlobalStorage()) {
+ return EntityGlobalAddr{
+ .Name = getUniqueDeclName(Var),
+ };
+ }
+ }
+ }
+ }
+ } else if (auto* Call = dyn_cast<CallExpr>(E)) {
+ if (isInterestingCall(Call)) {
+ return EntityReturn{
+ .Func = Call->getDirectCallee()->getNameAsString(),
+ };
+ }
+ }
+ return {};
+}
+
+std::optional<TypingEntity> Extractor::getDeclTypingEntity(const std::string& CurrentFunc,
+ std::unordered_map<const VarDecl*, int>& LocalVars,
+ std::unordered_map<std::string, int>& LocalSeq,
+ const Decl* Decl) {
+ if (auto* Parm = dyn_cast<ParmVarDecl>(Decl)) {
+ return EntityArgument{
+ .Func = CurrentFunc,
+ .Arg = Parm->getFunctionScopeIndex(),
+ };
+ } else if (auto* Var = dyn_cast<VarDecl>(Decl)) {
+ if (Var->hasLocalStorage()) {
+ std::string VarName = Var->getNameAsString();
+ // Theoretically there can be several local vars with the same name.
+ // Give them unique suffixes if that's the case.
+ if (LocalVars.count(Var) == 0)
+ LocalVars[Var] = LocalSeq[VarName]++;
+ if (int Seq = LocalVars[Var])
+ VarName += std::to_string(Seq);
+ return EntityLocal{
+ .Name = VarName,
+ };
+ }
+ }
+ return {};
+}
+
void Extractor::matchSyscall() {
const auto* Func = getResult<FunctionDecl>("syscall");
std::vector<Field> Args;
@@ -626,7 +775,7 @@ void Extractor::matchFileOps() {
return;
}
const auto* Var = getResult<VarDecl>("var");
- std::string VarName = Var->getNameAsString() + "_" + getDeclFileID(Var);
+ std::string VarName = getUniqueDeclName(Var);
int NameSeq = FileOpsDedup[VarName]++;
if (NameSeq)
VarName += std::to_string(NameSeq);
diff --git a/tools/syz-declextract/clangtool/json.h b/tools/syz-declextract/clangtool/json.h
index fbbcc12a1..873313a09 100644
--- a/tools/syz-declextract/clangtool/json.h
+++ b/tools/syz-declextract/clangtool/json.h
@@ -53,6 +53,7 @@ private:
};
inline void print(JSONPrinter& Printer, int V) { printf("%d", V); }
+inline void print(JSONPrinter& Printer, unsigned V) { printf("%u", 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 : ""); }
diff --git a/tools/syz-declextract/clangtool/output.h b/tools/syz-declextract/clangtool/output.h
index eea86afa2..24ab82f61 100644
--- a/tools/syz-declextract/clangtool/output.h
+++ b/tools/syz-declextract/clangtool/output.h
@@ -108,12 +108,62 @@ struct FileOps {
std::vector<IoctlCmd> IoctlCmds;
};
+struct EntityReturn {
+ std::string Func;
+};
+
+struct EntityArgument {
+ std::string Func;
+ unsigned Arg;
+};
+
+struct EntityField {
+ std::string Struct;
+ std::string Field;
+};
+
+struct EntityLocal {
+ std::string Name;
+};
+
+struct EntityGlobalAddr {
+ std::string Name;
+};
+
+struct EntityResource {
+ std::string Type;
+ std::string SubType;
+};
+
+struct TypingEntity {
+ std::unique_ptr<EntityReturn> Return;
+ std::unique_ptr<EntityArgument> Argument;
+ std::unique_ptr<EntityField> Field;
+ std::unique_ptr<EntityLocal> Local;
+ std::unique_ptr<EntityGlobalAddr> GlobalAddr;
+ std::unique_ptr<EntityResource> Resource;
+
+ TypingEntity() = default;
+ TypingEntity(EntityReturn&& E) : Return(std::make_unique<EntityReturn>(std::move(E))) {}
+ TypingEntity(EntityArgument&& E) : Argument(std::make_unique<EntityArgument>(std::move(E))) {}
+ TypingEntity(EntityField&& E) : Field(std::make_unique<EntityField>(std::move(E))) {}
+ TypingEntity(EntityLocal&& E) : Local(std::make_unique<EntityLocal>(std::move(E))) {}
+ TypingEntity(EntityGlobalAddr&& E) : GlobalAddr(std::make_unique<EntityGlobalAddr>(std::move(E))) {}
+ TypingEntity(EntityResource&& E) : Resource(std::make_unique<EntityResource>(std::move(E))) {}
+};
+
+struct TypingFact {
+ TypingEntity Src;
+ TypingEntity Dst;
+};
+
struct Function {
std::string Name;
std::string File;
bool IsStatic = false;
int LOC = 0;
std::vector<std::string> Calls;
+ std::vector<TypingFact> Facts;
};
struct Syscall {
@@ -129,7 +179,7 @@ struct IouringOp {
struct NetlinkOp {
std::string Name;
std::string Func;
- const char* Access;
+ const char* Access = nullptr;
std::string Policy;
};
@@ -245,13 +295,69 @@ inline void print(JSONPrinter& Printer, const FileOps& V) {
Printer.Field("ioctl_cmds", V.IoctlCmds, true);
}
+inline void print(JSONPrinter& Printer, const EntityReturn& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("func", V.Func, true);
+}
+
+inline void print(JSONPrinter& Printer, const EntityArgument& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("func", V.Func);
+ Printer.Field("arg", V.Arg, true);
+}
+
+inline void print(JSONPrinter& Printer, const EntityField& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("struct", V.Struct);
+ Printer.Field("field", V.Field, true);
+}
+
+inline void print(JSONPrinter& Printer, const EntityLocal& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name, true);
+}
+
+inline void print(JSONPrinter& Printer, const EntityGlobalAddr& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("name", V.Name, true);
+}
+
+inline void print(JSONPrinter& Printer, const EntityResource& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("type", V.Type);
+ Printer.Field("subtype", V.SubType, true);
+}
+
+inline void print(JSONPrinter& Printer, const TypingEntity& V) {
+ JSONPrinter::Scope Scope(Printer);
+ if (V.Return)
+ Printer.Field("return", *V.Return, true);
+ else if (V.Argument)
+ Printer.Field("argument", *V.Argument, true);
+ else if (V.Field)
+ Printer.Field("field", *V.Field, true);
+ else if (V.Local)
+ Printer.Field("local", *V.Local, true);
+ else if (V.GlobalAddr)
+ Printer.Field("global_addr", *V.GlobalAddr, true);
+ else
+ Printer.Field("resource", *V.Resource, true);
+}
+
+inline void print(JSONPrinter& Printer, const TypingFact& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("src", V.Src);
+ Printer.Field("dst", V.Dst, true);
+}
+
inline void print(JSONPrinter& Printer, const Function& V) {
JSONPrinter::Scope Scope(Printer);
Printer.Field("name", V.Name);
Printer.Field("file", V.File);
Printer.Field("is_static", V.IsStatic);
Printer.Field("loc", V.LOC);
- Printer.Field("calls", V.Calls, true);
+ Printer.Field("calls", V.Calls);
+ Printer.Field("facts", V.Facts, true);
}
inline void print(JSONPrinter& Printer, const Syscall& V) {
diff --git a/tools/syz-declextract/declextract.go b/tools/syz-declextract/declextract.go
index 62008d8ad..0853756bf 100644
--- a/tools/syz-declextract/declextract.go
+++ b/tools/syz-declextract/declextract.go
@@ -47,10 +47,11 @@ func main() {
return probe(cfg, *flagConfig)
}
if err := run(filepath.FromSlash("sys/linux/auto.txt"), loadProbeInfo, &clangtool.Config{
- ToolBin: *flagBinary,
- KernelSrc: cfg.KernelSrc,
- KernelObj: cfg.KernelObj,
- CacheFile: filepath.Join(cfg.Workdir, "declextract.cache"),
+ ToolBin: *flagBinary,
+ KernelSrc: cfg.KernelSrc,
+ KernelObj: cfg.KernelObj,
+ CacheFile: filepath.Join(cfg.Workdir, "declextract.cache"),
+ DebugTrace: os.Stderr,
}); err != nil {
tool.Fail(err)
}
@@ -61,7 +62,7 @@ func run(autoFile string, loadProbeInfo func() (*ifaceprobe.Info, error), cfg *c
if err != nil {
return err
}
- descriptions, interfaces, err := declextract.Run(out, probeInfo, syscallRename)
+ descriptions, interfaces, err := declextract.Run(out, probeInfo, syscallRename, cfg.DebugTrace)
if err != nil {
return err
}
diff --git a/tools/syz-declextract/declextract_test.go b/tools/syz-declextract/declextract_test.go
index 2f8dd70d5..ec1071327 100644
--- a/tools/syz-declextract/declextract_test.go
+++ b/tools/syz-declextract/declextract_test.go
@@ -17,6 +17,7 @@ import (
"github.com/google/syzkaller/pkg/compiler"
"github.com/google/syzkaller/pkg/ifaceprobe"
"github.com/google/syzkaller/pkg/osutil"
+ "github.com/google/syzkaller/pkg/testutil"
)
var (
@@ -132,10 +133,11 @@ func testEachFile(t *testing.T, fn func(t *testing.T, cfg *clangtool.Config, fil
t.Fatal(err)
}
cfg := &clangtool.Config{
- ToolBin: *flagBin,
- KernelSrc: testdata,
- KernelObj: buildDir,
- CacheFile: filepath.Join(buildDir, filepath.Base(file)+".json"),
+ ToolBin: *flagBin,
+ KernelSrc: testdata,
+ KernelObj: buildDir,
+ CacheFile: filepath.Join(buildDir, filepath.Base(file)+".json"),
+ DebugTrace: &testutil.Writer{TB: t},
}
fn(t, cfg, file)
})
diff --git a/tools/syz-declextract/testdata/functions.c b/tools/syz-declextract/testdata/functions.c
index 675489432..fd06fb455 100644
--- a/tools/syz-declextract/testdata/functions.c
+++ b/tools/syz-declextract/testdata/functions.c
@@ -11,15 +11,52 @@ static void func_bar() {
func_foo();
}
-void func_baz(int f) {
+int alloc_fd() {
+ return 1;
+}
+
+void __fget_light(int fd) {
+}
+
+int from_kuid() {
+ return 1;
+}
+
+int func_baz(int f) {
func_foo();
if (f)
func_bar();
if (__builtin_constant_p(f))
func_bar();
+ if (f)
+ return from_kuid();
+ return alloc_fd();
+}
+
+int func_qux() {
+ int fd = alloc_fd();
+ return fd;
+}
+
+SYSCALL_DEFINE1(functions, long x) {
+ __fget_light(x);
+ return func_baz(1);
+}
+
+struct Typed {
+ int a;
+ int b;
+ int c;
+};
+
+int typing1(int a, int b) {
+ return a;
}
-SYSCALL_DEFINE1(functions) {
- func_baz(1);
- return 0;
+int typing(struct Typed* t1, int i) {
+ struct Typed t2;
+ t2.a = t1->b;
+ int l = typing1(i, t2.a);
+ t1->c = l;
+ return l;
}
diff --git a/tools/syz-declextract/testdata/functions.c.info b/tools/syz-declextract/testdata/functions.c.info
index 0101daf12..2e2720113 100644
--- a/tools/syz-declextract/testdata/functions.c.info
+++ b/tools/syz-declextract/testdata/functions.c.info
@@ -1 +1 @@
-SYSCALL functions func:__do_sys_functions loc:8 access:unknown manual_desc:false auto_desc:true file:functions.c subsystem:kernel
+SYSCALL functions func:__do_sys_functions loc:13 access:unknown manual_desc:false auto_desc:true file:functions.c subsystem:kernel
diff --git a/tools/syz-declextract/testdata/functions.c.json b/tools/syz-declextract/testdata/functions.c.json
index 8a1fd4ee1..eb1b3b880 100644
--- a/tools/syz-declextract/testdata/functions.c.json
+++ b/tools/syz-declextract/testdata/functions.c.json
@@ -5,10 +5,48 @@
"file": "functions.c",
"loc": 2,
"calls": [
+ "__fget_light",
"func_baz"
+ ],
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "__do_sys_functions",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "argument": {
+ "func": "__fget_light",
+ "arg": 0
+ }
+ }
+ },
+ {
+ "src": {
+ "return": {
+ "func": "func_baz"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "__do_sys_functions"
+ }
+ }
+ }
]
},
{
+ "name": "__fget_light",
+ "file": "functions.c"
+ },
+ {
+ "name": "alloc_fd",
+ "file": "functions.c",
+ "loc": 1
+ },
+ {
"name": "atomic_load32",
"file": "include/types.h",
"is_static": true,
@@ -20,6 +58,11 @@
"loc": 1
},
{
+ "name": "from_kuid",
+ "file": "functions.c",
+ "loc": 1
+ },
+ {
"name": "func_bar",
"file": "functions.c",
"is_static": true,
@@ -31,21 +74,205 @@
{
"name": "func_baz",
"file": "functions.c",
- "loc": 5,
+ "loc": 8,
"calls": [
"func_foo",
- "func_bar"
+ "func_bar",
+ "from_kuid",
+ "alloc_fd"
+ ],
+ "facts": [
+ {
+ "src": {
+ "return": {
+ "func": "from_kuid"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "func_baz"
+ }
+ }
+ },
+ {
+ "src": {
+ "return": {
+ "func": "alloc_fd"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "func_baz"
+ }
+ }
+ }
]
},
{
"name": "func_foo",
"file": "functions.c",
"is_static": true
+ },
+ {
+ "name": "func_qux",
+ "file": "functions.c",
+ "loc": 2,
+ "calls": [
+ "alloc_fd"
+ ],
+ "facts": [
+ {
+ "src": {
+ "return": {
+ "func": "alloc_fd"
+ }
+ },
+ "dst": {
+ "local": {
+ "name": "fd"
+ }
+ }
+ },
+ {
+ "src": {
+ "local": {
+ "name": "fd"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "func_qux"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "typing",
+ "file": "functions.c",
+ "loc": 5,
+ "calls": [
+ "typing1"
+ ],
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "typing",
+ "arg": 1
+ }
+ },
+ "dst": {
+ "argument": {
+ "func": "typing1",
+ "arg": 0
+ }
+ }
+ },
+ {
+ "src": {
+ "field": {
+ "struct": "Typed",
+ "field": "a"
+ }
+ },
+ "dst": {
+ "argument": {
+ "func": "typing1",
+ "arg": 1
+ }
+ }
+ },
+ {
+ "src": {
+ "field": {
+ "struct": "Typed",
+ "field": "b"
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "Typed",
+ "field": "a"
+ }
+ }
+ },
+ {
+ "src": {
+ "local": {
+ "name": "l"
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "Typed",
+ "field": "c"
+ }
+ }
+ },
+ {
+ "src": {
+ "return": {
+ "func": "typing1"
+ }
+ },
+ "dst": {
+ "local": {
+ "name": "l"
+ }
+ }
+ },
+ {
+ "src": {
+ "local": {
+ "name": "l"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "typing"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "typing1",
+ "file": "functions.c",
+ "loc": 1,
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "typing1",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "typing1"
+ }
+ }
+ }
+ ]
}
],
"syscalls": [
{
"func": "__do_sys_functions",
+ "args": [
+ {
+ "name": "x",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "long",
+ "base": "long"
+ }
+ }
+ }
+ ],
"source_file": "functions.c"
}
]
diff --git a/tools/syz-declextract/testdata/functions.c.txt b/tools/syz-declextract/testdata/functions.c.txt
index cab813f8b..6dc51303b 100644
--- a/tools/syz-declextract/testdata/functions.c.txt
+++ b/tools/syz-declextract/testdata/functions.c.txt
@@ -8,4 +8,4 @@ include <vdso/bits.h>
include <linux/types.h>
include <net/netlink.h>
-functions$auto()
+functions$auto(x fd) fd
diff --git a/tools/syz-declextract/testdata/types.c b/tools/syz-declextract/testdata/types.c
index 8fc67aeb9..20f92673f 100644
--- a/tools/syz-declextract/testdata/types.c
+++ b/tools/syz-declextract/testdata/types.c
@@ -53,3 +53,14 @@ 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;
}
+
+void anon_flow(int x) {
+ struct anon_struct s;
+ s.a.x = x;
+ s.y = x;
+ s.w = x;
+ s.foo.f = x;
+ s.array[1].a = x;
+ s.ptr->a = x;
+ s.ptr_array[1]->b = x;
+}
diff --git a/tools/syz-declextract/testdata/types.c.json b/tools/syz-declextract/testdata/types.c.json
index 9733798ab..a5a7088db 100644
--- a/tools/syz-declextract/testdata/types.c.json
+++ b/tools/syz-declextract/testdata/types.c.json
@@ -4,6 +4,111 @@
"name": "__do_sys_types_syscall",
"file": "types.c",
"loc": 2
+ },
+ {
+ "name": "anon_flow",
+ "file": "types.c",
+ "loc": 8,
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "anon_flow",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "11253655576479126316",
+ "field": "x"
+ }
+ }
+ },
+ {
+ "src": {
+ "argument": {
+ "func": "anon_flow",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "11253655576479126318",
+ "field": "y"
+ }
+ }
+ },
+ {
+ "src": {
+ "argument": {
+ "func": "anon_flow",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "11253655576479126319",
+ "field": "w"
+ }
+ }
+ },
+ {
+ "src": {
+ "argument": {
+ "func": "anon_flow",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "11253655576479126309",
+ "field": "f"
+ }
+ }
+ },
+ {
+ "src": {
+ "argument": {
+ "func": "anon_flow",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "11253655576479126322",
+ "field": "a"
+ }
+ }
+ },
+ {
+ "src": {
+ "argument": {
+ "func": "anon_flow",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "11253655576479126323",
+ "field": "a"
+ }
+ }
+ },
+ {
+ "src": {
+ "argument": {
+ "func": "anon_flow",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "11253655576479126324",
+ "field": "b"
+ }
+ }
+ }
+ ]
}
],
"defines": [