diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2025-04-11 08:04:51 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2025-04-11 09:37:01 +0000 |
| commit | a2335417592dcd4545c1953807ae9c8906e133d5 (patch) | |
| tree | 919af28ce8d763f29270c69afdd895e194e088ee /pkg | |
| parent | c0efc34f5a3f6a494541381c0f5770200c2640b5 (diff) | |
tools/syz-declextract: export info about file ops interfaces
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/declextract/declextract.go | 3 | ||||
| -rw-r--r-- | pkg/declextract/fileops.go | 73 | ||||
| -rw-r--r-- | pkg/declextract/interface.go | 70 | ||||
| -rw-r--r-- | pkg/declextract/netlink.go | 2 |
4 files changed, 130 insertions, 18 deletions
diff --git a/pkg/declextract/declextract.go b/pkg/declextract/declextract.go index 0bf63f2e6..7320279bc 100644 --- a/pkg/declextract/declextract.go +++ b/pkg/declextract/declextract.go @@ -213,7 +213,7 @@ func (ctx *context) emitSyscall(syscalls *[]*Syscall, call *Syscall, IdentifyingConst: identifyingConst, Files: []string{call.SourceFile}, Func: call.Func, - AutoDescriptions: true, + AutoDescriptions: TristateYes, scopeArg: scopeArg, scopeVal: scopeVal, }) @@ -232,6 +232,7 @@ func (ctx *context) processIouring() { Files: []string{op.SourceFile}, Func: op.Func, Access: AccessUser, + AutoDescriptions: TristateNo, }) } } diff --git a/pkg/declextract/fileops.go b/pkg/declextract/fileops.go index 408ccc4fc..47a01ce53 100644 --- a/pkg/declextract/fileops.go +++ b/pkg/declextract/fileops.go @@ -9,7 +9,10 @@ import ( "strings" ) -// TODO: also emit interface entry for file_operations. +const ( + ioctlCmdArg = 1 + ioctlArgArg = 2 +) func (ctx *context) serializeFileOps() { for _, ioctl := range ctx.Ioctls { @@ -18,14 +21,60 @@ func (ctx *context) serializeFileOps() { fopsToFiles := ctx.mapFopsToFiles() for _, fops := range ctx.FileOps { files := fopsToFiles[fops] + canGenerate := Tristate(len(files) != 0) + for _, op := range []string{fops.Read, fops.Write, fops.Mmap} { + if op == "" { + continue + } + file := ctx.funcDefinitionFile(op, fops.SourceFile) + if file == "" { + // TODO: in some cases we misparse fops defined via macros, e.g. for: + // .write = (foo) ? bar : baz, + // We extract "foo". + continue + } + ctx.noteInterface(&Interface{ + Type: IfaceFileop, + Name: op, + Func: op, + Files: []string{file}, + AutoDescriptions: canGenerate, + }) + } + var ioctlCmds []string + if fops.Ioctl != "" { + ioctlCmds = ctx.inferCommandVariants(fops.Ioctl, fops.SourceFile, ioctlCmdArg) + file := ctx.funcDefinitionFile(fops.Ioctl, fops.SourceFile) + for _, cmd := range ioctlCmds { + ctx.noteInterface(&Interface{ + Type: IfaceIoctl, + Name: cmd, + IdentifyingConst: cmd, + Files: []string{file}, + Func: fops.Ioctl, + AutoDescriptions: canGenerate, + scopeArg: ioctlCmdArg, + scopeVal: cmd, + }) + } + if len(ioctlCmds) == 0 { + ctx.noteInterface(&Interface{ + Type: IfaceIoctl, + Name: fops.Ioctl, + Files: []string{file}, + Func: fops.Ioctl, + AutoDescriptions: canGenerate, + }) + } + } if len(files) == 0 { continue // each unmapped entry means some code we don't know how to cover yet } - ctx.createFops(fops, files) + ctx.createFops(fops, files, ioctlCmds) } } -func (ctx *context) createFops(fops *FileOps, files []string) { +func (ctx *context) createFops(fops *FileOps, files, ioctlCmds []string) { // If it has only open, then emit only openat that returns generic fd. fdt := "fd" if len(fops.ops()) > 1 || fops.Open == "" { @@ -55,21 +104,17 @@ func (ctx *context) createFops(fops *FileOps, files []string) { " flags flags[mmap_flags], fd %v, offset fileoff)\n", suffix, fdt) } if fops.Ioctl != "" { - ctx.createIoctls(fops, suffix, fdt) + ctx.createIoctls(fops, ioctlCmds, suffix, fdt) } ctx.fmt("\n") } -func (ctx *context) createIoctls(fops *FileOps, suffix, fdt string) { - const ( - cmdArg = 1 - argArg = 2 - defaultArgType = "ptr[in, array[int8]]" - ) - cmds := ctx.inferCommandVariants(fops.Ioctl, fops.SourceFile, cmdArg) +func (ctx *context) createIoctls(fops *FileOps, ioctlCmds []string, suffix, fdt string) { + const defaultArgType = "ptr[in, array[int8]]" + cmds := ctx.inferCommandVariants(fops.Ioctl, fops.SourceFile, ioctlCmdArg) if len(cmds) == 0 { retType := ctx.inferReturnType(fops.Ioctl, fops.SourceFile, -1, "") - argType := ctx.inferArgType(fops.Ioctl, fops.SourceFile, argArg, -1, "") + argType := ctx.inferArgType(fops.Ioctl, fops.SourceFile, ioctlArgArg, -1, "") if argType == "" { argType = defaultArgType } @@ -85,12 +130,12 @@ func (ctx *context) createIoctls(fops *FileOps, suffix, fdt string) { } argType = ctx.fieldType(f, nil, "", false) } else { - argType = ctx.inferArgType(fops.Ioctl, fops.SourceFile, argArg, cmdArg, cmd) + argType = ctx.inferArgType(fops.Ioctl, fops.SourceFile, ioctlArgArg, ioctlCmdArg, cmd) if argType == "" { argType = defaultArgType } } - retType := ctx.inferReturnType(fops.Ioctl, fops.SourceFile, cmdArg, cmd) + retType := ctx.inferReturnType(fops.Ioctl, fops.SourceFile, ioctlCmdArg, cmd) name := ctx.uniqualize("ioctl cmd", cmd) ctx.fmt("ioctl%v_%v(fd %v, cmd const[%v], arg %v) %v\n", autoSuffix, name, fdt, cmd, argType, retType) diff --git a/pkg/declextract/interface.go b/pkg/declextract/interface.go index dd5bfa1d4..e8ace450b 100644 --- a/pkg/declextract/interface.go +++ b/pkg/declextract/interface.go @@ -18,8 +18,8 @@ type Interface struct { Func string Access string Subsystems []string - ManualDescriptions bool - AutoDescriptions bool + ManualDescriptions TristateVal + AutoDescriptions TristateVal ReachableLOC int CoveredBlocks int TotalBlocks int @@ -28,9 +28,19 @@ type Interface struct { scopeVal string } +type TristateVal int + +const ( + TristateUnknown TristateVal = iota + TristateYes + TristateNo +) + const ( IfaceSyscall = "SYSCALL" IfaceNetlinkOp = "NETLINK" + IfaceFileop = "FILEOP" + IfaceIoctl = "IOCTL" IfaceIouring = "IOURING" AccessUnknown = "unknown" @@ -44,6 +54,17 @@ func (ctx *context) noteInterface(iface *Interface) { } func (ctx *context) finishInterfaces() { + ctx.interfaces = sortAndDedupSlice(ctx.interfaces) + count := make(map[string]int) + for _, iface := range ctx.interfaces { + count[iface.Type+iface.Name]++ + } + // Lots of file ops have the same name, add file name to them. + for _, iface := range ctx.interfaces { + if count[iface.Type+iface.Name] > 1 { + iface.Name = iface.Name + "_" + fileNameSuffix(iface.Files[0]) + } + } for _, iface := range ctx.interfaces { ctx.calculateLOC(iface) slices.Sort(iface.Files) @@ -51,6 +72,9 @@ func (ctx *context) finishInterfaces() { if iface.Access == "" { iface.Access = AccessUnknown } + if iface.Access == "" { + iface.Access = AccessUnknown + } } ctx.interfaces = sortAndDedupSlice(ctx.interfaces) } @@ -148,3 +172,45 @@ func (ctx *context) findFunc(name, file string) *Function { } return ctx.funcs[name] } + +func (ctx *context) funcDefinitionFile(name, calledFrom string) string { + fn := ctx.findFunc(name, calledFrom) + if fn == nil { + return "" + } + return fn.File +} + +func fileNameSuffix(file string) string { + // Remove file extension. + ext := strings.LastIndexByte(file, '.') + if ext != -1 { + file = file[:ext] + } + raw := []byte(file) + for i, v := range raw { + if v >= 'a' && v <= 'z' || v >= 'A' && v <= 'Z' || v >= '0' && v <= '9' { + continue + } + raw[i] = '_' + } + return string(raw) +} + +func Tristate(v bool) TristateVal { + if v { + return TristateYes + } + return TristateNo +} + +func (tv TristateVal) String() string { + switch tv { + case TristateYes: + return "true" + case TristateNo: + return "false" + default: + return "unknown" + } +} diff --git a/pkg/declextract/netlink.go b/pkg/declextract/netlink.go index 0adec5bc4..1b49bbd63 100644 --- a/pkg/declextract/netlink.go +++ b/pkg/declextract/netlink.go @@ -47,7 +47,7 @@ func (ctx *context) serializeNetlink() { Files: []string{fam.SourceFile}, Func: op.Func, Access: op.Access, - AutoDescriptions: true, + AutoDescriptions: TristateYes, }) } |
