diff options
| author | Ethan Graham <ethangraham@google.com> | 2025-07-16 12:46:20 +0000 |
|---|---|---|
| committer | Alexander Potapenko <glider@google.com> | 2025-08-04 12:25:52 +0000 |
| commit | f5bcc8dc6eb21df90aeb443032779df3e73c53a1 (patch) | |
| tree | f0a3e78563638e5e8418f604c7700449e7624d64 /prog/prog.go | |
| parent | abdcb21383cc97dd3eb98eaa27189415be7d8199 (diff) | |
pkg/csource: add call argument annotations to generated C-source files
The structure of arguments passed into syscalls is often hard to parse
since it is memcpy'd into mmap'd regions. Structural relations are often
lost in translation, resulting in reproducers that take longer for a
developer to understand.
This patch adds functionality for parsing syscall arguments semantically and
emitting a structured and human-readable comment which is inserted before each
syscall in the resulting C-source.
Diffstat (limited to 'prog/prog.go')
| -rw-r--r-- | prog/prog.go | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/prog/prog.go b/prog/prog.go index 89b0e794b..b4c8d692d 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -6,6 +6,7 @@ package prog import ( "fmt" "reflect" + "strings" ) type Prog struct { @@ -494,6 +495,107 @@ func (p *Prog) sanitizeFix() { } } +// FormatArg returns a string slice representation of an argument with one +// entry per line in the output. The formatting roughly corresponds to syzlang +// descriptions and is intended to be human readable. +func FormatArg(arg Arg, name string) []string { + const indent string = " " // Two spaces. + makeIndent := func(level int) string { + return strings.Repeat(indent, level) + } + + // Depth-first search starting at initial argument, incrementing the indent + // level as we go deeper. + var visit func(Arg, string, int) []string + visit = func(arg Arg, name string, depth int) []string { + var lines []string + + var lineBuilder strings.Builder + lineBuilder.WriteString(makeIndent(depth)) + + if name != "" { + fmt.Fprintf(&lineBuilder, "%s: ", name) + } + + switch a := arg.(type) { + case *GroupArg: + fmt.Fprintf(&lineBuilder, "%s {", a.Type().String()) + lines = append(lines, lineBuilder.String()) + + s, isStruct := a.ArgCommon.Type().(*StructType) + for i, inner := range a.Inner { + // For GroupArgs, only those of type StructType have named + // children. + childName := "" + if isStruct { + childName = s.Fields[i].Name + } + lines = append(lines, visit(inner, childName, depth+1)...) + } + lines = append(lines, makeIndent(depth)+"}") + case *ConstArg: + fmt.Fprintf(&lineBuilder, "%s = 0x%x (%d bytes)", a.Type().Name(), a.Val, a.Size()) + lines = append(lines, lineBuilder.String()) + case *DataArg: + tpe, ok := a.Type().(*BufferType) + if !ok { + panic("data args should be a buffer type") + } + + fmt.Fprintf(&lineBuilder, "%s: ", a.Type().String()) + + // Result buffer - nothing to display. + if a.Dir() == DirOut { + fmt.Fprint(&lineBuilder, "(DirOut)") + } else { + // Compressed buffers (e.g., fs images) tend to be very large + // and it doesn't make much sense to output their contents. + if tpe.Kind == BufferCompressed { + fmt.Fprintf(&lineBuilder, "(compressed buffer with length 0x%x)", len(a.Data())) + } else { + fmt.Fprintf(&lineBuilder, "{% x} (length 0x%x)", a.Data(), len(a.Data())) + } + } + lines = append(lines, lineBuilder.String()) + case *PointerArg: + if a.Res != nil { + fmt.Fprintf(&lineBuilder, "%s {", a.Type().String()) + lines = append(lines, lineBuilder.String()) + lines = append(lines, visit(a.Res, "", depth+1)...) + lines = append(lines, makeIndent(depth)+"}") + } else { + if a.VmaSize == 0 { + lineBuilder.WriteString("nil") + } else { + fmt.Fprintf(&lineBuilder, "VMA[0x%x]", a.VmaSize) + } + lines = append(lines, lineBuilder.String()) + } + case *UnionArg: + union, ok := a.ArgCommon.Type().(*UnionType) + if !ok { + panic("a UnionArg should have an ArgCommon of type UnionType") + } + fmt.Fprintf(&lineBuilder, "union %s {", a.Type().Name()) + lines = append(lines, lineBuilder.String()) + if a.Option != nil { + lines = append(lines, visit(a.Option, union.Fields[a.Index].Name, depth+1)...) + } + lines = append(lines, makeIndent(depth)+"}") + case *ResultArg: + fmt.Fprintf(&lineBuilder, "%s (resource)", a.ArgCommon.Type().String()) + lines = append(lines, lineBuilder.String()) + default: + // We shouldn't hit this because the switch statements cover every + // prog.Arg implementation. + panic("Unsupported argument type.") + } + return lines + } + + return visit(arg, name, 0) +} + func (p *Prog) sanitize(fix bool) error { for _, c := range p.Calls { if err := p.Target.sanitize(c, fix); err != nil { |
