aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2025-04-11 08:04:51 +0200
committerDmitry Vyukov <dvyukov@google.com>2025-04-11 09:37:01 +0000
commita2335417592dcd4545c1953807ae9c8906e133d5 (patch)
tree919af28ce8d763f29270c69afdd895e194e088ee /pkg
parentc0efc34f5a3f6a494541381c0f5770200c2640b5 (diff)
tools/syz-declextract: export info about file ops interfaces
Diffstat (limited to 'pkg')
-rw-r--r--pkg/declextract/declextract.go3
-rw-r--r--pkg/declextract/fileops.go73
-rw-r--r--pkg/declextract/interface.go70
-rw-r--r--pkg/declextract/netlink.go2
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,
})
}