From c8c15bb214509bafc8fe1a1e3abb8ccf90b3306e Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 13 Dec 2024 15:15:49 +0100 Subject: 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. --- pkg/clangtool/clangtool.go | 10 +- pkg/declextract/declextract.go | 25 ++- pkg/declextract/entity.go | 49 ++++- pkg/declextract/serialization.go | 7 +- pkg/declextract/typing.go | 268 ++++++++++++++++++++++++ pkg/testutil/testutil.go | 9 + sys/linux/auto.txt | 193 ++++++++--------- tools/syz-declextract/clangtool/declextract.cpp | 189 +++++++++++++++-- tools/syz-declextract/clangtool/json.h | 1 + tools/syz-declextract/clangtool/output.h | 110 +++++++++- tools/syz-declextract/declextract.go | 11 +- tools/syz-declextract/declextract_test.go | 10 +- tools/syz-declextract/testdata/functions.c | 45 +++- tools/syz-declextract/testdata/functions.c.info | 2 +- tools/syz-declextract/testdata/functions.c.json | 231 +++++++++++++++++++- tools/syz-declextract/testdata/functions.c.txt | 2 +- tools/syz-declextract/testdata/types.c | 11 + tools/syz-declextract/testdata/types.c.json | 105 ++++++++++ 18 files changed, 1134 insertions(+), 144 deletions(-) create mode 100644 pkg/declextract/typing.go 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 include include @@ -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> extractDesignatedInitConsts(const VarDecl& ArrayDecl); FieldType genType(QualType Typ, const std::string& BackupName = ""); std::unordered_map structFieldIndexes(const RecordDecl* Decl); @@ -129,6 +129,12 @@ private: std::optional getSizeofType(const Expr* E); int sizeofType(const Type* T); std::vector extractIoctlCommands(const std::string& Ioctl); + std::optional getTypingEntity(const std::string& CurrentFunc, + std::unordered_map& LocalVars, + std::unordered_map& LocalSeq, const Expr* E); + std::optional getDeclTypingEntity(const std::string& CurrentFunc, + std::unordered_map& LocalVars, + std::unordered_map& LocalSeq, const Decl* Decl); }; // PPCallbacksTracker records all macro definitions (name/value/source location). @@ -473,7 +479,7 @@ void Extractor::matchNetlinkPolicy() { std::unique_ptr 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(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(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 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(E)) + E = P->getSubExpr(); + else if (auto* C = dyn_cast(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("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(0, SourceManager->getExpansionLineNumber(Range.getEnd()) - SourceManager->getExpansionLineNumber(Range.getBegin()) - 1); + std::vector Facts; std::vector Callees; std::unordered_set CalleesDedup; + std::unordered_map LocalVars; + std::unordered_map LocalSeq; const auto& Calls = findAllMatches(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( + 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( + 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(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 Extractor::getTypingEntity(const std::string& CurrentFunc, + std::unordered_map& LocalVars, + std::unordered_map& LocalSeq, const Expr* E) { + if (!E) + return {}; + E = removeCasts(E); + if (auto* DeclRef = dyn_cast(E)) { + return getDeclTypingEntity(CurrentFunc, LocalVars, LocalSeq, DeclRef->getDecl()); + } else if (auto* Member = dyn_cast(E)) { + const Type* StructType = + Member->getBase()->getType().IgnoreParens().getUnqualifiedType().getDesugaredType(*Context).getTypePtr(); + if (auto* T = dyn_cast(StructType)) + StructType = T->getPointeeType().IgnoreParens().getUnqualifiedType().getDesugaredType(*Context).getTypePtr(); + auto* StructDecl = dyn_cast(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()(SourceFile) + std::hash()(Line)); + } + return EntityField{ + .Struct = StructName, + .Field = Member->getMemberDecl()->getNameAsString(), + }; + } else if (auto* Unary = dyn_cast(E)) { + if (Unary->getOpcode() == UnaryOperatorKind::UO_AddrOf) { + if (auto* DeclRef = dyn_cast(removeCasts(Unary->getSubExpr()))) { + if (auto* Var = dyn_cast(DeclRef->getDecl())) { + if (Var->hasGlobalStorage()) { + return EntityGlobalAddr{ + .Name = getUniqueDeclName(Var), + }; + } + } + } + } + } else if (auto* Call = dyn_cast(E)) { + if (isInterestingCall(Call)) { + return EntityReturn{ + .Func = Call->getDirectCallee()->getNameAsString(), + }; + } + } + return {}; +} + +std::optional Extractor::getDeclTypingEntity(const std::string& CurrentFunc, + std::unordered_map& LocalVars, + std::unordered_map& LocalSeq, + const Decl* Decl) { + if (auto* Parm = dyn_cast(Decl)) { + return EntityArgument{ + .Func = CurrentFunc, + .Arg = Parm->getFunctionScopeIndex(), + }; + } else if (auto* Var = dyn_cast(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("syscall"); std::vector Args; @@ -626,7 +775,7 @@ void Extractor::matchFileOps() { return; } const auto* Var = getResult("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 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 Return; + std::unique_ptr Argument; + std::unique_ptr Field; + std::unique_ptr Local; + std::unique_ptr GlobalAddr; + std::unique_ptr Resource; + + TypingEntity() = default; + TypingEntity(EntityReturn&& E) : Return(std::make_unique(std::move(E))) {} + TypingEntity(EntityArgument&& E) : Argument(std::make_unique(std::move(E))) {} + TypingEntity(EntityField&& E) : Field(std::make_unique(std::move(E))) {} + TypingEntity(EntityLocal&& E) : Local(std::make_unique(std::move(E))) {} + TypingEntity(EntityGlobalAddr&& E) : GlobalAddr(std::make_unique(std::move(E))) {} + TypingEntity(EntityResource&& E) : Resource(std::make_unique(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 Calls; + std::vector 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,9 +5,47 @@ "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", @@ -19,6 +57,11 @@ "file": "include/types.h", "loc": 1 }, + { + "name": "from_kuid", + "file": "functions.c", + "loc": 1 + }, { "name": "func_bar", "file": "functions.c", @@ -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 include include -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": [ -- cgit mrf-deployment