diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 759cd9d..d9b1422 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: jobs: build: name: releasing - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3487fd..d49b163 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,4 +19,4 @@ jobs: go-version: '1.19' cache: true - name: Run tests - run: sudo apt install -y musl-tools && make test + run: make test diff --git a/.gitignore b/.gitignore index ddd5654..0b05f2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea /grace /dist +/linux +/headers \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml index f6da274..4de3ec8 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,6 +1,6 @@ before: hooks: - - sudo apt install -y musl-tools + - make headers builds: - id: grace @@ -10,7 +10,7 @@ builds: - "-linkmode external -s -w -extldflags '-fno-PIC -static'" env: - CGO_ENABLED=1 - - CC=musl-gcc + - CGO_CFLAGS="-Iheaders/include" goos: - linux goarch: diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6f89ec7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:22.04 + +RUN apt-get update -y && apt-get install -y git build-essential golang ca-certificates rsync +RUN git clone --depth 1 https://github.com/torvalds/linux.git /linux +COPY . /src +WORKDIR /src +RUN make test \ No newline at end of file diff --git a/Makefile b/Makefile index ea043d5..938d29b 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,28 @@ default: build +.PHONY: clean +clean: + rm -rf headers + rm -rf linux + .PHONY: test -test: - CC=musl-gcc CGO_ENABLED=1 go test ./... +test: headers + CGO_ENABLED=1 CGO_CFLAGS="-I$$(pwd)/headers/include" go test ./tracer ./printer ./filter + +linux: + git clone --depth 1 https://github.com/torvalds/linux.git ./linux + +headers: linux + cd linux && make headers_install ARCH=x86_64 INSTALL_HDR_PATH=../headers + rm -rf linux .PHONY: build -build: - CGO_ENABLED=1 CC=musl-gcc go build --ldflags '-linkmode external -extldflags "-static"' +build: headers + CGO_ENABLED=1 CGO_CFLAGS="-I$$(pwd)/headers/include" go build --ldflags '-linkmode external -extldflags "-static"' + +.PHONY: install +install: headers + CGO_ENABLED=1 CGO_CFLAGS="-I$$(pwd)/headers/include" go install --ldflags '-linkmode external -extldflags "-static"' .PHONY: demo demo: build diff --git a/README.md b/README.md index 14d23fa..3e452c9 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,124 @@ # grace -_grace_ is a tool for monitoring and modifying syscalls for a given process. +_grace_ is a tool for monitoring and annotating syscalls for a given process. It's essentially a lightweight [strace](https://en.wikipedia.org/wiki/Strace), in Go, with colours and pretty output. -![](screenshot.png) +

+ +

-// TODO: new screenshot +

+ +

+ +It's possible to tweak and filter the output to make it quite readable, for example (using `-vnmx`): + +

+ +

+ +You can also review a summary of encountered syscalls (and sort by various columns): + +

+ +

-## Features/Usage Examples ### grace vs. strace _grace_ isn't meant to compete with _strace_, it's purely meant to be a user-friendly, lightweight alternative. However, the following should provide a rough idea of what is supported in _grace_ so far. +Over time grace is meant to become a simpler, more readable alternative to strace (_strace for dummies?_), albeit with reduced functionality/advanced features. + | Feature | grace | strace | |---------------------------------------------------------------------------------------|-------|--------| | Start a program and print all syscalls it makes | ✅ | ✅ | | Attach to an existing process by `pid` and print all syscalls it makes | ✅ | ✅ | -| Filter syscalls by name, e.g. only show occurrences of the `open` syscall | ❌ | ✅ | -| Filter syscalls using a given path, e.g. only show syscalls that access `/etc/passwd` | ❌ | ✅ | -| Dump I/O for certain file descriptors | ❌ | ✅ | -| Count occurrences and duration of all syscalls and present in a useful format | ❌ | ✅ | -| Print relative/absolute timestamps | ❌ | ✅ | +| Filter syscalls by name, e.g. only show occurrences of the `open` syscall | ✅ | ✅ | +| Filter syscalls using a given path, e.g. only show syscalls that access `/etc/passwd` | ✅ | ✅ | +| Dump I/O for certain file descriptors | ✅ | ✅ | +| Count occurrences and duration of all syscalls and present in a useful format | ✅ | ✅ | +| Print relative/absolute timestamps | ✅ | ✅ | | Tamper with syscalls | ❌ | ✅ | -| Print extra information about file descriptors, such as path, socket addresses etc. | some | ✅ | +| Print extra information about file descriptors, such as path, socket addresses etc. | ✅ | ✅ | | Print stack traces | ❌ | ✅ | -| Filter by return value | ❌ | ✅ | -| Decode SELinux context info | ❌ | ✅ | +| Filter by return value | ✅ | ✅ | | Pretty colours to make output easier to read | ✅ | ❌ | +| Lots of output options and customisation vectors | ✅ | ✅ | +| Output to file | ✅ | ✅ | +| Filter by failing/non-failing syscalls | ✅ | ✅ | + +_NOTE: Please feel free to add important strace features to this table, I'm working with a limited knowledge of strace._ -### Usage Examples -``` -// TODO -``` ## Installation Grab a statically compiled binary from the [latest release](https://github.com/liamg/grace/releases/latest). -## Build Dependencies - -If you want to build _grace_ yourself instead of using the precompiled binaries, you'll need a recent version of Go (1.19+), `musl-gcc` installed (you can install `musl-tools` on Ubuntu or `musl` on Arch), and kernel headers (install `linux-headers-$(uname -r)` on Ubuntu or `linux-headers` on Arch). _grace_ mainly just pulls constants from the kernel headers, so it's not a huge dependency. You should then have some success running `make build`. Note that many architectures are not yet supported (see below.) - ## Supported Platforms/Architecture Currently only Linux/amd64 is supported. Other architectures coming soon. If you'd like to implement a new architecture, you can duplicate `tracer/sys_amd64.go` and convert it to contain the syscall definitions for your arch. + +### Usage Examples + +#### Trace a program + +```bash +grace -- cat /dev/null # replace 'cat /dev/null' with your program +``` + +#### Trace an existing process + +```bash +grace -p 123 # replace 123 with your pid + +# e.g. you could use pgrep to find the pid of a process +grace -p `pgrep ping` +``` + +#### Trace a program and filter by syscall name + +```bash +grace -f "name=openat" -- cat /dev/null + +# you can also look for multiple syscalls +grace -f "name=openat&name=close" -- cat /dev/null +``` + +#### Trace a program and filter by syscall name and path + +```bash +grace -f "name=openat&path=/dev/null" -- cat /dev/null +``` + +#### Trace a program and wire up stdin/out/err with the terminal + +```bash +grace -F -- cat +``` + +#### Trace a program with maximum readability options + +```bash +grace -vnmx -- cat /dev/null +``` + +#### Trace only failing syscalls + +```bash +grace -Z -- cat /dev/null +``` + +#### Show a summary of syscalls with durations, counts and errors + +```bash +grace -S -- cat /dev/null +``` + +## Build Dependencies + +If you want to build _grace_ yourself instead of using the precompiled binaries, you'll need a recent version of Go (1.19+), `musl-gcc` installed (you can install `musl-tools` on Ubuntu or `musl` on Arch), and kernel headers (install `linux-headers-$(uname -r)` on Ubuntu or `linux-headers` and `kernel-headers-musl` on Arch). _grace_ mainly just pulls constants from the kernel headers, so it's not a huge dependency. You should then have some success running `make build`. Note that many architectures are not yet supported (see below.) diff --git a/compare.png b/compare.png new file mode 100644 index 0000000..a90cae9 Binary files /dev/null and b/compare.png differ diff --git a/demo.gif b/demo.gif new file mode 100644 index 0000000..a6f5feb Binary files /dev/null and b/demo.gif differ diff --git a/filter/filter.go b/filter/filter.go new file mode 100644 index 0000000..62a2115 --- /dev/null +++ b/filter/filter.go @@ -0,0 +1,127 @@ +package filter + +import ( + "fmt" + "strconv" + "strings" + + "github.com/liamg/grace/tracer" +) + +type Filter struct { + allowNames []string + allowPaths []string + allowReturns []uint64 + failingOnly bool + passingOnly bool +} + +func Parse(input string) (*Filter, error) { + filter := NewFilter() + parts := strings.Split(input, "&") + for _, part := range parts { + if part == "" { + continue + } + bits := strings.Split(part, "=") + key := bits[0] + value := bits[len(bits)-1] + switch key { + case "syscall", "name", "trace": + filter.allowNames = append(filter.allowNames, strings.Split(value, ",")...) + case "path": + filter.allowPaths = append(filter.allowPaths, strings.Split(value, ",")...) + case "ret", "retval", "return": + ret, err := parseUint64(value) + if err != nil { + return nil, fmt.Errorf("failed to parse return value filter: %w", err) + } + filter.allowReturns = append(filter.allowReturns, ret) + default: + return nil, fmt.Errorf("invalid filter key: %s", key) + } + } + return filter, nil +} + +func parseUint64(input string) (uint64, error) { + if strings.HasPrefix(input, "0x") { + return strconv.ParseUint(input[2:], 16, 64) + } + return strconv.ParseUint(input, 10, 64) +} + +func NewFilter() *Filter { + return &Filter{} +} + +func (f *Filter) Match(call *tracer.Syscall, exit bool) bool { + + if len(f.allowNames) > 0 { + var match bool + for _, name := range f.allowNames { + if name == call.Name() { + match = true + break + } + } + if !match { + return false + } + } + + if len(f.allowPaths) > 0 { + var match bool + for _, path := range f.allowPaths { + for _, realPath := range call.Paths() { + if realPath == path { + match = true + break + } + } + } + if !match { + return false + } + } + + if len(f.allowReturns) > 0 { + if !exit { + return false + } + var match bool + for _, ret := range f.allowReturns { + if uintptr(ret) == call.Return().Raw() { + match = true + break + } + } + if !match { + return false + } + } + + if (f.passingOnly || f.failingOnly) && !exit { + return false + } + + if f.failingOnly && call.Return().Int() >= 0 { + return false + } + + if f.passingOnly && call.Return().Int() < 0 { + return false + } + + // TODO check more filters + + return true +} + +func (f *Filter) SetFailingOnly(failing bool) { + f.failingOnly = failing +} + +func (f *Filter) SetPassingOnly(passing bool) { + f.passingOnly = passing +} diff --git a/go.mod b/go.mod index 3a81088..2db7b2e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/liamg/grace go 1.19 require ( + github.com/aquasecurity/table v1.8.0 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.1 golang.org/x/sys v0.1.0 @@ -11,7 +12,10 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fb9e32a..9b22dd2 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,17 @@ +github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0= +github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= @@ -20,6 +26,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index bf690bd..50fb6e2 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,8 @@ import ( "fmt" "os" + "github.com/liamg/grace/filter" + "github.com/liamg/grace/printer" "github.com/liamg/grace/tracer" @@ -12,15 +14,25 @@ import ( var ( flagDisableColours = false - flagMaxStringLen = 32 - flagHexDumpLongStrings = true + flagMaxStringLen = 16 + flagHexDumpLongStrings = false flagMaxHexDumpLen = 4096 flagPID = 0 - flagSuppressOutput = false + flagForwardIO = false flagMaxObjectProperties = 2 flagVerbose = false flagExtraNewLine = false flagMultiline = false + flagFilter = "" + flagAbsoluteTimestamps = false + flagRelativeTimestamps = false + flagSummarise = false + flagSortKey = "" + flagShowSyscallNumber = false + flagFilterPassing = false + flagFilterFailing = false + flagOutputFile = "" + flagRawOutput = false ) var rootCmd = &cobra.Command{ @@ -42,34 +54,60 @@ It's essentially strace, in Go, with colours and pretty output.`, if flagPID > 0 { t = tracer.New(flagPID) } else { - t, err = tracer.FromCommand(flagSuppressOutput, args[0], args[1:]...) + t, err = tracer.FromCommand(!flagForwardIO, args[0], args[1:]...) if err != nil { return err } } - p := printer.New(cmd.OutOrStdout()) + output := cmd.OutOrStdout() + if flagOutputFile != "" { + output, err = os.Create(flagOutputFile) + if err != nil { + return err + } + defer func() { + _ = output.(*os.File).Close() + }() + } - p.SetUseColours(!flagDisableColours) + p := printer.New(output) + + p.SetUseColours(!flagDisableColours && flagOutputFile == "") p.SetMaxStringLen(flagMaxStringLen) p.SetMaxHexDumpLen(flagMaxHexDumpLen) p.SetExtraNewLine(flagExtraNewLine) p.SetMultiLine(flagMultiline) + p.SetHexDumpLongStrings(flagHexDumpLongStrings) + p.SetShowAbsoluteTimestamps(flagAbsoluteTimestamps) + p.SetShowRelativeTimestamps(flagRelativeTimestamps) + p.SetShowSyscallNumber(flagShowSyscallNumber) + p.SetRawOutput(flagRawOutput) if flagVerbose { - p.SetHexDumpLongStrings(true) p.SetMaxObjectProperties(0) } else { - p.SetHexDumpLongStrings(flagHexDumpLongStrings) p.SetMaxObjectProperties(flagMaxObjectProperties) } - t.SetSyscallEnterHandler(p.PrintSyscallEnter) - t.SetSyscallExitHandler(p.PrintSyscallExit) - t.SetSignalHandler(p.PrintSignal) - t.SetProcessExitHandler(p.PrintProcessExit) + fltr, err := filter.Parse(flagFilter) + if err != nil { + return fmt.Errorf("failed to parse filter: %s", err) + } + fltr.SetFailingOnly(flagFilterFailing) + fltr.SetPassingOnly(flagFilterPassing) + p.SetFilter(fltr) - // TODO: set signal handler! + if flagSummarise { + configureSummary(t, output, flagSortKey) + } else { + t.SetSyscallEnterHandler(p.PrintSyscallEnter) + t.SetSyscallExitHandler(p.PrintSyscallExit) + t.SetSignalHandler(p.PrintSignal) + t.SetProcessExitHandler(p.PrintProcessExit) + t.SetAttachHandler(p.PrintAttach) + t.SetDetachHandler(p.PrintDetach) + } defer func() { _, _ = fmt.Fprintln(cmd.ErrOrStderr(), "") }() @@ -83,11 +121,21 @@ func init() { rootCmd.Flags().BoolVarP(&flagHexDumpLongStrings, "hex-dump-long-strings", "x", flagHexDumpLongStrings, "hex dump strings longer than --max-string-len") rootCmd.Flags().IntVarP(&flagMaxHexDumpLen, "max-hex-dump-len", "l", flagMaxHexDumpLen, "maximum length of hex dumps") rootCmd.Flags().IntVarP(&flagPID, "pid", "p", flagPID, "trace an existing process by PID") - rootCmd.Flags().BoolVarP(&flagSuppressOutput, "suppress-output", "S", flagSuppressOutput, "suppress output of command") - rootCmd.Flags().IntVarP(&flagMaxObjectProperties, "max-object-properties", "o", flagMaxObjectProperties, "maximum number of properties to print for objects (recursive) - this also applies to array elements") + rootCmd.Flags().BoolVarP(&flagForwardIO, "forward-io", "F", flagForwardIO, "forward stdin/stdout/stderr for the given command") + rootCmd.Flags().IntVarP(&flagMaxObjectProperties, "max-object-properties", "O", flagMaxObjectProperties, "maximum number of properties to print for objects (recursive) - this also applies to array elements") rootCmd.Flags().BoolVarP(&flagVerbose, "verbose", "v", flagVerbose, "enable verbose output (overrides other verbosity settings)") rootCmd.Flags().BoolVarP(&flagExtraNewLine, "extra-newline", "n", flagExtraNewLine, "print an extra newline after each syscall to aid readability") rootCmd.Flags().BoolVarP(&flagMultiline, "multiline", "m", flagMultiline, "print each syscall argument on a separate line to aid readability") + rootCmd.Flags().StringVarP(&flagFilter, "filter", "f", flagFilter, "Filter string to apply to output. The string should be formatted as a query string e.g. 'syscall=write&arg0=stdout'. The syscall parameter filters syscalls by name. The path parameter filters syscalls that reference a particular path. The ret parameter filters by return value (values for ret are assumed to be decimal unless prefixed with 0x). Each parameter can be specified multiple times with an OR match being appied to parameters of that type, and an AND match applied to parameters of a different type.") + rootCmd.Flags().BoolVarP(&flagAbsoluteTimestamps, "absolute-timestamps", "a", flagAbsoluteTimestamps, "print absolute timestamps for each event") + rootCmd.Flags().BoolVarP(&flagRelativeTimestamps, "relative-timestamps", "r", flagRelativeTimestamps, "print relative timestamps for each event") + rootCmd.Flags().BoolVarP(&flagSummarise, "summary", "S", flagSummarise, "summarise counts of all syscalls") + rootCmd.Flags().StringVarP(&flagSortKey, "sort-column", "c", flagSortKey, "sort key for summary output (time, seconds, count, errors) (default is sort by syscall name)") + rootCmd.Flags().BoolVarP(&flagShowSyscallNumber, "number", "N", flagShowSyscallNumber, "show syscall numbers in output") + rootCmd.Flags().BoolVarP(&flagFilterFailing, "only-failing", "Z", flagFilterFailing, "show only failing syscalls") + rootCmd.Flags().BoolVarP(&flagFilterPassing, "only-passing", "z", flagFilterPassing, "show only passing syscalls") + rootCmd.Flags().StringVarP(&flagOutputFile, "output-file", "o", flagOutputFile, "output file (default is stdout)") + rootCmd.Flags().BoolVarP(&flagRawOutput, "raw", "R", flagRawOutput, "Raw output format for arguments and return values (format everything as raw hex values)") } func main() { diff --git a/printer/arg.go b/printer/arg.go index 8c30c62..90fd4e3 100644 --- a/printer/arg.go +++ b/printer/arg.go @@ -30,6 +30,11 @@ func (p *Printer) NewLine(indent int) { func (p *Printer) PrintArgValue(arg *tracer.Arg, colour Colour, exit bool, propCount int, indent int) int { + if p.rawOutput { + p.PrintColour(colour, "0x%x", arg.Raw()) + return propCount + } + if arg.ReplaceValueWithAnnotation() { p.PrintColour(colour, "%s", arg.Annotation()) return propCount @@ -39,7 +44,7 @@ func (p *Printer) PrintArgValue(arg *tracer.Arg, colour Colour, exit bool, propC case tracer.ArgTypeData: data := arg.Data() if p.maxStringLen > 0 && len(data) > p.maxStringLen { - if exit && p.hexDumpLongStrings { + if p.hexDumpLongStrings { p.HexDump(arg.Raw(), arg.Data(), indent) return propCount } @@ -50,16 +55,15 @@ func (p *Printer) PrintArgValue(arg *tracer.Arg, colour Colour, exit bool, propC case tracer.ArgTypeInt, tracer.ArgTypeLong, tracer.ArgTypeUnsignedInt, tracer.ArgTypeUnsignedLong, tracer.ArgTypeUnknown: p.PrintColour(colour, "%d", arg.Int()) case tracer.ArgTypeErrorCode: - p.printError(colour, arg) + p.printError(arg) case tracer.ArgTypeAddress: p.PrintColour(colour, "0x%x", arg.Raw()) - case tracer.ArgTypeObject, tracer.ArgTypeStat, tracer.ArgTypeSigAction: + case tracer.ArgTypeObject: propCount += p.printObject(arg.Object(), colour, exit, propCount, indent) - case tracer.ArgTypeIntArray, tracer.ArgTypePollFdArray, tracer.ArgTypeIovecArray: + case tracer.ArgTypeArray: propCount += p.printArray(arg.Array(), colour, exit, propCount, indent) default: - // TODO: error here - p.PrintColour(colour, "%d (?)", arg.Raw()) + p.PrintColour(ColourRed, "UNKNOWN TYPE (raw=%d)", arg.Raw()) } if annotation := arg.Annotation(); annotation != "" { diff --git a/printer/colours.go b/printer/colours.go index 2c1bd08..b911a87 100644 --- a/printer/colours.go +++ b/printer/colours.go @@ -18,7 +18,6 @@ var colours = []Colour{ ColourBlue, ColourYellow, ColourGreen, - ColourRed, } func (p *Printer) currentColour() Colour { diff --git a/printer/error.go b/printer/error.go index 9cb3a2a..ae88712 100644 --- a/printer/error.go +++ b/printer/error.go @@ -1,14 +1,178 @@ package printer -import ( - "syscall" +/* +#include + +// the following definitions are never meant to be seen by user programs, other than via ptrace... +#ifndef ERESTARTSYS +#define ERESTARTSYS 512 +#endif + +#ifndef ERESTARTNOINTR +#define ERESTARTNOINTR 513 +#endif + +#ifndef ERESTARTNOHAND +#define ERESTARTNOHAND 514 +#endif + +#ifndef ENOIOCTLCMD +#define ENOIOCTLCMD 515 +#endif + +#ifndef ERESTART_RESTARTBLOCK +#define ERESTART_RESTARTBLOCK 516 +#endif + +// TODO: add NFSv3 errors - "golang.org/x/sys/unix" +*/ +import "C" + +import ( + "github.com/liamg/grace/tracer/annotation" "github.com/liamg/grace/tracer" ) -func (p *Printer) printError(colour Colour, arg *tracer.Arg) { +var errors = map[int]string{ + C.ERESTARTSYS: "ERESTARTSYS", + C.ERESTARTNOINTR: "ERESTARTNOINTR", + C.ERESTARTNOHAND: "ERESTARTNOHAND", + C.ENOIOCTLCMD: "ENOIOCTLCMD", + C.ERESTART_RESTARTBLOCK: "ERESTART_RESTARTBLOCK", + C.ERESTART: "ERESTART", + C.EPERM: "EPERM", + C.ENOENT: "ENOENT", + C.ESRCH: "ESRCH", + C.EINTR: "EINTR", + C.EIO: "EIO", + C.ENXIO: "ENXIO", + C.E2BIG: "E2BIG", + C.ENOEXEC: "ENOEXEC", + C.EBADF: "EBADF", + C.ECHILD: "ECHILD", + C.EAGAIN: "EAGAIN", + C.ENOMEM: "ENOMEM", + C.EACCES: "EACCES", + C.EFAULT: "EFAULT", + C.ENOTBLK: "ENOTBLK", + C.EBUSY: "EBUSY", + C.EEXIST: "EEXIST", + C.EXDEV: "EXDEV", + C.ENODEV: "ENODEV", + C.ENOTDIR: "ENOTDIR", + C.EISDIR: "EISDIR", + C.EINVAL: "EINVAL", + C.ENFILE: "ENFILE", + C.EMFILE: "EMFILE", + C.ENOTTY: "ENOTTY", + C.ETXTBSY: "ETXTBSY", + C.EFBIG: "EFBIG", + C.ENOSPC: "ENOSPC", + C.ESPIPE: "ESPIPE", + C.EROFS: "EROFS", + C.EMLINK: "EMLINK", + C.EPIPE: "EPIPE", + C.EDOM: "EDOM", + C.ERANGE: "ERANGE", + C.EDEADLK: "EDEADLK", + C.ENAMETOOLONG: "ENAMETOOLONG", + C.ENOLCK: "ENOLCK", + C.ENOSYS: "ENOSYS", + C.ENOTEMPTY: "ENOTEMPTY", + C.ELOOP: "ELOOP", + C.ENOMSG: "ENOMSG", + C.EIDRM: "EIDRM", + C.ECHRNG: "ECHRNG", + C.EL2NSYNC: "EL2NSYNC", + C.EL3HLT: "EL3HLT", + C.EL3RST: "EL3RST", + C.ELNRNG: "ELNRNG", + C.EUNATCH: "EUNATCH", + C.ENOCSI: "ENOCSI", + C.EL2HLT: "EL2HLT", + C.EBADE: "EBADE", + C.EBADR: "EBADR", + C.EXFULL: "EXFULL", + C.ENOANO: "ENOANO", + C.EBADRQC: "EBADRQC", + C.EBADSLT: "EBADSLT", + C.EBFONT: "EBFONT", + C.ENOSTR: "ENOSTR", + C.ENODATA: "ENODATA", + C.ETIME: "ETIME", + C.ENOSR: "ENOSR", + C.ENONET: "ENONET", + C.ENOPKG: "ENOPKG", + C.EREMOTE: "EREMOTE", + C.ENOLINK: "ENOLINK", + C.EADV: "EADV", + C.ESRMNT: "ESRMNT", + C.ECOMM: "ECOMM", + C.EPROTO: "EPROTO", + C.EMULTIHOP: "EMULTIHOP", + C.EBADMSG: "EBADMSG", + C.ENOTUNIQ: "ENOTUNIQ", + C.EBADFD: "EBADFD", + C.EREMCHG: "EREMCHG", + C.ELIBACC: "ELIBACC", + C.ELIBBAD: "ELIBBAD", + C.ELIBSCN: "ELIBSCN", + C.ELIBMAX: "ELIBMAX", + C.ELIBEXEC: "ELIBEXEC", + C.EILSEQ: "EILSEQ", + C.ESTRPIPE: "ESTRPIPE", + C.EUSERS: "EUSERS", + C.ENOTSOCK: "ENOTSOCK", + C.EDESTADDRREQ: "EDESTADDRREQ", + C.EMSGSIZE: "EMSGSIZE", + C.EPROTOTYPE: "EPROTOTYPE", + C.ENOPROTOOPT: "ENOPROTOOPT", + C.EPROTONOSUPPORT: "EPROTONOSUPPORT", + C.ESOCKTNOSUPPORT: "ESOCKTNOSUPPORT", + C.EOPNOTSUPP: "EOPNOTSUPP", + C.EPFNOSUPPORT: "EPFNOSUPPORT", + C.EAFNOSUPPORT: "EAFNOSUPPORT", + C.EADDRINUSE: "EADDRINUSE", + C.EADDRNOTAVAIL: "EADDRNOTAVAIL", + C.ENETDOWN: "ENETDOWN", + C.ENETUNREACH: "ENETUNREACH", + C.ENETRESET: "ENETRESET", + C.ECONNABORTED: "ECONNABORTED", + C.ECONNRESET: "ECONNRESET", + C.ENOBUFS: "ENOBUFS", + C.EISCONN: "EISCONN", + C.ENOTCONN: "ENOTCONN", + C.ESHUTDOWN: "ESHUTDOWN", + C.ETOOMANYREFS: "ETOOMANYREFS", + C.ETIMEDOUT: "ETIMEDOUT", + C.ECONNREFUSED: "ECONNREFUSED", + C.EHOSTDOWN: "EHOSTDOWN", + C.EHOSTUNREACH: "EHOSTUNREACH", + C.EALREADY: "EALREADY", + C.EINPROGRESS: "EINPROGRESS", + C.ESTALE: "ESTALE", + C.EUCLEAN: "EUCLEAN", + C.ENOTNAM: "ENOTNAM", + C.ENAVAIL: "ENAVAIL", + C.EISNAM: "EISNAM", + C.EREMOTEIO: "EREMOTEIO", + C.EDQUOT: "EDQUOT", + C.ENOMEDIUM: "ENOMEDIUM", + C.EMEDIUMTYPE: "EMEDIUMTYPE", + C.ECANCELED: "ECANCELED", + C.ENOKEY: "ENOKEY", + C.EKEYEXPIRED: "EKEYEXPIRED", + C.EKEYREVOKED: "EKEYREVOKED", + C.EKEYREJECTED: "EKEYREJECTED", + C.EOWNERDEAD: "EOWNERDEAD", + C.ENOTRECOVERABLE: "ENOTRECOVERABLE", + C.ERFKILL: "ERFKILL", + C.EHWPOISON: "EHWPOISON", +} + +func (p *Printer) printError(arg *tracer.Arg) { code := -arg.Int() if code == 0 { @@ -16,271 +180,5 @@ func (p *Printer) printError(colour Colour, arg *tracer.Arg) { return } - constant := "unknown" - - switch syscall.Errno(code) { - case unix.E2BIG: - constant = "E2BIG" - case unix.EACCES: - constant = "EACCES" - case unix.EADDRINUSE: - constant = "EADDRINUSE" - case unix.EADDRNOTAVAIL: - constant = "EADDRNOTAVAIL" - case unix.EADV: - constant = "EADV" - case unix.EAFNOSUPPORT: - constant = "EAFNOSUPPORT" - case unix.EAGAIN: - constant = "EAGAIN" - case unix.EALREADY: - constant = "EALREADY" - case unix.EBADE: - constant = "EBADE" - case unix.EBADF: - constant = "EBADF" - case unix.EBADFD: - constant = "EBADFD" - case unix.EBADMSG: - constant = "EBADMSG" - case unix.EBADR: - constant = "EBADR" - case unix.EBADRQC: - constant = "EBADRQC" - case unix.EBADSLT: - constant = "EBADSLT" - case unix.EBFONT: - constant = "EBFONT" - case unix.EBUSY: - constant = "EBUSY" - case unix.ECANCELED: - constant = "ECANCELED" - case unix.ECHILD: - constant = "ECHILD" - case unix.ECHRNG: - constant = "ECHRNG" - case unix.ECOMM: - constant = "ECOMM" - case unix.ECONNABORTED: - constant = "ECONNABORTED" - case unix.ECONNREFUSED: - constant = "ECONNREFUSED" - case unix.ECONNRESET: - constant = "ECONNRESET" - case unix.EDEADLOCK: - constant = "EDEADLOCK" - case unix.EDESTADDRREQ: - constant = "EDESTADDRREQ" - case unix.EDOM: - constant = "EDOM" - case unix.EDOTDOT: - constant = "EDOTDOT" - case unix.EDQUOT: - constant = "EDQUOT" - case unix.EEXIST: - constant = "EEXIST" - case unix.EFAULT: - constant = "EFAULT" - case unix.EFBIG: - constant = "EFBIG" - case unix.EHOSTDOWN: - constant = "EHOSTDOWN" - case unix.EHOSTUNREACH: - constant = "EHOSTUNREACH" - case unix.EIDRM: - constant = "EIDRM" - case unix.EILSEQ: - constant = "EILSEQ" - case unix.EINPROGRESS: - constant = "EINPROGRESS" - case unix.EINTR: - constant = "EINTR" - case unix.EINVAL: - constant = "EINVAL" - case unix.EIO: - constant = "EIO" - case unix.EISCONN: - constant = "EISCONN" - case unix.EISDIR: - constant = "EISDIR" - case unix.EISNAM: - constant = "EISNAM" - case unix.EKEYEXPIRED: - constant = "EKEYEXPIRED" - case unix.EKEYREJECTED: - constant = "EKEYREJECTED" - case unix.EKEYREVOKED: - constant = "EKEYREVOKED" - case unix.EL2HLT: - constant = "EL2HLT" - case unix.EL2NSYNC: - constant = "EL2NSYNC" - case unix.EL3HLT: - constant = "EL3HLT" - case unix.EL3RST: - constant = "EL3RST" - case unix.ELIBACC: - constant = "ELIBACC" - case unix.ELIBBAD: - constant = "ELIBBAD" - case unix.ELIBEXEC: - constant = "ELIBEXEC" - case unix.ELIBMAX: - constant = "ELIBMAX" - case unix.ELIBSCN: - constant = "ELIBSCN" - case unix.ELNRNG: - constant = "ELNRNG" - case unix.ELOOP: - constant = "ELOOP" - case unix.EMEDIUMTYPE: - constant = "EMEDIUMTYPE" - case unix.EMFILE: - constant = "EMFILE" - case unix.EMLINK: - constant = "EMLINK" - case unix.EMSGSIZE: - constant = "EMSGSIZE" - case unix.EMULTIHOP: - constant = "EMULTIHOP" - case unix.ENAMETOOLONG: - constant = "ENAMETOOLONG" - case unix.ENAVAIL: - constant = "ENAVAIL" - case unix.ENETDOWN: - constant = "ENETDOWN" - case unix.ENETRESET: - constant = "ENETRESET" - case unix.ENETUNREACH: - constant = "ENETUNREACH" - case unix.ENFILE: - constant = "ENFILE" - case unix.ENOANO: - constant = "ENOANO" - case unix.ENOBUFS: - constant = "ENOBUFS" - case unix.ENOCSI: - constant = "ENOCSI" - case unix.ENODATA: - constant = "ENODATA" - case unix.ENODEV: - constant = "ENODEV" - case unix.ENOENT: - constant = "ENOENT" - case unix.ENOEXEC: - constant = "ENOEXEC" - case unix.ENOKEY: - constant = "ENOKEY" - case unix.ENOLCK: - constant = "ENOLCK" - case unix.ENOLINK: - constant = "ENOLINK" - case unix.ENOMEDIUM: - constant = "ENOMEDIUM" - case unix.ENOMEM: - constant = "ENOMEM" - case unix.ENOMSG: - constant = "ENOMSG" - case unix.ENONET: - constant = "ENONET" - case unix.ENOPKG: - constant = "ENOPKG" - case unix.ENOPROTOOPT: - constant = "ENOPROTOOPT" - case unix.ENOSPC: - constant = "ENOSPC" - case unix.ENOSR: - constant = "ENOSR" - case unix.ENOSTR: - constant = "ENOSTR" - case unix.ENOSYS: - constant = "ENOSYS" - case unix.ENOTBLK: - constant = "ENOTBLK" - case unix.ENOTCONN: - constant = "ENOTCONN" - case unix.ENOTDIR: - constant = "ENOTDIR" - case unix.ENOTEMPTY: - constant = "ENOTEMPTY" - case unix.ENOTNAM: - constant = "ENOTNAM" - case unix.ENOTRECOVERABLE: - constant = "ENOTRECOVERABLE" - case unix.ENOTSOCK: - constant = "ENOTSOCK" - case unix.ENOTSUP: - constant = "ENOTSUP" - case unix.ENOTTY: - constant = "ENOTTY" - case unix.ENOTUNIQ: - constant = "ENOTUNIQ" - case unix.ENXIO: - constant = "ENXIO" - case unix.EOVERFLOW: - constant = "EOVERFLOW" - case unix.EOWNERDEAD: - constant = "EOWNERDEAD" - case unix.EPERM: - constant = "EPERM" - case unix.EPFNOSUPPORT: - constant = "EPFNOSUPPORT" - case unix.EPIPE: - constant = "EPIPE" - case unix.EPROTO: - constant = "EPROTO" - case unix.EPROTONOSUPPORT: - constant = "EPROTONOSUPPORT" - case unix.EPROTOTYPE: - constant = "EPROTOTYPE" - case unix.ERANGE: - constant = "ERANGE" - case unix.EREMCHG: - constant = "EREMCHG" - case unix.EREMOTE: - constant = "EREMOTE" - case unix.EREMOTEIO: - constant = "EREMOTEIO" - case unix.ERESTART: - constant = "ERESTART" - case unix.ERFKILL: - constant = "ERFKILL" - case unix.EROFS: - constant = "EROFS" - case unix.ESHUTDOWN: - constant = "ESHUTDOWN" - case unix.ESOCKTNOSUPPORT: - constant = "ESOCKTNOSUPPORT" - case unix.ESPIPE: - constant = "ESPIPE" - case unix.ESRCH: - constant = "ESRCH" - case unix.ESRMNT: - constant = "ESRMNT" - case unix.ESTALE: - constant = "ESTALE" - case unix.ESTRPIPE: - constant = "ESTRPIPE" - case unix.ETIME: - constant = "ETIME" - case unix.ETIMEDOUT: - constant = "ETIMEDOUT" - case unix.ETOOMANYREFS: - constant = "ETOOMANYREFS" - case unix.ETXTBSY: - constant = "ETXTBSY" - case unix.EUCLEAN: - constant = "EUCLEAN" - case unix.EUNATCH: - constant = "EUNATCH" - case unix.EUSERS: - constant = "EUSERS" - case unix.EXDEV: - constant = "EXDEV" - case unix.EXFULL: - constant = "EXFULL" - } - - msg := syscall.Errno(code).Error() - p.PrintColour(ColourRed, "%d %s (%s)", arg.Int()+1, constant, msg) + p.PrintColour(ColourRed, "%s", annotation.ErrNoToString(code)) } diff --git a/printer/printer.go b/printer/printer.go index 5e7456c..e3c336a 100644 --- a/printer/printer.go +++ b/printer/printer.go @@ -3,20 +3,34 @@ package printer import ( "fmt" "io" + "time" + + "github.com/liamg/grace/tracer" ) type Printer struct { - w io.Writer - useColours bool - maxStringLen int - hexDumpLongStrings bool - maxHexDumpLen int - maxObjectProperties int - colourIndex int - argProgress int - extraNewLine bool - multiline bool - inSyscall bool + w io.Writer + useColours bool + maxStringLen int + hexDumpLongStrings bool + maxHexDumpLen int + maxObjectProperties int + colourIndex int + argProgress int + extraNewLine bool + multiline bool + inSyscall bool + filter Filter + lastEntryMatchedFilter bool + relativeTimestamps bool + absoluteTimestamps bool + startTime time.Time + showNumbers bool + rawOutput bool +} + +type Filter interface { + Match(syscall *tracer.Syscall, exit bool) bool } func New(w io.Writer) *Printer { @@ -27,6 +41,7 @@ func New(w io.Writer) *Printer { hexDumpLongStrings: true, maxHexDumpLen: 4096, maxObjectProperties: 2, + startTime: time.Now(), } } @@ -56,6 +71,39 @@ func (p *Printer) SetExtraNewLine(extraNewLine bool) { p.extraNewLine = extraNewLine } +func (p *Printer) SetShowAbsoluteTimestamps(timestamps bool) { + p.absoluteTimestamps = timestamps +} + +func (p *Printer) SetShowRelativeTimestamps(timestamps bool) { + p.relativeTimestamps = timestamps +} + +func (p *Printer) SetMultiLine(multiline bool) { + p.multiline = multiline +} + +func (p *Printer) SetFilter(filter Filter) { + p.filter = filter +} + +func (p *Printer) SetShowSyscallNumber(number bool) { + p.showNumbers = number +} + +func (p *Printer) SetRawOutput(output bool) { + p.rawOutput = output +} + +func (p *Printer) PrefixEvent() { + if p.relativeTimestamps { + p.PrintDim("%12s ", time.Since(p.startTime)) + } + if p.absoluteTimestamps { + p.PrintDim("%18s ", time.Now().Format("15:04:05.999999999")) + } +} + func (p *Printer) Print(format string, args ...interface{}) { _, _ = fmt.Fprintf(p.w, format, args...) } @@ -74,10 +122,6 @@ func (p *Printer) PrintColour(colour Colour, format string, args ...interface{}) } } -func (p *Printer) SetMultiLine(multiline bool) { - p.multiline = multiline -} - func (p *Printer) PrintProcessExit(i int) { colour := ColourGreen if i != 0 { @@ -91,3 +135,17 @@ func (p *Printer) PrintProcessExit(i int) { } p.PrintColour(colour, "Process exited with status %d\n", i) } + +func (p *Printer) PrintAttach(pid int) { + p.PrintColour(ColourYellow, "Attached to process %d\n", pid) + if p.multiline { + p.Print("\n") + } +} + +func (p *Printer) PrintDetach(pid int) { + p.PrintColour(ColourYellow, "Detached from process %d\n", pid) + if p.multiline { + p.Print("\n") + } +} diff --git a/printer/signal.go b/printer/signal.go index 818c470..fba31cd 100644 --- a/printer/signal.go +++ b/printer/signal.go @@ -1,51 +1,25 @@ package printer import ( - "fmt" "syscall" -) - -var signals = map[syscall.Signal]string{ - syscall.SIGABRT: "SIGABRT", - syscall.SIGALRM: "SIGALRM", - syscall.SIGBUS: "SIGBUS", - syscall.SIGCHLD: "SIGCHLD", - syscall.SIGCONT: "SIGCONT", - syscall.SIGFPE: "SIGFPE", - syscall.SIGHUP: "SIGHUP", - syscall.SIGILL: "SIGILL", - syscall.SIGINT: "SIGINT", - syscall.SIGIO: "SIGIO", - syscall.SIGKILL: "SIGKILL", - syscall.SIGPIPE: "SIGPIPE", - syscall.SIGPROF: "SIGPROF", - syscall.SIGPWR: "SIGPWR", - syscall.SIGQUIT: "SIGQUIT", - syscall.SIGSEGV: "SIGSEGV", - syscall.SIGSTKFLT: "SIGSTKFLT", - syscall.SIGSTOP: "SIGSTOP", - syscall.SIGSYS: "SIGSYS", - syscall.SIGTERM: "SIGTERM", - syscall.SIGTRAP: "SIGTRAP", - syscall.SIGTSTP: "SIGTSTP", - syscall.SIGTTIN: "SIGTTIN", - syscall.SIGTTOU: "SIGTTOU", - syscall.SIGURG: "SIGURG", - syscall.SIGUSR1: "SIGUSR1", - syscall.SIGUSR2: "SIGUSR2", - syscall.SIGVTALRM: "SIGVTALRM", - syscall.SIGWINCH: "SIGWINCH", - syscall.SIGXCPU: "SIGXCPU", - syscall.SIGXFSZ: "SIGXFSZ", -} -func (p *Printer) PrintSignal(signal syscall.Signal) { - p.PrintColour(ColourYellow, "--> SIGNAL: %s <--\n", signalToString(signal)) -} + "github.com/liamg/grace/tracer" + "github.com/liamg/grace/tracer/annotation" +) -func signalToString(signal syscall.Signal) string { - if str, ok := signals[signal]; ok { - return str +func (p *Printer) PrintSignal(signal *tracer.SigInfo) { + p.PrefixEvent() + p.PrintColour(ColourMagenta, "--> ") + p.PrintColour( + ColourCyan, + "SIGNAL: %s (code=%s, pid=%d, uid=%d)", + annotation.SignalToString(int(signal.Signo)), + annotation.SignalCodeToString(syscall.Signal(signal.Signo), signal.Code), + signal.Pid, + signal.Uid, + ) + p.PrintColour(ColourMagenta, " <--\n") + if p.multiline { + p.Print("\n") } - return fmt.Sprintf("%d", signal) } diff --git a/printer/syscall.go b/printer/syscall.go index 9353678..2c1c1dd 100644 --- a/printer/syscall.go +++ b/printer/syscall.go @@ -1,10 +1,36 @@ package printer -import "github.com/liamg/grace/tracer" +import ( + "fmt" + + "github.com/liamg/grace/tracer" +) func (p *Printer) PrintSyscallEnter(syscall *tracer.Syscall) { + p.printSyscallEnter(syscall, false) +} + +func (p *Printer) printSyscallEnter(syscall *tracer.Syscall, overrideFilter bool) { + + if !overrideFilter { + if p.filter != nil { + if !p.filter.Match(syscall, false) { + p.lastEntryMatchedFilter = false + return + } + } + p.lastEntryMatchedFilter = true + } + + p.PrefixEvent() + p.colourIndex = 0 p.argProgress = 0 + + if p.showNumbers { + p.PrintDim("%4s", fmt.Sprintf("%d ", syscall.Number())) + } + if syscall.Unknown() { p.PrintColour(ColourRed, syscall.Name()) } else { @@ -15,10 +41,21 @@ func (p *Printer) PrintSyscallEnter(syscall *tracer.Syscall) { } func (p *Printer) PrintSyscallExit(syscall *tracer.Syscall) { + + if p.filter != nil { + if !p.lastEntryMatchedFilter && !p.filter.Match(syscall, true) { + return + } + } + + if !p.lastEntryMatchedFilter { + p.printSyscallEnter(syscall, true) + } + p.printRemainingArgs(syscall, true) p.PrintDim(" = ") ret := syscall.Return() - p.PrintArgValue(&ret, ColourWhite, true, 0, 0) + p.PrintArgValue(&ret, ColourGreen, true, 0, 0) p.Print("\n") if p.extraNewLine { p.Print("\n") @@ -30,25 +67,28 @@ func (p *Printer) printRemainingArgs(syscall *tracer.Syscall, exit bool) { if !exit { p.PrintDim("(") } - remaining := syscall.Args()[p.argProgress:] - for i, arg := range remaining { - if !arg.Known() { - break - } - if p.argProgress == 0 && p.multiline { - p.Print("\n") - } - p.PrintArg(arg, exit) - if i < len(remaining)-1 { - p.PrintDim(", ") - } - p.argProgress++ - if p.multiline { - p.Print("\n") + var remaining []tracer.Arg + if p.argProgress < len(syscall.Args()) { + remaining = syscall.Args()[p.argProgress:] + for i, arg := range remaining { + if !arg.Known() { + break + } + if p.argProgress == 0 && p.multiline { + p.Print("\n") + } + p.PrintArg(arg, exit) + if i < len(remaining)-1 || !syscall.Complete() { + p.PrintDim(", ") + } + p.argProgress++ + if p.multiline { + p.Print("\n") + } } } - if (exit && len(remaining) > 0) || (!exit && p.argProgress == len(syscall.Args())) { + if ((exit && len(remaining) > 0) || (!exit && p.argProgress == len(syscall.Args()))) && syscall.Complete() { p.PrintDim(")") } diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index af368ea..0000000 Binary files a/screenshot.png and /dev/null differ diff --git a/summary.go b/summary.go new file mode 100644 index 0000000..d65fe16 --- /dev/null +++ b/summary.go @@ -0,0 +1,121 @@ +package main + +import ( + "fmt" + "io" + "sort" + "time" + + "github.com/aquasecurity/table" + "github.com/liamg/grace/tracer" +) + +func configureSummary(t *tracer.Tracer, w io.Writer, sortKey string) { + + tracker := &tracker{ + counts: make(map[string]int), + errors: make(map[string]int), + durations: make(map[string]time.Duration), + starts: make(map[string]time.Time), + } + + t.SetSyscallEnterHandler(tracker.recordEnter) + t.SetSyscallExitHandler(tracker.recordExit) + t.SetDetachHandler(func(i int) { + tracker.print(w, sortKey) + }) +} + +type tracker struct { + counts map[string]int + errors map[string]int + durations map[string]time.Duration + starts map[string]time.Time +} + +func (t *tracker) recordEnter(s *tracer.Syscall) { + t.starts[s.Name()] = time.Now() +} + +func (t *tracker) recordExit(s *tracer.Syscall) { + stop := time.Now() + if start, ok := t.starts[s.Name()]; ok { + t.durations[s.Name()] += stop.Sub(start) + delete(t.starts, s.Name()) + } + if s.Return().Int() < 0 { + t.errors[s.Name()]++ + } + t.counts[s.Name()]++ +} + +func (t *tracker) print(w io.Writer, sortKey string) { + + tab := table.New(w) + tab.SetRowLines(false) + tab.AddHeaders("time %", "seconds", "usecs/call", "count", "errors", "syscall") + tab.SetAlignment(table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignLeft) + tab.SetLineStyle(table.StyleBlue) + + var total time.Duration + for _, duration := range t.durations { + total += duration + } + + type row struct { + sortKey int + cols []string + name string + } + + var rows []row + + for name, count := range t.counts { + + duration := t.durations[name] + + percent := float64(duration) * 100 / float64(total) + + var key int + switch sortKey { + case "count": + key = count + case "time": + key = int(percent * 100000) + case "seconds": + key = int(duration) + case "errors": + key = t.errors[name] + + } + + rows = append(rows, row{ + name: name, + sortKey: key, + cols: []string{ + fmt.Sprintf("%.2f", percent), + fmt.Sprintf("%.6f", duration.Seconds()), + fmt.Sprintf("%d", duration.Microseconds()/int64(count)), + fmt.Sprintf("%d", count), + fmt.Sprintf("%d", t.errors[name]), + name, + }, + }) + } + + if sortKey == "" { + sort.Slice(rows, func(i, j int) bool { + return rows[i].name < rows[j].name + }) + } else { + sort.Slice(rows, func(i, j int) bool { + return rows[i].sortKey > rows[j].sortKey + }) + } + + for _, row := range rows { + tab.AddRow(row.cols...) + } + + tab.Render() +} diff --git a/summary.png b/summary.png new file mode 100644 index 0000000..aab3a9a Binary files /dev/null and b/summary.png differ diff --git a/testdata/execve/main.go b/testdata/execve/main.go new file mode 100644 index 0000000..2db13f6 --- /dev/null +++ b/testdata/execve/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "os" + "syscall" +) + +func main() { + _ = syscall.Exec("/usr/bin/ls", []string{"ls", "/tmp"}, os.Environ()) +} diff --git a/testdata/pipe/main.go b/testdata/pipe/main.go index d82da37..1922c2c 100644 --- a/testdata/pipe/main.go +++ b/testdata/pipe/main.go @@ -6,7 +6,15 @@ import ( ) func main() { - fds := make([]int, 2) - _ = syscall.Pipe2(fds, 0) - fmt.Println(fds[0], fds[1]) + var buf syscall.Utsname + _ = syscall.Uname(&buf) + printUtsField(buf.Release) +} + +func printUtsField(f [65]int8) { + var str []byte + for i := 0; i < len(f); i++ { + str = append(str, byte(f[i])) + } + fmt.Println(string(str)) } diff --git a/tracer/annotation/access.go b/tracer/annotation/access.go new file mode 100644 index 0000000..5753c23 --- /dev/null +++ b/tracer/annotation/access.go @@ -0,0 +1,25 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +func AnnotateAccMode(arg Arg, _ int) { + var joins []string + if arg.Raw() == unix.F_OK { + joins = append(joins, "F_OK") + } else { + if arg.Raw()&unix.R_OK > 0 { + joins = append(joins, "R_OK") + } + if arg.Raw()&unix.W_OK > 0 { + joins = append(joins, "W_OK") + } + if arg.Raw()&unix.X_OK > 0 { + joins = append(joins, "X_OK") + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/arch_prctl.go b/tracer/annotation/arch_prctl.go new file mode 100644 index 0000000..a987c55 --- /dev/null +++ b/tracer/annotation/arch_prctl.go @@ -0,0 +1,34 @@ +package annotation + +/* +#include + +#ifndef _ASM_X86_PRCTL_H +#define _ASM_X86_PRCTL_H +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 +#endif + +int iARCH_SET_GS = ARCH_SET_GS; +int iARCH_SET_FS = ARCH_SET_FS; +int iARCH_GET_FS = ARCH_GET_FS; +int iARCH_GET_GS = ARCH_GET_GS; +*/ +import "C" + +var archPrCodes = map[int]string{ + int(C.iARCH_SET_GS): "ARCH_SET_GS", + int(C.iARCH_SET_FS): "ARCH_SET_FS", + int(C.iARCH_GET_FS): "ARCH_GET_FS", + int(C.iARCH_GET_GS): "ARCH_GET_GS", +} + +func AnnotateArchPrctrlCode(arg Arg, pid int) { + if s, ok := archPrCodes[int(arg.Raw())]; ok { + arg.SetAnnotation(s, true) + } else { + AnnotateHex(arg, pid) + } +} diff --git a/tracer/annotation/arg.go b/tracer/annotation/arg.go new file mode 100644 index 0000000..ea64332 --- /dev/null +++ b/tracer/annotation/arg.go @@ -0,0 +1,6 @@ +package annotation + +type Arg interface { + Raw() uintptr + SetAnnotation(annotation string, replace bool) +} diff --git a/tracer/annotation/bpf.go b/tracer/annotation/bpf.go new file mode 100644 index 0000000..37c8e63 --- /dev/null +++ b/tracer/annotation/bpf.go @@ -0,0 +1,47 @@ +package annotation + +import "golang.org/x/sys/unix" + +var bpfCmds = map[int]string{ + unix.BPF_MAP_CREATE: "BPF_MAP_CREATE", + unix.BPF_MAP_LOOKUP_ELEM: "BPF_MAP_LOOKUP_ELEM", + unix.BPF_MAP_UPDATE_ELEM: "BPF_MAP_UPDATE_ELEM", + unix.BPF_MAP_DELETE_ELEM: "BPF_MAP_DELETE_ELEM", + unix.BPF_MAP_GET_NEXT_KEY: "BPF_MAP_GET_NEXT_KEY", + unix.BPF_PROG_LOAD: "BPF_PROG_LOAD", + unix.BPF_OBJ_PIN: "BPF_OBJ_PIN", + unix.BPF_OBJ_GET: "BPF_OBJ_GET", + unix.BPF_PROG_ATTACH: "BPF_PROG_ATTACH", + unix.BPF_PROG_DETACH: "BPF_PROG_DETACH", + unix.BPF_PROG_TEST_RUN: "BPF_PROG_TEST_RUN", + unix.BPF_PROG_GET_NEXT_ID: "BPF_PROG_GET_NEXT_ID", + unix.BPF_MAP_GET_NEXT_ID: "BPF_MAP_GET_NEXT_ID", + unix.BPF_PROG_GET_FD_BY_ID: "BPF_PROG_GET_FD_BY_ID", + unix.BPF_MAP_GET_FD_BY_ID: "BPF_MAP_GET_FD_BY_ID", + unix.BPF_OBJ_GET_INFO_BY_FD: "BPF_OBJ_GET_INFO_BY_FD", + unix.BPF_PROG_QUERY: "BPF_PROG_QUERY", + unix.BPF_RAW_TRACEPOINT_OPEN: "BPF_RAW_TRACEPOINT_OPEN", + unix.BPF_BTF_LOAD: "BPF_BTF_LOAD", + unix.BPF_BTF_GET_FD_BY_ID: "BPF_BTF_GET_FD_BY_ID", + unix.BPF_TASK_FD_QUERY: "BPF_TASK_FD_QUERY", + unix.BPF_MAP_LOOKUP_AND_DELETE_ELEM: "BPF_MAP_LOOKUP_AND_DELETE_ELEM", + unix.BPF_MAP_FREEZE: "BPF_MAP_FREEZE", + unix.BPF_BTF_GET_NEXT_ID: "BPF_BTF_GET_NEXT_ID", + unix.BPF_MAP_LOOKUP_BATCH: "BPF_MAP_LOOKUP_BATCH", + unix.BPF_MAP_LOOKUP_AND_DELETE_BATCH: "BPF_MAP_LOOKUP_AND_DELETE_BATCH", + unix.BPF_MAP_UPDATE_BATCH: "BPF_MAP_UPDATE_BATCH", + unix.BPF_MAP_DELETE_BATCH: "BPF_MAP_DELETE_BATCH", + unix.BPF_LINK_CREATE: "BPF_LINK_CREATE", + unix.BPF_LINK_UPDATE: "BPF_LINK_UPDATE", + unix.BPF_LINK_GET_FD_BY_ID: "BPF_LINK_GET_FD_BY_ID", + unix.BPF_LINK_GET_NEXT_ID: "BPF_LINK_GET_NEXT_ID", + unix.BPF_ENABLE_STATS: "BPF_ENABLE_STATS", + unix.BPF_ITER_CREATE: "BPF_ITER_CREATE", + unix.BPF_LINK_DETACH: "BPF_LINK_DETACH", +} + +func AnnotateBPFCmd(arg Arg, _ int) { + if v, ok := bpfCmds[int(arg.Raw())]; ok { + arg.SetAnnotation(v, true) + } +} diff --git a/tracer/annotation/clockid.go b/tracer/annotation/clockid.go new file mode 100644 index 0000000..cab1976 --- /dev/null +++ b/tracer/annotation/clockid.go @@ -0,0 +1,22 @@ +package annotation + +import "golang.org/x/sys/unix" + +var clockIds = map[int]string{ + unix.CLOCK_REALTIME: "CLOCK_REALTIME", + unix.CLOCK_MONOTONIC: "CLOCK_MONOTONIC", + unix.CLOCK_PROCESS_CPUTIME_ID: "CLOCK_PROCESS_CPUTIME_ID", + unix.CLOCK_THREAD_CPUTIME_ID: "CLOCK_THREAD_CPUTIME_ID", + unix.CLOCK_MONOTONIC_RAW: "CLOCK_MONOTONIC_RAW", + unix.CLOCK_REALTIME_COARSE: "CLOCK_REALTIME_COARSE", + unix.CLOCK_MONOTONIC_COARSE: "CLOCK_MONOTONIC_COARSE", + unix.CLOCK_BOOTTIME: "CLOCK_BOOTTIME", + unix.CLOCK_REALTIME_ALARM: "CLOCK_REALTIME_ALARM", + unix.CLOCK_BOOTTIME_ALARM: "CLOCK_BOOTTIME_ALARM", +} + +func AnnotateClockID(arg Arg, pid int) { + if str, ok := clockIds[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/clone.go b/tracer/annotation/clone.go new file mode 100644 index 0000000..e257be7 --- /dev/null +++ b/tracer/annotation/clone.go @@ -0,0 +1,42 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var cloneFlags = map[int]string{ + unix.CLONE_CHILD_CLEARTID: "CLONE_CHILD_CLEARTID", + unix.CLONE_CHILD_SETTID: "CLONE_CHILD_SETTID", + unix.CLONE_FILES: "CLONE_FILES", + unix.CLONE_FS: "CLONE_FS", + unix.CLONE_IO: "CLONE_IO", + unix.CLONE_NEWCGROUP: "CLONE_NEWCGROUP", + unix.CLONE_NEWIPC: "CLONE_NEWIPC", + unix.CLONE_NEWNET: "CLONE_NEWNET", + unix.CLONE_NEWNS: "CLONE_NEWNS", + unix.CLONE_NEWPID: "CLONE_NEWPID", + unix.CLONE_NEWUSER: "CLONE_NEWUSER", + unix.CLONE_NEWUTS: "CLONE_NEWUTS", + unix.CLONE_PARENT: "CLONE_PARENT", + unix.CLONE_PARENT_SETTID: "CLONE_PARENT_SETTID", + unix.CLONE_PTRACE: "CLONE_PTRACE", + unix.CLONE_SETTLS: "CLONE_SETTLS", + unix.CLONE_SIGHAND: "CLONE_SIGHAND", + unix.CLONE_SYSVSEM: "CLONE_SYSVSEM", + unix.CLONE_THREAD: "CLONE_THREAD", + unix.CLONE_UNTRACED: "CLONE_UNTRACED", + unix.CLONE_VFORK: "CLONE_VFORK", + unix.CLONE_VM: "CLONE_VM", +} + +func AnnotateCloneFlags(arg Arg, _ int) { + var joins []string + for flag, name := range cloneFlags { + if arg.Raw()&uintptr(flag) != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/closerange.go b/tracer/annotation/closerange.go new file mode 100644 index 0000000..22c6fbc --- /dev/null +++ b/tracer/annotation/closerange.go @@ -0,0 +1,22 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var closeRangeFlags = map[int]string{ + unix.CLOSE_RANGE_CLOEXEC: "CLOSE_RANGE_CLOEXEC", + unix.CLOSE_RANGE_UNSHARE: "CLOSE_RANGE_UNSHARE", +} + +func AnnotateCloseRangeFlags(arg Arg, _ int) { + var joins []string + for flag, str := range closeRangeFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/control.go b/tracer/annotation/control.go new file mode 100644 index 0000000..b366125 --- /dev/null +++ b/tracer/annotation/control.go @@ -0,0 +1,57 @@ +package annotation + +import "golang.org/x/sys/unix" + +var socketLevels = map[int]string{ + unix.SOL_SOCKET: "SOL_SOCKET", + unix.SOL_AAL: "SOL_AAL", + unix.SOL_ALG: "SOL_ALG", + unix.SOL_ATM: "SOL_ATM", + unix.SOL_CAIF: "SOL_CAIF", + unix.SOL_CAN_BASE: "SOL_CAN_BASE", + unix.SOL_CAN_RAW: "SOL_CAN_RAW", + unix.SOL_DCCP: "SOL_DCCP", + unix.SOL_DECNET: "SOL_DECNET", + unix.SOL_ICMPV6: "SOL_ICMPV6", + unix.SOL_IP: "SOL_IP", + unix.SOL_IPV6: "SOL_IPV6", + unix.SOL_IRDA: "SOL_IRDA", + unix.SOL_IUCV: "SOL_IUCV", + unix.SOL_KCM: "SOL_KCM", + unix.SOL_LLC: "SOL_LLC", + unix.SOL_MCTP: "SOL_MCTP", + unix.SOL_MPTCP: "SOL_MPTCP", + unix.SOL_NETBEUI: "SOL_NETBEUI", + unix.SOL_NETLINK: "SOL_NETLINK", + unix.SOL_NFC: "SOL_NFC", + unix.SOL_PACKET: "SOL_PACKET", + unix.SOL_PNPIPE: "SOL_PNPIPE", + unix.SOL_PPPOL2TP: "SOL_PPPOL2TP", + unix.SOL_RAW: "SOL_RAW", + unix.SOL_RDS: "SOL_RDS", + unix.SOL_RXRPC: "SOL_RXRPC", + unix.SOL_SMC: "SOL_SMC", + unix.SOL_TCP: "SOL_TCP", + unix.SOL_TIPC: "SOL_TIPC", + unix.SOL_TLS: "SOL_TLS", + unix.SOL_X25: "SOL_X25", + unix.SOL_XDP: "SOL_XDP", +} + +func AnnotateSocketLevel(arg Arg, _ int) { + if str, ok := socketLevels[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} + +var cmsgTypes = map[int]string{ + unix.SCM_RIGHTS: "SCM_RIGHTS", + unix.SCM_CREDENTIALS: "SCM_CREDENTIALS", + unix.SCM_TIMESTAMP: "SCM_TIMESTAMP", +} + +func AnnotateControlMessageType(arg Arg, _ int) { + if str, ok := cmsgTypes[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/device.go b/tracer/annotation/device.go new file mode 100644 index 0000000..d9158bb --- /dev/null +++ b/tracer/annotation/device.go @@ -0,0 +1,23 @@ +package annotation + +import ( + "fmt" + + "golang.org/x/sys/unix" +) + +func AnnotateDevice(arg Arg, pid int) { + arg.SetAnnotation(DeviceToString(uint64(arg.Raw())), true) +} + +func DeviceToString(dev uint64) string { + major := "0" + if m := unix.Major(dev); m != 0 { + major = fmt.Sprintf("0x%x", m) + } + minor := "0" + if m := unix.Minor(dev); m != 0 { + minor = fmt.Sprintf("0x%x", m) + } + return fmt.Sprintf("makedev(%s, %s)", major, minor) +} diff --git a/tracer/annotation/dirent.go b/tracer/annotation/dirent.go new file mode 100644 index 0000000..3db4e98 --- /dev/null +++ b/tracer/annotation/dirent.go @@ -0,0 +1,21 @@ +package annotation + +import "golang.org/x/sys/unix" + +var dirEntTypes = map[int]string{ + unix.DT_BLK: "DT_BLK", + unix.DT_CHR: "DT_CHR", + unix.DT_DIR: "DT_DIR", + unix.DT_FIFO: "DT_FIFO", + unix.DT_LNK: "DT_LNK", + unix.DT_REG: "DT_REG", + unix.DT_SOCK: "DT_SOCK", + unix.DT_WHT: "DT_WHT", + unix.DT_UNKNOWN: "DT_UNKNOWN", +} + +func AnnotateDirEntType(arg Arg, _ int) { + if str, ok := dirEntTypes[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/epoll.go b/tracer/annotation/epoll.go new file mode 100644 index 0000000..44c62b1 --- /dev/null +++ b/tracer/annotation/epoll.go @@ -0,0 +1,15 @@ +package annotation + +import "golang.org/x/sys/unix" + +var epollCtlOps = map[int]string{ + unix.EPOLL_CTL_ADD: "EPOLL_CTL_ADD", + unix.EPOLL_CTL_DEL: "EPOLL_CTL_DEL", + unix.EPOLL_CTL_MOD: "EPOLL_CTL_MOD", +} + +func AnnotateEpollCtlOp(arg Arg, _ int) { + if str, ok := epollCtlOps[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/error.go b/tracer/annotation/error.go new file mode 100644 index 0000000..5e96a89 --- /dev/null +++ b/tracer/annotation/error.go @@ -0,0 +1,180 @@ +package annotation + +import "C" + +/* + #include + + // the following definitions are never meant to be seen by user programs, other than via ptrace... + #ifndef ERESTARTSYS + #define ERESTARTSYS 512 + #endif + + #ifndef ERESTARTNOINTR + #define ERESTARTNOINTR 513 + #endif + + #ifndef ERESTARTNOHAND + #define ERESTARTNOHAND 514 + #endif + + #ifndef ENOIOCTLCMD + #define ENOIOCTLCMD 515 + #endif + + #ifndef ERESTART_RESTARTBLOCK + #define ERESTART_RESTARTBLOCK 516 + #endif + + // TODO: add NFSv3 errors + +*/ +import "C" + +import ( + "syscall" +) + +var errors = map[int]string{ + C.ERESTARTSYS: "ERESTARTSYS", + C.ERESTARTNOINTR: "ERESTARTNOINTR", + C.ERESTARTNOHAND: "ERESTARTNOHAND", + C.ENOIOCTLCMD: "ENOIOCTLCMD", + C.ERESTART_RESTARTBLOCK: "ERESTART_RESTARTBLOCK", + C.ERESTART: "ERESTART", + C.EPERM: "EPERM", + C.ENOENT: "ENOENT", + C.ESRCH: "ESRCH", + C.EINTR: "EINTR", + C.EIO: "EIO", + C.ENXIO: "ENXIO", + C.E2BIG: "E2BIG", + C.ENOEXEC: "ENOEXEC", + C.EBADF: "EBADF", + C.ECHILD: "ECHILD", + C.EAGAIN: "EAGAIN", + C.ENOMEM: "ENOMEM", + C.EACCES: "EACCES", + C.EFAULT: "EFAULT", + C.ENOTBLK: "ENOTBLK", + C.EBUSY: "EBUSY", + C.EEXIST: "EEXIST", + C.EXDEV: "EXDEV", + C.ENODEV: "ENODEV", + C.ENOTDIR: "ENOTDIR", + C.EISDIR: "EISDIR", + C.EINVAL: "EINVAL", + C.ENFILE: "ENFILE", + C.EMFILE: "EMFILE", + C.ENOTTY: "ENOTTY", + C.ETXTBSY: "ETXTBSY", + C.EFBIG: "EFBIG", + C.ENOSPC: "ENOSPC", + C.ESPIPE: "ESPIPE", + C.EROFS: "EROFS", + C.EMLINK: "EMLINK", + C.EPIPE: "EPIPE", + C.EDOM: "EDOM", + C.ERANGE: "ERANGE", + C.EDEADLK: "EDEADLK", + C.ENAMETOOLONG: "ENAMETOOLONG", + C.ENOLCK: "ENOLCK", + C.ENOSYS: "ENOSYS", + C.ENOTEMPTY: "ENOTEMPTY", + C.ELOOP: "ELOOP", + C.ENOMSG: "ENOMSG", + C.EIDRM: "EIDRM", + C.ECHRNG: "ECHRNG", + C.EL2NSYNC: "EL2NSYNC", + C.EL3HLT: "EL3HLT", + C.EL3RST: "EL3RST", + C.ELNRNG: "ELNRNG", + C.EUNATCH: "EUNATCH", + C.ENOCSI: "ENOCSI", + C.EL2HLT: "EL2HLT", + C.EBADE: "EBADE", + C.EBADR: "EBADR", + C.EXFULL: "EXFULL", + C.ENOANO: "ENOANO", + C.EBADRQC: "EBADRQC", + C.EBADSLT: "EBADSLT", + C.EBFONT: "EBFONT", + C.ENOSTR: "ENOSTR", + C.ENODATA: "ENODATA", + C.ETIME: "ETIME", + C.ENOSR: "ENOSR", + C.ENONET: "ENONET", + C.ENOPKG: "ENOPKG", + C.EREMOTE: "EREMOTE", + C.ENOLINK: "ENOLINK", + C.EADV: "EADV", + C.ESRMNT: "ESRMNT", + C.ECOMM: "ECOMM", + C.EPROTO: "EPROTO", + C.EMULTIHOP: "EMULTIHOP", + C.EBADMSG: "EBADMSG", + C.ENOTUNIQ: "ENOTUNIQ", + C.EBADFD: "EBADFD", + C.EREMCHG: "EREMCHG", + C.ELIBACC: "ELIBACC", + C.ELIBBAD: "ELIBBAD", + C.ELIBSCN: "ELIBSCN", + C.ELIBMAX: "ELIBMAX", + C.ELIBEXEC: "ELIBEXEC", + C.EILSEQ: "EILSEQ", + C.ESTRPIPE: "ESTRPIPE", + C.EUSERS: "EUSERS", + C.ENOTSOCK: "ENOTSOCK", + C.EDESTADDRREQ: "EDESTADDRREQ", + C.EMSGSIZE: "EMSGSIZE", + C.EPROTOTYPE: "EPROTOTYPE", + C.ENOPROTOOPT: "ENOPROTOOPT", + C.EPROTONOSUPPORT: "EPROTONOSUPPORT", + C.ESOCKTNOSUPPORT: "ESOCKTNOSUPPORT", + C.EOPNOTSUPP: "EOPNOTSUPP", + C.EPFNOSUPPORT: "EPFNOSUPPORT", + C.EAFNOSUPPORT: "EAFNOSUPPORT", + C.EADDRINUSE: "EADDRINUSE", + C.EADDRNOTAVAIL: "EADDRNOTAVAIL", + C.ENETDOWN: "ENETDOWN", + C.ENETUNREACH: "ENETUNREACH", + C.ENETRESET: "ENETRESET", + C.ECONNABORTED: "ECONNABORTED", + C.ECONNRESET: "ECONNRESET", + C.ENOBUFS: "ENOBUFS", + C.EISCONN: "EISCONN", + C.ENOTCONN: "ENOTCONN", + C.ESHUTDOWN: "ESHUTDOWN", + C.ETOOMANYREFS: "ETOOMANYREFS", + C.ETIMEDOUT: "ETIMEDOUT", + C.ECONNREFUSED: "ECONNREFUSED", + C.EHOSTDOWN: "EHOSTDOWN", + C.EHOSTUNREACH: "EHOSTUNREACH", + C.EALREADY: "EALREADY", + C.EINPROGRESS: "EINPROGRESS", + C.ESTALE: "ESTALE", + C.EUCLEAN: "EUCLEAN", + C.ENOTNAM: "ENOTNAM", + C.ENAVAIL: "ENAVAIL", + C.EISNAM: "EISNAM", + C.EREMOTEIO: "EREMOTEIO", + C.EDQUOT: "EDQUOT", + C.ENOMEDIUM: "ENOMEDIUM", + C.EMEDIUMTYPE: "EMEDIUMTYPE", + C.ECANCELED: "ECANCELED", + C.ENOKEY: "ENOKEY", + C.EKEYEXPIRED: "EKEYEXPIRED", + C.EKEYREVOKED: "EKEYREVOKED", + C.EKEYREJECTED: "EKEYREJECTED", + C.EOWNERDEAD: "EOWNERDEAD", + C.ENOTRECOVERABLE: "ENOTRECOVERABLE", + C.ERFKILL: "ERFKILL", + C.EHWPOISON: "EHWPOISON", +} + +func ErrNoToString(errno int) string { + if err, ok := errors[errno]; ok { + return err + } + return syscall.Errno(errno).Error() +} diff --git a/tracer/annotation/eventfd.go b/tracer/annotation/eventfd.go new file mode 100644 index 0000000..abc8961 --- /dev/null +++ b/tracer/annotation/eventfd.go @@ -0,0 +1,23 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var eventFdFlags = map[int]string{ + unix.O_CLOEXEC: "EFD_CLOEXEC", + unix.O_NONBLOCK: "EFD_NONBLOCK", + unix.EFD_SEMAPHORE: "EFD_SEMAPHORE", +} + +func AnnotateEventFdFlags(arg Arg, _ int) { + var joins []string + for flag, str := range eventFdFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/fadvice.go b/tracer/annotation/fadvice.go new file mode 100644 index 0000000..cc22756 --- /dev/null +++ b/tracer/annotation/fadvice.go @@ -0,0 +1,21 @@ +package annotation + +/* +#include +*/ +import "C" + +var fadviceFlags = map[int]string{ + C.POSIX_FADV_NORMAL: "POSIX_FADV_NORMAL", + C.POSIX_FADV_RANDOM: "POSIX_FADV_RANDOM", + C.POSIX_FADV_SEQUENTIAL: "POSIX_FADV_SEQUENTIAL", + C.POSIX_FADV_WILLNEED: "POSIX_FADV_WILLNEED", + C.POSIX_FADV_DONTNEED: "POSIX_FADV_DONTNEED", + C.POSIX_FADV_NOREUSE: "POSIX_FADV_NOREUSE", +} + +func AnnotateFAdvice(arg Arg, pid int) { + if str, ok := fadviceFlags[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/fallocate.go b/tracer/annotation/fallocate.go new file mode 100644 index 0000000..6ceedf7 --- /dev/null +++ b/tracer/annotation/fallocate.go @@ -0,0 +1,18 @@ +package annotation + +import "golang.org/x/sys/unix" + +var fallocateModes = map[int]string{ + unix.FALLOC_FL_KEEP_SIZE: "FALLOC_FL_KEEP_SIZE", + unix.FALLOC_FL_PUNCH_HOLE: "FALLOC_FL_PUNCH_HOLE", + unix.FALLOC_FL_COLLAPSE_RANGE: "FALLOC_FL_COLLAPSE_RANGE", + unix.FALLOC_FL_ZERO_RANGE: "FALLOC_FL_ZERO_RANGE", + unix.FALLOC_FL_INSERT_RANGE: "FALLOC_FL_INSERT_RANGE", + unix.FALLOC_FL_UNSHARE_RANGE: "FALLOC_FL_UNSHARE_RANGE", +} + +func AnnotateFallocateMode(arg Arg, _ int) { + if str, ok := fallocateModes[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/fanotify.go b/tracer/annotation/fanotify.go new file mode 100644 index 0000000..259218f --- /dev/null +++ b/tracer/annotation/fanotify.go @@ -0,0 +1,57 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var fanotifyFlags = map[int]string{ + unix.FAN_CLASS_PRE_CONTENT: "FAN_CLASS_PRE_CONTENT", + unix.FAN_CLASS_CONTENT: "FAN_CLASS_CONTENT", + unix.FAN_CLASS_NOTIF: "FAN_CLASS_NOTIF", + unix.FAN_REPORT_FID: "FAN_REPORT_FID", + unix.FAN_REPORT_NAME: "FAN_REPORT_NAME", + unix.FAN_REPORT_DIR_FID: "FAN_REPORT_DIR_FID", + unix.FAN_REPORT_TID: "FAN_REPORT_TID", + unix.FAN_NONBLOCK: "FAN_NONBLOCK", + unix.FAN_UNLIMITED_QUEUE: "FAN_UNLIMITED_QUEUE", + unix.FAN_UNLIMITED_MARKS: "FAN_UNLIMITED_MARKS", + unix.FAN_ENABLE_AUDIT: "FAN_ENABLE_AUDIT", +} + +var fanotifyMarkFlags = map[int]string{ + unix.FAN_MARK_ADD: "FAN_MARK_ADD", + unix.FAN_MARK_REMOVE: "FAN_MARK_REMOVE", + unix.FAN_MARK_DONT_FOLLOW: "FAN_MARK_DONT_FOLLOW", + unix.FAN_MARK_ONLYDIR: "FAN_MARK_ONLYDIR", + unix.FAN_MARK_MOUNT: "FAN_MARK_MOUNT", + unix.FAN_MARK_IGNORED_MASK: "FAN_MARK_IGNORED_MASK", + unix.FAN_MARK_IGNORED_SURV_MODIFY: "FAN_MARK_IGNORED_SURV_MODIFY", + unix.FAN_MARK_FLUSH: "FAN_MARK_FLUSH", + unix.FAN_MARK_FILESYSTEM: "FAN_MARK_FILESYSTEM", +} + +func AnnotateFANotifyFlags(arg Arg, pid int) { + var joins []string + for flag, str := range fanotifyFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +func AnnotateFANotifyEventFlags(arg Arg, pid int) { + AnnotateOpenFlags(arg, pid) +} + +func AnnotateFANotifyMarkFlags(arg Arg, pid int) { + var joins []string + for flag, str := range fanotifyMarkFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/fcntl.go b/tracer/annotation/fcntl.go new file mode 100644 index 0000000..3daabc1 --- /dev/null +++ b/tracer/annotation/fcntl.go @@ -0,0 +1,54 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var fcntlCmds = map[int]string{ + unix.F_DUPFD: "F_DUPFD", + unix.F_DUPFD_CLOEXEC: "F_DUPFD_CLOEXEC", + unix.F_GETFD: "F_GETFD", + unix.F_SETFD: "F_SETFD", + unix.F_GETFL: "F_GETFL", + unix.F_SETFL: "F_SETFL", + unix.F_GETLK: "F_GETLK", + unix.F_SETLK: "F_SETLK", + unix.F_SETLKW: "F_SETLKW", + unix.F_SETOWN: "F_SETOWN", + unix.F_GETOWN: "F_GETOWN", + unix.F_GETOWN_EX: "F_GETOWN_EX", + unix.F_SETOWN_EX: "F_SETOWN_EX", + unix.F_GETSIG: "F_GETSIG", + unix.F_SETSIG: "F_SETSIG", + unix.F_GETLEASE: "F_GETLEASE", + unix.F_SETLEASE: "F_SETLEASE", + unix.F_NOTIFY: "F_NOTIFY", + unix.F_GETPIPE_SZ: "F_GETPIPE_SZ", + unix.F_SETPIPE_SZ: "F_SETPIPE_SZ", +} + +func AnnotateFcntlCmd(arg Arg, pid int) { + if str, ok := fcntlCmds[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} + +var atFlags = map[int]string{ + unix.AT_SYMLINK_NOFOLLOW: "AT_SYMLINK_NOFOLLOW", + unix.AT_REMOVEDIR: "AT_REMOVEDIR", + unix.AT_SYMLINK_FOLLOW: "AT_SYMLINK_FOLLOW", + unix.AT_EMPTY_PATH: "AT_EMPTY_PATH", + unix.AT_NO_AUTOMOUNT: "AT_NO_AUTOMOUNT", +} + +func AnnotateAtFlags(arg Arg, pid int) { + var joins []string + for flag, str := range atFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/fd.go b/tracer/annotation/fd.go new file mode 100644 index 0000000..8c1cf51 --- /dev/null +++ b/tracer/annotation/fd.go @@ -0,0 +1,57 @@ +package annotation + +import ( + "fmt" + "os" + "strconv" + "strings" + "syscall" + + "github.com/liamg/grace/tracer/netw" + + "golang.org/x/sys/unix" +) + +func AnnotateFd(arg Arg, pid int) { + + if path, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/%d", pid, arg.Raw())); err == nil { + + switch { + case strings.HasPrefix(path, "socket:["): + fd := strings.TrimPrefix(path, "socket:[") + fd = strings.TrimSuffix(fd, "]") + if inode, err := strconv.Atoi(fd); err == nil { + if str := socketInoToString(inode); str != "" { + arg.SetAnnotation(str, false) + return + } + } + } + arg.SetAnnotation(path, false) + } + + if int32(arg.Raw()) == unix.AT_FDCWD { + arg.SetAnnotation("AT_FDCWD", true) + return + } + + switch int(arg.Raw()) { + case syscall.Stdin: + arg.SetAnnotation("STDIN", false) + case syscall.Stdout: + arg.SetAnnotation("STDOUT", false) + case syscall.Stderr: + arg.SetAnnotation("STDERR", false) + } +} + +func socketInoToString(inode int) string { + if conns, err := netw.ListConnections(); err == nil { + for _, conn := range conns { + if conn.INode == inode { + return fmt.Sprintf("%s %s:%d -> %s:%d", conn.Protocol, conn.LocalAddress, conn.LocalPort, conn.RemoteAddress, conn.RemotePort) + } + } + } + return "" +} diff --git a/tracer/annotation/flock.go b/tracer/annotation/flock.go new file mode 100644 index 0000000..aaf3a4b --- /dev/null +++ b/tracer/annotation/flock.go @@ -0,0 +1,16 @@ +package annotation + +import "golang.org/x/sys/unix" + +var flockOps = map[int]string{ + unix.LOCK_SH: "LOCK_SH", + unix.LOCK_EX: "LOCK_EX", + unix.LOCK_NB: "LOCK_NB", + unix.LOCK_UN: "LOCK_UN", +} + +func AnnotateFlockOperation(arg Arg, _ int) { + if str, ok := flockOps[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/futex.go b/tracer/annotation/futex.go new file mode 100644 index 0000000..2a47acb --- /dev/null +++ b/tracer/annotation/futex.go @@ -0,0 +1,64 @@ +package annotation + +var futexOps = map[int]string{ + 0: "FUTEX_WAIT", + 1: "FUTEX_WAKE", + 2: "FUTEX_FD", + 3: "FUTEX_REQUEUE", + 4: "FUTEX_CMP_REQUEUE", + 5: "FUTEX_WAKE_OP", + 6: "FUTEX_LOCK_PI", + 7: "FUTEX_UNLOCK_PI", + 8: "FUTEX_TRYLOCK_PI", + 9: "FUTEX_WAIT_BITSET", + 10: "FUTEX_WAKE_BITSET", + 11: "FUTEX_WAIT_REQUEUE_PI", + 12: "FUTEX_CMP_REQUEUE_PI", + 128 | 0: "FUTEX_PRIVATE_FLAG|FUTEX_WAIT", + 128 | 1: "FUTEX_PRIVATE_FLAG|FUTEX_WAKE", + 128 | 2: "FUTEX_PRIVATE_FLAG|FUTEX_FD", + 128 | 3: "FUTEX_PRIVATE_FLAG|FUTEX_REQUEUE", + 128 | 4: "FUTEX_PRIVATE_FLAG|FUTEX_CMP_REQUEUE", + 128 | 5: "FUTEX_PRIVATE_FLAG|FUTEX_WAKE_OP", + 128 | 6: "FUTEX_PRIVATE_FLAG|FUTEX_LOCK_PI", + 128 | 7: "FUTEX_PRIVATE_FLAG|FUTEX_UNLOCK_PI", + 128 | 8: "FUTEX_PRIVATE_FLAG|FUTEX_TRYLOCK_PI", + 128 | 9: "FUTEX_PRIVATE_FLAG|FUTEX_WAIT_BITSET", + 128 | 10: "FUTEX_PRIVATE_FLAG|FUTEX_WAKE_BITSET", + 128 | 11: "FUTEX_PRIVATE_FLAG|FUTEX_WAIT_REQUEUE_PI", + 128 | 12: "FUTEX_PRIVATE_FLAG|FUTEX_CMP_REQUEUE_PI", + 256 | 0: "FUTEX_CLOCK_REALTIME|FUTEX_WAIT", + 256 | 1: "FUTEX_CLOCK_REALTIME|FUTEX_WAKE", + 256 | 2: "FUTEX_CLOCK_REALTIME|FUTEX_FD", + 256 | 3: "FUTEX_CLOCK_REALTIME|FUTEX_REQUEUE", + 256 | 4: "FUTEX_CLOCK_REALTIME|FUTEX_CMP_REQUEUE", + 256 | 5: "FUTEX_CLOCK_REALTIME|FUTEX_WAKE_OP", + 256 | 6: "FUTEX_CLOCK_REALTIME|FUTEX_LOCK_PI", + 256 | 7: "FUTEX_CLOCK_REALTIME|FUTEX_UNLOCK_PI", + 256 | 8: "FUTEX_CLOCK_REALTIME|FUTEX_TRYLOCK_PI", + 256 | 9: "FUTEX_CLOCK_REALTIME|FUTEX_WAIT_BITSET", + 256 | 10: "FUTEX_CLOCK_REALTIME|FUTEX_WAKE_BITSET", + 256 | 11: "FUTEX_CLOCK_REALTIME|FUTEX_WAIT_REQUEUE_PI", + 256 | 12: "FUTEX_CLOCK_REALTIME|FUTEX_CMP_REQUEUE_PI", + 384 | 0: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAIT", + 384 | 1: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAKE", + 384 | 2: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_FD", + 384 | 3: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_REQUEUE", + 384 | 4: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_CMP_REQUEUE", + 384 | 5: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAKE_OP", + 384 | 6: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_LOCK_PI", + 384 | 7: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_UNLOCK_PI", + 384 | 8: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_TRYLOCK_PI", + 384 | 9: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAIT_BITSET", + 384 | 10: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAKE_BITSET", + 384 | 11: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAIT_REQUEUE_PI", + 384 | 12: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_CMP_REQUEUE_PI", +} + +func AnnotateFutexOp(arg Arg, pid int) { + if s, ok := futexOps[int(arg.Raw())]; ok { + arg.SetAnnotation(s, true) + } else { + AnnotateHex(arg, pid) + } +} diff --git a/tracer/annotation/hex.go b/tracer/annotation/hex.go new file mode 100644 index 0000000..9c03d88 --- /dev/null +++ b/tracer/annotation/hex.go @@ -0,0 +1,7 @@ +package annotation + +import "fmt" + +func AnnotateHex(arg Arg, _ int) { + arg.SetAnnotation(fmt.Sprintf("0x%x", arg.Raw()), true) +} diff --git a/tracer/annotation/ioprio.go b/tracer/annotation/ioprio.go new file mode 100644 index 0000000..8038e40 --- /dev/null +++ b/tracer/annotation/ioprio.go @@ -0,0 +1,13 @@ +package annotation + +var ioPrioWhiches = map[int]string{ + 1: "IOPRIO_WHO_PROCESS", + 2: "IOPRIO_WHO_PGRP", + 3: "IOPRIO_WHO_USER", +} + +func AnnotateIoPrioWhich(arg Arg, _ int) { + if which, ok := ioPrioWhiches[int(arg.Raw())]; ok { + arg.SetAnnotation(which, true) + } +} diff --git a/tracer/annotation/ioring.go b/tracer/annotation/ioring.go new file mode 100644 index 0000000..017de61 --- /dev/null +++ b/tracer/annotation/ioring.go @@ -0,0 +1,57 @@ +package annotation + +import "strings" + +var ioringEnterFlags = map[int]string{ + 1: "IORING_ENTER_GETEVENTS", + 2: "IORING_ENTER_SQ_WAKEUP", + 4: "IORING_ENTER_SQ_WAIT", + 8: "IORING_ENTER_EXT_ARG", + 16: "IORING_ENTER_REGISTERED_RING", +} + +func AnnotateIoUringEnterFlags(arg Arg, pid int) { + var joins []string + for flag, name := range ioringEnterFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +var ioringOpCodes = []string{ + "IORING_REGISTER_BUFFERS", + "IORING_UNREGISTER_BUFFERS", + "IORING_REGISTER_FILES", + "IORING_UNREGISTER_FILES", + "IORING_REGISTER_EVENTFD", + "IORING_UNREGISTER_EVENTFD", + "IORING_REGISTER_FILES_UPDATE", + "IORING_REGISTER_EVENTFD_ASYNC", + "IORING_REGISTER_PROBE", + "IORING_REGISTER_PERSONALITY", + "IORING_UNREGISTER_PERSONALITY", + "IORING_REGISTER_RESTRICTIONS", + "IORING_REGISTER_ENABLE_RINGS", + "IORING_REGISTER_FILES2", + "IORING_REGISTER_FILES_UPDATE2", + "IORING_REGISTER_BUFFERS2", + "IORING_REGISTER_BUFFERS_UPDATE", + "IORING_REGISTER_IOWQ_AFF", + "IORING_UNREGISTER_IOWQ_AFF", + "IORING_REGISTER_IOWQ_MAX_WORKERS", + "IORING_REGISTER_RING_FDS", + "IORING_UNREGISTER_RING_FDS", + "IORING_REGISTER_PBUF_RING", + "IORING_UNREGISTER_PBUF_RING", + "IORING_REGISTER_SYNC_CANCEL", + "IORING_REGISTER_FILE_ALLOC_RANGE", +} + +func AnnotateIORingOpCode(arg Arg, _ int) { + opcode := int(arg.Raw()) + if opcode >= 0 && opcode < len(ioringOpCodes) { + arg.SetAnnotation(ioringOpCodes[opcode], true) + } +} diff --git a/tracer/annotation/kcmp.go b/tracer/annotation/kcmp.go new file mode 100644 index 0000000..48d9cd9 --- /dev/null +++ b/tracer/annotation/kcmp.go @@ -0,0 +1,19 @@ +package annotation + +var kcmpTypes = map[int]string{ + 0: "KCMP_FILE", + 1: "KCMP_VM", + 2: "KCMP_FILES", + 3: "KCMP_FS", + 4: "KCMP_SIGHAND", + 5: "KCMP_IO", + 6: "KCMP_SYSVSEM", + 7: "KCMP_TYPES", + 8: "KCMP_EPOLL_TFD", +} + +func AnnotateKcmpType(arg Arg, pid int) { + if str, ok := kcmpTypes[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/kexec.go b/tracer/annotation/kexec.go new file mode 100644 index 0000000..c654077 --- /dev/null +++ b/tracer/annotation/kexec.go @@ -0,0 +1,23 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var kexecLoadFlags = map[int]string{ + unix.KEXEC_FILE_UNLOAD: "KEXEC_FILE_UNLOAD", + unix.KEXEC_FILE_ON_CRASH: "KEXEC_FILE_ON_CRASH", + unix.KEXEC_FILE_NO_INITRAMFS: "KEXEC_FILE_NO_INITRAMFS", +} + +func AnnotateKexecFlags(arg Arg, _ int) { + var joins []string + for flag, name := range kexecLoadFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/key.go b/tracer/annotation/key.go new file mode 100644 index 0000000..925906a --- /dev/null +++ b/tracer/annotation/key.go @@ -0,0 +1,34 @@ +package annotation + +import "golang.org/x/sys/unix" + +var keyctlCmds = map[int]string{ + unix.KEYCTL_GET_KEYRING_ID: "KEYCTL_GET_KEYRING_ID", + unix.KEYCTL_JOIN_SESSION_KEYRING: "KEYCTL_JOIN_SESSION_KEYRING", + unix.KEYCTL_UPDATE: "KEYCTL_UPDATE", + unix.KEYCTL_REVOKE: "KEYCTL_REVOKE", + unix.KEYCTL_DESCRIBE: "KEYCTL_DESCRIBE", + unix.KEYCTL_CLEAR: "KEYCTL_CLEAR", + unix.KEYCTL_LINK: "KEYCTL_LINK", + unix.KEYCTL_UNLINK: "KEYCTL_UNLINK", + unix.KEYCTL_SEARCH: "KEYCTL_SEARCH", + unix.KEYCTL_READ: "KEYCTL_READ", + unix.KEYCTL_CHOWN: "KEYCTL_CHOWN", + unix.KEYCTL_SETPERM: "KEYCTL_SETPERM", + unix.KEYCTL_INSTANTIATE: "KEYCTL_INSTANTIATE", + unix.KEYCTL_NEGATE: "KEYCTL_NEGATE", + unix.KEYCTL_SET_REQKEY_KEYRING: "KEYCTL_SET_REQKEY_KEYRING", + unix.KEYCTL_SET_TIMEOUT: "KEYCTL_SET_TIMEOUT", + unix.KEYCTL_ASSUME_AUTHORITY: "KEYCTL_ASSUME_AUTHORITY", + unix.KEYCTL_GET_SECURITY: "KEYCTL_GET_SECURITY", + unix.KEYCTL_SESSION_TO_PARENT: "KEYCTL_SESSION_TO_PARENT", + unix.KEYCTL_REJECT: "KEYCTL_REJECT", + unix.KEYCTL_INSTANTIATE_IOV: "KEYCTL_INSTANTIATE_IOV", + unix.KEYCTL_INVALIDATE: "KEYCTL_INVALIDATE", +} + +func AnnotateKeyctlCommand(arg Arg, pid int) { + if cmd, ok := keyctlCmds[int(arg.Raw())]; ok { + arg.SetAnnotation(cmd, true) + } +} diff --git a/tracer/annotation/keyring.go b/tracer/annotation/keyring.go new file mode 100644 index 0000000..9cf71f9 --- /dev/null +++ b/tracer/annotation/keyring.go @@ -0,0 +1,18 @@ +package annotation + +import "golang.org/x/sys/unix" + +var keyringIDs = map[int]string{ + unix.KEY_SPEC_THREAD_KEYRING: "KEY_SPEC_THREAD_KEYRING", + unix.KEY_SPEC_PROCESS_KEYRING: "KEY_SPEC_PROCESS_KEYRING", + unix.KEY_SPEC_SESSION_KEYRING: "KEY_SPEC_SESSION_KEYRING", + unix.KEY_SPEC_USER_KEYRING: "KEY_SPEC_USER_KEYRING", + unix.KEY_SPEC_USER_SESSION_KEYRING: "KEY_SPEC_USER_SESSION_KEYRING", + unix.KEY_SPEC_GROUP_KEYRING: "KEY_SPEC_GROUP_KEYRING", +} + +func AnnotateKeyringID(arg Arg, _ int) { + if id, ok := keyringIDs[int(arg.Raw())]; ok { + arg.SetAnnotation(id, true) + } +} diff --git a/tracer/annotation/landlock.go b/tracer/annotation/landlock.go new file mode 100644 index 0000000..1f7a9f8 --- /dev/null +++ b/tracer/annotation/landlock.go @@ -0,0 +1,35 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var landlockFlags = map[int]string{ + unix.LANDLOCK_CREATE_RULESET_VERSION: "LANDLOCK_CREATE_RULESET_VERSION", +} + +func AnnotateLandlockFlags(arg Arg, _ int) { + var joins []string + for flag, str := range landlockFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +var landlockRuleTypes = map[int]string{ + unix.LANDLOCK_RULE_PATH_BENEATH: "LANDLOCK_RULE_PATH_BENEATH", +} + +func AnnotateLandlockRuleType(arg Arg, _ int) { + var joins []string + for flag, str := range landlockRuleTypes { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/madvise.go b/tracer/annotation/madvise.go new file mode 100644 index 0000000..d39d6dd --- /dev/null +++ b/tracer/annotation/madvise.go @@ -0,0 +1,36 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +func AnnotateMAdviseAdvice(arg Arg, _ int) { + flags := map[int]string{ + unix.MADV_NORMAL: "MADV_NORMAL", + unix.MADV_RANDOM: "MADV_RANDOM", + unix.MADV_SEQUENTIAL: "MADV_SEQUENTIAL", + unix.MADV_WILLNEED: "MADV_WILLNEED", + unix.MADV_DONTNEED: "MADV_DONTNEED", + unix.MADV_REMOVE: "MADV_REMOVE", + unix.MADV_DONTFORK: "MADV_DONTFORK", + unix.MADV_DOFORK: "MADV_DOFORK", + unix.MADV_MERGEABLE: "MADV_MERGEABLE", + unix.MADV_UNMERGEABLE: "MADV_UNMERGEABLE", + unix.MADV_HUGEPAGE: "MADV_HUGEPAGE", + unix.MADV_NOHUGEPAGE: "MADV_NOHUGEPAGE", + unix.MADV_DONTDUMP: "MADV_DONTDUMP", + unix.MADV_DODUMP: "MADV_DODUMP", + unix.MADV_HWPOISON: "MADV_HWPOISON", + unix.MADV_COLD: "MADV_COLD", + unix.MADV_PAGEOUT: "MADV_PAGEOUT", + } + var joins []string + for flag, str := range flags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/mbind.go b/tracer/annotation/mbind.go new file mode 100644 index 0000000..c25634d --- /dev/null +++ b/tracer/annotation/mbind.go @@ -0,0 +1,23 @@ +package annotation + +import "C" + +var numaModeFlags = map[int]string{ + 0: "MPOL_DEFAULT", + 1: "MPOL_PREFERRED", + 2: "MPOL_BIND", + 3: "MPOL_INTERLEAVE", + 4: "MPOL_LOCAL", + 5: "MPOL_MAX", + 6: "MPOL_F_STATIC_NODES", + 7: "MPOL_F_RELATIVE_NODES", + 8: "MPOL_MF_STRICT", + 9: "MPOL_MF_MOVE", + 10: "MPOL_MF_MOVE_ALL", +} + +func AnnotateNumaModeFlag(arg Arg, pid int) { + if str, ok := numaModeFlags[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/membarrier.go b/tracer/annotation/membarrier.go new file mode 100644 index 0000000..7904483 --- /dev/null +++ b/tracer/annotation/membarrier.go @@ -0,0 +1,38 @@ +package annotation + +import "strings" + +/* +#include +*/ +import "C" + +var membarrierCmds = map[int]string{ + C.MEMBARRIER_CMD_QUERY: "MEMBARRIER_CMD_QUERY", + C.MEMBARRIER_CMD_GLOBAL: "MEMBARRIER_CMD_GLOBAL", + C.MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED: "MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED", + C.MEMBARRIER_CMD_PRIVATE_EXPEDITED: "MEMBARRIER_CMD_PRIVATE_EXPEDITED", + C.MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED: "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED", + C.MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE: "MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE", + C.MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE: "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE", +} + +var membarrierCmdFlags = map[int]string{ + 1: "MEMBARRIER_CMD_FLAG_CPU", +} + +func AnnotateMembarrierCmd(arg Arg, _ int) { + if name, ok := membarrierCmds[int(arg.Raw())]; ok { + arg.SetAnnotation(name, true) + } +} + +func AnnotateMembarrierCmdFlags(arg Arg, _ int) { + var joins []string + for flag, name := range membarrierCmdFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/memfd.go b/tracer/annotation/memfd.go new file mode 100644 index 0000000..5898919 --- /dev/null +++ b/tracer/annotation/memfd.go @@ -0,0 +1,35 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var memfdFlags = map[int]string{ + unix.MFD_CLOEXEC: "MFD_CLOEXEC", + unix.MFD_ALLOW_SEALING: "MFD_ALLOW_SEALING", + unix.MFD_HUGETLB: "MFD_HUGETLB", + unix.MFD_HUGE_16GB: "MFD_HUGE_16GB", + unix.MFD_HUGE_16MB: "MFD_HUGE_16MB", + unix.MFD_HUGE_1GB: "MFD_HUGE_1GB", + unix.MFD_HUGE_1MB: "MFD_HUGE_1MB", + unix.MFD_HUGE_256MB: "MFD_HUGE_256MB", + unix.MFD_HUGE_2GB: "MFD_HUGE_2GB", + unix.MFD_HUGE_2MB: "MFD_HUGE_2MB", + unix.MFD_HUGE_32MB: "MFD_HUGE_32MB", + unix.MFD_HUGE_512KB: "MFD_HUGE_512KB", + unix.MFD_HUGE_512MB: "MFD_HUGE_512MB", + unix.MFD_HUGE_64KB: "MFD_HUGE_64KB", + unix.MFD_HUGE_8MB: "MFD_HUGE_8MB", +} + +func AnnotateMemfdFlags(arg Arg, pid int) { + var joins []string + for flag, name := range memfdFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/mlock.go b/tracer/annotation/mlock.go new file mode 100644 index 0000000..605ecdd --- /dev/null +++ b/tracer/annotation/mlock.go @@ -0,0 +1,23 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var mlockFlags = map[int]string{ + unix.MCL_CURRENT: "MCL_CURRENT", + unix.MCL_FUTURE: "MCL_FUTURE", + unix.MCL_ONFAULT: "MCL_ONFAULT", +} + +func AnnotateMlockFlags(arg Arg, _ int) { + var joins []string + for flag, str := range mlockFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), true) +} diff --git a/tracer/annotation/mmap.go b/tracer/annotation/mmap.go new file mode 100644 index 0000000..5d9a254 --- /dev/null +++ b/tracer/annotation/mmap.go @@ -0,0 +1,47 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +func AnnotateMMapFlags(arg Arg, _ int) { + var joins []string + + switch arg.Raw() & 0x3 { + case unix.MAP_SHARED: + joins = append(joins, "MAP_SHARED") + case unix.MAP_PRIVATE: + joins = append(joins, "MAP_PRIVATE") + case unix.MAP_SHARED_VALIDATE: + joins = append(joins, "MAP_SHARED_VALIDATE") + } + + mapConsts := map[int]string{ + unix.MAP_32BIT: "MAP_32BIT", + unix.MAP_ANONYMOUS: "MAP_ANONYMOUS", + unix.MAP_DENYWRITE: "MAP_DENYWRITE", + unix.MAP_EXECUTABLE: "MAP_EXECUTABLE", + unix.MAP_FILE: "MAP_FILE", + unix.MAP_FIXED: "MAP_FIXED", + unix.MAP_FIXED_NOREPLACE: "MAP_FIXED_NOREPLACE", + unix.MAP_GROWSDOWN: "MAP_GROWSDOWN", + unix.MAP_HUGETLB: "MAP_HUGETLB", + 21 << unix.MAP_HUGE_SHIFT: "MAP_HUGE_2MB", + 30 << unix.MAP_HUGE_SHIFT: "MAP_HUGE_1GB", + unix.MAP_LOCKED: "MAP_LOCKED", + unix.MAP_NONBLOCK: "MAP_NONBLOCK", + unix.MAP_NORESERVE: "MAP_NORESERVE", + unix.MAP_POPULATE: "MAP_POPULATE", + unix.MAP_STACK: "MAP_STACK", + unix.MAP_SYNC: "MAP_SYNC", + } + + for flag, str := range mapConsts { + if arg.Raw()&uintptr(flag) > 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/module.go b/tracer/annotation/module.go new file mode 100644 index 0000000..b24bb86 --- /dev/null +++ b/tracer/annotation/module.go @@ -0,0 +1,53 @@ +package annotation + +import "C" +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var deleteModuleFlags = map[int]string{ + unix.O_NONBLOCK: "O_NONBLOCK", + unix.O_TRUNC: "O_TRUNC", +} + +func AnnotateDeleteModuleFlags(arg Arg, _ int) { + var joins []string + for flag, name := range deleteModuleFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +var queryModuleWhiches = map[int]string{ + 1: "QM_MODULES", + 2: "QM_DEPS", + 3: "QM_REFS", + 4: "QM_SYMBOLS", + 5: "QM_INFO", + 6: "QM_EXPORTS", +} + +func AnnotateQueryModuleWhich(arg Arg, _ int) { + if name, ok := queryModuleWhiches[int(arg.Raw())]; ok { + arg.SetAnnotation(name, true) + } +} + +var moduleInitFlags = map[int]string{ + unix.MODULE_INIT_IGNORE_MODVERSIONS: "MODULE_INIT_IGNORE_MODVERSIONS", + unix.MODULE_INIT_IGNORE_VERMAGIC: "MODULE_INIT_IGNORE_VERMAGIC", +} + +func AnnotateModuleInitFlags(arg Arg, _ int) { + var joins []string + for flag, name := range moduleInitFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/mount.go b/tracer/annotation/mount.go new file mode 100644 index 0000000..b0cb352 --- /dev/null +++ b/tracer/annotation/mount.go @@ -0,0 +1,53 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var mountFlags = map[int]string{ + unix.MS_BIND: "MS_BIND", + unix.MS_DIRSYNC: "MS_DIRSYNC", + unix.MS_MANDLOCK: "MS_MANDLOCK", + unix.MS_MOVE: "MS_MOVE", + unix.MS_NOATIME: "MS_NOATIME", + unix.MS_NODEV: "MS_NODEV", + unix.MS_NODIRATIME: "MS_NODIRATIME", + unix.MS_NOEXEC: "MS_NOEXEC", + unix.MS_NOSUID: "MS_NOSUID", + unix.MS_RDONLY: "MS_RDONLY", + unix.MS_REC: "MS_REC", + unix.MS_RELATIME: "MS_RELATIME", + unix.MS_REMOUNT: "MS_REMOUNT", + unix.MS_SILENT: "MS_SILENT", + unix.MS_STRICTATIME: "MS_STRICTATIME", + unix.MS_SYNCHRONOUS: "MS_SYNCHRONOUS", +} + +func AnnotateMountFlags(arg Arg, _ int) { + var joins []string + for flag, name := range mountFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +var umountFlags = map[int]string{ + unix.MNT_FORCE: "MNT_FORCE", + unix.MNT_DETACH: "MNT_DETACH", + unix.MNT_EXPIRE: "MNT_EXPIRE", + unix.UMOUNT_NOFOLLOW: "UMOUNT_NOFOLLOW", +} + +func AnnotateUmountFlags(arg Arg, _ int) { + var joins []string + for flag, name := range umountFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/mremap.go b/tracer/annotation/mremap.go new file mode 100644 index 0000000..a2520b5 --- /dev/null +++ b/tracer/annotation/mremap.go @@ -0,0 +1,19 @@ +package annotation + +import "strings" + +// see https://github.com/torvalds/linux/blob/master/include/uapi/linux/mman.h +func AnnotateMRemapFlags(arg Arg, _ int) { + flags := map[int]string{ + 1: "MREMAP_MAYMOVE", + 2: "MREMAP_FIXED", + 4: "MREMAP_DONTUNMAP", + } + var joins []string + for flag, str := range flags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/msg.go b/tracer/annotation/msg.go new file mode 100644 index 0000000..0ab9594 --- /dev/null +++ b/tracer/annotation/msg.go @@ -0,0 +1,43 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var msgFlags = map[int]string{ + unix.MSG_OOB: "MSG_OOB", + unix.MSG_PEEK: "MSG_PEEK", + unix.MSG_DONTROUTE: "MSG_DONTROUTE", + unix.MSG_CTRUNC: "MSG_CTRUNC", + unix.MSG_PROXY: "MSG_PROXY", + unix.MSG_TRUNC: "MSG_TRUNC", + unix.MSG_DONTWAIT: "MSG_DONTWAIT", + unix.MSG_EOR: "MSG_EOR", + unix.MSG_WAITALL: "MSG_WAITALL", + unix.MSG_FIN: "MSG_FIN", + unix.MSG_SYN: "MSG_SYN", + unix.MSG_CONFIRM: "MSG_CONFIRM", + unix.MSG_RST: "MSG_RST", + unix.MSG_ERRQUEUE: "MSG_ERRQUEUE", + unix.MSG_NOSIGNAL: "MSG_NOSIGNAL", + unix.MSG_MORE: "MSG_MORE", + unix.MSG_WAITFORONE: "MSG_WAITFORONE", + unix.MSG_FASTOPEN: "MSG_FASTOPEN", + unix.MSG_CMSG_CLOEXEC: "MSG_CMSG_CLOEXEC", +} + +func AnnotateMsgFlags(arg Arg, _ int) { + var joins []string + for flag, str := range msgFlags { + if arg.Raw()&uintptr(flag) != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +func AnnotateMsgType(arg Arg, pid int) { + // TODO: +} diff --git a/tracer/annotation/msync.go b/tracer/annotation/msync.go new file mode 100644 index 0000000..6371ad8 --- /dev/null +++ b/tracer/annotation/msync.go @@ -0,0 +1,23 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var mSyncFlags = map[int]string{ + unix.MS_ASYNC: "MS_ASYNC", + unix.MS_INVALIDATE: "MS_INVALIDATE", + unix.MS_SYNC: "MS_SYNC", +} + +func AnnotateMSyncFlags(arg Arg, _ int) { + var joins []string + for flag, str := range mSyncFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/null.go b/tracer/annotation/null.go new file mode 100644 index 0000000..8a26423 --- /dev/null +++ b/tracer/annotation/null.go @@ -0,0 +1,7 @@ +package annotation + +func AnnotateNull(arg Arg, _ int) { + if arg.Raw() == 0 { + arg.SetAnnotation("NULL", true) + } +} diff --git a/tracer/annotation/open.go b/tracer/annotation/open.go new file mode 100644 index 0000000..9c30c4f --- /dev/null +++ b/tracer/annotation/open.go @@ -0,0 +1,58 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var openFlags = map[int]string{ + unix.O_APPEND: "O_APPEND", + unix.O_ASYNC: "O_ASYNC", + unix.O_CLOEXEC: "O_CLOEXEC", + unix.O_CREAT: "O_CREAT", + unix.O_DIRECT: "O_DIRECT", + unix.O_DIRECTORY: "O_DIRECTORY", + unix.O_DSYNC: "O_DSYNC", + unix.O_EXCL: "O_EXCL", + unix.O_NOATIME: "O_NOATIME", + unix.O_NOCTTY: "O_NOCTTY", + unix.O_NOFOLLOW: "O_NOFOLLOW", + unix.O_NONBLOCK: "O_NONBLOCK", + unix.O_PATH: "O_PATH", + unix.O_SYNC: "O_SYNC", + unix.O_TMPFILE: "O_TMPFILE", + unix.O_TRUNC: "O_TRUNC", + unix.O_RDONLY: "O_RDONLY", + unix.O_WRONLY: "O_WRONLY", + unix.O_RDWR: "O_RDWR", +} + +func AnnotateOpenFlags(arg Arg, _ int) { + var joins []string + for flag, name := range openFlags { + if (int(arg.Raw())&flag != 0) || (int(arg.Raw()) == flag) { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +var resolveFlags = map[int]string{ + unix.RESOLVE_BENEATH: "RESOLVE_BENEATH", + unix.RESOLVE_IN_ROOT: "RESOLVE_IN_ROOT", + unix.RESOLVE_NO_XDEV: "RESOLVE_NO_XDEV", + unix.RESOLVE_NO_MAGICLINKS: "RESOLVE_NO_MAGICLINKS", + unix.RESOLVE_NO_SYMLINKS: "RESOLVE_NO_SYMLINKS", + // RESOLVE_CACHED is not available... +} + +func AnnotateResolveFlags(arg Arg, _ int) { + var joins []string + for flag, name := range resolveFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/p2.go b/tracer/annotation/p2.go new file mode 100644 index 0000000..ff49070 --- /dev/null +++ b/tracer/annotation/p2.go @@ -0,0 +1,25 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var p2Flags = map[int]string{ + unix.RWF_APPEND: "RWF_APPEND", + unix.RWF_DSYNC: "RWF_DSYNC", + unix.RWF_HIPRI: "RWF_HIPRI", + unix.RWF_NOWAIT: "RWF_NOWAIT", + unix.RWF_SYNC: "RWF_SYNC", +} + +func AnnotatePReadWrite2Flags(arg Arg, _ int) { + var joins []string + for flag, str := range p2Flags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/perf.go b/tracer/annotation/perf.go new file mode 100644 index 0000000..b5cfc64 --- /dev/null +++ b/tracer/annotation/perf.go @@ -0,0 +1,24 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var perfFlags = map[int]string{ + unix.PERF_FLAG_FD_CLOEXEC: "PERF_FLAG_FD_CLOEXEC", + unix.PERF_FLAG_FD_OUTPUT: "PERF_FLAG_FD_OUTPUT", + unix.PERF_FLAG_FD_NO_GROUP: "PERF_FLAG_FD_NO_GROUP", + unix.PERF_FLAG_PID_CGROUP: "PERF_FLAG_PID_CGROUP", +} + +func AnnotatePerfFlags(arg Arg, _ int) { + var joins []string + for flag, str := range perfFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/pkey.go b/tracer/annotation/pkey.go new file mode 100644 index 0000000..71e5689 --- /dev/null +++ b/tracer/annotation/pkey.go @@ -0,0 +1,12 @@ +package annotation + +var pkeyAccessRights = map[int]string{ + 1: "PKEY_DISABLE_ACCESS", + 2: "PKEY_DISABLE_WRITE", +} + +func AnnotatePkeyAccessRights(arg Arg, _ int) { + if name, ok := pkeyAccessRights[int(arg.Raw())]; ok { + arg.SetAnnotation(name, true) + } +} diff --git a/tracer/annotation/prctl.go b/tracer/annotation/prctl.go new file mode 100644 index 0000000..90d9cc9 --- /dev/null +++ b/tracer/annotation/prctl.go @@ -0,0 +1,60 @@ +package annotation + +import "golang.org/x/sys/unix" + +var prctlOptions = map[int]string{ + unix.PR_CAP_AMBIENT: "PR_CAP_AMBIENT", + unix.PR_CAP_AMBIENT_RAISE: "PR_CAP_AMBIENT_RAISE", + unix.PR_CAP_AMBIENT_LOWER: "PR_CAP_AMBIENT_LOWER", + unix.PR_CAP_AMBIENT_IS_SET: "PR_CAP_AMBIENT_IS_SET", + unix.PR_CAP_AMBIENT_CLEAR_ALL: "PR_CAP_AMBIENT_CLEAR_ALL", + unix.PR_CAPBSET_READ: "PR_CAPBSET_READ", + unix.PR_CAPBSET_DROP: "PR_CAPBSET_DROP", + unix.PR_SET_CHILD_SUBREAPER: "PR_SET_CHILD_SUBREAPER", + unix.PR_GET_CHILD_SUBREAPER: "PR_GET_CHILD_SUBREAPER", + unix.PR_SET_ENDIAN: "PR_SET_ENDIAN", + unix.PR_GET_ENDIAN: "PR_GET_ENDIAN", + unix.PR_SET_FPEMU: "PR_SET_FPEMU", + unix.PR_GET_FPEMU: "PR_GET_FPEMU", + unix.PR_SET_FPEXC: "PR_SET_FPEXC", + unix.PR_GET_FPEXC: "PR_GET_FPEXC", + unix.PR_SET_FP_MODE: "PR_SET_FP_MODE", + unix.PR_GET_FP_MODE: "PR_GET_FP_MODE", + unix.PR_MPX_ENABLE_MANAGEMENT: "PR_MPX_ENABLE_MANAGEMENT", + unix.PR_MPX_DISABLE_MANAGEMENT: "PR_MPX_DISABLE_MANAGEMENT", + unix.PR_SET_KEEPCAPS: "PR_SET_KEEPCAPS", + unix.PR_GET_KEEPCAPS: "PR_GET_KEEPCAPS", + unix.PR_MCE_KILL: "PR_MCE_KILL", + unix.PR_MCE_KILL_GET: "PR_MCE_KILL_GET", + unix.PR_SET_MM: "PR_SET_MM", + unix.PR_SET_MM_START_STACK: "PR_SET_MM_START_STACK", + unix.PR_SET_MM_START_BRK: "PR_SET_MM_START_BRK", + unix.PR_SET_MM_EXE_FILE: "PR_SET_MM_EXE_FILE", + unix.PR_SET_MM_MAP: "PR_SET_MM_MAP", + unix.PR_SET_NAME: "PR_SET_NAME", + unix.PR_GET_NAME: "PR_GET_NAME", + unix.PR_SET_NO_NEW_PRIVS: "PR_SET_NO_NEW_PRIVS", + unix.PR_GET_NO_NEW_PRIVS: "PR_GET_NO_NEW_PRIVS", + unix.PR_SET_PTRACER: "PR_SET_PTRACER", + unix.PR_SET_SECCOMP: "PR_SET_SECCOMP", + unix.PR_GET_SECCOMP: "PR_GET_SECCOMP", + unix.PR_SET_SECUREBITS: "PR_SET_SECUREBITS", + unix.PR_GET_SECUREBITS: "PR_GET_SECUREBITS", + unix.PR_SET_THP_DISABLE: "PR_SET_THP_DISABLE", + unix.PR_TASK_PERF_EVENTS_DISABLE: "PR_TASK_PERF_EVENTS_DISABLE", + unix.PR_TASK_PERF_EVENTS_ENABLE: "PR_TASK_PERF_EVENTS_ENABLE", + unix.PR_GET_THP_DISABLE: "PR_GET_THP_DISABLE", + unix.PR_GET_TID_ADDRESS: "PR_GET_TID_ADDRESS", + unix.PR_SET_TIMERSLACK: "PR_SET_TIMERSLACK", + unix.PR_GET_TIMERSLACK: "PR_GET_TIMERSLACK", + unix.PR_SET_TSC: "PR_SET_TSC", + unix.PR_GET_TSC: "PR_GET_TSC", + unix.PR_MCE_KILL_CLEAR: "PR_MCE_KILL_CLEAR", + unix.PR_SET_VMA: "PR_SET_VMA", +} + +func AnnotatePrctlOption(arg Arg, _ int) { + if s, ok := prctlOptions[int(arg.Raw())]; ok { + arg.SetAnnotation(s, true) + } +} diff --git a/tracer/annotation/priority.go b/tracer/annotation/priority.go new file mode 100644 index 0000000..1bdb4a8 --- /dev/null +++ b/tracer/annotation/priority.go @@ -0,0 +1,15 @@ +package annotation + +import "golang.org/x/sys/unix" + +var priorityWhiches = map[int]string{ + unix.PRIO_PROCESS: "PRIO_PROCESS", + unix.PRIO_PGRP: "PRIO_PGRP", + unix.PRIO_USER: "PRIO_USER", +} + +func AnnotatePriorityWhich(arg Arg, _ int) { + if s, ok := priorityWhiches[int(arg.Raw())]; ok { + arg.SetAnnotation(s, true) + } +} diff --git a/tracer/annotation/prot.go b/tracer/annotation/prot.go new file mode 100644 index 0000000..f7b574d --- /dev/null +++ b/tracer/annotation/prot.go @@ -0,0 +1,31 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +func AnnotateProt(arg Arg, _ int) { + if arg.Raw() == unix.PROT_NONE { + arg.SetAnnotation("PROT_NONE", true) + return + } + var joins []string + if arg.Raw()&unix.PROT_READ > 0 { + joins = append(joins, "PROT_READ") + } + if arg.Raw()&unix.PROT_WRITE > 0 { + joins = append(joins, "PROT_WRITE") + } + if arg.Raw()&unix.PROT_EXEC > 0 { + joins = append(joins, "PROT_EXEC") + } + if arg.Raw()&unix.PROT_GROWSUP > 0 { + joins = append(joins, "PROT_GROWSUP") + } + if arg.Raw()&unix.PROT_GROWSDOWN > 0 { + joins = append(joins, "PROT_GROWSDOWN") + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/ptrace.go b/tracer/annotation/ptrace.go new file mode 100644 index 0000000..f7f48b8 --- /dev/null +++ b/tracer/annotation/ptrace.go @@ -0,0 +1,43 @@ +package annotation + +import "golang.org/x/sys/unix" + +var ptraceRequests = map[int]string{ + unix.PTRACE_TRACEME: "PTRACE_TRACEME", + unix.PTRACE_PEEKTEXT: "PTRACE_PEEKTEXT", + unix.PTRACE_PEEKDATA: "PTRACE_PEEKDATA", + unix.PTRACE_PEEKUSR: "PTRACE_PEEKUSR", + unix.PTRACE_POKETEXT: "PTRACE_POKETEXT", + unix.PTRACE_POKEDATA: "PTRACE_POKEDATA", + unix.PTRACE_POKEUSR: "PTRACE_POKEUSR", + unix.PTRACE_CONT: "PTRACE_CONT", + unix.PTRACE_KILL: "PTRACE_KILL", + unix.PTRACE_SINGLESTEP: "PTRACE_SINGLESTEP", + unix.PTRACE_GETREGS: "PTRACE_GETREGS", + unix.PTRACE_SETREGS: "PTRACE_SETREGS", + unix.PTRACE_GETFPREGS: "PTRACE_GETFPREGS", + unix.PTRACE_SETFPREGS: "PTRACE_SETFPREGS", + unix.PTRACE_ATTACH: "PTRACE_ATTACH", + unix.PTRACE_DETACH: "PTRACE_DETACH", + unix.PTRACE_GETFPXREGS: "PTRACE_GETFPXREGS", + unix.PTRACE_SETFPXREGS: "PTRACE_SETFPXREGS", + unix.PTRACE_SYSCALL: "PTRACE_SYSCALL", + unix.PTRACE_SETOPTIONS: "PTRACE_SETOPTIONS", + unix.PTRACE_GETEVENTMSG: "PTRACE_GETEVENTMSG", + unix.PTRACE_GETSIGINFO: "PTRACE_GETSIGINFO", + unix.PTRACE_SETSIGINFO: "PTRACE_SETSIGINFO", + unix.PTRACE_GETREGSET: "PTRACE_GETREGSET", + unix.PTRACE_SETREGSET: "PTRACE_SETREGSET", + unix.PTRACE_SEIZE: "PTRACE_SEIZE", + unix.PTRACE_INTERRUPT: "PTRACE_INTERRUPT", + unix.PTRACE_LISTEN: "PTRACE_LISTEN", + unix.PTRACE_PEEKSIGINFO: "PTRACE_PEEKSIGINFO", + unix.PTRACE_GETSIGMASK: "PTRACE_GETSIGMASK", + unix.PTRACE_SETSIGMASK: "PTRACE_SETSIGMASK", +} + +func AnnotatePtraceRequest(arg Arg, _ int) { + if name, ok := ptraceRequests[int(arg.Raw())]; ok { + arg.SetAnnotation(name, true) + } +} diff --git a/tracer/annotation/quotactl.go b/tracer/annotation/quotactl.go new file mode 100644 index 0000000..6dcac9e --- /dev/null +++ b/tracer/annotation/quotactl.go @@ -0,0 +1,17 @@ +package annotation + +/* +#include +*/ +import "C" + +var quotactlCmds = map[uintptr]string{ + C.Q_QUOTAON: "Q_QUOTAON", + C.Q_QUOTAOFF: "Q_QUOTAOFF", +} + +func AnnotateQuotactlCmd(arg Arg, _ int) { + if cmd, ok := quotactlCmds[arg.Raw()]; ok { + arg.SetAnnotation(cmd, true) + } +} diff --git a/tracer/annotation/random.go b/tracer/annotation/random.go new file mode 100644 index 0000000..74fcbdc --- /dev/null +++ b/tracer/annotation/random.go @@ -0,0 +1,22 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var randomFlags = map[int]string{ + unix.GRND_RANDOM: "GRND_RANDOM", + unix.GRND_NONBLOCK: "GRND_NONBLOCK", +} + +func AnnotateRandomFlags(arg Arg, _ int) { + var joins []string + for flag, name := range randomFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/reboot.go b/tracer/annotation/reboot.go new file mode 100644 index 0000000..587edc0 --- /dev/null +++ b/tracer/annotation/reboot.go @@ -0,0 +1,35 @@ +package annotation + +import "C" + +// from reboot(2) manpages +var rebootMagics = map[uintptr]string{ + 0xfee1dead: "LINUX_REBOOT_MAGIC1", + 672274793: "LINUX_REBOOT_MAGIC2", + 85072278: "LINUX_REBOOT_MAGIC2A", + 369367448: "LINUX_REBOOT_MAGIC2B", + 537993216: "LINUX_REBOOT_MAGIC2C", +} + +func AnnotateRebootMagic(arg Arg, _ int) { + if magic, ok := rebootMagics[arg.Raw()]; ok { + arg.SetAnnotation(magic, true) + } +} + +var rebootCmds = map[uintptr]string{ + 0x00000000: "LINUX_REBOOT_CMD_CAD_OFF", + 0x89ABCDEF: "LINUX_REBOOT_CMD_CAD_ON", + 0xCDEF0123: "LINUX_REBOOT_CMD_HALT", + 0x45584543: "LINUX_REBOOT_CMD_KEXEC", + 0x4321FEDC: "LINUX_REBOOT_CMD_POWER_OFF", + 0x01234567: "LINUX_REBOOT_CMD_RESTART", + 0xA1B2C3D4: "LINUX_REBOOT_CMD_RESTART2", + 0xD000FCE2: "LINUX_REBOOT_CMD_SW_SUSPEND", +} + +func AnnotateRebootCmd(arg Arg, _ int) { + if cmd, ok := rebootCmds[arg.Raw()]; ok { + arg.SetAnnotation(cmd, true) + } +} diff --git a/tracer/annotation/rlimit.go b/tracer/annotation/rlimit.go new file mode 100644 index 0000000..f6bf6ac --- /dev/null +++ b/tracer/annotation/rlimit.go @@ -0,0 +1,30 @@ +package annotation + +import ( + "golang.org/x/sys/unix" +) + +var rlimitFlags = map[int]string{ + unix.RLIMIT_AS: "RLIMIT_AS", + unix.RLIMIT_CORE: "RLIMIT_CORE", + unix.RLIMIT_CPU: "RLIMIT_CPU", + unix.RLIMIT_DATA: "RLIMIT_DATA", + unix.RLIMIT_FSIZE: "RLIMIT_FSIZE", + unix.RLIMIT_LOCKS: "RLIMIT_LOCKS", + unix.RLIMIT_MEMLOCK: "RLIMIT_MEMLOCK", + unix.RLIMIT_MSGQUEUE: "RLIMIT_MSGQUEUE", + unix.RLIMIT_NICE: "RLIMIT_NICE", + unix.RLIMIT_NOFILE: "RLIMIT_NOFILE", + unix.RLIMIT_NPROC: "RLIMIT_NPROC", + unix.RLIMIT_RSS: "RLIMIT_RSS", + unix.RLIMIT_RTPRIO: "RLIMIT_RTPRIO", + unix.RLIMIT_RTTIME: "RLIMIT_RTTIME", + unix.RLIMIT_SIGPENDING: "RLIMIT_SIGPENDING", + unix.RLIMIT_STACK: "RLIMIT_STACK", +} + +func AnnotateRLimitResourceFlags(arg Arg, pid int) { + if str, ok := rlimitFlags[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/rusage.go b/tracer/annotation/rusage.go new file mode 100644 index 0000000..2fb1337 --- /dev/null +++ b/tracer/annotation/rusage.go @@ -0,0 +1,15 @@ +package annotation + +import "golang.org/x/sys/unix" + +var rusageWho = map[int]string{ + unix.RUSAGE_CHILDREN: "RUSAGE_CHILDREN", + unix.RUSAGE_SELF: "RUSAGE_SELF", + unix.RUSAGE_THREAD: "RUSAGE_THREAD", +} + +func AnnotateRUsageWho(arg Arg, _ int) { + if name, ok := rusageWho[int(arg.Raw())]; ok { + arg.SetAnnotation(name, true) + } +} diff --git a/tracer/annotation/sched.go b/tracer/annotation/sched.go new file mode 100644 index 0000000..9786dc2 --- /dev/null +++ b/tracer/annotation/sched.go @@ -0,0 +1,20 @@ +package annotation + +/* +#include +*/ +import "C" + +var schedPolicies = map[int]string{ + C.SCHED_NORMAL: "SCHED_NORMAL", + C.SCHED_FIFO: "SCHED_FIFO", + C.SCHED_RR: "SCHED_RR", + C.SCHED_BATCH: "SCHED_BATCH", + C.SCHED_IDLE: "SCHED_IDLE", +} + +func AnnotateSchedPolicy(arg Arg, pid int) { + if s, ok := schedPolicies[int(arg.Raw())]; ok { + arg.SetAnnotation(s, true) + } +} diff --git a/tracer/annotation/seccomp.go b/tracer/annotation/seccomp.go new file mode 100644 index 0000000..bc8bf9b --- /dev/null +++ b/tracer/annotation/seccomp.go @@ -0,0 +1,18 @@ +package annotation + +/* +#include +*/ +import "C" + +var seccompOps = map[int]string{ + C.SECCOMP_SET_MODE_STRICT: "SECCOMP_SET_MODE_STRICT", + C.SECCOMP_SET_MODE_FILTER: "SECCOMP_SET_MODE_FILTER", + C.SECCOMP_GET_ACTION_AVAIL: "SECCOMP_GET_ACTION_AVAIL", +} + +func AnnotateSeccompOp(arg Arg, pid int) { + if s, ok := seccompOps[int(arg.Raw())]; ok { + arg.SetAnnotation(s, true) + } +} diff --git a/tracer/annotation/sem.go b/tracer/annotation/sem.go new file mode 100644 index 0000000..46e9a01 --- /dev/null +++ b/tracer/annotation/sem.go @@ -0,0 +1,61 @@ +package annotation + +/* +#include +#include +*/ +import "C" + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var semFlags = map[int]string{ + unix.IPC_CREAT: "IPC_CREAT", + unix.IPC_EXCL: "IPC_EXCL", + unix.IPC_NOWAIT: "IPC_NOWAIT", + unix.S_IRUSR: "S_IRUSR", + unix.S_IWUSR: "S_IWUSR", + unix.S_IXUSR: "S_IXUSR", + unix.S_IRGRP: "S_IRGRP", + unix.S_IWGRP: "S_IWGRP", + unix.S_IXGRP: "S_IXGRP", + unix.S_IROTH: "S_IROTH", + unix.S_IWOTH: "S_IWOTH", + unix.S_IXOTH: "S_IXOTH", +} + +func AnnotateSemFlags(arg Arg, pid int) { + var joins []string + for flag, name := range semFlags { + if arg.Raw()&uintptr(flag) != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +var semCmds = map[int]string{ + unix.IPC_STAT: "IPC_STAT", + unix.IPC_SET: "IPC_SET", + unix.IPC_RMID: "IPC_RMID", + C.IPC_INFO: "IPC_INFO", + C.SEM_INFO: "SEM_INFO", + C.SEM_STAT: "SEM_STAT", + C.SEM_STAT_ANY: "SEM_STAT_ANY", + C.GETALL: "GETALL", + C.GETNCNT: "GETNCNT", + C.GETPID: "GETPID", + C.GETVAL: "GETVAL", + C.GETZCNT: "GETZCNT", + C.SETALL: "SETALL", + C.SETVAL: "SETVAL", +} + +func AnnotateSemCmd(arg Arg, _ int) { + if name, ok := semCmds[int(arg.Raw())]; ok { + arg.SetAnnotation(name, true) + } +} diff --git a/tracer/annotation/shm.go b/tracer/annotation/shm.go new file mode 100644 index 0000000..599480d --- /dev/null +++ b/tracer/annotation/shm.go @@ -0,0 +1,56 @@ +package annotation + +/* +#include +*/ +import "C" +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var shmCommands = map[int]string{ + unix.IPC_STAT: "IPC_STAT", + unix.IPC_SET: "IPC_SET", + unix.IPC_RMID: "IPC_RMID", +} + +func AnnotateSHMCTLCommand(arg Arg, _ int) { + str, ok := shmCommands[int(arg.Raw())] + arg.SetAnnotation(str, ok) +} + +var shmAtFlags = map[int]string{ + unix.SHM_RDONLY: "SHM_RDONLY", + C.SHM_REMAP: "SHM_REMAP", +} + +func AnnotateSHMAtFlags(arg Arg, _ int) { + var joins []string + for flag, str := range shmAtFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +var shmGetFlags = map[int]string{ + unix.IPC_CREAT: "IPC_CREAT", + unix.IPC_EXCL: "IPC_EXCL", + C.SHM_HUGETLB: "SHM_HUGETLB", + C.SHM_HUGE_1GB: "SHM_HUGE_1GB", + C.SHM_HUGE_2MB: "SHM_HUGE_2MB", + C.SHM_NORESERVE: "SHM_NO_RESERVE", +} + +func AnnotateSHMGetFlags(arg Arg, _ int) { + var joins []string + for flag, name := range shmGetFlags { + if arg.Raw()&uintptr(flag) != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/shutdown.go b/tracer/annotation/shutdown.go new file mode 100644 index 0000000..2ed1938 --- /dev/null +++ b/tracer/annotation/shutdown.go @@ -0,0 +1,14 @@ +package annotation + +import "golang.org/x/sys/unix" + +var shutdownHow = map[int]string{ + unix.SHUT_RD: "SHUT_RD", + unix.SHUT_WR: "SHUT_WR", + unix.SHUT_RDWR: "SHUT_RDWR", +} + +func AnnotateShutdownHow(arg Arg, _ int) { + str, ok := shutdownHow[int(arg.Raw())] + arg.SetAnnotation(str, ok) +} diff --git a/tracer/annotation/signal.go b/tracer/annotation/signal.go new file mode 100644 index 0000000..bfb570e --- /dev/null +++ b/tracer/annotation/signal.go @@ -0,0 +1,173 @@ +package annotation + +/* +#include +#include + +*/ +import "C" +import ( + "fmt" + "strings" + "syscall" + + "golang.org/x/sys/unix" +) + +func AnnotateSigProcMaskFlags(arg Arg, _ int) { + var joins []string + if arg.Raw()&C.SIG_BLOCK != 0 { + joins = append(joins, "SIG_BLOCK") + } + if arg.Raw()&C.SIG_UNBLOCK != 0 { + joins = append(joins, "SIG_UNBLOCK") + } + if arg.Raw()&C.SIG_SETMASK != 0 { + joins = append(joins, "SIG_SETMASK") + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +var signals = map[int]string{ + C.SIGHUP: "SIGHUP", + C.SIGINT: "SIGINT", + C.SIGQUIT: "SIGQUIT", + C.SIGILL: "SIGILL", + C.SIGTRAP: "SIGTRAP", + C.SIGABRT: "SIGABRT", + C.SIGBUS: "SIGBUS", + C.SIGFPE: "SIGFPE", + C.SIGKILL: "SIGKILL", + C.SIGUSR1: "SIGUSR1", + C.SIGSEGV: "SIGSEGV", + C.SIGUSR2: "SIGUSR2", + C.SIGPIPE: "SIGPIPE", + C.SIGALRM: "SIGALRM", + C.SIGTERM: "SIGTERM", + C.SIGSTKFLT: "SIGSTKFLT", + C.SIGCHLD: "SIGCHLD", + C.SIGCONT: "SIGCONT", + C.SIGSTOP: "SIGSTOP", + C.SIGTSTP: "SIGTSTP", + C.SIGTTIN: "SIGTTIN", + C.SIGTTOU: "SIGTTOU", + C.SIGURG: "SIGURG", + C.SIGXCPU: "SIGXCPU", + C.SIGXFSZ: "SIGXFSZ", + C.SIGVTALRM: "SIGVTALRM", + C.SIGPROF: "SIGPROF", + C.SIGWINCH: "SIGWINCH", + C.SIGIO: "SIGIO", + C.SIGPWR: "SIGPWR", + C.SIGSYS: "SIGSYS", +} + +func AnnotateSignal(arg Arg, _ int) { + arg.SetAnnotation(SignalToString(int(arg.Raw())), true) +} + +func SignalToString(signal int) string { + var extra string + if signal&0x80 != 0 { + extra = "|0x80" + signal &= ^0x80 + } + if name, ok := signals[signal]; ok { + return name + extra + } + return fmt.Sprintf("0x%x%s", signal, extra) +} + +var signalCodes = map[syscall.Signal]map[int]string{ + 0: { + C.SI_USER: "SI_USER", + C.SI_KERNEL: "SI_KERNEL", + C.SI_QUEUE: "SI_QUEUE", + C.SI_TIMER: "SI_TIMER", + C.SI_MESGQ: "SI_MESGQ", + C.SI_ASYNCIO: "SI_ASYNCIO", + C.SI_SIGIO: "SI_SIGIO", + C.SI_TKILL: "SI_TKILL", + }, + syscall.SIGILL: { + C.ILL_ILLOPC: "ILL_ILLOPC", + C.ILL_ILLOPN: "ILL_ILLOPN", + C.ILL_ILLADR: "ILL_ILLADR", + C.ILL_ILLTRP: "ILL_ILLTRP", + C.ILL_PRVOPC: "ILL_PRVOPC", + C.ILL_PRVREG: "ILL_PRVREG", + C.ILL_COPROC: "ILL_COPROC", + C.ILL_BADSTK: "ILL_BADSTK", + }, + syscall.SIGFPE: { + C.FPE_INTDIV: "FPE_INTDIV", + C.FPE_INTOVF: "FPE_INTOVF", + C.FPE_FLTDIV: "FPE_FLTDIV", + C.FPE_FLTOVF: "FPE_FLTOVF", + C.FPE_FLTUND: "FPE_FLTUND", + C.FPE_FLTRES: "FPE_FLTRES", + C.FPE_FLTINV: "FPE_FLTINV", + C.FPE_FLTSUB: "FPE_FLTSUB", + }, + syscall.SIGSEGV: { + C.SEGV_MAPERR: "SEGV_MAPERR", + C.SEGV_ACCERR: "SEGV_ACCERR", + C.SEGV_BNDERR: "SEGV_BNDERR", + C.SEGV_PKUERR: "SEGV_PKUERR", + }, + syscall.SIGBUS: { + C.BUS_ADRALN: "BUS_ADRALN", + C.BUS_ADRERR: "BUS_ADRERR", + C.BUS_OBJERR: "BUS_OBJERR", + C.BUS_MCEERR_AR: "BUS_MCEERR_AR", + C.BUS_MCEERR_AO: "BUS_MCEERR_AO", + }, + syscall.SIGTRAP: { + C.TRAP_BRKPT: "TRAP_BRKPT", + C.TRAP_TRACE: "TRAP_TRACE", + C.TRAP_BRANCH: "TRAP_BRANCH", + C.TRAP_HWBKPT: "TRAP_HWBKPT", + }, + syscall.SIGCHLD: { + C.CLD_EXITED: "CLD_EXITED", + C.CLD_KILLED: "CLD_KILLED", + C.CLD_DUMPED: "CLD_DUMPED", + C.CLD_TRAPPED: "CLD_TRAPPED", + C.CLD_STOPPED: "CLD_STOPPED", + C.CLD_CONTINUED: "CLD_CONTINUED", + }, + syscall.SIGPOLL: { + C.POLL_IN: "POLL_IN", + C.POLL_OUT: "POLL_OUT", + C.POLL_MSG: "POLL_MSG", + C.POLL_ERR: "POLL_ERR", + C.POLL_PRI: "POLL_PRI", + C.POLL_HUP: "POLL_HUP", + }, + syscall.SIGSYS: { + C.SYS_SECCOMP: "SYS_SECCOMP", + }, +} + +func SignalCodeToString(signal syscall.Signal, code int32) string { + if codes, ok := signalCodes[signal]; ok { + if str, ok := codes[int(code)]; ok { + return str + } + } + if str, ok := signalCodes[0][int(code)]; ok { + return str + } + return fmt.Sprintf("%d", code) +} + +var signalFDFlags = map[int]string{ + unix.SFD_NONBLOCK: "SFD_NONBLOCK", + unix.SFD_CLOEXEC: "SFD_CLOEXEC", +} + +func AnnotateSignalFdFlags(arg Arg, _ int) { + if str, ok := signalFDFlags[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/sigstack.go b/tracer/annotation/sigstack.go new file mode 100644 index 0000000..1c69090 --- /dev/null +++ b/tracer/annotation/sigstack.go @@ -0,0 +1,18 @@ +package annotation + +/* +#include +*/ +import "C" + +var sigStackFlags = map[int]string{ + C.SS_AUTODISARM: "SS_AUTODISARM", +} + +func AnnotateSigStackFlags(arg Arg, _ int) { + for flag, name := range sigStackFlags { + if int(arg.Raw())&flag != 0 { + arg.SetAnnotation(name, true) + } + } +} diff --git a/tracer/annotation/socket.go b/tracer/annotation/socket.go new file mode 100644 index 0000000..34c1b74 --- /dev/null +++ b/tracer/annotation/socket.go @@ -0,0 +1,174 @@ +package annotation + +import "golang.org/x/sys/unix" + +var socketDomains = map[int]string{ + unix.AF_UNIX: "AF_UNIX", + unix.AF_INET: "AF_INET", + unix.AF_INET6: "AF_INET6", + unix.AF_AX25: "AF_AX25", + unix.AF_IPX: "AF_IPX", + unix.AF_APPLETALK: "AF_APPLETALK", + unix.AF_X25: "AF_X25", + unix.AF_DECnet: "AF_DECnet", + unix.AF_KEY: "AF_KEY", + unix.AF_NETLINK: "AF_NETLINK", + unix.AF_PACKET: "AF_PACKET", + unix.AF_RDS: "AF_RDS", + unix.AF_PPPOX: "AF_PPPOX", + unix.AF_LLC: "AF_LLC", + unix.AF_IB: "AF_IB", + unix.AF_MPLS: "AF_MPLS", + unix.AF_CAN: "AF_CAN", + unix.AF_TIPC: "AF_TIPC", + unix.AF_BLUETOOTH: "AF_BLUETOOTH", + unix.AF_ALG: "AF_ALG", + unix.AF_VSOCK: "AF_VSOCK", + unix.AF_KCM: "AF_KCM", + unix.AF_XDP: "AF_XDP", +} + +func SocketFamily(family int) string { + return socketDomains[family] +} + +func AnnotateSocketDomain(arg Arg, _ int) { + if str, ok := socketDomains[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} + +var socketTypes = map[int]string{ + unix.SOCK_STREAM: "SOCK_STREAM", + unix.SOCK_DGRAM: "SOCK_DGRAM", + unix.SOCK_SEQPACKET: "SOCK_SEQPACKET", + unix.SOCK_RAW: "SOCK_RAW", + unix.SOCK_RDM: "SOCK_RDM", + unix.SOCK_PACKET: "SOCK_PACKET", + unix.SOCK_NONBLOCK: "SOCK_NONBLOCK", + unix.SOCK_CLOEXEC: "SOCK_CLOEXEC", +} + +func AnnotateSocketType(arg Arg, _ int) { + if str, ok := socketTypes[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} + +var socketProtocols = map[int]string{ + unix.IPPROTO_IP: "IPPROTO_IP", + unix.IPPROTO_IPV6: "IPPROTO_IPV6", + unix.IPPROTO_ICMP: "IPPROTO_ICMP", + unix.IPPROTO_ICMPV6: "IPPROTO_ICMPV6", + unix.IPPROTO_IGMP: "IPPROTO_IGMP", + unix.IPPROTO_IPIP: "IPPROTO_IPIP", + unix.IPPROTO_TCP: "IPPROTO_TCP", + unix.IPPROTO_EGP: "IPPROTO_EGP", + unix.IPPROTO_PUP: "IPPROTO_PUP", + unix.IPPROTO_UDP: "IPPROTO_UDP", + unix.IPPROTO_IDP: "IPPROTO_IDP", + unix.IPPROTO_TP: "IPPROTO_TP", + unix.IPPROTO_DCCP: "IPPROTO_DCCP", + unix.IPPROTO_RSVP: "IPPROTO_RSVP", + unix.IPPROTO_GRE: "IPPROTO_GRE", + unix.IPPROTO_ESP: "IPPROTO_ESP", + unix.IPPROTO_AH: "IPPROTO_AH", + unix.IPPROTO_MTP: "IPPROTO_MTP", + unix.IPPROTO_BEETPH: "IPPROTO_BEETPH", + unix.IPPROTO_ENCAP: "IPPROTO_ENCAP", + unix.IPPROTO_PIM: "IPPROTO_PIM", + unix.IPPROTO_COMP: "IPPROTO_COMP", + unix.IPPROTO_SCTP: "IPPROTO_SCTP", + unix.IPPROTO_UDPLITE: "IPPROTO_UDPLITE", + unix.IPPROTO_MPLS: "IPPROTO_MPLS", + unix.IPPROTO_RAW: "IPPROTO_RAW", +} + +func AnnotateSocketProtocol(arg Arg, _ int) { + if str, ok := socketProtocols[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} + +var socketOptions = map[int]string{ + unix.SO_ACCEPTCONN: "SO_ACCEPTCONN", + unix.SO_ATTACH_BPF: "SO_ATTACH_BPF", + unix.SO_ATTACH_REUSEPORT_CBPF: "SO_ATTACH_REUSEPORT_CBPF", + unix.SO_ATTACH_REUSEPORT_EBPF: "SO_ATTACH_REUSEPORT_EBPF", + unix.SO_BINDTODEVICE: "SO_BINDTODEVICE", + unix.SO_BINDTOIFINDEX: "SO_BINDTOIFINDEX", + unix.SO_BPF_EXTENSIONS: "SO_BPF_EXTENSIONS", + unix.SO_BROADCAST: "SO_BROADCAST", + unix.SO_BSDCOMPAT: "SO_BSDCOMPAT", + unix.SO_BUF_LOCK: "SO_BUF_LOCK", + unix.SO_BUSY_POLL: "SO_BUSY_POLL", + unix.SO_BUSY_POLL_BUDGET: "SO_BUSY_POLL_BUDGET", + unix.SO_CNX_ADVICE: "SO_CNX_ADVICE", + unix.SO_COOKIE: "SO_COOKIE", + unix.SO_DETACH_REUSEPORT_BPF: "SO_DETACH_REUSEPORT_BPF", + unix.SO_DOMAIN: "SO_DOMAIN", + unix.SO_DONTROUTE: "SO_DONTROUTE", + unix.SO_ERROR: "SO_ERROR", + unix.SO_INCOMING_CPU: "SO_INCOMING_CPU", + unix.SO_INCOMING_NAPI_ID: "SO_INCOMING_NAPI_ID", + unix.SO_KEEPALIVE: "SO_KEEPALIVE", + unix.SO_LINGER: "SO_LINGER", + unix.SO_LOCK_FILTER: "SO_LOCK_FILTER", + unix.SO_MARK: "SO_MARK", + unix.SO_MAX_PACING_RATE: "SO_MAX_PACING_RATE", + unix.SO_MEMINFO: "SO_MEMINFO", + unix.SO_NETNS_COOKIE: "SO_NETNS_COOKIE", + unix.SO_NOFCS: "SO_NOFCS", + unix.SO_OOBINLINE: "SO_OOBINLINE", + unix.SO_PASSCRED: "SO_PASSCRED", + unix.SO_PASSSEC: "SO_PASSSEC", + unix.SO_PEEK_OFF: "SO_PEEK_OFF", + unix.SO_PEERCRED: "SO_PEERCRED", + unix.SO_PEERGROUPS: "SO_PEERGROUPS", + unix.SO_PEERSEC: "SO_PEERSEC", + unix.SO_PREFER_BUSY_POLL: "SO_PREFER_BUSY_POLL", + unix.SO_PROTOCOL: "SO_PROTOCOL", + unix.SO_RCVBUF: "SO_RCVBUF", + unix.SO_RCVBUFFORCE: "SO_RCVBUFFORCE", + unix.SO_RCVLOWAT: "SO_RCVLOWAT", + unix.SO_RCVMARK: "SO_RCVMARK", + unix.SO_RCVTIMEO_NEW: "SO_RCVTIMEO_NEW", + unix.SO_RCVTIMEO_OLD: "SO_RCVTIMEO_OLD", + unix.SO_RESERVE_MEM: "SO_RESERVE_MEM", + unix.SO_REUSEADDR: "SO_REUSEADDR", + unix.SO_REUSEPORT: "SO_REUSEPORT", + unix.SO_RXQ_OVFL: "SO_RXQ_OVFL", + unix.SO_SECURITY_AUTHENTICATION: "SO_SECURITY_AUTHENTICATION", + unix.SO_SECURITY_ENCRYPTION_NETWORK: "SO_SECURITY_ENCRYPTION_NETWORK", + unix.SO_SECURITY_ENCRYPTION_TRANSPORT: "SO_SECURITY_ENCRYPTION_TRANSPORT", + unix.SO_SELECT_ERR_QUEUE: "SO_SELECT_ERR_QUEUE", + unix.SO_SNDBUF: "SO_SNDBUF", + unix.SO_SNDBUFFORCE: "SO_SNDBUFFORCE", + unix.SO_SNDLOWAT: "SO_SNDLOWAT", + unix.SO_SNDTIMEO_NEW: "SO_SNDTIMEO_NEW", + unix.SO_SNDTIMEO_OLD: "SO_SNDTIMEO_OLD", + unix.SO_TIMESTAMPING_NEW: "SO_TIMESTAMPING_NEW", + unix.SO_TIMESTAMPING_OLD: "SO_TIMESTAMPING_OLD", + unix.SO_TIMESTAMPNS_NEW: "SO_TIMESTAMPNS_NEW", + unix.SO_TIMESTAMPNS_OLD: "SO_TIMESTAMPNS_OLD", + unix.SO_TIMESTAMP_NEW: "SO_TIMESTAMP_NEW", + unix.SO_TXREHASH: "SO_TXREHASH", + unix.SO_TXTIME: "SO_TXTIME", + unix.SO_TYPE: "SO_TYPE", + unix.SO_WIFI_STATUS: "SO_WIFI_STATUS", + unix.SO_ZEROCOPY: "SO_ZEROCOPY", + unix.SO_PEERNAME: "SO_PEERNAME", + unix.SO_PRIORITY: "SO_PRIORITY", + unix.SO_TIMESTAMP_OLD: "SO_TIMESTAMP_OLD", + unix.SO_DETACH_FILTER: "SO_DETACH_FILTER", + unix.SO_EE_ORIGIN_NONE: "SO_EE_ORIGIN_NONE", + unix.SO_EE_RFC4884_FLAG_INVALID: "SO_EE_RFC4884_FLAG_INVALID", + unix.SO_GET_FILTER: "SO_GET_FILTER", + unix.SO_NO_CHECK: "SO_NO_CHECK", +} + +func AnnotateSocketOption(arg Arg, _ int) { + if str, ok := socketOptions[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/splice.go b/tracer/annotation/splice.go new file mode 100644 index 0000000..385c25f --- /dev/null +++ b/tracer/annotation/splice.go @@ -0,0 +1,24 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var spliceFlags = map[int]string{ + unix.SPLICE_F_MOVE: "SPLICE_F_MOVE", + unix.SPLICE_F_NONBLOCK: "SPLICE_F_NONBLOCK", + unix.SPLICE_F_MORE: "SPLICE_F_MORE", + unix.SPLICE_F_GIFT: "SPLICE_F_GIFT", +} + +func AnnotateSpliceFlags(arg Arg, _ int) { + var joins []string + for flag, name := range spliceFlags { + if (int(arg.Raw())&flag != 0) || (int(arg.Raw()) == flag) { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/statx.go b/tracer/annotation/statx.go new file mode 100644 index 0000000..9f618ff --- /dev/null +++ b/tracer/annotation/statx.go @@ -0,0 +1,35 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var statxFlags = map[int]string{ + unix.STATX_TYPE: "STATX_TYPE", + unix.STATX_MODE: "STATX_MODE", + unix.STATX_NLINK: "STATX_NLINK", + unix.STATX_UID: "STATX_UID", + unix.STATX_GID: "STATX_GID", + unix.STATX_ATIME: "STATX_ATIME", + unix.STATX_MTIME: "STATX_MTIME", + unix.STATX_CTIME: "STATX_CTIME", + unix.STATX_INO: "STATX_INO", + unix.STATX_SIZE: "STATX_SIZE", + unix.STATX_BLOCKS: "STATX_BLOCKS", + unix.STATX_BASIC_STATS: "STATX_BASIC_STATS", + unix.STATX_BTIME: "STATX_BTIME", + unix.STATX_MNT_ID: "STATX_MNT_ID", + unix.STATX_ALL: "STATX_ALL", +} + +func AnnotateStatxMask(arg Arg, _ int) { + var joins []string + for flag, str := range statxFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/syncfilerange.go b/tracer/annotation/syncfilerange.go new file mode 100644 index 0000000..886da16 --- /dev/null +++ b/tracer/annotation/syncfilerange.go @@ -0,0 +1,23 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var syncFileRangeFlags = map[int]string{ + unix.SYNC_FILE_RANGE_WAIT_BEFORE: "SYNC_FILE_RANGE_WAIT_BEFORE", + unix.SYNC_FILE_RANGE_WRITE: "SYNC_FILE_RANGE_WRITE", + unix.SYNC_FILE_RANGE_WAIT_AFTER: "SYNC_FILE_RANGE_WAIT_AFTER", +} + +func AnnotateSyncFileRangeFlags(arg Arg, _ int) { + var joins []string + for flag, name := range syncFileRangeFlags { + if (int(arg.Raw())&flag != 0) || (int(arg.Raw()) == flag) { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/syslog.go b/tracer/annotation/syslog.go new file mode 100644 index 0000000..6e3e12b --- /dev/null +++ b/tracer/annotation/syslog.go @@ -0,0 +1,21 @@ +package annotation + +import "golang.org/x/sys/unix" + +var syslogTypes = map[int]string{ + unix.SYSLOG_ACTION_READ: "SYSLOG_ACTION_READ", + unix.SYSLOG_ACTION_READ_ALL: "SYSLOG_ACTION_READ_ALL", + unix.SYSLOG_ACTION_READ_CLEAR: "SYSLOG_ACTION_READ_CLEAR", + unix.SYSLOG_ACTION_CLEAR: "SYSLOG_ACTION_CLEAR", + unix.SYSLOG_ACTION_CONSOLE_OFF: "SYSLOG_ACTION_CONSOLE_OFF", + unix.SYSLOG_ACTION_CONSOLE_ON: "SYSLOG_ACTION_CONSOLE_ON", + unix.SYSLOG_ACTION_CONSOLE_LEVEL: "SYSLOG_ACTION_CONSOLE_LEVEL", + unix.SYSLOG_ACTION_SIZE_UNREAD: "SYSLOG_ACTION_SIZE_UNREAD", + unix.SYSLOG_ACTION_SIZE_BUFFER: "SYSLOG_ACTION_SIZE_BUFFER", +} + +func AnnotateSyslogType(arg Arg, _ int) { + if name, ok := syslogTypes[int(arg.Raw())]; ok { + arg.SetAnnotation(name, true) + } +} diff --git a/tracer/annotation/time.go b/tracer/annotation/time.go new file mode 100644 index 0000000..87b5372 --- /dev/null +++ b/tracer/annotation/time.go @@ -0,0 +1,18 @@ +package annotation + +import "golang.org/x/sys/unix" + +var clockStates = map[int]string{ + unix.TIME_OK: "TIME_OK", + unix.TIME_INS: "TIME_INS", + unix.TIME_DEL: "TIME_DEL", + unix.TIME_OOP: "TIME_OOP", + unix.TIME_WAIT: "TIME_WAIT", + unix.TIME_ERROR: "TIME_ERROR", +} + +func AnnotateClockState(arg Arg, _ int) { + if s, ok := clockStates[int(arg.Raw())]; ok { + arg.SetAnnotation(s, true) + } +} diff --git a/tracer/annotation/timer.go b/tracer/annotation/timer.go new file mode 100644 index 0000000..2ba9552 --- /dev/null +++ b/tracer/annotation/timer.go @@ -0,0 +1,45 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +func AnnotateWhichTimer(arg Arg, _ int) { + switch arg.Raw() { + case unix.ITIMER_REAL: + arg.SetAnnotation("ITIMER_REAL", true) + case unix.ITIMER_VIRTUAL: + arg.SetAnnotation("ITIMER_VIRTUAL", true) + case unix.ITIMER_PROF: + arg.SetAnnotation("ITIMER_PROF", true) + } +} + +var timerFlags = map[int]string{ + unix.TIMER_ABSTIME: "TIMER_ABSTIME", +} + +func AnnotateTimerFlags(arg Arg, _ int) { + if str, ok := timerFlags[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} + +var timerFdFlags = map[int]string{ + unix.O_CLOEXEC: "TFD_CLOEXEC", + unix.O_NONBLOCK: "TFD_NONBLOCK", + unix.TFD_TIMER_ABSTIME: "TFD_TIMER_ABSTIME", + unix.TFD_TIMER_CANCEL_ON_SET: "TFD_TIMER_CANCEL_ON_SET", +} + +func AnnotateTimerFdFlags(arg Arg, _ int) { + var joins []string + for flag, str := range timerFdFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, str) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotation/wait.go b/tracer/annotation/wait.go new file mode 100644 index 0000000..fb59b64 --- /dev/null +++ b/tracer/annotation/wait.go @@ -0,0 +1,94 @@ +package annotation + +/* +#include +#include +#include + +int fWIFEXITED(int status) { + return WIFEXITED(status); +} +int fWEXITSTATUS(int status) { + return WEXITSTATUS(status); +} +int fWIFSIGNALED(int status) { + return WIFSIGNALED(status); +} +int fWTERMSIG(int status) { + return WTERMSIG(status); +} +int fWIFSTOPPED(int status) { + return WIFSTOPPED(status); +} +int fWSTOPSIG(int status) { + return WSTOPSIG(status); +} +*/ +import "C" + +import ( + "fmt" + "strings" + "syscall" + + "golang.org/x/sys/unix" +) + +var waitOptions = map[int]string{ + unix.WNOHANG: "WNOHANG", + unix.WUNTRACED: "WUNTRACED", + unix.WCONTINUED: "WCONTINUED", + unix.WEXITED: "WEXITED", + unix.WNOWAIT: "WNOWAIT", + unix.WALL: "__WALL", + unix.WNOTHREAD: "__WNOTHREAD", + unix.WCLONE: "__WCLONE", +} + +func AnnotateWaitOptions(arg Arg, _ int) { + var joins []string + for flag, name := range waitOptions { + if arg.Raw()&uintptr(flag) != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} + +func AnnotateWaitStatus(arg Arg, _ int) { + status := syscall.WaitStatus(arg.Raw()) + + var joins []string + + if C.fWIFEXITED(C.int(status)) != 0 { + joins = append(joins, "WIFEXITED(s)") + result := C.fWEXITSTATUS(C.int(status)) + joins = append(joins, fmt.Sprintf("WEXITSTATUS(s) == %d", result)) + } + + if C.fWIFSIGNALED(C.int(status)) != 0 { + joins = append(joins, "WIFSIGNALED(s)") + result := int(C.fWTERMSIG(C.int(status))) + joins = append(joins, fmt.Sprintf("WTERMSIG(s) == %s", SignalToString(result))) + } + + if C.fWIFSTOPPED(C.int(status)) != 0 { + joins = append(joins, "WIFSTOPPED(s)") + result := int(C.fWSTOPSIG(C.int(status))) + joins = append(joins, fmt.Sprintf("WSTOPSIG(s) == %s", SignalToString(result))) + } + + arg.SetAnnotation(strings.Join(joins, " && "), len(joins) > 0) +} + +var waitIDs = map[int]string{ + unix.P_PID: "P_PID", + unix.P_PGID: "P_PGID", + unix.P_ALL: "P_ALL", +} + +func AnnotateIDType(arg Arg, _ int) { + if str, ok := waitIDs[int(arg.Raw())]; ok { + arg.SetAnnotation(str, true) + } +} diff --git a/tracer/annotation/whence.go b/tracer/annotation/whence.go new file mode 100644 index 0000000..ba2f08d --- /dev/null +++ b/tracer/annotation/whence.go @@ -0,0 +1,18 @@ +package annotation + +import "golang.org/x/sys/unix" + +func AnnotateWhence(arg Arg, _ int) { + switch int(arg.Raw()) { + case unix.SEEK_SET: + arg.SetAnnotation("SEEK_SET", true) + case unix.SEEK_CUR: + arg.SetAnnotation("SEEK_CUR", true) + case unix.SEEK_END: + arg.SetAnnotation("SEEK_END", true) + case unix.SEEK_DATA: + arg.SetAnnotation("SEEK_DATA", true) + case unix.SEEK_HOLE: + arg.SetAnnotation("SEEK_HOLE", true) + } +} diff --git a/tracer/annotation/x.go b/tracer/annotation/x.go new file mode 100644 index 0000000..4803b28 --- /dev/null +++ b/tracer/annotation/x.go @@ -0,0 +1,22 @@ +package annotation + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +var xFlags = map[int]string{ + unix.XATTR_CREATE: "XATTR_CREATE", + unix.XATTR_REPLACE: "XATTR_REPLACE", +} + +func AnnotateXFlags(arg Arg, _ int) { + var joins []string + for flag, name := range xFlags { + if int(arg.Raw())&flag != 0 { + joins = append(joins, name) + } + } + arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) +} diff --git a/tracer/annotations.go b/tracer/annotations.go deleted file mode 100644 index a3aa53a..0000000 --- a/tracer/annotations.go +++ /dev/null @@ -1,132 +0,0 @@ -package tracer - -import ( - "fmt" - "os" - "strings" - "syscall" - - "golang.org/x/sys/unix" -) - -func annotateFd(arg *Arg, pid int) { - switch int(arg.Raw()) { - case syscall.Stdin: - arg.annotation = "stdin" - case syscall.Stdout: - arg.annotation = "stdout" - case syscall.Stderr: - arg.annotation = "stderr" - default: - if path, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/%d", pid, arg.Raw())); err == nil { - arg.annotation = path - } else if int32(arg.Raw()) == unix.AT_FDCWD { - arg.annotation = "AT_FDCWD" - arg.replace = true - } - } -} - -func annotateAccMode(arg *Arg, _ int) { - var joins []string - if arg.Raw() == unix.F_OK { - joins = append(joins, "F_OK") - } else { - if arg.Raw()&unix.R_OK > 0 { - joins = append(joins, "R_OK") - } - if arg.Raw()&unix.W_OK > 0 { - joins = append(joins, "W_OK") - } - if arg.Raw()&unix.X_OK > 0 { - joins = append(joins, "X_OK") - } - } - - arg.annotation = strings.Join(joins, "|") - arg.replace = arg.annotation != "" -} - -func annotateNull(arg *Arg, _ int) { - if arg.Raw() == 0 { - arg.annotation = "NULL" - arg.replace = true - } -} - -func annotateOpenFlags(arg *Arg, pid int) { - - mapFlags := map[int]string{ - unix.O_APPEND: "O_APPEND", - unix.O_ASYNC: "O_ASYNC", - unix.O_CLOEXEC: "O_CLOEXEC", - unix.O_CREAT: "O_CREAT", - unix.O_DIRECT: "O_DIRECT", - unix.O_DIRECTORY: "O_DIRECTORY", - unix.O_DSYNC: "O_DSYNC", - unix.O_EXCL: "O_EXCL", - unix.O_NOATIME: "O_NOATIME", - unix.O_NOCTTY: "O_NOCTTY", - unix.O_NOFOLLOW: "O_NOFOLLOW", - unix.O_NONBLOCK: "O_NONBLOCK", - unix.O_PATH: "O_PATH", - unix.O_SYNC: "O_SYNC", - unix.O_TMPFILE: "O_TMPFILE", - unix.O_TRUNC: "O_TRUNC", - unix.O_RDONLY: "O_RDONLY", - } - - var joins []string - - for flag, name := range mapFlags { - if (int(arg.Raw())&flag != 0) || (int(arg.Raw()) == flag) { - joins = append(joins, name) - } - } - - arg.annotation = strings.Join(joins, "|") - arg.replace = arg.annotation != "" -} - -func annotateWhence(arg *Arg, pid int) { - switch int(arg.Raw()) { - case unix.SEEK_SET: - arg.annotation = "SEEK_SET" - case unix.SEEK_CUR: - arg.annotation = "SEEK_CUR" - case unix.SEEK_END: - arg.annotation = "SEEK_END" - case unix.SEEK_DATA: - arg.annotation = "SEEK_DATA" - case unix.SEEK_HOLE: - arg.annotation = "SEEK_HOLE" - } - arg.replace = arg.annotation != "" -} - -func annotateProt(arg *Arg, pid int) { - if arg.Raw() == unix.PROT_NONE { - arg.annotation = "PROT_NONE" - arg.replace = true - return - } - var joins []string - if arg.Raw()&unix.PROT_READ > 0 { - joins = append(joins, "PROT_READ") - } - if arg.Raw()&unix.PROT_WRITE > 0 { - joins = append(joins, "PROT_WRITE") - } - if arg.Raw()&unix.PROT_EXEC > 0 { - joins = append(joins, "PROT_EXEC") - } - if arg.Raw()&unix.PROT_GROWSUP > 0 { - joins = append(joins, "PROT_GROWSUP") - } - if arg.Raw()&unix.PROT_GROWSDOWN > 0 { - joins = append(joins, "PROT_GROWSDOWN") - } - - arg.annotation = strings.Join(joins, "|") - arg.replace = arg.annotation != "" -} diff --git a/tracer/args.go b/tracer/args.go index c2a6aae..54ae5c5 100644 --- a/tracer/args.go +++ b/tracer/args.go @@ -1,23 +1,30 @@ package tracer +import ( + "unsafe" + + "github.com/liamg/grace/tracer/annotation" +) + type ArgMetadata struct { Name string Type ArgType - Annotator func(arg *Arg, pid int) - CountFrom CountLocation + Annotator func(arg annotation.Arg, pid int) + LenSource LenSource Optional bool Destination bool FixedCount int } -type CountLocation uint8 +type LenSource uint8 const ( - CountLocationNone CountLocation = iota - CountLocationNext - CountLocationResult - CountLocationNullTerminator - CountLocationFixed + LenSourceNone LenSource = iota + LenSourcePrev + LenSourceNext + LenSourceNextPointer + LenSourceReturnValue + LenSourceFixed ) type ReturnMetadata ArgMetadata @@ -92,7 +99,12 @@ func (s Arg) Array() []Arg { return s.array } -func processArgument(raw uintptr, next uintptr, ret uintptr, metadata ArgMetadata, pid int, exit bool) (*Arg, error) { +func (s *Arg) SetAnnotation(annotation string, replace bool) { + s.annotation = annotation + s.replace = replace +} + +func processArgument(raw uintptr, next, prev uintptr, ret uintptr, metadata ArgMetadata, pid int, exit bool) (*Arg, error) { arg := &Arg{ name: metadata.Name, t: metadata.Type, @@ -107,8 +119,18 @@ func processArgument(raw uintptr, next uintptr, ret uintptr, metadata ArgMetadat return arg, nil } + // resolve next to int from next pointer + if metadata.LenSource == LenSourceNextPointer && (exit || !metadata.Destination) && next > 0 { + var realNext uint32 + buf, err := readSize(pid, next, unsafe.Sizeof(realNext)) + if err != nil { + return nil, err + } + next = uintptr(decodeInt(buf)) + } + // process the argument data into something meaningful - if err := handleType(arg, metadata, raw, next, ret, pid); err != nil { + if err := handleType(arg, metadata, raw, next, prev, ret, pid); err != nil { return nil, err } diff --git a/tracer/decode.go b/tracer/decode.go index 5e5b840..b1a582a 100644 --- a/tracer/decode.go +++ b/tracer/decode.go @@ -22,6 +22,10 @@ func decodeStruct(memory []byte, target interface{}) error { case reflect.Struct: for i := 0; i < sValue.Type().NumField(); i++ { size := sValue.Type().Field(i).Type.Size() + if sValue.Type().Field(i).Name == "_" { + index += size + continue + } raw := memory[index : index+size] if err := decodeAnonymous(sValue.Field(i), raw); err != nil { return err @@ -38,9 +42,14 @@ func decodeStruct(memory []byte, target interface{}) error { func decodeAnonymous(target reflect.Value, raw []byte) error { if target.Kind() == reflect.Ptr { + target.Set(reflect.New(target.Type().Elem())) target = target.Elem() } + if !target.CanSet() { + return fmt.Errorf("cannot set %s", target.Type()) + } + switch target.Kind() { case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint, reflect.Uintptr: target.SetUint(decodeUint(raw)) diff --git a/tracer/decode_uint_test.go b/tracer/decode_uint_test.go new file mode 100644 index 0000000..2b589e8 --- /dev/null +++ b/tracer/decode_uint_test.go @@ -0,0 +1,72 @@ +package tracer + +import "testing" + +func Test_DecodeUint(t *testing.T) { + + tests := []struct { + name string + data []byte + want uint64 + }{ + { + name: "zero length", + data: []byte{}, + want: 0, + }, + { + name: "filled byte", + data: []byte{ + 0xff, + }, + want: 0xff, + }, + { + name: "byte order", + data: []byte{ + 0x11, + 0xff, + 0x00, + }, + want: 0xff11, + }, + { + name: "byte max value", + data: []byte{ + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + }, + want: 0xffffffffffffffff, + }, + { + name: "overflow ignored", + data: []byte{ + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0x1, + }, + want: 0xffffffffffffffff, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := decodeUint(test.data); got != test.want { + t.Errorf("decodeUint() = %v, want %v", got, test.want) + } + }) + } + +} diff --git a/tracer/iovec.go b/tracer/iovec.go deleted file mode 100644 index c8a8a23..0000000 --- a/tracer/iovec.go +++ /dev/null @@ -1,36 +0,0 @@ -package tracer - -type iovec struct { - Base uintptr /* Starting address */ - Len uint /* Number of bytes to transfer */ -} - -func convertIovecs(vecs []iovec) []Arg { - var output []Arg - for _, fd := range vecs { - output = append(output, convertIovec(fd)) - } - return output -} - -func convertIovec(vec iovec) Arg { - return Arg{ - t: ArgTypeObject, - obj: &Object{ - Name: "iovec", - Properties: []Arg{ - { - name: "base", - t: ArgTypeAddress, - raw: vec.Base, - }, - { - name: "len", - t: ArgTypeUnsignedInt, - raw: uintptr(vec.Len), - }, - }, - }, - known: true, - } -} diff --git a/tracer/netw/netw.go b/tracer/netw/netw.go new file mode 100644 index 0000000..0c614a8 --- /dev/null +++ b/tracer/netw/netw.go @@ -0,0 +1,17 @@ +package netw + +func ListConnections() ([]Connection, error) { + tcpConnections, err := ListTCPConnections() + if err != nil { + return nil, err + } + udpConnections, err := ListUDPConnections() + if err != nil { + return nil, err + } + icmpConnections, err := ListICMPConnections() + if err != nil { + return nil, err + } + return append(append(tcpConnections, udpConnections...), icmpConnections...), nil +} diff --git a/tracer/netw/proc.go b/tracer/netw/proc.go new file mode 100644 index 0000000..dee7ba5 --- /dev/null +++ b/tracer/netw/proc.go @@ -0,0 +1,164 @@ +package netw + +import ( + "fmt" + "net" + "os" + "strconv" + "strings" +) + +type Connection struct { + Protocol string + LocalAddress net.IP + LocalPort int + RemoteAddress net.IP + RemotePort int + INode int + State int // TCP_ESTABLISHED etc. see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp_states.h +} + +const netPath = "/proc/net/" + +func ListTCPConnections() ([]Connection, error) { + return parseBaseProtocol("tcp") +} + +func ListUDPConnections() ([]Connection, error) { + return parseBaseProtocol("udp") +} + +func ListICMPConnections() ([]Connection, error) { + return parseBaseProtocol("icmp") +} + +func parseBaseProtocol(protocol string) ([]Connection, error) { + v4, err := parseFile(protocol) + if err != nil { + return nil, err + } + + v6, err := parseFile(protocol + "6") + if err != nil { + return nil, err + } + + return append(v4, v6...), nil +} + +func parseFile(protocol string) ([]Connection, error) { + + file := netPath + protocol + data, err := os.ReadFile(file) + if err != nil { + return nil, err + } + var connections []Connection + lines := strings.Split(string(data), "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if line == "" { + continue + } + if strings.HasPrefix(line, "sl") { // skip headings + continue + } + connection, err := parseLine(line) + if err != nil { + return nil, err + } + connection.Protocol = protocol + connections = append(connections, *connection) + } + return connections, nil +} + +func parseLine(line string) (*Connection, error) { + + line = strings.TrimSpace(line) + fields := strings.Fields(line) + + if len(fields) < 10 { + return nil, fmt.Errorf("invalid tcp connection: %s", line) + } + + inode, err := strconv.Atoi(fields[9]) + if err != nil { + return nil, fmt.Errorf("invalid inode '%s': %w", fields[9], err) + } + + localip, localport, err := parseIPAndPortFromHex(fields[1]) + if err != nil { + return nil, fmt.Errorf("invalid local ip '%s': %w", fields[1], err) + } + + remoteip, remoteport, err := parseIPAndPortFromHex(fields[2]) + if err != nil { + return nil, fmt.Errorf("invalid remote ip '%s': %w", fields[2], err) + } + + return &Connection{ + INode: inode, + LocalAddress: localip, + LocalPort: localport, + RemoteAddress: remoteip, + RemotePort: remoteport, + State: int(hexToByte(fields[3])), + }, nil +} + +func parseIPAndPortFromHex(hex string) (net.IP, int, error) { + + rawip, rawport, found := strings.Cut(hex, ":") + if !found { + return nil, 0, fmt.Errorf("invalid hex '%s'", hex) + } + + var ip net.IP + + switch len(rawip) { + case 8: + ip = net.IPv4( + hexToByte(rawip[6:8]), + hexToByte(rawip[4:6]), + hexToByte(rawip[2:4]), + hexToByte(rawip[0:2]), + ) + case 32: + ip = []byte{ + hexToByte(rawip[30:32]), + hexToByte(rawip[28:30]), + hexToByte(rawip[26:28]), + hexToByte(rawip[24:26]), + hexToByte(rawip[22:24]), + hexToByte(rawip[20:22]), + hexToByte(rawip[18:20]), + hexToByte(rawip[16:18]), + hexToByte(rawip[14:16]), + hexToByte(rawip[12:14]), + hexToByte(rawip[10:12]), + hexToByte(rawip[8:10]), + hexToByte(rawip[6:8]), + hexToByte(rawip[4:6]), + hexToByte(rawip[2:4]), + hexToByte(rawip[0:2]), + } + default: + return nil, 0, fmt.Errorf("invalid ipv4 hex '%s'", hex) + } + + port, err := strconv.ParseInt(rawport, 16, 32) + if err != nil { + return nil, 0, fmt.Errorf("invalid port '%s': %w", rawport, err) + } + + return ip, int(port), nil +} + +func hexToByte(hex string) byte { + b, err := strconv.ParseUint(hex, 16, 8) + if err != nil { + return 0 + } + return byte(b) +} diff --git a/tracer/poll.go b/tracer/poll.go deleted file mode 100644 index b325707..0000000 --- a/tracer/poll.go +++ /dev/null @@ -1,42 +0,0 @@ -package tracer - -type pollfd struct { - Fd int /* file descriptor */ - Events uint16 /* events requested for polling */ - REvents uint32 /* events that occurred during polling */ -} - -func convertPollFds(fds []pollfd) []Arg { - var output []Arg - for _, fd := range fds { - output = append(output, convertPollFd(fd)) - } - return output -} - -func convertPollFd(fd pollfd) Arg { - return Arg{ - t: ArgTypeObject, - obj: &Object{ - Name: "pollfd", - Properties: []Arg{ - { - name: "fd", - t: ArgTypeInt, - raw: uintptr(fd.Fd), - }, - { - name: "events", - t: ArgTypeInt, - raw: uintptr(fd.Events), - }, - { - name: "revents", - t: ArgTypeInt, - raw: uintptr(fd.REvents), - }, - }, - }, - known: true, - } -} diff --git a/tracer/signal.go b/tracer/signal.go new file mode 100644 index 0000000..01c568b --- /dev/null +++ b/tracer/signal.go @@ -0,0 +1,27 @@ +package tracer + +import ( + "fmt" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +type SigInfo struct { + Signo int32 + Errno int32 + Code int32 + TrapNo int32 + Pid int32 + Uid int32 +} + +func getSignalInfo(pid int) (*SigInfo, error) { + var info SigInfo + _, _, e1 := syscall.Syscall6(syscall.SYS_PTRACE, uintptr(unix.PTRACE_GETSIGINFO), uintptr(pid), 0, uintptr(unsafe.Pointer(&info)), 0, 0) + if e1 != 0 { + return nil, fmt.Errorf("ptrace get signal info failed: %v", e1) + } + return &info, nil +} diff --git a/tracer/sys.go b/tracer/sys.go index 7ee360a..fa83191 100644 --- a/tracer/sys.go +++ b/tracer/sys.go @@ -1,35 +1,38 @@ package tracer -import "fmt" +import ( + "fmt" + "os" + "strings" +) type Syscall struct { - pid int - number int - rawArgs [6]uintptr - args []Arg - rawRet uintptr - ret Arg - unknown bool + pid int + number int + rawArgs [6]uintptr + args []Arg + rawRet uintptr + ret Arg + unknown bool + paths []string + complete bool } type SyscallMetadata struct { Name string Args []ArgMetadata ReturnValue ReturnMetadata -} - -func LookupSyscall(number int) (*SyscallMetadata, error) { - meta, ok := sysMap[number] - if !ok { - return nil, fmt.Errorf("unknown syscall %d", number) - } - return &meta, nil + Modifier func(call *Syscall) } func (s *Syscall) Number() int { return s.number } +func (s *Syscall) Paths() []string { + return s.paths +} + func (s *Syscall) Name() string { meta, ok := sysMap[s.number] if !ok { @@ -50,16 +53,23 @@ func (s *Syscall) Unknown() bool { return s.unknown } +func (s *Syscall) Complete() bool { + return s.complete +} + func (s *Syscall) populate(exit bool) error { meta, ok := sysMap[s.number] if !ok { s.unknown = true } - ret, err := processArgument(s.rawRet, 0, 0, ArgMetadata(meta.ReturnValue), s.pid, exit) - if err != nil { - return fmt.Errorf("failed to set return value of syscall %s (%d): %w", meta.Name, s.number, err) + + if exit { + ret, err := processArgument(s.rawRet, 0, 0, 0, ArgMetadata(meta.ReturnValue), s.pid, exit) + if err != nil { + return fmt.Errorf("failed to set return value of syscall %s (%d): %w", meta.Name, s.number, err) + } + s.ret = *ret } - s.ret = *ret for i, argMeta := range meta.Args { if exit && !argMeta.Destination && i < len(s.args) { continue @@ -69,15 +79,39 @@ func (s *Syscall) populate(exit bool) error { if i < len(meta.Args)-1 { next = s.rawArgs[i+1] } - arg, err := processArgument(s.rawArgs[i], next, s.rawRet, argMeta, s.pid, exit) + var prev uintptr + if i > 0 { + prev = s.rawArgs[i-1] + } + + arg, err := processArgument(s.rawArgs[i], next, prev, s.rawRet, argMeta, s.pid, exit) if err != nil { return fmt.Errorf("failed to set argument %d (%s) of syscall %s (%d): %w", i, argMeta.Name, meta.Name, s.number, err) } + if !arg.known { + break + } if i >= len(s.args) { s.args = append(s.args, *arg) } else { s.args[i] = *arg } + + // best attempt to set path information + if argMeta.Type == argTypeString && (strings.Contains(argMeta.Name, "path") || strings.Contains(argMeta.Name, "file")) { + s.paths = append(s.paths, string(arg.Data())) + } else if argMeta.Type == ArgTypeInt && strings.Contains(argMeta.Name, "fd") { + if path, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/%d", s.pid, arg.Raw())); err == nil { + s.paths = append(s.paths, path) + } + } + } + s.complete = len(s.args) == len(meta.Args) + for _, arg := range s.args { + if !arg.known { + s.complete = false + break + } } // strip off trailing optional args if they have no value @@ -92,5 +126,9 @@ func (s *Syscall) populate(exit bool) error { s.args = s.args[:lastIndex+1] } + if exit && meta.Modifier != nil { + meta.Modifier(s) + } + return nil } diff --git a/tracer/sys_amd64.go b/tracer/sys_amd64.go index 2e08cac..5164d5b 100644 --- a/tracer/sys_amd64.go +++ b/tracer/sys_amd64.go @@ -3,9 +3,11 @@ package tracer import ( - "strings" + "bytes" "syscall" + "github.com/liamg/grace/tracer/annotation" + "golang.org/x/sys/unix" ) @@ -33,18 +35,18 @@ var ( unix.SYS_READ: { Name: "read", ReturnValue: ReturnMetadata{ - Type: ArgTypeInt, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "buf", Type: ArgTypeData, - CountFrom: CountLocationResult, + LenSource: LenSourceReturnValue, Destination: true, }, { @@ -56,18 +58,18 @@ var ( unix.SYS_WRITE: { Name: "write", ReturnValue: ReturnMetadata{ - Type: ArgTypeInt, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "buf", Type: ArgTypeData, - CountFrom: CountLocationNext, + LenSource: LenSourceNext, }, { Name: "count", @@ -78,23 +80,22 @@ var ( unix.SYS_OPEN: { Name: "open", ReturnValue: ReturnMetadata{ - Type: ArgTypeInt, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { - Name: "filename", - Type: ArgTypeData, - CountFrom: CountLocationNullTerminator, + Name: "filename", + Type: argTypeString, }, { Name: "flags", Type: ArgTypeInt, - Annotator: annotateOpenFlags, + Annotator: annotation.AnnotateOpenFlags, }, { Name: "mode", Type: ArgTypeInt, - Annotator: annotateAccMode, + Annotator: annotation.AnnotateAccMode, }, }, }, @@ -107,7 +108,7 @@ var ( { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, }, }, @@ -118,13 +119,12 @@ var ( }, Args: []ArgMetadata{ { - Name: "filename", - Type: ArgTypeData, - CountFrom: CountLocationNullTerminator, + Name: "filename", + Type: argTypeString, }, { Name: "stat", - Type: ArgTypeStat, + Type: argTypeStat, Destination: true, }, }, @@ -138,11 +138,11 @@ var ( { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "stat", - Type: ArgTypeStat, + Type: argTypeStat, Destination: true, }, }, @@ -154,13 +154,12 @@ var ( }, Args: []ArgMetadata{ { - Name: "filename", - Type: ArgTypeData, - CountFrom: CountLocationNullTerminator, + Name: "filename", + Type: argTypeString, }, { Name: "stat", - Type: ArgTypeStat, + Type: argTypeStat, Destination: true, }, }, @@ -168,12 +167,12 @@ var ( unix.SYS_POLL: { Name: "poll", ReturnValue: ReturnMetadata{ - Type: ArgTypeErrorCode, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { Name: "ufds", - Type: ArgTypePollFdArray, + Type: argTypePollFdArray, Destination: true, }, { @@ -195,7 +194,7 @@ var ( { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "offset", @@ -204,7 +203,7 @@ var ( { Name: "whence", Type: ArgTypeUnsignedInt, - Annotator: annotateWhence, + Annotator: annotation.AnnotateWhence, }, }, }, @@ -217,7 +216,7 @@ var ( { Name: "addr", Type: ArgTypeAddress, - Annotator: annotateNull, + Annotator: annotation.AnnotateNull, }, { Name: "len", @@ -226,56 +225,17 @@ var ( { Name: "prot", Type: ArgTypeUnsignedLong, - Annotator: annotateProt, + Annotator: annotation.AnnotateProt, }, { - Name: "flags", - Type: ArgTypeUnsignedLong, - Annotator: func(arg *Arg, _ int) { - var joins []string - - switch arg.Raw() & 0x3 { - case unix.MAP_SHARED: - joins = append(joins, "MAP_SHARED") - case unix.MAP_PRIVATE: - joins = append(joins, "MAP_PRIVATE") - case unix.MAP_SHARED_VALIDATE: - joins = append(joins, "MAP_SHARED_VALIDATE") - } - - mapConsts := map[int]string{ - unix.MAP_32BIT: "MAP_32BIT", - unix.MAP_ANONYMOUS: "MAP_ANONYMOUS", - unix.MAP_DENYWRITE: "MAP_DENYWRITE", - unix.MAP_EXECUTABLE: "MAP_EXECUTABLE", - unix.MAP_FILE: "MAP_FILE", - unix.MAP_FIXED: "MAP_FIXED", - unix.MAP_FIXED_NOREPLACE: "MAP_FIXED_NOREPLACE", - unix.MAP_GROWSDOWN: "MAP_GROWSDOWN", - unix.MAP_HUGETLB: "MAP_HUGETLB", - 21 << unix.MAP_HUGE_SHIFT: "MAP_HUGE_2MB", - 30 << unix.MAP_HUGE_SHIFT: "MAP_HUGE_1GB", - unix.MAP_LOCKED: "MAP_LOCKED", - unix.MAP_NONBLOCK: "MAP_NONBLOCK", - unix.MAP_NORESERVE: "MAP_NORESERVE", - unix.MAP_POPULATE: "MAP_POPULATE", - unix.MAP_STACK: "MAP_STACK", - unix.MAP_SYNC: "MAP_SYNC", - } - - for flag, str := range mapConsts { - if arg.Raw()&uintptr(flag) > 0 { - joins = append(joins, str) - } - } - arg.annotation = strings.Join(joins, "|") - arg.replace = arg.annotation != "" - }, + Name: "flags", + Type: ArgTypeUnsignedLong, + Annotator: annotation.AnnotateMMapFlags, }, { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "off", @@ -292,7 +252,7 @@ var ( { Name: "start", Type: ArgTypeAddress, - Annotator: annotateNull, + Annotator: annotation.AnnotateNull, }, { Name: "len", @@ -301,7 +261,7 @@ var ( { Name: "prot", Type: ArgTypeUnsignedLong, - Annotator: annotateProt, + Annotator: annotation.AnnotateProt, }, }, }, @@ -314,7 +274,7 @@ var ( { Name: "start", Type: ArgTypeAddress, - Annotator: annotateNull, + Annotator: annotation.AnnotateNull, }, { Name: "len", @@ -331,7 +291,7 @@ var ( { Name: "brk", Type: ArgTypeAddress, - Annotator: annotateNull, + Annotator: annotation.AnnotateNull, }, }, }, @@ -347,11 +307,11 @@ var ( }, { Name: "act", - Type: ArgTypeSigAction, + Type: argTypeSigAction, }, { Name: "oldact", - Type: ArgTypeSigAction, + Type: argTypeSigAction, Destination: true, // TODO: is this correct? }, }, @@ -365,7 +325,7 @@ var ( { Name: "how", Type: ArgTypeInt, - Annotator: annotateSigProcMaskFlags, + Annotator: annotation.AnnotateSigProcMaskFlags, }, { Name: "set", @@ -400,7 +360,7 @@ var ( { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "request", @@ -415,19 +375,19 @@ var ( unix.SYS_PREAD64: { Name: "pread64", ReturnValue: ReturnMetadata{ - Type: ArgTypeInt, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "buf", Type: ArgTypeData, - CountFrom: CountLocationNext, + LenSource: LenSourceNext, Destination: true, }, { @@ -443,19 +403,19 @@ var ( unix.SYS_PWRITE64: { Name: "pwrite64", ReturnValue: ReturnMetadata{ - Type: ArgTypeInt, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "buf", Type: ArgTypeData, - CountFrom: CountLocationNext, + LenSource: LenSourceNext, }, { Name: "count", @@ -470,17 +430,17 @@ var ( unix.SYS_READV: { Name: "readv", ReturnValue: ReturnMetadata{ - Type: ArgTypeInt, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "iov", - Type: ArgTypeIovecArray, + Type: argTypeIovecArray, }, { Name: "iovcnt", @@ -491,17 +451,17 @@ var ( unix.SYS_WRITEV: { Name: "writev", ReturnValue: ReturnMetadata{ - Type: ArgTypeInt, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { Name: "fd", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateFd, }, { Name: "iov", - Type: ArgTypeIovecArray, + Type: argTypeIovecArray, }, { Name: "iovcnt", @@ -516,14 +476,13 @@ var ( }, Args: []ArgMetadata{ { - Name: "path", - Type: ArgTypeData, - CountFrom: CountLocationNullTerminator, + Name: "path", + Type: argTypeString, }, { Name: "mode", Type: ArgTypeUnsignedInt, - Annotator: annotateAccMode, + Annotator: annotation.AnnotateAccMode, }, }, }, @@ -535,114 +494,6871 @@ var ( Args: []ArgMetadata{ { Name: "pipefd", - Type: ArgTypeIntArray, - CountFrom: CountLocationFixed, + Type: argTypeIntArray, + LenSource: LenSourceFixed, FixedCount: 2, Destination: true, }, }, }, - // <-- progress - // TODO: add ReturnValue to everything below here... - unix.SYS_PIPE2: { - Name: "pipe2", + unix.SYS_SELECT: { + Name: "select", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "nfds", + Type: ArgTypeInt, + }, + { + Name: "readfds", + Type: argTypeFdSet, + }, + { + Name: "writefds", + Type: argTypeFdSet, + }, + { + Name: "exceptfds", + Type: argTypeFdSet, + }, + { + Name: "timeout", + Type: argTypeTimeval, + }, + }, + }, + unix.SYS_SCHED_YIELD: { + Name: "sched_yield", ReturnValue: ReturnMetadata{ Type: ArgTypeErrorCode, }, + }, + unix.SYS_MREMAP: { + Name: "mremap", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, Args: []ArgMetadata{ { - Name: "pipefd", - Type: ArgTypeIntArray, - CountFrom: CountLocationFixed, - FixedCount: 2, - Destination: true, + Name: "old_address", + Type: ArgTypeAddress, }, { - Name: "flags", + Name: "old_size", Type: ArgTypeInt, - // TODO: annotate pipe2 flags + }, + { + Name: "new_size", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMRemapFlags, }, }, }, - unix.SYS_OPENAT: { - Name: "openat", + unix.SYS_MSYNC: { + Name: "msync", ReturnValue: ReturnMetadata{ - Type: ArgTypeInt, + Type: ArgTypeErrorCode, }, Args: []ArgMetadata{ { - Name: "dfd", - Type: ArgTypeInt, - Annotator: annotateFd, + Name: "addr", + Type: ArgTypeAddress, }, { - Name: "filename", - Type: ArgTypeData, - CountFrom: CountLocationNullTerminator, + Name: "length", + Type: ArgTypeInt, }, { Name: "flags", Type: ArgTypeInt, - Annotator: annotateOpenFlags, + Annotator: annotation.AnnotateMSyncFlags, }, + }, + }, + unix.SYS_MINCORE: { + Name: "mincore", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ { - Name: "mode", - Type: ArgTypeInt, - Optional: true, - Annotator: annotateAccMode, + Name: "addr", + Type: ArgTypeAddress, + }, + { + Name: "length", + Type: ArgTypeInt, + }, + { + Name: "vec", + Type: ArgTypeAddress, }, }, }, - unix.SYS_NEWFSTATAT: { - Name: "newfstatat", + unix.SYS_MADVISE: { + Name: "madvise", ReturnValue: ReturnMetadata{ Type: ArgTypeErrorCode, }, Args: []ArgMetadata{ { - Name: "dfd", + Name: "addr", + Type: ArgTypeAddress, + }, + { + Name: "length", + Type: ArgTypeInt, + }, + { + Name: "advice", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMAdviseAdvice, + }, + }, + }, + unix.SYS_SHMGET: { + Name: "shmget", + ReturnValue: ReturnMetadata{ + Type: ArgTypeAddress, + }, + Args: []ArgMetadata{ + { + Name: "key", + Type: ArgTypeInt, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + { + Name: "shmflg", Type: ArgTypeInt, - Annotator: annotateFd, + Annotator: annotation.AnnotateSHMGetFlags, + }, + }, + }, + unix.SYS_SHMAT: { + Name: "shmat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeAddress, + }, + Args: []ArgMetadata{ + { + Name: "shmid", + Type: ArgTypeInt, }, { - Name: "filename", - Type: ArgTypeData, - CountFrom: CountLocationNullTerminator, + Name: "shmaddr", + Type: ArgTypeAddress, }, { - Name: "statbuf", - Type: ArgTypeStat, + Name: "shmflg", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSHMAtFlags, + }, + }, + }, + unix.SYS_SHMCTL: { + Name: "shmctl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "shmid", + Type: ArgTypeInt, + }, + { + Name: "cmd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSHMCTLCommand, + }, + { + Name: "buf", + Type: argTypeSHMIDDS, Destination: true, }, + }, + }, + unix.SYS_DUP: { + Name: "dup", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ { - Name: "flag", - Type: ArgTypeInt, // TODO: annotate flags + Name: "oldfd", + Type: ArgTypeInt, }, }, }, - unix.SYS_EXIT: { - Name: "exit", + unix.SYS_DUP2: { + Name: "dup2", ReturnValue: ReturnMetadata{ - Type: ArgTypeErrorCode, + Type: argTypeIntOrErrorCode, }, Args: []ArgMetadata{ { - Name: "status", + Name: "oldfd", + Type: ArgTypeInt, + }, + { + Name: "newfd", Type: ArgTypeInt, }, }, }, - unix.SYS_EXIT_GROUP: { - Name: "exit_group", + unix.SYS_PAUSE: { + Name: "pause", ReturnValue: ReturnMetadata{ Type: ArgTypeErrorCode, }, + }, + unix.SYS_NANOSLEEP: { + Name: "nanosleep", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, Args: []ArgMetadata{ { - Name: "status", - Type: ArgTypeInt, + Name: "req", + Type: argTypeTimespec, + }, + { + Name: "rem", + Type: argTypeTimespec, + Destination: true, }, }, }, - } -) + unix.SYS_GETITIMER: { + Name: "getitimer", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "which", + Type: ArgTypeInt, + Annotator: annotation.AnnotateWhichTimer, + }, + { + Name: "curr_value", + Type: argTypeItimerval, + Destination: true, + }, + }, + }, + unix.SYS_ALARM: { + Name: "alarm", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "seconds", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_SETITIMER: { + Name: "setitimer", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "which", + Type: ArgTypeInt, + Annotator: annotation.AnnotateWhichTimer, + }, + { + Name: "new_value", + Type: argTypeItimerval, + }, + { + Name: "old_value", + Type: argTypeItimerval, + Annotator: annotation.AnnotateNull, + }, + }, + }, + unix.SYS_GETPID: { + Name: "getpid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + }, + unix.SYS_SENDFILE: { + Name: "sendfile", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "out_fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "in_fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "offset", + Type: ArgTypeUnsignedInt, + }, + { + Name: "count", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_SOCKET: { + Name: "socket", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "domain", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketDomain, + }, + { + Name: "type", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketType, + }, + { + Name: "protocol", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketProtocol, + }, + }, + }, + unix.SYS_CONNECT: { + Name: "connect", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "addr", + Type: argTypeSockaddr, + LenSource: LenSourceNext, + }, + { + Name: "addrlen", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_ACCEPT: { + Name: "accept", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "addr", + Type: argTypeSockaddr, + LenSource: LenSourceNextPointer, + }, + { + Name: "addrlen", + Type: argTypeUnsignedIntPtr, + }, + }, + }, + unix.SYS_SENDTO: { + Name: "sendto", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "buf", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "len", + Type: ArgTypeUnsignedInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + }, + }, + unix.SYS_RECVFROM: { + Name: "recvfrom", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "buf", + Type: ArgTypeData, + LenSource: LenSourceNext, + Destination: true, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + { + Name: "addr", + Type: argTypeSockaddr, + Destination: true, + LenSource: LenSourceNextPointer, + }, + { + Name: "addrlen", + Type: argTypeUnsignedIntPtr, + Destination: true, + }, + }, + }, + unix.SYS_SENDMSG: { + Name: "sendmsg", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "msg", + Type: argTypeMsghdr, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + }, + }, + unix.SYS_RECVMSG: { + Name: "recvmsg", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "msg", + Type: argTypeMsghdr, + Destination: true, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + }, + }, + unix.SYS_SHUTDOWN: { + Name: "shutdown", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "how", + Type: ArgTypeInt, + Annotator: annotation.AnnotateShutdownHow, + }, + }, + }, + unix.SYS_BIND: { + Name: "bind", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "addr", + Type: argTypeSockaddr, + }, + { + Name: "addrlen", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_LISTEN: { + Name: "listen", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "backlog", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_GETSOCKNAME: { + Name: "getsockname", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "addr", + Type: argTypeSockaddr, + Destination: true, + LenSource: LenSourceNextPointer, + }, + { + Name: "addrlen", + Type: argTypeUnsignedIntPtr, + Destination: true, + }, + }, + }, + unix.SYS_GETPEERNAME: { + Name: "getpeername", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "addr", + Type: argTypeSockaddr, + Destination: true, + LenSource: LenSourceNextPointer, + }, + { + Name: "addrlen", + Type: argTypeUnsignedIntPtr, + Destination: true, + }, + }, + }, + unix.SYS_SOCKETPAIR: { + Name: "socketpair", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "domain", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketDomain, + }, + { + Name: "type", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketType, + }, + { + Name: "protocol", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketProtocol, + }, + { + Name: "fds", + Type: argTypeIntArray, + LenSource: LenSourceFixed, + FixedCount: 2, + Annotator: func(arg annotation.Arg, pid int) { + if underlying, ok := arg.(*Arg); ok { + for i := 0; i < 2; i++ { + annotation.AnnotateFd(&underlying.array[i], pid) + } + } + }, + }, + }, + }, + unix.SYS_SETSOCKOPT: { + Name: "setsockopt", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "level", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketLevel, + }, + { + Name: "optname", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketOption, + }, + { + Name: "optval", + Type: argTypeSockoptval, + LenSource: LenSourceNext, + }, + { + Name: "optlen", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_GETSOCKOPT: { + Name: "getsockopt", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "level", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketLevel, + }, + { + Name: "optname", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSocketOption, + }, + { + Name: "optval", + Type: argTypeSockoptval, + LenSource: LenSourceNextPointer, + Destination: true, + }, + { + Name: "optlen", + Type: argTypeUnsignedIntPtr, + Destination: true, + }, + }, + }, + unix.SYS_CLONE: { + Name: "clone", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fn", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, + }, + { + Name: "child_stack", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateCloneFlags, + }, + { + Name: "arg", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, + }, + }, + }, + unix.SYS_FORK: { + Name: "fork", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_VFORK: { + Name: "vfork", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_EXECVE: { + Name: "execve", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "filename", + Type: argTypeString, + }, + { + Name: "argv", + Type: argTypeStringArray, + }, + { + Name: "envp", + Type: argTypeStringArray, + }, + }, + }, + unix.SYS_EXIT: { + Name: "exit", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "status", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_WAIT4: { + Name: "wait4", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + /* + < -1 + + wait for any child process whose process group ID is equal to the absolute value of pid. + -1 + + wait for any child process; this is equivalent to calling wait3(). + 0 + + wait for any child process whose process group ID is equal to that of the calling process. + > 0 + + wait for the child whose process ID is equal to the value of pid. + */ + }, + { + Name: "status", + Type: argTypeWaitStatus, + Annotator: annotation.AnnotateWaitStatus, + }, + { + Name: "options", + Type: ArgTypeInt, + Annotator: annotation.AnnotateWaitOptions, + }, + { + Name: "rusage", + Type: argTypeRUsage, + Destination: true, + }, + }, + }, + unix.SYS_KILL: { + Name: "kill", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "sig", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignal, + }, + }, + }, + unix.SYS_UNAME: { + Name: "uname", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "buf", + Type: argTypeUname, + Destination: true, + }, + }, + }, + unix.SYS_SEMGET: { + Name: "semget", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "key", + Type: ArgTypeInt, + }, + { + Name: "nsems", + Type: ArgTypeInt, + }, + { + Name: "semflg", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSemFlags, + }, + }, + }, + unix.SYS_SEMOP: { + Name: "semop", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "semid", + Type: ArgTypeInt, + }, + { + Name: "sops", + Type: argTypeSembuf, + }, + { + Name: "nsops", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SEMCTL: { + Name: "semctl", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "semid", + Type: ArgTypeInt, + }, + { + Name: "semnum", + Type: ArgTypeInt, + }, + { + Name: "cmd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSemCmd, + }, + { + Name: "arg", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, // TODO: annotate this based on cmd? + Destination: true, // sometimes true, so always wait + }, + }, + }, + unix.SYS_SHMDT: { + Name: "shmdt", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "shmaddr", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_MSGGET: { + Name: "msgget", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "key", + Type: ArgTypeInt, + }, + { + Name: "msgflg", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + }, + }, + unix.SYS_MSGSND: { + Name: "msgsnd", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "msqid", + Type: ArgTypeInt, + }, + { + Name: "msgp", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "msgsz", + Type: ArgTypeInt, + }, + { + Name: "msgflg", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + }, + }, + unix.SYS_MSGRCV: { + Name: "msgrcv", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "msqid", + Type: ArgTypeInt, + }, + { + Name: "msgp", + Type: ArgTypeData, + LenSource: LenSourceNext, + Destination: true, + }, + { + Name: "msgsz", + Type: ArgTypeInt, + }, + { + Name: "msgtyp", + Type: ArgTypeLong, + Annotator: annotation.AnnotateMsgType, + }, + { + Name: "msgflg", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + }, + }, + unix.SYS_MSGCTL: { + Name: "msgctl", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "msqid", + Type: ArgTypeInt, + }, + { + Name: "cmd", + Type: ArgTypeInt, + }, + { + Name: "buf", + Type: ArgTypeAddress, // TODO: this needs it's own type so we can print more info + Destination: true, + }, + }, + }, + unix.SYS_FCNTL: { + Name: "fcntl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "cmd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFcntlCmd, + }, + { + Name: "arg", + Type: ArgTypeAddress, + }, + }, + Modifier: func(call *Syscall) { + + args := call.args[2:] + + switch call.args[1].raw { + case unix.F_DUPFD, unix.F_DUPFD_CLOEXEC, unix.F_SETFD, unix.F_SETFL, unix.F_SETOWN, unix.F_SETSIG, unix.F_SETLEASE, unix.F_NOTIFY, unix.F_SETPIPE_SZ: + args = args[:1] + args[0] = Arg{ + raw: args[0].raw, + t: ArgTypeInt, + name: "arg", + } + case unix.F_GETLK, unix.F_SETLK, unix.F_SETLKW: + args = args[:1] + args[0] = Arg{ + raw: args[0].raw, + t: ArgTypeAddress, // TODO create flock type + name: "arg", + } + default: + args = nil + } + + call.args = append(call.args[:2], args...) + }, + }, + unix.SYS_FLOCK: { + Name: "flock", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "operation", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFlockOperation, + }, + }, + }, + unix.SYS_FSYNC: { + Name: "fsync", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + }, + }, + unix.SYS_FDATASYNC: { + Name: "fdatasync", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + }, + }, + unix.SYS_TRUNCATE: { + Name: "truncate", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "length", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_FTRUNCATE: { + Name: "ftruncate", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "length", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_GETDENTS: { + Name: "getdents", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "dirp", + Type: ArgTypeAddress, + }, + { + Name: "count", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_GETCWD: { + Name: "getcwd", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "buf", + Type: ArgTypeData, + LenSource: LenSourceReturnValue, + Destination: true, + Annotator: trimToNull, + }, + { + Name: "size", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_CHDIR: { + Name: "chdir", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + }, + }, + unix.SYS_FCHDIR: { + Name: "fchdir", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + }, + }, + unix.SYS_RENAME: { + Name: "rename", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "oldpath", + Type: argTypeString, + }, + { + Name: "newpath", + Type: argTypeString, + }, + }, + }, + unix.SYS_MKDIR: { + Name: "mkdir", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + }, + }, + unix.SYS_RMDIR: { + Name: "rmdir", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pathname", + Type: argTypeString, + }, + }, + }, + unix.SYS_CREAT: { + Name: "creat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + }, + }, + unix.SYS_LINK: { + Name: "link", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "oldpath", + Type: argTypeString, + }, + { + Name: "newpath", + Type: argTypeString, + }, + }, + }, + unix.SYS_UNLINK: { + Name: "unlink", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pathname", + Type: argTypeString, + }, + }, + }, + unix.SYS_SYMLINK: { + Name: "symlink", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "oldpath", + Type: argTypeString, + }, + { + Name: "newpath", + Type: argTypeString, + }, + }, + }, + unix.SYS_READLINK: { + Name: "readlink", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "buf", + Type: ArgTypeData, + LenSource: LenSourceNext, + Destination: true, + Annotator: trimToNull, + }, + { + Name: "bufsiz", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_CHMOD: { + Name: "chmod", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + }, + }, + unix.SYS_FCHMOD: { + Name: "fchmod", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + }, + }, + unix.SYS_CHOWN: { + Name: "chown", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "owner", + Type: ArgTypeInt, + }, + { + Name: "group", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FCHOWN: { + Name: "fchown", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "owner", + Type: ArgTypeInt, + }, + { + Name: "group", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_LCHOWN: { + Name: "lchown", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "owner", + Type: ArgTypeInt, + }, + { + Name: "group", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_UMASK: { + Name: "umask", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + Args: []ArgMetadata{ + { + Name: "mask", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + }, + }, + unix.SYS_GETTIMEOFDAY: { + Name: "gettimeofday", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "tv", + Type: argTypeTimeval, + Destination: true, + }, + { + Name: "tz", + Type: argTypeTimezone, + Destination: true, + }, + }, + }, + unix.SYS_GETRLIMIT: { + Name: "getrlimit", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "resource", + Type: ArgTypeInt, + Annotator: annotation.AnnotateRLimitResourceFlags, + }, + { + Name: "rlim", + Type: argTypeRLimit, + Destination: true, + }, + }, + }, + unix.SYS_GETRUSAGE: { + Name: "getrusage", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "who", + Type: ArgTypeInt, + Annotator: annotation.AnnotateRUsageWho, + }, + { + Name: "usage", + Type: argTypeRUsage, + Destination: true, + }, + }, + }, + unix.SYS_SYSINFO: { + Name: "sysinfo", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "info", + Type: argTypeSysinfo, + Destination: true, + }, + }, + }, + unix.SYS_TIMES: { + Name: "times", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "tbuf", + Type: argTypeTms, + Destination: true, + }, + }, + }, + unix.SYS_PTRACE: { + Name: "ptrace", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "request", + Type: ArgTypeInt, + Annotator: annotation.AnnotatePtraceRequest, + }, + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "addr", + Type: ArgTypeAddress, + }, + { + Name: "data", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_GETUID: { + Name: "getuid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + }, + unix.SYS_SYSLOG: { + Name: "syslog", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "type", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSyslogType, + }, + { + Name: "buf", + Type: ArgTypeData, + Destination: true, + LenSource: LenSourceReturnValue, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GETGID: { + Name: "getgid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + }, + unix.SYS_SETUID: { + Name: "setuid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "uid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SETGID: { + Name: "setgid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "gid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GETEUID: { + Name: "geteuid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + }, + unix.SYS_GETEGID: { + Name: "getegid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + }, + unix.SYS_SETPGID: { + Name: "setpgid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "pgid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GETPPID: { + Name: "getppid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + }, + unix.SYS_GETPGRP: { + Name: "getpgrp", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + }, + unix.SYS_SETSID: { + Name: "setsid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + }, + unix.SYS_SETREUID: { + Name: "setreuid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ruid", + Type: ArgTypeInt, + }, + { + Name: "euid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SETREGID: { + Name: "setregid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "rgid", + Type: ArgTypeInt, + }, + { + Name: "egid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GETGROUPS: { + Name: "getgroups", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "size", + Type: ArgTypeInt, + Destination: true, + }, + { + Name: "list", + Type: argTypeIntArray, + Destination: true, + }, + }, + }, + unix.SYS_SETGROUPS: { + Name: "setgroups", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "size", + Type: ArgTypeInt, + }, + { + Name: "list", + Type: argTypeIntArray, + }, + }, + }, + unix.SYS_SETRESUID: { + Name: "setresuid", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ruid", + Type: ArgTypeInt, + }, + { + Name: "euid", + Type: ArgTypeInt, + }, + { + Name: "suid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GETRESUID: { + Name: "getresuid", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ruid", + Type: ArgTypeInt, + Destination: true, + }, + { + Name: "euid", + Type: ArgTypeInt, + Destination: true, + }, + { + Name: "suid", + Type: ArgTypeInt, + Destination: true, + }, + }, + }, + unix.SYS_SETRESGID: { + Name: "setresgid", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "rgid", + Type: ArgTypeInt, + }, + { + Name: "egid", + Type: ArgTypeInt, + }, + { + Name: "sgid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GETRESGID: { + Name: "getresgid", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "rgid", + Type: ArgTypeInt, + Destination: true, + }, + { + Name: "egid", + Type: ArgTypeInt, + Destination: true, + }, + { + Name: "sgid", + Type: ArgTypeInt, + Destination: true, + }, + }, + }, + unix.SYS_GETPGID: { + Name: "getpgid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SETFSUID: { + Name: "setfsuid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + Args: []ArgMetadata{ + { + Name: "fsuid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SETFSGID: { + Name: "setfsgid", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + Args: []ArgMetadata{ + { + Name: "fsgid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GETSID: { + Name: "getsid", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_CAPGET: { + Name: "capget", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "header", + Type: argTypeCapUserHeader, + Destination: true, + }, + { + Name: "data", + Type: argTypeCapUserData, + Destination: true, + }, + }, + }, + unix.SYS_CAPSET: { + Name: "capset", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "header", + Type: argTypeCapUserHeader, + }, + { + Name: "data", + Type: argTypeCapUserData, + }, + }, + }, + unix.SYS_RT_SIGPENDING: { + Name: "rt_sigpending", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "set", + Type: ArgTypeAddress, + }, + { + Name: "sigsetsize", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_RT_SIGTIMEDWAIT: { + Name: "rt_sigtimedwait", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "set", + Type: ArgTypeAddress, + }, + { + Name: "info", + Type: argTypeSigInfo, + }, + { + Name: "timeout", + Type: argTypeTimespec, + }, + }, + }, + unix.SYS_RT_SIGQUEUEINFO: { + Name: "rt_sigqueueinfo", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "sig", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignal, + }, + { + Name: "info", + Type: argTypeSigInfo, + }, + }, + }, + unix.SYS_RT_SIGSUSPEND: { + Name: "rt_sigsuspend", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + Args: []ArgMetadata{ + { + Name: "set", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_SIGALTSTACK: { + Name: "sigaltstack", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ss", + Type: argTypeStack, + }, + { + Name: "old_ss", + Type: argTypeStack, + Destination: true, + }, + }, + }, + unix.SYS_UTIME: { + Name: "utime", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "filename", + Type: argTypeString, + }, + { + Name: "times", + Type: argTypeUtimbuf, + Destination: true, + }, + }, + }, + unix.SYS_MKNOD: { + Name: "mknod", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + { + Name: "dev", + Type: ArgTypeInt, + Annotator: annotation.AnnotateDevice, + }, + }, + }, + unix.SYS_USELIB: { + Name: "uselib", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "library", + Type: argTypeString, + }, + }, + }, + unix.SYS_PERSONALITY: { + Name: "personality", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "persona", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_USTAT: { + Name: "ustat", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dev", + Type: ArgTypeInt, + Annotator: annotation.AnnotateDevice, + }, + { + Name: "ubuf", + Type: argTypeUstat, + Destination: true, + }, + }, + }, + unix.SYS_STATFS: { + Name: "statfs", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "buf", + Type: argTypeStatfs, + Destination: true, + }, + }, + }, + unix.SYS_FSTATFS: { + Name: "fstatfs", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "buf", + Type: argTypeStatfs, + Destination: true, + }, + }, + }, + unix.SYS_SYSFS: { + Name: "sysfs", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "option", + Type: ArgTypeInt, + }, + }, + Modifier: func(call *Syscall) { + switch call.args[0].raw { + case 1: // int sysfs(int option, const char *fsname) + str, _ := readString(call.pid, call.rawArgs[1]) + call.args = append(call.args, Arg{ + name: "fsname", + t: ArgTypeData, + data: []byte(str), + }) + case 2: // int sysfs(int option, int fs_index, const char *buf) + call.args = append(call.args, Arg{ + name: "fs_index", + t: ArgTypeInt, + raw: call.rawArgs[1], + }) + str, _ := readString(call.pid, call.rawArgs[2]) + call.args = append(call.args, Arg{ + name: "buf", + t: ArgTypeData, + data: []byte(str), + }) + } + }, + }, + unix.SYS_GETPRIORITY: { + Name: "getpriority", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "which", + Type: ArgTypeInt, + Annotator: annotation.AnnotatePriorityWhich, + }, + { + Name: "who", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SETPRIORITY: { + Name: "setpriority", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "which", + Type: ArgTypeInt, + Annotator: annotation.AnnotatePriorityWhich, + }, + { + Name: "who", + Type: ArgTypeInt, + }, + { + Name: "prio", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SCHED_SETPARAM: { + Name: "sched_setparam", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "param", + Type: argTypeSchedParam, + }, + }, + }, + unix.SYS_SCHED_GETPARAM: { + Name: "sched_getparam", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "param", + Type: argTypeSchedParam, + Destination: true, + }, + }, + }, + unix.SYS_SCHED_SETSCHEDULER: { + Name: "sched_setscheduler", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "policy", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSchedPolicy, + }, + { + Name: "param", + Type: argTypeSchedParam, + }, + }, + }, + unix.SYS_SCHED_GETSCHEDULER: { + Name: "sched_getscheduler", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + Annotator: annotation.AnnotateSchedPolicy, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SCHED_GET_PRIORITY_MAX: { + Name: "sched_get_priority_max", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "policy", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSchedPolicy, + }, + }, + }, + unix.SYS_SCHED_GET_PRIORITY_MIN: { + Name: "sched_get_priority_min", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "policy", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSchedPolicy, + }, + }, + }, + unix.SYS_SCHED_RR_GET_INTERVAL: { + Name: "sched_rr_get_interval", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "interval", + Type: argTypeTimespec, + Destination: true, + }, + }, + }, + unix.SYS_MLOCK: { + Name: "mlock", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "addr", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_MUNLOCK: { + Name: "munlock", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "addr", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_MLOCKALL: { + Name: "mlockall", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMlockFlags, + }, + }, + }, + unix.SYS_MUNLOCKALL: { + Name: "munlockall", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_VHANGUP: { + Name: "vhangup", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_MODIFY_LDT: { + Name: "modify_ldt", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "func", + Type: ArgTypeInt, + Annotator: func(arg annotation.Arg, pid int) { + switch arg.Raw() { + case 0: + arg.SetAnnotation("read_ldt", true) + case 1: + arg.SetAnnotation("write_ldt", true) + } + }, + }, + { + Name: "ptr", + Type: argTypeUserDesc, + Destination: true, + }, + { + Name: "bytecount", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PIVOT_ROOT: { + Name: "pivot_root", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "new_root", + Type: argTypeString, + }, + { + Name: "put_old", + Type: argTypeString, + }, + }, + }, + unix.SYS__SYSCTL: { + Name: "sysctl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "args", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_PRCTL: { + Name: "prctl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "option", + Type: ArgTypeInt, + Annotator: annotation.AnnotatePrctlOption, + }, + // TODO: make these options dynamic based on the option via Modifier + { + Name: "arg2", + Type: ArgTypeUnsignedLong, + }, + { + Name: "arg3", + Type: ArgTypeUnsignedLong, + }, + { + Name: "arg4", + Type: ArgTypeUnsignedLong, + }, + { + Name: "arg5", + Type: ArgTypeUnsignedLong, + }, + }, + }, + unix.SYS_ARCH_PRCTL: { + Name: "arch_prctl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "code", + Type: ArgTypeInt, + Annotator: annotation.AnnotateArchPrctrlCode, + }, + { + Name: "addr", + Type: ArgTypeAddress, + Destination: true, + }, + }, + }, + unix.SYS_ADJTIMEX: { + Name: "adjtimex", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + Annotator: annotation.AnnotateClockState, + }, + Args: []ArgMetadata{ + { + Name: "buf", + Type: argTypeTimex, + }, + }, + }, + unix.SYS_SETRLIMIT: { + Name: "setrlimit", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "resource", + Type: ArgTypeInt, + Annotator: annotation.AnnotateRLimitResourceFlags, + }, + { + Name: "rlim", + Type: argTypeRLimit, + }, + }, + }, + unix.SYS_CHROOT: { + Name: "chroot", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + }, + }, + unix.SYS_SYNC: { + Name: "sync", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_ACCT: { + Name: "acct", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "filename", + Type: argTypeString, + }, + }, + }, + unix.SYS_SETTIMEOFDAY: { + Name: "settimeofday", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "tv", + Type: argTypeTimeval, + }, + { + Name: "tz", + Type: argTypeTimezone, + }, + }, + }, + unix.SYS_MOUNT: { + Name: "mount", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "source", + Type: argTypeString, + }, + { + Name: "target", + Type: argTypeString, + }, + { + Name: "filesystemtype", + Type: argTypeString, + }, + { + Name: "mountflags", + Type: ArgTypeUnsignedLong, + Annotator: annotation.AnnotateMountFlags, + }, + { + Name: "data", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_UMOUNT2: { + Name: "umount2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "target", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateUmountFlags, + }, + }, + }, + unix.SYS_SWAPON: { + Name: "swapon", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + }, + }, + unix.SYS_SWAPOFF: { + Name: "swapoff", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + }, + }, + unix.SYS_REBOOT: { + Name: "reboot", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "magic", + Type: ArgTypeInt, + Annotator: annotation.AnnotateRebootMagic, + }, + { + Name: "magic2", + Type: ArgTypeInt, + Annotator: annotation.AnnotateRebootMagic, + }, + { + Name: "cmd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateRebootCmd, + }, + { + Name: "arg", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_SETHOSTNAME: { + Name: "sethostname", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "name", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_SETDOMAINNAME: { + Name: "setdomainname", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "name", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_IOPL: { + Name: "iopl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "level", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_IOPERM: { + Name: "ioperm", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "from", + Type: ArgTypeUnsignedLong, + }, + { + Name: "num", + Type: ArgTypeUnsignedLong, + }, + { + Name: "turn_on", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_CREATE_MODULE: { + Name: "create_module", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "name", + Type: argTypeString, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_INIT_MODULE: { + Name: "init_module", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "module_image", + Type: ArgTypeAddress, + }, + { + Name: "len", + Type: ArgTypeUnsignedLong, + }, + { + Name: "param_values", + Type: argTypeString, + }, + }, + }, + unix.SYS_DELETE_MODULE: { + Name: "delete_module", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "name", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateDeleteModuleFlags, + }, + }, + }, + unix.SYS_GET_KERNEL_SYMS: { + Name: "get_kernel_syms", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "table", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, + }, + }, + }, + unix.SYS_QUERY_MODULE: { + Name: "query_module", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "name", + Type: argTypeString, + }, + { + Name: "which", + Type: ArgTypeInt, + Annotator: annotation.AnnotateQueryModuleWhich, + }, + { + Name: "buf", + Type: ArgTypeAddress, + }, + { + Name: "bufsize", + Type: ArgTypeInt, + }, + { + Name: "ret", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_QUOTACTL: { + Name: "quotactl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "cmd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateQuotactlCmd, + }, + { + Name: "special", + Type: argTypeString, + }, + { + Name: "id", + Type: ArgTypeInt, + }, + { + Name: "addr", + Type: ArgTypeAddress, // TODO: annotate structs for each possible cmd + }, + }, + }, + unix.SYS_NFSSERVCTL: { // removed in 3.1 + Name: "nfsservctl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "cmd", + Type: ArgTypeInt, + }, + { + Name: "argp", + Type: ArgTypeAddress, + }, + { + Name: "resp", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_GETPMSG: { + Name: "getpmsg", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "ctlptr", + Type: ArgTypeAddress, + }, + { + Name: "dataptr", + Type: ArgTypeAddress, + }, + { + Name: "bandp", + Type: ArgTypeAddress, + }, + { + Name: "flagsp", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_PUTPMSG: { + Name: "putpmsg", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "ctlptr", + Type: ArgTypeAddress, + }, + { + Name: "dataptr", + Type: ArgTypeAddress, + }, + { + Name: "band", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_AFS_SYSCALL: { + Name: "afs_syscall", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_TUXCALL: { + Name: "tuxcall", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_SECURITY: { + Name: "security", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_GETTID: { + Name: "gettid", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_READAHEAD: { + Name: "readahead", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "offset", + Type: ArgTypeLong, + }, + { + Name: "count", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_SETXATTR: { + Name: "setxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "name", + Type: argTypeString, + }, + { + Name: "value", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateXFlags, + }, + }, + }, + unix.SYS_LSETXATTR: { + Name: "lsetxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "name", + Type: argTypeString, + }, + { + Name: "value", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateXFlags, + }, + }, + }, + unix.SYS_FSETXATTR: { + Name: "fsetxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "name", + Type: argTypeString, + }, + { + Name: "value", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateXFlags, + }, + }, + }, + unix.SYS_GETXATTR: { + Name: "getxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "name", + Type: argTypeString, + }, + { + Name: "value", + Type: ArgTypeData, + LenSource: LenSourceReturnValue, + Destination: true, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_LGETXATTR: { + Name: "lgetxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "name", + Type: argTypeString, + }, + { + Name: "value", + Type: ArgTypeData, + LenSource: LenSourceReturnValue, + Destination: true, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FGETXATTR: { + Name: "fgetxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "name", + Type: argTypeString, + }, + { + Name: "value", + Type: ArgTypeData, + LenSource: LenSourceReturnValue, + Destination: true, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_LISTXATTR: { + Name: "listxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "list", + Type: argTypeStringArray, + Destination: true, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_LLISTXATTR: { + Name: "llistxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "list", + Type: argTypeStringArray, + Destination: true, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FLISTXATTR: { + Name: "flistxattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "list", + Type: argTypeStringArray, + Destination: true, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_REMOVEXATTR: { + Name: "removexattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "name", + Type: argTypeString, + }, + }, + }, + unix.SYS_LREMOVEXATTR: { + Name: "lremovexattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "path", + Type: argTypeString, + }, + { + Name: "name", + Type: argTypeString, + }, + }, + }, + unix.SYS_FREMOVEXATTR: { + Name: "fremovexattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "name", + Type: argTypeString, + }, + }, + }, + unix.SYS_TKILL: { + Name: "tkill", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "tid", + Type: ArgTypeInt, + }, + { + Name: "sig", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignal, + }, + }, + }, + unix.SYS_TIME: { + Name: "time", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "t", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_FUTEX: { + Name: "futex", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "uaddr", + Type: ArgTypeAddress, + }, + { + Name: "op", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFutexOp, + }, + { + Name: "val", + Type: ArgTypeInt, + }, + { + Name: "timeout", + Type: argTypeTimespec, + Optional: true, + }, + { + Name: "uaddr2", + Type: ArgTypeAddress, + Optional: true, + }, + { + Name: "val3", + Type: ArgTypeInt, + Optional: true, + }, + }, + }, + unix.SYS_SCHED_SETAFFINITY: { + Name: "sched_setaffinity", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + { + Name: "user_mask_ptr", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_SCHED_GETAFFINITY: { + Name: "sched_getaffinity", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + { + Name: "user_mask_ptr", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_SET_THREAD_AREA: { + Name: "set_thread_area", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "u_info", + Type: argTypeUserDesc, + }, + }, + }, + unix.SYS_IO_SETUP: { + Name: "io_setup", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "nr_events", + Type: ArgTypeInt, + }, + { + Name: "ctx_idp", + Type: argTypeUnsignedIntPtr, + Destination: true, + }, + }, + }, + unix.SYS_IO_DESTROY: { + Name: "io_destroy", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ctx_id", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_IO_GETEVENTS: { + Name: "io_getevents", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ctx_id", + Type: ArgTypeUnsignedInt, + }, + { + Name: "min_nr", + Type: ArgTypeLong, + }, + { + Name: "nr", + Type: ArgTypeLong, + }, + { + Name: "events", + Type: argTypeIoEvents, + }, + { + Name: "timeout", + Type: argTypeTimespec, + }, + }, + }, + unix.SYS_IO_SUBMIT: { + Name: "io_submit", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ctx_id", + Type: ArgTypeUnsignedInt, + }, + { + Name: "nr", + Type: ArgTypeLong, + }, + { + Name: "iocbpp", + Type: argTypeIoCB, + }, + }, + }, + unix.SYS_IO_CANCEL: { + Name: "io_cancel", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ctx_id", + Type: ArgTypeUnsignedInt, + }, + { + Name: "iocb", + Type: argTypeIoCB, + }, + { + Name: "result", + Type: argTypeIoEvent, + }, + }, + }, + unix.SYS_GET_THREAD_AREA: { + Name: "get_thread_area", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "u_info", + Type: argTypeUserDesc, + Destination: true, + }, + }, + }, + unix.SYS_LOOKUP_DCOOKIE: { + Name: "lookup_dcookie", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "cookie64", + Type: ArgTypeUnsignedLong, + }, + { + Name: "buf", + Type: ArgTypeAddress, + LenSource: LenSourceReturnValue, + Annotator: trimToNull, + }, + { + Name: "len", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_EPOLL_CREATE: { + Name: "epoll_create", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_EPOLL_CTL_OLD: { + Name: "epoll_ctl_old", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "epfd", + Type: ArgTypeInt, + }, + { + Name: "op", + Type: ArgTypeInt, + }, + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "event", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_EPOLL_WAIT_OLD: { + Name: "epoll_wait_old", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "epfd", + Type: ArgTypeInt, + }, + { + Name: "events", + Type: ArgTypeAddress, + }, + { + Name: "maxevents", + Type: ArgTypeInt, + }, + { + Name: "timeout", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_REMAP_FILE_PAGES: { + Name: "remap_file_pages", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "start", + Type: ArgTypeAddress, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + { + Name: "prot", + Type: ArgTypeInt, + Annotator: annotation.AnnotateProt, + }, + { + Name: "pgoff", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GETDENTS64: { + Name: "getdents64", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "dirp", + Type: ArgTypeAddress, + }, + { + Name: "count", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_SET_TID_ADDRESS: { + Name: "set_tid_address", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "tidptr", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_RESTART_SYSCALL: { + Name: "restart_syscall", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_SEMTIMEDOP: { + Name: "semtimedop", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "semid", + Type: ArgTypeInt, + }, + { + Name: "sops", + Type: argTypeSembuf, + }, + { + Name: "nsops", + Type: ArgTypeUnsignedInt, + }, + { + Name: "timeout", + Type: argTypeTimespec, + }, + }, + }, + unix.SYS_FADVISE64: { + Name: "fadvise64", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "offset", + Type: ArgTypeLong, + }, + { + Name: "len", + Type: ArgTypeLong, + }, + { + Name: "advice", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFAdvice, + }, + }, + }, + unix.SYS_TIMER_CREATE: { + Name: "timer_create", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "clockid", + Type: ArgTypeInt, + Annotator: annotation.AnnotateClockID, + }, + { + Name: "sevp", + Type: ArgTypeAddress, // TODO: create sigevent type? + }, + { + Name: "timerid", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_TIMER_SETTIME: { + Name: "timer_settime", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "timerid", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateTimerFlags, + }, + { + Name: "new_value", + Type: argTypeItimerspec, + }, + { + Name: "old_value", + Type: argTypeItimerspec, + Destination: true, + }, + }, + }, + unix.SYS_TIMER_GETTIME: { + Name: "timer_gettime", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "timerid", + Type: ArgTypeInt, + }, + { + Name: "curr_value", + Type: argTypeItimerspec, + Destination: true, + }, + }, + }, + unix.SYS_TIMER_GETOVERRUN: { + Name: "timer_getoverrun", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "timerid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_TIMER_DELETE: { + Name: "timer_delete", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "timerid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_CLOCK_SETTIME: { + Name: "clock_settime", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "clockid", + Type: ArgTypeInt, + Annotator: annotation.AnnotateClockID, + }, + { + Name: "tp", + Type: argTypeTimespec, + }, + }, + }, + unix.SYS_CLOCK_GETTIME: { + Name: "clock_gettime", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "clockid", + Type: ArgTypeInt, + Annotator: annotation.AnnotateClockID, + }, + { + Name: "tp", + Type: argTypeTimespec, + Destination: true, + }, + }, + }, + unix.SYS_CLOCK_GETRES: { + Name: "clock_getres", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "clockid", + Type: ArgTypeInt, + }, + { + Name: "res", + Type: argTypeTimespec, + Destination: true, + }, + }, + }, + unix.SYS_CLOCK_NANOSLEEP: { + Name: "clock_nanosleep", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "clockid", + Type: ArgTypeInt, + Annotator: annotation.AnnotateClockID, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateTimerFlags, + }, + { + Name: "rqtp", + Type: argTypeTimespec, + }, + { + Name: "rmtp", + Type: argTypeTimespec, + Destination: true, + }, + }, + }, + unix.SYS_EXIT_GROUP: { + Name: "exit_group", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "status", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_EPOLL_WAIT: { + Name: "epoll_wait", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "epfd", + Type: ArgTypeInt, + }, + { + Name: "events", + Type: argTypeEpollEvent, + Destination: true, + }, + { + Name: "maxevents", + Type: ArgTypeInt, + }, + { + Name: "timeout", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_EPOLL_CTL: { + Name: "epoll_ctl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "epfd", + Type: ArgTypeInt, + }, + { + Name: "op", + Type: ArgTypeInt, + Annotator: annotation.AnnotateEpollCtlOp, + }, + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "event", + Type: argTypeEpollEvent, + }, + }, + }, + unix.SYS_TGKILL: { + Name: "tgkill", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "tgid", + Type: ArgTypeInt, + }, + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "sig", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignal, + }, + }, + }, + unix.SYS_UTIMES: { + Name: "utimes", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "filename", + Type: argTypeString, + }, + { + Name: "times", + Type: argTypeTimevalArray, + LenSource: LenSourceFixed, + FixedCount: 2, + }, + }, + }, + unix.SYS_VSERVER: { + Name: "vserver", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_MBIND: { + Name: "mbind", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "addr", + Type: ArgTypeAddress, + }, + { + Name: "len", + Type: ArgTypeUnsignedLong, + }, + { + Name: "mode", + Type: ArgTypeInt, + }, + { + Name: "nodemask", + Type: ArgTypeUnsignedLong, + }, + { + Name: "maxnode", + Type: ArgTypeUnsignedLong, + }, + { + Name: "flags", + Type: ArgTypeUnsignedLong, + Annotator: annotation.AnnotateNumaModeFlag, + }, + }, + }, + unix.SYS_SET_MEMPOLICY: { + Name: "set_mempolicy", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateNumaModeFlag, + }, + { + Name: "nodemask", + Type: ArgTypeAddress, + }, + { + Name: "maxnode", + Type: ArgTypeUnsignedLong, + }, + }, + }, + unix.SYS_GET_MEMPOLICY: { + Name: "get_mempolicy", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateNumaModeFlag, + }, + { + Name: "nodemask", + Type: ArgTypeAddress, + }, + { + Name: "maxnode", + Type: ArgTypeAddress, + }, + { + Name: "addr", + Type: ArgTypeAddress, + }, + { + Name: "flags", + Type: ArgTypeUnsignedLong, // TODO: annotate flags + }, + }, + }, + unix.SYS_MQ_OPEN: { + Name: "mq_open", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "name", + Type: argTypeString, + }, + { + Name: "oflag", + Type: ArgTypeInt, + Annotator: annotation.AnnotateOpenFlags, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + Optional: true, + }, + { + Name: "attr", + Type: argTypeMqAttr, + Optional: true, + }, + }, + }, + unix.SYS_MQ_UNLINK: { + Name: "mq_unlink", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "name", + Type: argTypeString, + }, + }, + }, + unix.SYS_MQ_TIMEDSEND: { + Name: "mq_timedsend", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "mqdes", + Type: ArgTypeInt, + }, + { + Name: "msg_ptr", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "msg_len", + Type: ArgTypeUnsignedLong, + }, + { + Name: "msg_prio", + Type: ArgTypeUnsignedLong, + }, + { + Name: "abs_timeout", + Type: argTypeTimespec, + }, + }, + }, + unix.SYS_MQ_TIMEDRECEIVE: { + Name: "mq_timedreceive", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "mqdes", + Type: ArgTypeInt, + }, + { + Name: "msg_ptr", + Type: ArgTypeData, + LenSource: LenSourceReturnValue, + Destination: true, + }, + { + Name: "msg_len", + Type: ArgTypeUnsignedLong, + }, + }, + }, + unix.SYS_MQ_NOTIFY: { + Name: "mq_notify", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "mqdes", + Type: ArgTypeInt, + }, + { + Name: "notification", + Type: ArgTypeAddress, // TODO: sigevent type + }, + }, + }, + unix.SYS_MQ_GETSETATTR: { + Name: "mq_getsetattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "mqdes", + Type: ArgTypeInt, + }, + { + Name: "mqstat", + Type: argTypeMqAttr, + }, + { + Name: "omqstat", + Type: argTypeMqAttr, + Destination: true, + }, + }, + }, + unix.SYS_KEXEC_LOAD: { + Name: "kexec_load", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "entry", + Type: ArgTypeUnsignedLong, + }, + { + Name: "nr_segments", + Type: ArgTypeUnsignedLong, + }, + { + Name: "segments", + Type: ArgTypeAddress, + }, + { + Name: "flags", + Type: ArgTypeUnsignedLong, + Annotator: annotation.AnnotateKexecFlags, + }, + }, + }, + unix.SYS_WAITID: { + Name: "waitid", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "idtype", + Type: ArgTypeInt, + Annotator: annotation.AnnotateIDType, + }, + { + Name: "id", + Type: ArgTypeInt, + }, + { + Name: "infop", + Type: argTypeSigInfo, + Destination: true, + }, + { + Name: "options", + Type: ArgTypeInt, + Annotator: annotation.AnnotateWaitOptions, + }, + }, + }, + unix.SYS_ADD_KEY: { + Name: "add_key", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "type", + Type: argTypeString, + }, + { + Name: "description", + Type: argTypeString, + }, + { + Name: "payload", + Type: ArgTypeData, + LenSource: LenSourceNext, + }, + { + Name: "plen", + Type: ArgTypeUnsignedLong, + }, + { + Name: "keyring", + Type: ArgTypeInt, + Annotator: annotation.AnnotateKeyringID, + }, + }, + }, + unix.SYS_REQUEST_KEY: { + Name: "request_key", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "type", + Type: argTypeString, + }, + { + Name: "description", + Type: argTypeString, + }, + { + Name: "callout_info", + Type: argTypeString, + }, + { + Name: "keyring", + Type: ArgTypeInt, + Annotator: annotation.AnnotateKeyringID, + }, + }, + }, + unix.SYS_KEYCTL: { + Name: "keyctl", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "cmd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateKeyctlCommand, + }, + }, + // TODO: add a modifier here to dynamically add arguments for specific commands + }, + unix.SYS_IOPRIO_SET: { + Name: "ioprio_set", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "which", + Type: ArgTypeInt, + Annotator: annotation.AnnotateIoPrioWhich, + }, + { + Name: "who", + Type: ArgTypeInt, + }, + { + Name: "ioprio", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_IOPRIO_GET: { + Name: "ioprio_get", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "which", + Type: ArgTypeInt, + Annotator: annotation.AnnotateIoPrioWhich, + }, + { + Name: "who", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_INOTIFY_INIT: { + Name: "inotify_init", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + }, + unix.SYS_INOTIFY_ADD_WATCH: { + Name: "inotify_add_watch", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mask", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_INOTIFY_RM_WATCH: { + Name: "inotify_rm_watch", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "wd", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_MIGRATE_PAGES: { + Name: "migrate_pages", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "maxnode", + Type: ArgTypeUnsignedLong, + }, + { + Name: "old_nodes", + Type: ArgTypeAddress, + }, + { + Name: "new_nodes", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_OPENAT: { + Name: "openat", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "filename", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateOpenFlags, + }, + { + Name: "mode", + Type: ArgTypeInt, + Optional: true, + Annotator: annotation.AnnotateAccMode, + }, + }, + }, + unix.SYS_MKDIRAT: { + Name: "mkdirat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + }, + }, + unix.SYS_MKNODAT: { + Name: "mknodat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + { + Name: "dev", + Type: ArgTypeInt, + Annotator: annotation.AnnotateDevice, + }, + }, + }, + unix.SYS_FCHOWNAT: { + Name: "fchownat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "filename", + Type: argTypeString, + }, + { + Name: "owner", + Type: ArgTypeInt, + }, + { + Name: "group", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + }, + }, + unix.SYS_FUTIMESAT: { + Name: "futimesat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "filename", + Type: argTypeString, + }, + { + Name: "times", + Type: argTypeTimevalArray, + LenSource: LenSourceFixed, + FixedCount: 2, + }, + }, + }, + unix.SYS_NEWFSTATAT: { + Name: "newfstatat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "filename", + Type: argTypeString, + }, + { + Name: "statbuf", + Type: argTypeStat, + Destination: true, + }, + { + Name: "flag", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + }, + }, + unix.SYS_UNLINKAT: { + Name: "unlinkat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + }, + }, + unix.SYS_RENAMEAT: { + Name: "renameat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "olddfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "oldname", + Type: argTypeString, + }, + { + Name: "newdfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "newname", + Type: argTypeString, + }, + }, + }, + unix.SYS_LINKAT: { + Name: "linkat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "olddfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "oldname", + Type: argTypeString, + }, + { + Name: "newdfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "newname", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + }, + }, + unix.SYS_SYMLINKAT: { + Name: "symlinkat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "oldname", + Type: argTypeString, + }, + { + Name: "newdfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "newname", + Type: argTypeString, + }, + }, + }, + unix.SYS_READLINKAT: { + Name: "readlinkat", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "buf", + Type: ArgTypeData, + LenSource: LenSourceReturnValue, + }, + { + Name: "bufsiz", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FCHMODAT: { + Name: "fchmodat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + }, + }, + unix.SYS_FACCESSAT: { + Name: "faccessat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + Optional: true, + }, + }, + }, + unix.SYS_PSELECT6: { + Name: "pselect6", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "nfds", + Type: ArgTypeInt, + }, + { + Name: "readfds", + Type: argTypeFdSet, + }, + { + Name: "writefds", + Type: argTypeFdSet, + }, + { + Name: "exceptfds", + Type: argTypeFdSet, + }, + { + Name: "timeout", + Type: argTypeTimeval, + }, + { + Name: "sigmask", + Type: ArgTypeAddress, // TODO: sigset type + }, + }, + }, + unix.SYS_PPOLL: { + Name: "ppoll", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fds", + Type: argTypePollFdArray, + }, + { + Name: "nfds", + Type: ArgTypeInt, + }, + { + Name: "timeout", + Type: argTypeTimespec, + }, + { + Name: "sigmask", + Type: ArgTypeAddress, // TODO: sigset type (check all sigmask properties) + }, + }, + }, + unix.SYS_UNSHARE: { + Name: "unshare", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateCloneFlags, + }, + }, + }, + unix.SYS_SET_ROBUST_LIST: { + Name: "set_robust_list", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "head", + Type: ArgTypeAddress, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_GET_ROBUST_LIST: { + Name: "get_robust_list", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "head_ptr", + Type: ArgTypeAddress, + }, + { + Name: "len_ptr", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_SPLICE: { + Name: "splice", + ReturnValue: ReturnMetadata{ + Type: ArgTypeInt, + }, + Args: []ArgMetadata{ + { + Name: "fd_in", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "off_in", + Type: ArgTypeAddress, + }, + { + Name: "fd_out", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "off_out", + Type: ArgTypeAddress, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSpliceFlags, + }, + }, + }, + unix.SYS_TEE: { + Name: "tee", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd_in", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "fd_out", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSpliceFlags, + }, + }, + }, + unix.SYS_SYNC_FILE_RANGE: { + Name: "sync_file_range", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "offset", + Type: ArgTypeInt, + }, + { + Name: "nbytes", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSyncFileRangeFlags, + }, + }, + }, + unix.SYS_VMSPLICE: { + Name: "vmsplice", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "iov", + Type: argTypeIovecArray, + Destination: true, + }, + { + Name: "nr_segs", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSpliceFlags, + }, + }, + }, + unix.SYS_MOVE_PAGES: { + Name: "move_pages", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "nr_pages", + Type: ArgTypeUnsignedLong, + }, + { + Name: "pages", + Type: ArgTypeAddress, + }, + { + Name: "nodes", + Type: argTypeIntArray, + }, + { + Name: "status", + Type: argTypeIntArray, + Destination: true, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateNumaModeFlag, + }, + }, + }, + unix.SYS_UTIMENSAT: { + Name: "utimensat", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dirfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "times", + Type: argTypeTimespecArray, + LenSource: LenSourceFixed, + FixedCount: 2, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + }, + }, + unix.SYS_EPOLL_PWAIT: { + Name: "epoll_pwait", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "epfd", + Type: ArgTypeInt, + }, + { + Name: "events", + Type: argTypeEpollEvent, + Destination: true, + }, + { + Name: "maxevents", + Type: ArgTypeInt, + }, + { + Name: "timeout", + Type: ArgTypeInt, + }, + { + Name: "sigmask", + Type: ArgTypeAddress, // TODO: sigset type + }, + }, + }, + unix.SYS_SIGNALFD: { + Name: "signalfd", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "mask", + Type: ArgTypeAddress, // TODO: sigset type + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignalFdFlags, + }, + }, + }, + unix.SYS_TIMERFD_CREATE: { + Name: "timerfd_create", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "clockid", + Type: ArgTypeInt, + Annotator: annotation.AnnotateClockID, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateTimerFdFlags, + }, + }, + }, + unix.SYS_EVENTFD: { + Name: "eventfd", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "count", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateEventFdFlags, + }, + }, + }, + unix.SYS_FALLOCATE: { + Name: "fallocate", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFallocateMode, + }, + { + Name: "offset", + Type: ArgTypeInt, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_TIMERFD_SETTIME: { + Name: "timerfd_settime", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateTimerFdFlags, + }, + { + Name: "utmr", + Type: argTypeItimerspec, + }, + { + Name: "otmr", + Type: argTypeItimerspec, + Destination: true, + }, + }, + }, + unix.SYS_TIMERFD_GETTIME: { + Name: "timerfd_gettime", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "otmr", + Type: argTypeItimerspec, + Destination: true, + }, + }, + }, + unix.SYS_ACCEPT4: { + Name: "accept4", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "addr", + Type: argTypeSockaddr, + LenSource: LenSourceNextPointer, + }, + { + Name: "addrlen", + Type: argTypeUnsignedIntPtr, + }, + }, + }, + unix.SYS_SIGNALFD4: { + Name: "signalfd4", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "mask", + Type: ArgTypeAddress, // TODO: sigset type + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignalFdFlags, + }, + }, + }, + unix.SYS_EVENTFD2: { + Name: "eventfd2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "count", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_EPOLL_CREATE1: { + Name: "epoll_create1", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_DUP3: { + Name: "dup3", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "oldfd", + Type: ArgTypeInt, + }, + { + Name: "newfd", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PIPE2: { + Name: "pipe2", + ReturnValue: ReturnMetadata{ + Type: ArgTypeErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pipefd", + Type: argTypeIntArray, + LenSource: LenSourceFixed, + FixedCount: 2, + Destination: true, + }, + { + Name: "flags", + Type: ArgTypeInt, + // TODO: annotate pipe2 flags + }, + }, + }, + unix.SYS_INOTIFY_INIT1: { + Name: "inotify_init1", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PREADV: { + Name: "preadv", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "iov", + Type: argTypeIovecArray, + Destination: true, + }, + { + Name: "iovcnt", + Type: ArgTypeInt, + }, + { + Name: "offset", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PWRITEV: { + Name: "pwritev", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "iov", + Type: argTypeIovecArray, + }, + { + Name: "iovcnt", + Type: ArgTypeInt, + }, + { + Name: "offset", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_RT_TGSIGQUEUEINFO: { + Name: "rt_tgsigqueueinfo", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "tgid", + Type: ArgTypeInt, + }, + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "sig", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignal, + }, + { + Name: "uinfo", + Type: argTypeSigInfo, + }, + }, + }, + unix.SYS_PERF_EVENT_OPEN: { + Name: "perf_event_open", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "attr", + Type: ArgTypeAddress, + }, + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "cpu", + Type: ArgTypeInt, + }, + { + Name: "group_fd", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeUnsignedLong, + Annotator: annotation.AnnotatePerfFlags, + }, + }, + }, + unix.SYS_RECVMMSG: { + Name: "recvmmsg", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "msgvec", + Type: argTypeMMsgHdrArray, + Destination: true, + }, + { + Name: "vlen", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + { + Name: "timeout", + Type: argTypeTimeval, + }, + }, + }, + unix.SYS_FANOTIFY_INIT: { + Name: "fanotify_init", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "flags", + Type: ArgTypeUnsignedLong, + Annotator: annotation.AnnotateFANotifyFlags, + }, + { + Name: "event_f_flags", + Type: ArgTypeUnsignedLong, + Annotator: annotation.AnnotateFANotifyEventFlags, + }, + }, + }, + unix.SYS_FANOTIFY_MARK: { + Name: "fanotify_mark", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fanotify_fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "flags", + Type: ArgTypeUnsignedLong, + Annotator: annotation.AnnotateFANotifyMarkFlags, + }, + { + Name: "mask", + Type: ArgTypeUnsignedLong, + }, + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + }, + }, + unix.SYS_PRLIMIT64: { + Name: "prlimit64", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "resource", + Type: ArgTypeInt, + Annotator: annotation.AnnotateRLimitResourceFlags, + }, + { + Name: "new_rlim", + Type: argTypeRLimit, + }, + { + Name: "old_rlim", + Type: argTypeRLimit, + Destination: true, + }, + }, + }, + unix.SYS_NAME_TO_HANDLE_AT: { + Name: "name_to_handle_at", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "handle", + Type: ArgTypeAddress, // TODO: create type for this + }, + { + Name: "mount_id", + Type: argTypeUnsignedIntPtr, + Destination: true, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateOpenFlags, + }, + }, + }, + unix.SYS_OPEN_BY_HANDLE_AT: { + Name: "open_by_handle_at", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "mount_fd", + Type: ArgTypeInt, + }, + { + Name: "handle", + Type: ArgTypeAddress, // TODO: create type for this + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateOpenFlags, + }, + }, + }, + unix.SYS_CLOCK_ADJTIME: { + Name: "clock_adjtime", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "clk_id", + Type: ArgTypeInt, + Annotator: annotation.AnnotateClockID, + }, + { + Name: "utx", + Type: argTypeTimex, + }, + }, + }, + unix.SYS_SYNCFS: { + Name: "syncfs", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + }, + }, + unix.SYS_SENDMMSG: { + Name: "sendmmsg", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "msgvec", + Type: argTypeMMsgHdrArray, + }, + { + Name: "vlen", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMsgFlags, + }, + }, + }, + unix.SYS_SETNS: { + Name: "setns", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "nstype", + Type: ArgTypeInt, + Annotator: annotation.AnnotateCloneFlags, + }, + }, + }, + unix.SYS_GETCPU: { + Name: "getcpu", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "cpu", + Type: argTypeUnsignedIntPtr, + Destination: true, + }, + { + Name: "node", + Type: argTypeUnsignedIntPtr, + Destination: true, + }, + { + Name: "tcache", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, + }, + }, + }, + unix.SYS_PROCESS_VM_READV: { + Name: "process_vm_readv", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "local_iov", + Type: argTypeIovecArray, + }, + { + Name: "liovcnt", + Type: ArgTypeInt, + }, + { + Name: "remote_iov", + Type: argTypeIovecArray, + }, + { + Name: "riovcnt", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PROCESS_VM_WRITEV: { + Name: "process_vm_writev", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "local_iov", + Type: argTypeIovecArray, + }, + { + Name: "liovcnt", + Type: ArgTypeInt, + }, + { + Name: "remote_iov", + Type: argTypeIovecArray, + }, + { + Name: "riovcnt", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_KCMP: { + Name: "kcmp", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid1", + Type: ArgTypeInt, + }, + { + Name: "pid2", + Type: ArgTypeInt, + }, + { + Name: "type", + Type: ArgTypeInt, + Annotator: annotation.AnnotateKcmpType, + }, + { + Name: "idx1", + Type: ArgTypeUnsignedLong, + }, + { + Name: "idx2", + Type: ArgTypeUnsignedLong, + }, + }, + }, + unix.SYS_FINIT_MODULE: { + Name: "finit_module", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "uargs", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateModuleInitFlags, + }, + }, + }, + unix.SYS_SCHED_SETATTR: { + Name: "sched_setattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "attr", + Type: argTypeSchedAttr, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSchedPolicy, + }, + }, + }, + unix.SYS_SCHED_GETATTR: { + Name: "sched_getattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "attr", + Type: argTypeSchedAttr, + Destination: true, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSchedPolicy, + }, + }, + }, + unix.SYS_RENAMEAT2: { + Name: "renameat2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "oldfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "oldname", + Type: argTypeString, + }, + { + Name: "newfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "newname", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + }, + }, + unix.SYS_SECCOMP: { + Name: "seccomp", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "op", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSeccompOp, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + { + Name: "uargs", + Type: ArgTypeAddress, + }, + // TODO: add a modifier to annotate flags and args depending on op + }, + }, + unix.SYS_GETRANDOM: { + Name: "getrandom", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "buf", + Type: ArgTypeData, + LenSource: LenSourceReturnValue, + Destination: true, + }, + { + Name: "count", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateRandomFlags, + }, + }, + }, + unix.SYS_MEMFD_CREATE: { + Name: "memfd_create", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "name", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMemfdFlags, + }, + }, + }, + unix.SYS_KEXEC_FILE_LOAD: { + Name: "kexec_file_load", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "kernel_fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "initrd_fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "cmdline_len", + Type: ArgTypeInt, + }, + { + Name: "cmdline_ptr", + Type: ArgTypeAddress, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateKexecFlags, + }, + }, + }, + unix.SYS_BPF: { + Name: "bpf", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "cmd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateBPFCmd, + }, + { + Name: "attr", + Type: ArgTypeAddress, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_EXECVEAT: { + Name: "execveat", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dirfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "argv", + Type: argTypeStringArray, + }, + { + Name: "envp", + Type: argTypeStringArray, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + }, + }, + unix.SYS_USERFAULTFD: { + Name: "userfaultfd", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateOpenFlags, + }, + }, + }, + unix.SYS_MEMBARRIER: { + Name: "membarrier", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "cmd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMembarrierCmd, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMembarrierCmdFlags, + }, + { + Name: "cpuid", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_MLOCK2: { + Name: "mlock2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "addr", + Type: ArgTypeAddress, + Annotator: annotation.AnnotateNull, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMlockFlags, + }, + }, + }, + unix.SYS_COPY_FILE_RANGE: { + Name: "copy_file_range", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd_in", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "off_in", + Type: argTypeUnsignedInt64Ptr, + }, + { + Name: "fd_out", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "off_out", + Type: argTypeUnsignedInt64Ptr, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PREADV2: { + Name: "preadv2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "iov", + Type: argTypeIovecArray, + Destination: true, + }, + { + Name: "iovcnt", + Type: ArgTypeInt, + }, + { + Name: "offset", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotatePReadWrite2Flags, + }, + }, + }, + unix.SYS_PWRITEV2: { + Name: "pwritev2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "iov", + Type: argTypeIovecArray, + Destination: true, + }, + { + Name: "iovcnt", + Type: ArgTypeInt, + }, + { + Name: "offset", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotatePReadWrite2Flags, + }, + }, + }, + unix.SYS_PKEY_MPROTECT: { + Name: "pkey_mprotect", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "addr", + Type: ArgTypeAddress, + }, + { + Name: "len", + Type: ArgTypeInt, + }, + { + Name: "prot", + Type: ArgTypeInt, + Annotator: annotation.AnnotateProt, + }, + { + Name: "pkey", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PKEY_ALLOC: { + Name: "pkey_alloc", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "flags", + Type: ArgTypeInt, + }, + { + Name: "access_rights", + Type: ArgTypeInt, + Annotator: annotation.AnnotatePkeyAccessRights, + }, + }, + }, + unix.SYS_PKEY_FREE: { + Name: "pkey_free", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pkey", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_STATX: { + Name: "statx", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dirfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + { + Name: "mask", + Type: ArgTypeInt, + Annotator: annotation.AnnotateStatxMask, + }, + { + Name: "statxbuf", + Type: argTypeStatX, + Destination: true, + }, + }, + }, + unix.SYS_IO_PGETEVENTS: { + Name: "io_pgetevents", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ctx_id", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "nr", + Type: ArgTypeUnsignedLong, + }, + { + Name: "events", + Type: argTypeIoEvents, + Destination: true, + }, + { + Name: "timeout", + Type: argTypeTimespec, + }, + { + Name: "usig", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_RSEQ: { + Name: "rseq", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "rseq", + Type: ArgTypeAddress, + }, + { + Name: "rseq_len", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + { + Name: "sig", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignal, + }, + }, + }, + unix.SYS_PIDFD_SEND_SIGNAL: { + Name: "pidfd_send_signal", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pidfd", + Type: ArgTypeInt, + }, + { + Name: "sig", + Type: ArgTypeInt, + Annotator: annotation.AnnotateSignal, + }, + { + Name: "info", + Type: argTypeSigInfo, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_IO_URING_SETUP: { + Name: "io_uring_setup", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "entries", + Type: ArgTypeUnsignedInt, + }, + { + Name: "params", + Type: argTypeIoUringParams, + Destination: true, + }, + }, + }, + unix.SYS_IO_URING_ENTER: { + Name: "io_uring_enter", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "to_submit", + Type: ArgTypeUnsignedInt, + }, + { + Name: "min_complete", + Type: ArgTypeUnsignedInt, + }, + { + Name: "flags", + Type: ArgTypeUnsignedInt, + Annotator: annotation.AnnotateIoUringEnterFlags, + }, + { + Name: "sig", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_IO_URING_REGISTER: { + Name: "io_uring_register", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "opcode", + Type: ArgTypeUnsignedInt, + Annotator: annotation.AnnotateIORingOpCode, + }, + { + Name: "arg", + Type: ArgTypeAddress, + }, + { + Name: "nr_args", + Type: ArgTypeUnsignedInt, + }, + }, + }, + unix.SYS_OPEN_TREE: { + Name: "open_tree", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "filename", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_MOVE_MOUNT: { + Name: "move_mount", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "from_dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "from_pathname", + Type: argTypeString, + }, + { + Name: "to_dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "to_pathname", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FSOPEN: { + Name: "fsopen", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fs_name", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FSCONFIG: { + Name: "fsconfig", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fs_fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "cmd", + Type: ArgTypeInt, + }, + { + Name: "key", + Type: argTypeString, + }, + { + Name: "value", + Type: argTypeString, + }, + { + Name: "aux", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FSMOUNT: { + Name: "fsmount", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fs_fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + { + Name: "ms_flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FSPICK: { + Name: "fspick", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "path", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PIDFD_OPEN: { + Name: "pidfd_open", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pid", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_CLONE3: { + Name: "clone3", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "clone_args", + Type: argTypeCloneArgs, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_CLOSE_RANGE: { + Name: "close_range", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "first", + Type: ArgTypeInt, + }, + { + Name: "last", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateCloseRangeFlags, + }, + }, + }, + unix.SYS_OPENAT2: { + Name: "openat2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dirfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "how", + Type: argTypeOpenHow, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_PIDFD_GETFD: { + Name: "pidfd_getfd", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pidfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "targetfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FACCESSAT2: { + Name: "faccessat2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "dfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "pathname", + Type: argTypeString, + }, + { + Name: "mode", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAccMode, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + Optional: true, + }, + }, + }, + unix.SYS_PROCESS_MADVISE: { + Name: "process_madvise", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pidfd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "iovec", + Type: argTypeIovecArray, + }, + { + Name: "iovcnt", + Type: ArgTypeInt, + }, + { + Name: "advice", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMAdviseAdvice, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_EPOLL_PWAIT2: { + Name: "epoll_pwait2", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "epfd", + Type: ArgTypeInt, + }, + { + Name: "events", + Type: argTypeEpollEvent, + Destination: true, + }, + { + Name: "maxevents", + Type: ArgTypeInt, + }, + { + Name: "timeout", + Type: argTypeTimespec, + }, + { + Name: "sigmask", + Type: ArgTypeAddress, // TODO: sigset type + }, + }, + }, + unix.SYS_MOUNT_SETATTR: { + Name: "mount_setattr", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "path", + Type: argTypeString, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateAtFlags, + }, + { + Name: "uattr", + Type: argTypeMountAttr, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_QUOTACTL_FD: { + Name: "quotactl_fd", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "fd", + Type: ArgTypeInt, + Annotator: annotation.AnnotateFd, + }, + { + Name: "cmd", + Type: ArgTypeInt, + }, + { + Name: "id", + Type: ArgTypeInt, + }, + { + Name: "addr", + Type: ArgTypeAddress, + }, + }, + }, + unix.SYS_LANDLOCK_CREATE_RULESET: { + Name: "landlock_create_ruleset", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ruleset_attr", + Type: argTypeLandlockRulesetAttr, + }, + { + Name: "size", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateLandlockFlags, + }, + }, + }, + unix.SYS_LANDLOCK_ADD_RULE: { + Name: "landlock_add_rule", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ruleset_fd", + Type: ArgTypeInt, + }, + { + Name: "rule_type", + Type: ArgTypeInt, + Annotator: annotation.AnnotateLandlockRuleType, + }, + { + Name: "rule_attr", + Type: ArgTypeAddress, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_LANDLOCK_RESTRICT_SELF: { + Name: "landlock_restrict_self", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "ruleset_fd", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_MEMFD_SECRET: { + Name: "memfd_secret", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "flags", + Type: ArgTypeInt, + Annotator: annotation.AnnotateMemfdFlags, + }, + }, + }, + unix.SYS_PROCESS_MRELEASE: { + Name: "process_mrelease", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "pidfd", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + unix.SYS_FUTEX_WAITV: { + Name: "futex_waitv", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "waiters", + Type: ArgTypeAddress, + }, + { + Name: "nr_waiters", + Type: ArgTypeInt, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + { + Name: "timeout", + Type: argTypeTimespec, + }, + { + Name: "clockid", + Type: ArgTypeInt, + Annotator: annotation.AnnotateClockID, + }, + }, + }, + unix.SYS_SET_MEMPOLICY_HOME_NODE: { + Name: "set_mempolicy_home_node", + ReturnValue: ReturnMetadata{ + Type: argTypeIntOrErrorCode, + }, + Args: []ArgMetadata{ + { + Name: "start", + Type: ArgTypeAddress, + }, + { + Name: "len", + Type: ArgTypeUnsignedLong, + }, + { + Name: "home_node", + Type: ArgTypeUnsignedLong, + }, + { + Name: "flags", + Type: ArgTypeInt, + }, + }, + }, + } +) + +func trimToNull(arg annotation.Arg, _ int) { + // clean up terminating null byte from output + underlying := arg.(*Arg) + if index := bytes.Index(underlying.data, []byte{0}); index >= 0 { + underlying.data = underlying.data[:index] + } +} diff --git a/tracer/sys_arm64.go b/tracer/sys_arm64.go new file mode 100644 index 0000000..d445fa8 --- /dev/null +++ b/tracer/sys_arm64.go @@ -0,0 +1,29 @@ +//go:build arm64 + +package tracer + +import ( + "syscall" +) + +const bitSize = 64 + +// useful info: https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md + +func parseSyscall(regs *syscall.PtraceRegs) *Syscall { + return &Syscall{ + number: int(regs.Regs[8]), + rawArgs: [6]uintptr{ + uintptr(regs.Regs[0]), + uintptr(regs.Regs[1]), + uintptr(regs.Regs[2]), + uintptr(regs.Regs[3]), + uintptr(regs.Regs[4]), + uintptr(regs.Regs[5]), + }, + rawRet: uintptr(regs.Regs[0]), + } +} + +// TODO: add syscall table for arm64 +var sysMap = map[int]SyscallMetadata{} diff --git a/tracer/sys_test.go b/tracer/sys_test.go index b06885f..8d45382 100644 --- a/tracer/sys_test.go +++ b/tracer/sys_test.go @@ -9,12 +9,33 @@ import ( ) func Test_SyscallSupport(t *testing.T) { - for i := 0; i <= 335; i++ { - t.Run(fmt.Sprintf("syscall %d", i), func(t *testing.T) { - meta, err := LookupSyscall(i) - require.NoError(t, err) - require.NotNil(t, meta) - assert.NotEmpty(t, meta.Name) + + for number, meta := range sysMap { + t.Run(fmt.Sprintf("syscall %d: %s", number, meta.Name), func(t *testing.T) { + checkSyscall(t, number, meta) }) } } + +func checkSyscall(t *testing.T, number int, meta SyscallMetadata) { + require.NotNil(t, meta) + assert.NotEmpty(t, meta.Name) + assert.NotEqualf(t, ArgTypeUnknown, meta.ReturnValue.Type, "syscall %d (%s) has unspecified return value type", number, meta.Name) + for i, arg := range meta.Args { + assert.NotEqualf(t, ArgTypeUnknown, arg.Type, "syscall %d (%s) has unspecified argument type", number, meta.Name) + switch arg.Type { + case ArgTypeData: + assert.NotEqual(t, LenSourceNone, arg.LenSource) + } + switch arg.LenSource { + case LenSourceFixed: + assert.NotZero(t, arg.FixedCount) + case LenSourceNextPointer, LenSourceNext: + assert.Less(t, i, len(meta.Args)-1) + case LenSourcePrev: + assert.Greater(t, i, 0) + case LenSourceReturnValue: + assert.NotEqual(t, ArgTypeErrorCode, meta.ReturnValue.Type) + } + } +} diff --git a/tracer/tracer.go b/tracer/tracer.go index eecc936..ccccd2a 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -13,14 +13,17 @@ type Tracer struct { handlers struct { syscallExit func(*Syscall) syscallEnter func(*Syscall) - signal func(syscall.Signal) + signal func(*SigInfo) processExit func(int) - } - pid int - cmd *exec.Cmd - isExit bool - lastCall *Syscall - lastSignal int + attach func(int) + detach func(int) + } + pid int + cmd *exec.Cmd + isExit bool + lastCall *Syscall + lastSignal int + receivedSignal syscall.Signal } func New(pid int) *Tracer { @@ -59,7 +62,7 @@ func (t *Tracer) SetSyscallEnterHandler(handler func(*Syscall)) { t.handlers.syscallEnter = handler } -func (t *Tracer) SetSignalHandler(handler func(syscall.Signal)) { +func (t *Tracer) SetSignalHandler(handler func(*SigInfo)) { t.handlers.signal = handler } @@ -67,6 +70,14 @@ func (t *Tracer) SetProcessExitHandler(handler func(int)) { t.handlers.processExit = handler } +func (t *Tracer) SetAttachHandler(handler func(int)) { + t.handlers.attach = handler +} + +func (t *Tracer) SetDetachHandler(handler func(int)) { + t.handlers.detach = handler +} + func (t *Tracer) Start() error { runtime.LockOSThread() @@ -81,6 +92,11 @@ func (t *Tracer) Start() error { } else if err != nil { return err } + + } + + if t.handlers.attach != nil { + t.handlers.attach(t.pid) } status := syscall.WaitStatus(0) @@ -88,10 +104,17 @@ func (t *Tracer) Start() error { return err } + defer func() { + if t.handlers.detach != nil { + t.handlers.detach(t.pid) + } + }() + if t.cmd == nil { defer func() { _ = syscall.PtraceDetach(t.pid) _, _ = syscall.Wait4(t.pid, &status, 0, nil) + }() } @@ -104,8 +127,7 @@ func (t *Tracer) Start() error { signal.Notify(signalChan, syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGPIPE, syscall.SIGQUIT) go func() { for sig := range signalChan { - interrupted := sig.(syscall.Signal) - fmt.Printf("((SIGNAL: %s))", interrupted) // TODO + t.receivedSignal = sig.(syscall.Signal) _ = syscall.Kill(t.pid, syscall.SIGSTOP) } }() @@ -123,11 +145,11 @@ func (t *Tracer) loop() error { } return err } + if t.receivedSignal > 0 { + break + } } -} - -func isStopSig(sig syscall.Signal) bool { - return sig == syscall.SIGSTOP || sig == syscall.SIGTSTP || sig == syscall.SIGTTIN || sig == syscall.SIGTTOU + return nil } func (t *Tracer) waitForSyscall() error { @@ -158,14 +180,18 @@ func (t *Tracer) waitForSyscall() error { if status.StopSignal() != syscall.SIGTRAP|0x80 { - // NOTE: once https://groups.google.com/g/golang-codereviews/c/t2SwaIV-hFs is merged, we can use - // syscall.PtraceGetSigInfo() to retrieve the siginfo_t struct and pass it to the signal handler instead if t.handlers.signal != nil { - t.handlers.signal(status.StopSignal()) + info, err := getSignalInfo(t.pid) + if err != nil { + return err + } + t.handlers.signal(info) } - if isStopSig(status.StopSignal()) { - // TODO: if we received an interrupt via notify above, return an error here to break the loop + if sig := status.StopSignal(); sig == syscall.SIGSTOP || sig == syscall.SIGTSTP || sig == syscall.SIGTTIN || sig == syscall.SIGTTOU { + if t.receivedSignal != 0 { + return nil + } t.lastSignal = int(status.StopSignal()) } else if t.lastSignal != 0 { if status.StopSignal() == syscall.SIGCONT { @@ -180,12 +206,6 @@ func (t *Tracer) waitForSyscall() error { return nil } - // if interrupted, stop tracing - if status.StopSignal().String() == "interrupt" { - _ = syscall.PtraceSyscall(t.pid, int(status.StopSignal())) - return fmt.Errorf("process interrupted") - } - // read registers regs := &syscall.PtraceRegs{} if err := syscall.PtraceGetRegs(t.pid, regs); err != nil { @@ -202,6 +222,7 @@ func (t *Tracer) waitForSyscall() error { if t.isExit && t.lastCall != nil { if call.number == t.lastCall.number { call.args = t.lastCall.args + call.paths = t.lastCall.paths } else { return fmt.Errorf("syscall exit mismatch: %d != %d - this is likely a bug in grace due to an unprocessed signal", call.number, t.lastCall.number) } diff --git a/tracer/type.go b/tracer/type.go new file mode 100644 index 0000000..62540d2 --- /dev/null +++ b/tracer/type.go @@ -0,0 +1,114 @@ +package tracer + +import ( + "fmt" + "sync" +) + +type ArgType int + +const ( + // public + ArgTypeUnknown ArgType = iota + ArgTypeData + ArgTypeInt + ArgTypeLong + ArgTypeAddress + ArgTypeUnsignedInt + ArgTypeUnsignedLong + ArgTypeObject + ArgTypeErrorCode + ArgTypeArray + + argStartInternal // TODO: use this to test all type converters are registered + + // internal + argTypeString + argTypeSockaddr + argTypeIntOrErrorCode + argTypeStat + argTypePollFdArray + argTypeSigAction + argTypeIovecArray + argTypeIntArray + argTypeStringArray + argTypeFdSet + argTypeTimeval + argTypeTimevalArray + argTypeTimezone + argTypeSHMIDDS + argTypeTimespec + argTypeTimespecArray + argTypeItimerval + argTypeMsghdr + argTypeUnsignedIntPtr + argTypeUnsignedInt64Ptr + argTypeSockoptval + argTypeWaitStatus + argTypeRUsage + argTypeRLimit + argTypeUname + argTypeSembuf + argTypeSysinfo + argTypeTms + argTypeCapUserHeader + argTypeCapUserData + argTypeSigInfo + argTypeStack + argTypeUtimbuf + argTypeUstat + argTypeStatfs + argTypeSchedParam + argTypeUserDesc + argTypeTimex + argTypeIoEvent + argTypeIoEvents + argTypeIoCB + argTypeItimerspec + argTypeEpollEvent + argTypeMqAttr + argTypeMMsgHdrArray + argTypeSchedAttr + argTypeStatX + argTypeIoUringParams + argTypeCloneArgs + argTypeOpenHow + argTypeMountAttr + argTypeLandlockRulesetAttr + + argEndInternal +) + +type typeHandler func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error + +var typesRegistry = map[ArgType]typeHandler{} +var typesRegistryMutex = sync.RWMutex{} + +func registerTypeHandler(t ArgType, h typeHandler) { + typesRegistryMutex.Lock() + defer typesRegistryMutex.Unlock() + if _, ok := typesRegistry[t]; ok { + panic(fmt.Sprintf("type handler for %d already registered", t)) + } + typesRegistry[t] = h +} + +func getHandler(t ArgType) typeHandler { + typesRegistryMutex.RLock() + defer typesRegistryMutex.RUnlock() + return typesRegistry[t] +} + +func handleType(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) (err error) { + typesRegistryMutex.RLock() + defer typesRegistryMutex.RUnlock() + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("error handling type %s with value 0x%x: %v", metadata.Name, raw, r) + } + }() + if h, ok := typesRegistry[metadata.Type]; ok { + return h(arg, metadata, raw, next, prev, ret, pid) + } + return nil +} diff --git a/tracer/type_test.go b/tracer/type_test.go new file mode 100644 index 0000000..64fd1e1 --- /dev/null +++ b/tracer/type_test.go @@ -0,0 +1,16 @@ +package tracer + +import ( + "fmt" + "testing" +) + +func TestTypeConversions(t *testing.T) { + for i := argStartInternal + 1; i < argEndInternal; i++ { + t.Run(fmt.Sprintf("ArgType %d", i), func(t *testing.T) { + if getHandler(i) == nil { + t.Errorf("No handler for type %d", i) + } + }) + } +} diff --git a/tracer/types.go b/tracer/types.go deleted file mode 100644 index 15d786d..0000000 --- a/tracer/types.go +++ /dev/null @@ -1,48 +0,0 @@ -package tracer - -import ( - "fmt" - "sync" -) - -type ArgType int - -const ( - ArgTypeUnknown ArgType = iota - ArgTypeData - ArgTypeInt - ArgTypeStat - ArgTypeLong - ArgTypeAddress - ArgTypeUnsignedInt - ArgTypeUnsignedLong - ArgTypePollFdArray - ArgTypeObject - ArgTypeErrorCode - ArgTypeSigAction - ArgTypeIovecArray - ArgTypeIntArray -) - -type typeHandler func(arg *Arg, metadata ArgMetadata, raw uintptr, next uintptr, ret uintptr, pid int) error - -var typesRegistry = map[ArgType]typeHandler{} -var typesRegistryMutex = sync.RWMutex{} - -func registerTypeHandler(t ArgType, h typeHandler) { - typesRegistryMutex.Lock() - defer typesRegistryMutex.Unlock() - if _, ok := typesRegistry[t]; ok { - panic(fmt.Sprintf("type handler for %d already registered", t)) - } - typesRegistry[t] = h -} - -func handleType(arg *Arg, metadata ArgMetadata, raw uintptr, next uintptr, ret uintptr, pid int) error { - typesRegistryMutex.RLock() - defer typesRegistryMutex.RUnlock() - if h, ok := typesRegistry[metadata.Type]; ok { - return h(arg, metadata, raw, next, ret, pid) - } - return nil -} diff --git a/tracer/types_cap.go b/tracer/types_cap.go new file mode 100644 index 0000000..e5aa130 --- /dev/null +++ b/tracer/types_cap.go @@ -0,0 +1,98 @@ +package tracer + +import ( + "unsafe" +) + +type capHeader struct { + Version uint32 + Pid int32 +} + +type capData struct { + Effective uint32 + Permitted uint32 + Inheritable uint32 +} + +func init() { + registerTypeHandler(argTypeCapUserHeader, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + mem, err := readSize(pid, raw, unsafe.Sizeof(capHeader{})) + if err != nil { + return err + } + var cap capHeader + if err := decodeStruct(mem, &cap); err != nil { + return err + } + arg.obj = convertCapHeader(&cap) + arg.t = ArgTypeObject + } else { + arg.t = ArgTypeAddress + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) + registerTypeHandler(argTypeCapUserData, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + mem, err := readSize(pid, raw, unsafe.Sizeof(capData{})) + if err != nil { + return err + } + var cap capData + if err := decodeStruct(mem, &cap); err != nil { + return err + } + arg.obj = convertCapData(&cap) + arg.t = ArgTypeObject + } else { + arg.t = ArgTypeAddress + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertCapHeader(cap *capHeader) *Object { + return &Object{ + Name: "hdr", + Properties: []Arg{ + { + name: "version", + t: ArgTypeInt, + raw: uintptr(cap.Version), + }, + { + name: "pid", + t: ArgTypeInt, + raw: uintptr(cap.Pid), + }, + }, + } +} + +func convertCapData(cap *capData) *Object { + return &Object{ + Name: "data", + Properties: []Arg{ + { + name: "effective", + t: ArgTypeInt, + raw: uintptr(cap.Effective), + }, + { + name: "permitted", + t: ArgTypeInt, + raw: uintptr(cap.Permitted), + }, + { + name: "inheritable", + t: ArgTypeInt, + raw: uintptr(cap.Inheritable), + }, + }, + } +} diff --git a/tracer/types_cloneargs.go b/tracer/types_cloneargs.go new file mode 100644 index 0000000..2804ac4 --- /dev/null +++ b/tracer/types_cloneargs.go @@ -0,0 +1,108 @@ +package tracer + +import ( + "unsafe" +) + +// used by clone3 syscall +type cloneArgs struct { + Flags uint64 + PidFd uint64 + ChildTid uint64 + ParentTid uint64 + ExitSignal uint64 + Stack uint64 + StackSize uint64 + SetTLS uint64 + SetTid uint64 + SetTidSize uint64 + Cgroup uint64 +} + +func init() { + registerTypeHandler(argTypeCloneArgs, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(cloneArgs{})) + if err != nil { + return err + } + + var args cloneArgs + if err := decodeStruct(mem, &args); err != nil { + return err + } + + arg.obj = convertCloneArgs(args) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertCloneArgs(args cloneArgs) *Object { + return &Object{ + Name: "clone_args", + Properties: []Arg{ + { + name: "flags", + t: ArgTypeUnsignedLong, + raw: uintptr(args.Flags), + }, + { + name: "pid_fd", + t: ArgTypeUnsignedLong, + raw: uintptr(args.PidFd), + }, + { + name: "child_tid", + t: ArgTypeUnsignedLong, + raw: uintptr(args.ChildTid), + }, + { + name: "parent_tid", + t: ArgTypeUnsignedLong, + raw: uintptr(args.ParentTid), + }, + { + name: "exit_signal", + t: ArgTypeUnsignedLong, + raw: uintptr(args.ExitSignal), + }, + { + name: "stack", + t: ArgTypeUnsignedLong, + raw: uintptr(args.Stack), + }, + { + name: "stack_size", + t: ArgTypeUnsignedLong, + raw: uintptr(args.StackSize), + }, + { + name: "set_tls", + t: ArgTypeUnsignedLong, + raw: uintptr(args.SetTLS), + }, + { + name: "set_tid", + t: ArgTypeUnsignedLong, + raw: uintptr(args.SetTid), + }, + { + name: "set_tid_size", + t: ArgTypeUnsignedLong, + raw: uintptr(args.SetTidSize), + }, + { + name: "cgroup", + t: ArgTypeUnsignedLong, + raw: uintptr(args.Cgroup), + }, + }, + } +} diff --git a/tracer/types_data.go b/tracer/types_data.go index f4fa39c..4130fa0 100644 --- a/tracer/types_data.go +++ b/tracer/types_data.go @@ -6,26 +6,38 @@ import ( ) func init() { - registerTypeHandler(ArgTypeData, func(arg *Arg, metadata ArgMetadata, raw uintptr, next uintptr, ret uintptr, pid int) error { - switch metadata.CountFrom { - case CountLocationNext: - data, err := readSize(pid, raw, next) + registerTypeHandler(ArgTypeData, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + switch metadata.LenSource { + case LenSourceNextPointer: + if next == 0 { + return nil + } + if buf, err := readSize(pid, next, 4); err == nil { + size := uintptr(decodeInt(buf)) + data, err := readSize(pid, raw, size) + if err != nil { + return err + } + arg.data = data + } + case LenSourcePrev: + data, err := readSize(pid, raw, prev) if err != nil { return err } arg.data = data - case CountLocationResult: - data, err := readSize(pid, raw, ret) + case LenSourceNext: + data, err := readSize(pid, raw, next) if err != nil { return err } arg.data = data - case CountLocationNullTerminator: - str, err := readString(pid, raw) + case LenSourceReturnValue: + data, err := readSize(pid, raw, ret) if err != nil { return err } - arg.data = []byte(str) + arg.data = data default: return fmt.Errorf("syscall %s has no supported count location", metadata.Name) } @@ -34,7 +46,8 @@ func init() { } func readSize(pid int, addr uintptr, size uintptr) ([]byte, error) { - if size == 0 { + + if size == 0 || size>>(bitSize-1) == 1 { // if negative for this arch return nil, nil } data := make([]byte, size) @@ -44,22 +57,3 @@ func readSize(pid int, addr uintptr, size uintptr) ([]byte, error) { } return data[:count], nil } - -func readString(pid int, addr uintptr) (string, error) { - var output string - if addr == 0 { - return output, nil - } - data := make([]byte, 1) - for { - if _, err := syscall.PtracePeekData(pid, addr, data); err != nil { - return "", fmt.Errorf("read of 0x%x failed: %w", addr, err) - } - if data[0] == 0 { - break - } - output += string(data) - addr++ - } - return output, nil -} diff --git a/tracer/types_epollevent.go b/tracer/types_epollevent.go new file mode 100644 index 0000000..eb44ead --- /dev/null +++ b/tracer/types_epollevent.go @@ -0,0 +1,53 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeEpollEvent, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.EpollEvent{})) + if err != nil { + return err + } + + var event unix.EpollEvent + if err := decodeStruct(rawTimeVal, &event); err != nil { + return err + } + + arg.obj = convertEpollEvent(&event) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertEpollEvent(ev *unix.EpollEvent) *Object { + if ev == nil { + return nil + } + return &Object{ + Name: "epoll_event", + Properties: []Arg{ + { + name: "events", + t: ArgTypeUnsignedLong, + raw: uintptr(ev.Events), + }, + { + name: "fd", + t: ArgTypeAddress, + raw: uintptr(ev.Fd), + }, + }, + } +} diff --git a/tracer/types_error.go b/tracer/types_error.go new file mode 100644 index 0000000..e91a922 --- /dev/null +++ b/tracer/types_error.go @@ -0,0 +1,12 @@ +package tracer + +func init() { + registerTypeHandler(argTypeIntOrErrorCode, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if arg.Int() < 0 { + arg.t = ArgTypeErrorCode + } else { + arg.t = ArgTypeInt + } + return nil + }) +} diff --git a/tracer/types_fdset.go b/tracer/types_fdset.go new file mode 100644 index 0000000..93088bf --- /dev/null +++ b/tracer/types_fdset.go @@ -0,0 +1,49 @@ +package tracer + +import ( + "unsafe" + + "github.com/liamg/grace/tracer/annotation" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeFdSet, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + // read the raw C struct from the process memory + rawFdset, err := readSize(pid, raw, unsafe.Sizeof(unix.FdSet{})) + if err != nil { + return err + } + + // safely squish it into a struct in our own memory space + var fdset unix.FdSet + if err := decodeStruct(rawFdset, &fdset); err != nil { + return err + } + + // convert into a nice array for output + arg.array = convertFdset(&fdset, pid) + } + arg.t = ArgTypeArray + return nil + }) +} + +func convertFdset(fdset *unix.FdSet, pid int) []Arg { + var fds []Arg + for _, fd := range fdset.Bits { + if fd == 0 { + break + } + item := Arg{ + name: "fd", + t: ArgTypeInt, + raw: uintptr(fd), + } + annotation.AnnotateFd(&item, pid) + fds = append(fds, item) + } + return fds +} diff --git a/tracer/types_int.go b/tracer/types_int.go index dec7e7c..ba890fe 100644 --- a/tracer/types_int.go +++ b/tracer/types_int.go @@ -6,14 +6,16 @@ import ( ) func init() { - registerTypeHandler(ArgTypeIntArray, func(arg *Arg, metadata ArgMetadata, raw uintptr, next uintptr, ret uintptr, pid int) error { + registerTypeHandler(argTypeIntArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { var count int - switch metadata.CountFrom { - case CountLocationNext: + switch metadata.LenSource { + case LenSourcePrev: + count = int(prev) + case LenSourceNext: count = int(next) - case CountLocationResult: + case LenSourceReturnValue: count = int(ret) - case CountLocationFixed: + case LenSourceFixed: count = metadata.FixedCount default: return fmt.Errorf("syscall %s has no supported count location", metadata.Name) @@ -25,7 +27,7 @@ func init() { } target := make([]int32, count) - if err := decodeAnonymous(reflect.ValueOf(target), mem); err != nil { + if err := decodeAnonymous(reflect.ValueOf(&target).Elem(), mem); err != nil { return err } @@ -36,6 +38,7 @@ func init() { raw: uintptr(target[i]), }) } + arg.t = ArgTypeArray return nil }) diff --git a/tracer/types_int_ptr.go b/tracer/types_int_ptr.go new file mode 100644 index 0000000..730bfd9 --- /dev/null +++ b/tracer/types_int_ptr.go @@ -0,0 +1,36 @@ +package tracer + +import ( + "unsafe" +) + +func init() { + registerTypeHandler(argTypeUnsignedIntPtr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + var underlying uint32 + if buf, err := readSize(pid, next, unsafe.Sizeof(underlying)); err == nil { + arg.raw = uintptr(decodeInt(buf)) + arg.t = ArgTypeUnsignedInt + } else { + arg.t = ArgTypeAddress + if raw == 0 { + arg.annotation = "NULL" + arg.replace = true + } + } + return nil + }) + registerTypeHandler(argTypeUnsignedInt64Ptr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + var underlying uint64 + if buf, err := readSize(pid, next, unsafe.Sizeof(underlying)); err == nil { + arg.raw = uintptr(decodeInt(buf)) + arg.t = ArgTypeUnsignedInt + } else { + arg.t = ArgTypeAddress + if raw == 0 { + arg.annotation = "NULL" + arg.replace = true + } + } + return nil + }) +} diff --git a/tracer/types_iocb.go b/tracer/types_iocb.go new file mode 100644 index 0000000..ef3c142 --- /dev/null +++ b/tracer/types_iocb.go @@ -0,0 +1,114 @@ +package tracer + +import ( + "unsafe" +) + +type iocb struct { + Data uint64 + Key uint32 + Opcode uint16 + Priority uint16 + Flags uint32 + Fd uint32 + Offset uint64 + Addr uint64 + Len uint32 + Pos uint64 + Reserved2 uint32 + Reserved3 uint64 +} + +func init() { + registerTypeHandler(argTypeIoCB, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(ioevent{})) + if err != nil { + return err + } + + var rawIoCB iocb + if err := decodeStruct(mem, &rawIoCB); err != nil { + return err + } + + arg.t = ArgTypeObject + arg.obj = convertIoCB(rawIoCB) + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertIoCB(cb iocb) *Object { + return &Object{ + Name: "iocb", + Properties: []Arg{ + { + name: "data", + t: ArgTypeUnsignedLong, + raw: uintptr(cb.Data), + }, + { + name: "key", + t: ArgTypeUnsignedInt, + raw: uintptr(cb.Key), + }, + { + name: "opcode", + t: ArgTypeUnsignedInt, + raw: uintptr(cb.Opcode), + }, + { + name: "priority", + t: ArgTypeUnsignedInt, + raw: uintptr(cb.Priority), + }, + { + name: "flags", + t: ArgTypeUnsignedInt, + raw: uintptr(cb.Flags), + }, + { + name: "fd", + t: ArgTypeUnsignedInt, + raw: uintptr(cb.Fd), + }, + { + name: "offset", + t: ArgTypeUnsignedLong, + raw: uintptr(cb.Offset), + }, + { + name: "addr", + t: ArgTypeUnsignedLong, + raw: uintptr(cb.Addr), + }, + { + name: "len", + t: ArgTypeUnsignedInt, + raw: uintptr(cb.Len), + }, + { + name: "pos", + t: ArgTypeUnsignedLong, + raw: uintptr(cb.Pos), + }, + { + name: "reserved2", + t: ArgTypeUnsignedInt, + raw: uintptr(cb.Reserved2), + }, + { + name: "reserved3", + t: ArgTypeUnsignedLong, + raw: uintptr(cb.Reserved3), + }, + }, + } +} diff --git a/tracer/types_ioevents.go b/tracer/types_ioevents.go new file mode 100644 index 0000000..0920425 --- /dev/null +++ b/tracer/types_ioevents.go @@ -0,0 +1,109 @@ +package tracer + +import ( + "reflect" + "unsafe" +) + +type ioevent struct { + Data uint64 + Obj uint64 + Res int64 + Res2 int64 +} + +func init() { + registerTypeHandler(argTypeIoEvents, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + + var events []Arg + + // ret contains number of events read + if ret > 0 { + + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(ioevent{})*ret) + if err != nil { + return err + } + + var rawEvents []ioevent + if err := decodeAnonymous(reflect.ValueOf(&rawEvents).Elem(), mem); err != nil { + return err + } + + events = convertIoEvents(rawEvents) + } + + arg.t = ArgTypeArray + arg.array = events + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) + registerTypeHandler(argTypeIoEvent, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(ioevent{})) + if err != nil { + return err + } + + var rawEvent ioevent + if err := decodeStruct(mem, &rawEvent); err != nil { + return err + } + + arg.t = ArgTypeObject + arg.obj = convertIoEvent(rawEvent) + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertIoEvents(events []ioevent) []Arg { + var output []Arg + for _, event := range events { + output = append(output, Arg{ + t: ArgTypeObject, + obj: convertIoEvent(event), + }) + } + return output +} + +func convertIoEvent(event ioevent) *Object { + return &Object{ + Name: "io_event", + Properties: []Arg{ + { + name: "data", + t: ArgTypeUnsignedLong, + raw: uintptr(event.Data), + }, + { + name: "obj", + t: ArgTypeUnsignedLong, + raw: uintptr(event.Obj), + }, + { + name: "res", + t: ArgTypeLong, + raw: uintptr(event.Res), + }, + { + name: "res2", + t: ArgTypeLong, + raw: uintptr(event.Res2), + }, + }, + } +} diff --git a/tracer/types_iouring.go b/tracer/types_iouring.go new file mode 100644 index 0000000..d49340c --- /dev/null +++ b/tracer/types_iouring.go @@ -0,0 +1,83 @@ +package tracer + +import ( + "unsafe" +) + +type iouringParams struct { + SqEntries uint32 + CqEntries uint32 + Flags uint32 + SqThreadCpu uint32 + SqThreadIdle uint32 + Features uint32 + WqFd uint32 +} + +func init() { + registerTypeHandler(argTypeIoUringParams, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(iouringParams{})) + if err != nil { + return err + } + + var params iouringParams + if err := decodeStruct(rawTimeVal, ¶ms); err != nil { + return err + } + + arg.obj = convertIoUringParams(params) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertIoUringParams(u iouringParams) *Object { + return &Object{ + Name: "io_uring_params", + Properties: []Arg{ + { + name: "sq_entries", + t: ArgTypeUnsignedLong, + raw: uintptr(u.SqEntries), + }, + { + name: "cq_entries", + t: ArgTypeUnsignedLong, + raw: uintptr(u.CqEntries), + }, + { + name: "flags", + t: ArgTypeUnsignedLong, + raw: uintptr(u.Flags), + }, + { + name: "sq_thread_cpu", + t: ArgTypeUnsignedLong, + raw: uintptr(u.SqThreadCpu), + }, + { + name: "sq_thread_idle", + t: ArgTypeUnsignedLong, + raw: uintptr(u.SqThreadIdle), + }, + { + name: "features", + t: ArgTypeUnsignedLong, + raw: uintptr(u.Features), + }, + { + name: "wq_fd", + t: ArgTypeUnsignedLong, + raw: uintptr(u.WqFd), + }, + }, + } +} diff --git a/tracer/types_iovec.go b/tracer/types_iovec.go index 0c9f9de..7040110 100644 --- a/tracer/types_iovec.go +++ b/tracer/types_iovec.go @@ -6,7 +6,7 @@ import ( ) func init() { - registerTypeHandler(ArgTypeIovecArray, func(arg *Arg, metadata ArgMetadata, raw uintptr, next uintptr, ret uintptr, pid int) error { + registerTypeHandler(argTypeIovecArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { // read the raw C struct from the process memory mem, err := readSize(pid, raw, next*unsafe.Sizeof(iovec{})) if err != nil { @@ -14,11 +14,61 @@ func init() { } iovecs := make([]iovec, next) - if err := decodeAnonymous(reflect.ValueOf(iovecs), mem); err != nil { + if err := decodeAnonymous(reflect.ValueOf(&iovecs).Elem(), mem); err != nil { return err } - arg.array = convertIovecs(iovecs) + arg.array, err = convertIovecs(iovecs, pid) + if err != nil { + return err + } + arg.t = ArgTypeArray return nil }) } + +type iovec struct { + Base uintptr /* Starting address */ + Len uintptr /* Number of bytes to transfer */ +} + +func convertIovecs(vecs []iovec, pid int) ([]Arg, error) { + var output []Arg + for _, fd := range vecs { + vec, err := convertIovec(fd, pid) + if err != nil { + return nil, err + } + output = append(output, *vec) + } + return output, nil +} + +func convertIovec(vec iovec, pid int) (*Arg, error) { + + base, err := readSize(pid, vec.Base, uintptr(vec.Len)) + if err != nil { + return nil, err + } + + return &Arg{ + t: ArgTypeObject, + obj: &Object{ + Name: "iovec", + Properties: []Arg{ + { + name: "base", + t: ArgTypeData, + data: base, + raw: vec.Base, + }, + { + name: "len", + t: ArgTypeUnsignedInt, + raw: vec.Len, + }, + }, + }, + known: true, + }, nil +} diff --git a/tracer/types_itimerspec.go b/tracer/types_itimerspec.go new file mode 100644 index 0000000..7c81aa5 --- /dev/null +++ b/tracer/types_itimerspec.go @@ -0,0 +1,53 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeItimerspec, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.ItimerSpec{})) + if err != nil { + return err + } + + var timeVal unix.ItimerSpec + if err := decodeStruct(rawTimeVal, &timeVal); err != nil { + return err + } + + arg.obj = convertITimerSpec(&timeVal) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertITimerSpec(u *unix.ItimerSpec) *Object { + if u == nil { + return nil + } + return &Object{ + Name: "itimerspec", + Properties: []Arg{ + { + name: "interval", + t: ArgTypeObject, + obj: convertTimeSpec(&u.Interval), + }, + { + name: "value", + t: ArgTypeObject, + obj: convertTimeSpec(&u.Value), + }, + }, + } +} diff --git a/tracer/types_itimerval.go b/tracer/types_itimerval.go new file mode 100644 index 0000000..1308c50 --- /dev/null +++ b/tracer/types_itimerval.go @@ -0,0 +1,50 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeItimerval, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Itimerval{})) + if err != nil { + return err + } + + var timeVal unix.Itimerval + if err := decodeStruct(rawTimeVal, &timeVal); err != nil { + return err + } + + arg.obj = convertITimerVal(&timeVal) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertITimerVal(u *unix.Itimerval) *Object { + return &Object{ + Name: "itimerval", + Properties: []Arg{ + { + name: "interval", + t: ArgTypeObject, + obj: convertTimeVal(&u.Interval), + }, + { + name: "value", + t: ArgTypeObject, + obj: convertTimeVal(&u.Value), + }, + }, + } +} diff --git a/tracer/types_landlock.go b/tracer/types_landlock.go new file mode 100644 index 0000000..9a3336c --- /dev/null +++ b/tracer/types_landlock.go @@ -0,0 +1,48 @@ +package tracer + +import ( + "unsafe" +) + +// used by clone3 syscall +type landlockRulesetAttr struct { + HandledAccessFS uint64 +} + +func init() { + registerTypeHandler(argTypeLandlockRulesetAttr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(landlockRulesetAttr{})) + if err != nil { + return err + } + + var attr landlockRulesetAttr + if err := decodeStruct(mem, &attr); err != nil { + return err + } + + arg.obj = convertLandlockRulesetAttr(attr) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertLandlockRulesetAttr(attr landlockRulesetAttr) *Object { + return &Object{ + Name: "landlock_ruleset_attr", + Properties: []Arg{ + { + name: "handled_access_fs", + t: ArgTypeUnsignedLong, + raw: uintptr(attr.HandledAccessFS), + }, + }, + } +} diff --git a/tracer/types_mountattr.go b/tracer/types_mountattr.go new file mode 100644 index 0000000..96b3c82 --- /dev/null +++ b/tracer/types_mountattr.go @@ -0,0 +1,66 @@ +package tracer + +import ( + "unsafe" +) + +// used by clone3 syscall +type mountAttr struct { + Set uint64 + Clear uint64 + Propagation uint64 + UserNamespaceFd uint64 +} + +func init() { + registerTypeHandler(argTypeMountAttr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(mountAttr{})) + if err != nil { + return err + } + + var attr mountAttr + if err := decodeStruct(mem, &attr); err != nil { + return err + } + + arg.obj = convertMountAttr(attr) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertMountAttr(attr mountAttr) *Object { + return &Object{ + Name: "mount_attr", + Properties: []Arg{ + { + name: "set", + t: ArgTypeUnsignedLong, + raw: uintptr(attr.Set), + }, + { + name: "clear", + t: ArgTypeUnsignedLong, + raw: uintptr(attr.Clear), + }, + { + name: "propagation", + t: ArgTypeUnsignedLong, + raw: uintptr(attr.Propagation), + }, + { + name: "user_namespace_fd", + t: ArgTypeUnsignedLong, + raw: uintptr(attr.UserNamespaceFd), + }, + }, + } +} diff --git a/tracer/types_mqattr.go b/tracer/types_mqattr.go new file mode 100644 index 0000000..138967f --- /dev/null +++ b/tracer/types_mqattr.go @@ -0,0 +1,65 @@ +package tracer + +import ( + "unsafe" +) + +type mqattr struct { + Flags int32 + Maxmsg int32 + Msgsize int32 + Curmsgs int32 +} + +func init() { + registerTypeHandler(argTypeMqAttr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(mqattr{})) + if err != nil { + return err + } + + var attr mqattr + if err := decodeStruct(rawTimeVal, &attr); err != nil { + return err + } + + arg.obj = convertMqAttr(attr) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertMqAttr(a mqattr) *Object { + return &Object{ + Name: "mq_attr", + Properties: []Arg{ + { + name: "flags", + t: ArgTypeUnsignedLong, + raw: uintptr(a.Flags), + }, + { + name: "maxmsg", + t: ArgTypeUnsignedLong, + raw: uintptr(a.Maxmsg), + }, + { + name: "msgsize", + t: ArgTypeUnsignedLong, + raw: uintptr(a.Msgsize), + }, + { + name: "curmsgs", + t: ArgTypeUnsignedLong, + raw: uintptr(a.Curmsgs), + }, + }, + } +} diff --git a/tracer/types_msghdr.go b/tracer/types_msghdr.go new file mode 100644 index 0000000..95c6a85 --- /dev/null +++ b/tracer/types_msghdr.go @@ -0,0 +1,210 @@ +package tracer + +import ( + "reflect" + "syscall" + "unsafe" + + "github.com/liamg/grace/tracer/annotation" + + "golang.org/x/sys/unix" +) + +// we need our own struct here as the unix.Msghdr struct appears to have a hardcoded 32-bit pointer size??? +// is this a bug in Go? or is some preprocessing done per-arch in the syscall pkg? +type msghdr struct { + Name uintptr + Namelen uintptr + Iov uintptr + Iovlen uintptr + Control uintptr + Controllen uintptr + Flags uintptr +} + +type mmsghdr struct { + MsgHdr msghdr + MsgLen uintptr +} + +func init() { + registerTypeHandler(argTypeMsghdr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(msghdr{})) + if err != nil { + return err + } + + var msghdr msghdr + if err := decodeStruct(rawVal, &msghdr); err != nil { + return err + } + + arg.obj, err = convertMsgHdr(&msghdr, pid) + if err != nil { + return err + } + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) + registerTypeHandler(argTypeMMsgHdrArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + arg.t = ArgTypeArray + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(mmsghdr{})*ret) + if err != nil { + return err + } + var mmsghdrs []mmsghdr + if err := decodeAnonymous(reflect.ValueOf(&mmsghdrs).Elem(), rawVal); err != nil { + return err + } + + arg.array = convertMMsghdrs(mmsghdrs, pid) + } + return nil + }) +} + +func convertMMsghdrs(mmsghdrs []mmsghdr, pid int) []Arg { + + var args []Arg + + for _, hdr := range mmsghdrs { + obj, err := convertMsgHdr(&hdr.MsgHdr, pid) + if err != nil { + continue + } + args = append(args, Arg{ + name: "msg_hdr", + t: ArgTypeObject, + obj: &Object{ + Name: "mmsghdr", + Properties: []Arg{ + { + name: "msg_hdr", + t: ArgTypeObject, + obj: obj, + }, + { + name: "msg_len", + t: ArgTypeInt, + raw: hdr.MsgLen, + }, + }, + }, + }) + } + + return args +} + +func convertMsgHdr(hdr *msghdr, pid int) (*Object, error) { + + rawFamily, err := readSize(pid, hdr.Name, unsafe.Sizeof(syscall.RawSockaddrInet4{}.Family)) + if err != nil { + return nil, err + } + + family := decodeInt(rawFamily) + + rawSockAddr, err := readSize(pid, hdr.Name, hdr.Namelen) + if err != nil { + return nil, err + } + + obj, err := convertSockAddr(family, rawSockAddr) + if err != nil { + return nil, err + } + + iovecBytes, err := readSize(pid, hdr.Iov, unsafe.Sizeof(iovec{})*hdr.Iovlen) + if err != nil { + return nil, err + } + + iovecs := make([]iovec, hdr.Iovlen) + if err := decodeAnonymous(reflect.ValueOf(&iovecs).Elem(), iovecBytes); err != nil { + return nil, err + } + + vecs, err := convertIovecs(iovecs, pid) + if err != nil { + return nil, err + } + + controlBytes, err := readSize(pid, hdr.Control, hdr.Controllen) + if err != nil { + return nil, err + } + + var control unix.Cmsghdr + if err := decodeStruct(controlBytes, &control); err != nil { + return nil, err + } + + flags := Arg{ + name: "flags", + t: ArgTypeInt, + raw: hdr.Flags, + } + annotation.AnnotateMsgFlags(&flags, pid) + + return &Object{ + Name: "msghdr", + Properties: []Arg{ + { + name: "name", + t: ArgTypeObject, + obj: obj, + }, + { + name: "iovec", + t: ArgTypeArray, + array: vecs, + }, + { + name: "control", + t: ArgTypeObject, + obj: convertCmsghdr(control, pid), + }, + flags, + }, + }, nil +} + +func convertCmsghdr(control unix.Cmsghdr, pid int) *Object { + + level := Arg{ + name: "level", + t: ArgTypeInt, + raw: uintptr(control.Level), + } + + annotation.AnnotateSocketLevel(&level, pid) + + typ := Arg{ + name: "type", + t: ArgTypeInt, + raw: uintptr(control.Type), + } + + annotation.AnnotateControlMessageType(&typ, pid) + + return &Object{ + Name: "cmsghdr", + Properties: []Arg{ + { + name: "len", + t: ArgTypeInt, + raw: uintptr(control.Len), + }, + level, + typ, + }, + } +} diff --git a/tracer/types_openhow.go b/tracer/types_openhow.go new file mode 100644 index 0000000..8972451 --- /dev/null +++ b/tracer/types_openhow.go @@ -0,0 +1,72 @@ +package tracer + +import ( + "unsafe" + + "github.com/liamg/grace/tracer/annotation" +) + +// used by clone3 syscall +type openHow struct { + Flags uint64 + Mode uint64 + Resolve uint64 +} + +func init() { + registerTypeHandler(argTypeOpenHow, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(openHow{})) + if err != nil { + return err + } + + var how openHow + if err := decodeStruct(mem, &how); err != nil { + return err + } + + arg.obj = convertOpenHow(how) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertOpenHow(how openHow) *Object { + + flags := Arg{ + name: "flags", + t: ArgTypeUnsignedLong, + raw: uintptr(how.Flags), + } + annotation.AnnotateOpenFlags(&flags, 0) + + mode := Arg{ + name: "flags", + t: ArgTypeUnsignedLong, + raw: uintptr(how.Mode), + } + annotation.AnnotateAccMode(&mode, 0) + + resolve := Arg{ + name: "resolve", + t: ArgTypeUnsignedLong, + raw: uintptr(how.Resolve), + } + annotation.AnnotateResolveFlags(&mode, 0) + + return &Object{ + Name: "open_how", + Properties: []Arg{ + flags, + mode, + resolve, + }, + } +} diff --git a/tracer/types_pollfd.go b/tracer/types_pollfd.go index 20a59ca..eee65e2 100644 --- a/tracer/types_pollfd.go +++ b/tracer/types_pollfd.go @@ -6,7 +6,7 @@ import ( ) func init() { - registerTypeHandler(ArgTypePollFdArray, func(arg *Arg, metadata ArgMetadata, raw uintptr, next uintptr, ret uintptr, pid int) error { + registerTypeHandler(argTypePollFdArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { if raw > 0 { // read the raw C struct from the process memory rawPollFds, err := readSize(pid, raw, next*unsafe.Sizeof(pollfd{})) @@ -15,12 +15,54 @@ func init() { } pollFds := make([]pollfd, next) - if err := decodeAnonymous(reflect.ValueOf(pollFds), rawPollFds); err != nil { + if err := decodeAnonymous(reflect.ValueOf(&pollFds).Elem(), rawPollFds); err != nil { return err } arg.array = convertPollFds(pollFds) } + arg.t = ArgTypeArray return nil }) } + +type pollfd struct { + Fd int /* file descriptor */ + Events uint16 /* events requested for polling */ + REvents uint32 /* events that occurred during polling */ +} + +func convertPollFds(fds []pollfd) []Arg { + var output []Arg + for _, fd := range fds { + output = append(output, convertPollFd(fd)) + } + return output +} + +func convertPollFd(fd pollfd) Arg { + return Arg{ + t: ArgTypeObject, + obj: &Object{ + Name: "pollfd", + Properties: []Arg{ + { + name: "fd", + t: ArgTypeInt, + raw: uintptr(fd.Fd), + }, + { + name: "events", + t: ArgTypeInt, + raw: uintptr(fd.Events), + }, + { + name: "revents", + t: ArgTypeInt, + raw: uintptr(fd.REvents), + }, + }, + }, + known: true, + } +} diff --git a/tracer/types_rlimit.go b/tracer/types_rlimit.go new file mode 100644 index 0000000..4c2f86c --- /dev/null +++ b/tracer/types_rlimit.go @@ -0,0 +1,48 @@ +package tracer + +import ( + "syscall" + "unsafe" +) + +func init() { + registerTypeHandler(argTypeRLimit, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + data, err := readSize(pid, raw, unsafe.Sizeof(syscall.Rlimit{})) + if err != nil { + return err + } + + var rlimit syscall.Rlimit + if err := decodeStruct(data, &rlimit); err != nil { + return err + } + + arg.obj = convertRLimit(&rlimit) + arg.t = ArgTypeObject + } else { + arg.t = ArgTypeAddress + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertRLimit(rlimit *syscall.Rlimit) *Object { + return &Object{ + Name: "rlimit", + Properties: []Arg{ + { + name: "cur", + t: ArgTypeUnsignedLong, + raw: uintptr(rlimit.Cur), + }, + { + name: "max", + t: ArgTypeUnsignedLong, + raw: uintptr(rlimit.Max), + }, + }, + } +} diff --git a/tracer/types_rusage.go b/tracer/types_rusage.go new file mode 100644 index 0000000..79da79b --- /dev/null +++ b/tracer/types_rusage.go @@ -0,0 +1,146 @@ +package tracer + +import ( + "syscall" + "unsafe" +) + +func init() { + registerTypeHandler(argTypeRUsage, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + data, err := readSize(pid, raw, unsafe.Sizeof(syscall.Rusage{})) + if err != nil { + return err + } + + var rusage syscall.Rusage + if err := decodeStruct(data, &rusage); err != nil { + return err + } + + arg.obj = convertRUsage(&rusage) + arg.t = ArgTypeObject + } else { + arg.t = ArgTypeAddress + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertRUsage(rusage *syscall.Rusage) *Object { + return &Object{ + Name: "rusage", + Properties: []Arg{ + { + name: "utime", + t: ArgTypeObject, + obj: &Object{ + Name: "timeval", + Properties: []Arg{ + { + name: "tv_sec", + t: ArgTypeLong, + raw: uintptr(rusage.Utime.Sec), + }, + { + name: "tv_usec", + t: ArgTypeLong, + raw: uintptr(rusage.Utime.Usec), + }, + }, + }, + }, + { + name: "stime", + t: ArgTypeObject, + obj: &Object{ + Name: "timeval", + Properties: []Arg{ + { + name: "tv_sec", + t: ArgTypeLong, + raw: uintptr(rusage.Stime.Sec), + }, + { + name: "tv_usec", + t: ArgTypeLong, + raw: uintptr(rusage.Stime.Usec), + }, + }, + }, + }, + { + name: "maxrss", + t: ArgTypeLong, + raw: uintptr(rusage.Maxrss), + }, + { + name: "ixrss", + t: ArgTypeLong, + raw: uintptr(rusage.Ixrss), + }, + { + name: "idrss", + t: ArgTypeLong, + raw: uintptr(rusage.Idrss), + }, + { + name: "isrss", + t: ArgTypeLong, + raw: uintptr(rusage.Isrss), + }, + { + name: "minflt", + t: ArgTypeLong, + raw: uintptr(rusage.Minflt), + }, + { + name: "majflt", + t: ArgTypeLong, + raw: uintptr(rusage.Majflt), + }, + { + name: "nswap", + t: ArgTypeLong, + raw: uintptr(rusage.Nswap), + }, + { + name: "inblock", + t: ArgTypeLong, + raw: uintptr(rusage.Inblock), + }, + { + name: "oublock", + t: ArgTypeLong, + raw: uintptr(rusage.Oublock), + }, + { + name: "msgsnd", + t: ArgTypeLong, + raw: uintptr(rusage.Msgsnd), + }, + { + name: "msgrcv", + t: ArgTypeLong, + raw: uintptr(rusage.Msgrcv), + }, + { + name: "nsignals", + t: ArgTypeLong, + raw: uintptr(rusage.Nsignals), + }, + { + name: "nvcsw", + t: ArgTypeLong, + raw: uintptr(rusage.Nvcsw), + }, + { + name: "nivcsw", + t: ArgTypeLong, + raw: uintptr(rusage.Nivcsw), + }, + }, + } +} diff --git a/tracer/types_schedattr.go b/tracer/types_schedattr.go new file mode 100644 index 0000000..65bb24b --- /dev/null +++ b/tracer/types_schedattr.go @@ -0,0 +1,91 @@ +package tracer + +import ( + "unsafe" +) + +type schedAttr struct { + Size uint32 + SchedPolicy uint32 + SchedFlags uint64 + SchedNice int32 + SchedPriority uint32 + SchedRuntime uint64 + SchedDeadline uint64 + SchedPeriod uint64 +} + +func init() { + registerTypeHandler(argTypeSchedAttr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(schedAttr{})) + if err != nil { + return err + } + + var attr schedAttr + if err := decodeStruct(rawVal, &attr); err != nil { + return err + } + + arg.obj = convertSchedAttr(attr) + if err != nil { + return err + } + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertSchedAttr(param schedAttr) *Object { + return &Object{ + Name: "sched_attr", + Properties: []Arg{ + { + name: "size", + t: ArgTypeInt, + raw: uintptr(param.Size), + }, + { + name: "sched_policy", + t: ArgTypeInt, + raw: uintptr(param.SchedPolicy), + }, + { + name: "sched_flags", + t: ArgTypeInt, + raw: uintptr(param.SchedFlags), + }, + { + name: "sched_nice", + t: ArgTypeInt, + raw: uintptr(param.SchedNice), + }, + { + name: "sched_priority", + t: ArgTypeInt, + raw: uintptr(param.SchedPriority), + }, + { + name: "sched_runtime", + t: ArgTypeInt, + raw: uintptr(param.SchedRuntime), + }, + { + name: "sched_deadline", + t: ArgTypeInt, + raw: uintptr(param.SchedDeadline), + }, + { + name: "sched_period", + t: ArgTypeInt, + raw: uintptr(param.SchedPeriod), + }, + }, + } +} diff --git a/tracer/types_schedparam.go b/tracer/types_schedparam.go new file mode 100644 index 0000000..41f3314 --- /dev/null +++ b/tracer/types_schedparam.go @@ -0,0 +1,49 @@ +package tracer + +import ( + "unsafe" +) + +type schedParam struct { + Priority int32 +} + +func init() { + registerTypeHandler(argTypeSchedParam, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(schedParam{})) + if err != nil { + return err + } + + var param schedParam + if err := decodeStruct(rawVal, ¶m); err != nil { + return err + } + + arg.obj = convertSchedParam(param) + if err != nil { + return err + } + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertSchedParam(param schedParam) *Object { + return &Object{ + Name: "sched_param", + Properties: []Arg{ + { + name: "priority", + t: ArgTypeInt, + raw: uintptr(param.Priority), + }, + }, + } +} diff --git a/tracer/types_sembuf.go b/tracer/types_sembuf.go new file mode 100644 index 0000000..9ead88d --- /dev/null +++ b/tracer/types_sembuf.go @@ -0,0 +1,77 @@ +package tracer + +import ( + "reflect" + "unsafe" + + "github.com/liamg/grace/tracer/annotation" +) + +type sembuf struct { + Num uint16 /* semaphore index in array */ + Op int16 /* semaphore operation */ + Flg int16 /* flags for operation */ +} + +func init() { + registerTypeHandler(argTypeSembuf, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, next*unsafe.Sizeof(sembuf{})*next) + if err != nil { + return err + } + + sembufs := make([]sembuf, next) + if err := decodeAnonymous(reflect.ValueOf(&sembufs).Elem(), mem); err != nil { + return err + } + + arg.array, err = convertSembufs(sembufs, pid) + if err != nil { + return err + } + arg.t = ArgTypeArray + return nil + }) +} + +func convertSembufs(bufs []sembuf, pid int) ([]Arg, error) { + var items []Arg + for _, buf := range bufs { + obj := convertSembuf(buf, pid) + items = append(items, obj) + } + return items, nil +} + +func convertSembuf(buf sembuf, pid int) Arg { + + flagsArg := Arg{ + name: "flags", + t: ArgTypeInt, + raw: uintptr(buf.Flg), + } + annotation.AnnotateSemFlags(&flagsArg, pid) + + return Arg{ + name: "sops", + t: ArgTypeObject, + obj: &Object{ + Name: "sembuf", + Properties: []Arg{ + { + name: "sem_num", + t: ArgTypeInt, + raw: uintptr(buf.Num), + }, + { + name: "sem_op", + t: ArgTypeInt, + raw: uintptr(buf.Op), + }, + flagsArg, + }, + }, + } + +} diff --git a/tracer/types_shmidds.go b/tracer/types_shmidds.go new file mode 100644 index 0000000..4764cf9 --- /dev/null +++ b/tracer/types_shmidds.go @@ -0,0 +1,152 @@ +package tracer + +import ( + "unsafe" +) + +type shmidds struct { + IPCPerm struct { + Key uint32 /* Key provided to shmget */ + Uid uint32 /* Effective UID of owner */ + Gid uint32 /* Effective GID of owner */ + Cuid uint32 /* Effective UID of creator */ + Cgid uint32 /* Effective GID of creator */ + Mode uint32 /* Permissions and SHM_DEST + SHM_LOCKED flags */ + Pad1 uint16 + Seq uint16 /* Sequence */ + Pad2 uint16 + Unused1 uint + Unused2 uint + } /* Ownership and permissions */ + Segsz uint32 /* Size of shared segment (bytes) */ + Atime uint64 /* Last attach time */ + Dtime uint64 /* Last detach time */ + Ctime uint64 /* Last change time */ + Cpid uint32 /* PID of shared segment creator */ + Lpid uint32 /* PID of last shmat(2)/shmdt(2) syscall */ + Nattch uint16 /* Number of current attaches */ + Unused uint16 + Unused2 uintptr + Unused3 uintptr +} + +func init() { + registerTypeHandler(argTypeSHMIDDS, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(shmidds{})) + if err != nil { + return err + } + + var ds shmidds + if err := decodeStruct(rawTimeVal, &ds); err != nil { + return err + } + + arg.obj = convertSHMIDDS(&ds) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertSHMIDDS(ds *shmidds) *Object { + + var permProps []Arg + + permProps = append(permProps, Arg{ + name: "key", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.IPCPerm.Key), + }) + permProps = append(permProps, Arg{ + name: "uid", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.IPCPerm.Uid), + }) + permProps = append(permProps, Arg{ + name: "gid", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.IPCPerm.Gid), + }) + permProps = append(permProps, Arg{ + name: "cuid", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.IPCPerm.Cuid), + }) + permProps = append(permProps, Arg{ + name: "cgid", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.IPCPerm.Cgid), + }) + permProps = append(permProps, Arg{ + name: "mode", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.IPCPerm.Mode), + }) + permProps = append(permProps, Arg{ + name: "seq", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.IPCPerm.Seq), + }) + + var props []Arg + + props = append(props, Arg{ + name: "perm", + t: ArgTypeObject, + obj: &Object{ + Name: "ipc_perm", + }, + }) + props = append(props, Arg{ + name: "segsize", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.Segsz), + }) + + // TODO: read time structs from process memory instead of storing raw addresses + + props = append(props, Arg{ + name: "atime", + t: ArgTypeAddress, + raw: uintptr(ds.Atime), + }) + props = append(props, Arg{ + name: "dtime", + t: ArgTypeAddress, + raw: uintptr(ds.Dtime), + }) + props = append(props, Arg{ + name: "ctime", + t: ArgTypeAddress, + raw: uintptr(ds.Ctime), + }) + + props = append(props, Arg{ + name: "cpid", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.Cpid), + }) + props = append(props, Arg{ + name: "lpid", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.Lpid), + }) + props = append(props, Arg{ + name: "nattch", + t: ArgTypeUnsignedInt, + raw: uintptr(ds.Nattch), + }) + + return &Object{ + Name: "shmid_ds", + Properties: props, + } +} diff --git a/tracer/types_sigaction.go b/tracer/types_sigaction.go index 1966ec7..1ccd424 100644 --- a/tracer/types_sigaction.go +++ b/tracer/types_sigaction.go @@ -6,7 +6,7 @@ import ( ) /* -#include +#include const void* sig_dfl = SIG_DFL; const void* sig_ign = SIG_IGN; @@ -22,7 +22,7 @@ type sigAction struct { } func init() { - registerTypeHandler(ArgTypeSigAction, func(arg *Arg, metadata ArgMetadata, raw uintptr, next uintptr, ret uintptr, pid int) error { + registerTypeHandler(argTypeSigAction, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { if raw > 0 { raw, err := readSize(pid, raw, unsafe.Sizeof(sigAction{})) @@ -36,6 +36,10 @@ func init() { } arg.obj = convertSigAction(&action) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true } return nil }) @@ -120,20 +124,3 @@ func convertSigAction(action *sigAction) *Object { return &obj } - -func annotateSigProcMaskFlags(arg *Arg, pid int) { - - var joins []string - if arg.Raw()&C.SIG_BLOCK != 0 { - joins = append(joins, "SIG_BLOCK") - } - if arg.Raw()&C.SIG_UNBLOCK != 0 { - joins = append(joins, "SIG_UNBLOCK") - } - if arg.Raw()&C.SIG_SETMASK != 0 { - joins = append(joins, "SIG_SETMASK") - } - - arg.annotation = strings.Join(joins, "|") - arg.replace = arg.annotation != "" -} diff --git a/tracer/types_siginfo.go b/tracer/types_siginfo.go new file mode 100644 index 0000000..89445cf --- /dev/null +++ b/tracer/types_siginfo.go @@ -0,0 +1,77 @@ +package tracer + +import ( + "syscall" + "unsafe" + + "github.com/liamg/grace/tracer/annotation" + + "golang.org/x/sys/unix" +) + +/* +#include +*/ +import "C" + +func init() { + registerTypeHandler(argTypeSigInfo, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + + raw, err := readSize(pid, raw, unsafe.Sizeof(unix.Siginfo{})) + if err != nil { + return err + } + + var info unix.Siginfo + if err := decodeStruct(raw, &info); err != nil { + return err + } + + arg.obj = convertSigInfo(&info, pid) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertSigInfo(u *unix.Siginfo, pid int) *Object { + + signo := Arg{ + name: "signo", + t: ArgTypeInt, + raw: uintptr(u.Signo), + } + annotation.AnnotateSignal(&signo, pid) + + errStr := annotation.ErrNoToString(int(u.Errno)) + errno := Arg{ + name: "errno", + t: ArgTypeInt, + raw: uintptr(u.Errno), + annotation: errStr, + replace: errStr != "", + } + + codeStr := annotation.SignalCodeToString(syscall.Signal(u.Signo), u.Code) + + code := Arg{ + name: "code", + t: ArgTypeInt, + raw: uintptr(u.Code), + annotation: codeStr, + replace: codeStr != "", + } + + return &Object{ + Name: "siginfo", + Properties: []Arg{ + signo, + errno, + code, + }, + } +} diff --git a/tracer/types_sockaddr.go b/tracer/types_sockaddr.go new file mode 100644 index 0000000..d14cd12 --- /dev/null +++ b/tracer/types_sockaddr.go @@ -0,0 +1,189 @@ +package tracer + +import ( + "fmt" + "syscall" + "unsafe" + + "github.com/liamg/grace/tracer/annotation" +) + +func init() { + registerTypeHandler(argTypeSockaddr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + rawFamily, err := readSize(pid, raw, unsafe.Sizeof(syscall.RawSockaddrInet4{}.Family)) + if err != nil { + return err + } + + family := decodeInt(rawFamily) + + rawSockAddr, err := readSize(pid, raw, next) + if err != nil { + return err + } + + arg.obj, err = convertSockAddr(family, rawSockAddr) + if err != nil { + return err + } + arg.t = ArgTypeObject + } else { + arg.t = ArgTypeAddress + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func htons(port uint16) uint16 { + return port>>8 | port<<8 +} + +func convertSockAddr(family int64, raw []byte) (*Object, error) { + switch family { + case syscall.AF_INET: + var target syscall.RawSockaddrInet4 + if err := decodeStruct(raw, &target); err != nil { + return nil, err + } + return &Object{ + Name: "sockaddr", + Properties: []Arg{ + { + name: "family", + annotation: "AF_INET", + replace: true, + }, + { + name: "port", + t: ArgTypeInt, + raw: uintptr(htons(target.Port)), + }, + { + name: "addr", + t: ArgTypeData, + data: []byte(fmt.Sprintf("%d.%d.%d.%d", target.Addr[0], target.Addr[1], target.Addr[2], target.Addr[3])), + }, + }, + }, nil + case syscall.AF_INET6: + var target syscall.RawSockaddrInet6 + if err := decodeStruct(raw, &target); err != nil { + return nil, err + } + return &Object{ + Name: "sockaddr", + Properties: []Arg{ + { + name: "family", + annotation: "AF_INET6", + replace: true, + }, + { + name: "port", + t: ArgTypeInt, + raw: uintptr(htons(target.Port)), + }, + { + name: "addr", + t: ArgTypeData, + data: []byte(fmt.Sprintf("%x:%x:%x:%x:%x:%x:%x:%x", target.Addr[0], target.Addr[1], target.Addr[2], target.Addr[3], target.Addr[4], target.Addr[5], target.Addr[6], target.Addr[7])), + }, + { + name: "flowinfo", + t: ArgTypeInt, + raw: uintptr(target.Flowinfo), + }, + { + name: "scope_id", + t: ArgTypeInt, + raw: uintptr(target.Scope_id), + }, + }, + }, nil + case syscall.AF_UNIX: + var target syscall.RawSockaddrUnix + + if len(raw) < int(unsafe.Sizeof(target)) { + raw = append(raw, make([]byte, int(unsafe.Sizeof(target))-len(raw))...) + } + + if err := decodeStruct(raw, &target); err != nil { + return nil, err + } + var path string + for _, b := range target.Path { + if b == 0 { + break + } + path += string(byte(b)) + } + return &Object{ + Name: "sockaddr", + Properties: []Arg{ + { + name: "family", + annotation: "AF_UNIX", + replace: true, + }, + { + name: "path", + t: ArgTypeData, + data: []byte(path), + }, + }, + }, nil + case syscall.AF_NETLINK: + var target syscall.RawSockaddrNetlink + if err := decodeStruct(raw, &target); err != nil { + return nil, err + } + return &Object{ + Name: "sockaddr", + Properties: []Arg{ + { + name: "family", + annotation: "AF_NETLINK", + replace: true, + }, + { + name: "pid", + t: ArgTypeInt, + raw: uintptr(target.Pid), + }, + { + name: "groups", + t: ArgTypeInt, + raw: uintptr(target.Groups), + }, + }, + }, nil + default: + var target syscall.RawSockaddr + if err := decodeStruct(raw, &target); err != nil { + return nil, err + } + data := make([]byte, len(target.Data)) + for i, b := range target.Data { + data[i] = byte(b) + } + familyStr := annotation.SocketFamily(int(target.Family)) + return &Object{ + Name: "sockaddr", + Properties: []Arg{ + { + name: "family", + annotation: familyStr, + replace: familyStr != "", + }, + { + name: "data", + t: ArgTypeData, + data: data, + }, + }, + }, nil + } +} diff --git a/tracer/types_sockoptval.go b/tracer/types_sockoptval.go new file mode 100644 index 0000000..a73b331 --- /dev/null +++ b/tracer/types_sockoptval.go @@ -0,0 +1,29 @@ +package tracer + +func init() { + registerTypeHandler(argTypeSockoptval, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + rawSockOptVal, err := readSize(pid, raw, next) + if err != nil { + return err + } + + switch prev { + // TODO: expect specific types for specific options? + default: + if next == 4 { // if it's exactly 4 bytes, it's probably an int + arg.raw = uintptr(decodeInt(rawSockOptVal)) + arg.t = ArgTypeInt + } else { // otherwise it's a big pile of misc data + arg.data = rawSockOptVal + arg.t = ArgTypeData + } + } + } else { + arg.t = ArgTypeAddress + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} diff --git a/tracer/types_stack.go b/tracer/types_stack.go new file mode 100644 index 0000000..3901262 --- /dev/null +++ b/tracer/types_stack.go @@ -0,0 +1,65 @@ +package tracer + +import ( + "unsafe" + + "github.com/liamg/grace/tracer/annotation" +) + +type stack struct { + Sp uintptr + Flags uint32 + Size uint32 +} + +func init() { + registerTypeHandler(argTypeStack, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(stack{})) + if err != nil { + return err + } + + var stack stack + if err := decodeStruct(mem, &stack); err != nil { + return err + } + + arg.obj = convertStack(&stack) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertStack(st *stack) *Object { + + flagsArg := Arg{ + name: "flags", + t: ArgTypeUnsignedInt, + raw: uintptr(st.Flags), + } + annotation.AnnotateSigStackFlags(&flagsArg, 0) + + return &Object{ + Name: "stack", + Properties: []Arg{ + { + name: "sp", + t: ArgTypeAddress, + raw: st.Sp, + }, + flagsArg, + { + name: "size", + t: ArgTypeUnsignedInt, + raw: uintptr(st.Size), + }, + }, + } +} diff --git a/tracer/types_stat.go b/tracer/types_stat.go index 671d293..8ea1051 100644 --- a/tracer/types_stat.go +++ b/tracer/types_stat.go @@ -2,14 +2,17 @@ package tracer import ( "fmt" - "golang.org/x/sys/unix" "strings" "syscall" "unsafe" + + "github.com/liamg/grace/tracer/annotation" + + "golang.org/x/sys/unix" ) func init() { - registerTypeHandler(ArgTypeStat, func(arg *Arg, metadata ArgMetadata, raw uintptr, next uintptr, ret uintptr, pid int) error { + registerTypeHandler(argTypeStat, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { if raw > 0 { // read the raw C struct from the process memory rawStat, err := readSize(pid, raw, unsafe.Sizeof(syscall.Stat_t{})) @@ -17,14 +20,16 @@ func init() { return err } - // safely squish it into a syscall.Stat_t in our own memory space var stat syscall.Stat_t if err := decodeStruct(rawStat, &stat); err != nil { return err } - // convert the stat into a nice object for output arg.obj = convertStat(&stat) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true } return nil }) @@ -47,7 +52,7 @@ func convertStat(stat *syscall.Stat_t) *Object { name: "dev", t: ArgTypeUnsignedInt, raw: uintptr(stat.Dev), - annotation: deviceToString(stat.Dev), + annotation: annotation.DeviceToString(stat.Dev), replace: true, }) @@ -103,25 +108,13 @@ func convertStat(stat *syscall.Stat_t) *Object { name: "rdev", t: ArgTypeUnsignedInt, raw: uintptr(stat.Rdev), - annotation: deviceToString(stat.Rdev), + annotation: annotation.DeviceToString(stat.Rdev), replace: true, }) return &obj } -func deviceToString(dev uint64) string { - major := "0" - if m := unix.Major(dev); m != 0 { - major = fmt.Sprintf("0x%x", m) - } - minor := "0" - if m := unix.Minor(dev); m != 0 { - minor = fmt.Sprintf("0x%x", m) - } - return fmt.Sprintf("makedev(%s, %s)", major, minor) -} - func permModeToString(mode uint32) string { flags := map[uint32]string{ diff --git a/tracer/types_statfs.go b/tracer/types_statfs.go new file mode 100644 index 0000000..8ca1151 --- /dev/null +++ b/tracer/types_statfs.go @@ -0,0 +1,93 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeStatfs, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Statfs_t{})) + if err != nil { + return err + } + + var stat unix.Statfs_t + if err := decodeStruct(rawVal, &stat); err != nil { + return err + } + + arg.obj, err = convertStatfs(&stat, pid) + if err != nil { + return err + } + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertStatfs(stat *unix.Statfs_t, _ int) (*Object, error) { + return &Object{ + Name: "statfs", + Properties: []Arg{ + { + name: "type", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Type), + }, + { + name: "bsize", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Bsize), + }, + + { + name: "blocks", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Blocks), + }, + { + name: "bfree", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Bfree), + }, + { + name: "bavail", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Bavail), + }, + { + name: "files", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Files), + }, + { + name: "ffree", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Ffree), + }, + { + name: "namelen", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Namelen), + }, + { + name: "frsize", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Frsize), + }, + { + name: "flags", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Flags), + }, + }, + }, nil +} diff --git a/tracer/types_statx.go b/tracer/types_statx.go new file mode 100644 index 0000000..0e7cec6 --- /dev/null +++ b/tracer/types_statx.go @@ -0,0 +1,177 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeStatX, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + // read the raw C struct from the process memory + rawStat, err := readSize(pid, raw, unsafe.Sizeof(unix.Statx_t{})) + if err != nil { + return err + } + + var stat unix.Statx_t + if err := decodeStruct(rawStat, &stat); err != nil { + return err + } + + arg.obj = convertStatx(&stat) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertStatx(stat *unix.Statx_t) *Object { + obj := Object{ + Name: "statx", + } + + obj.Properties = append(obj.Properties, Arg{ + name: "mask", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Mask), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "blksize", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Blksize), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "attributes", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Attributes), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "nlink", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Nlink), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "uid", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Uid), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "gid", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Gid), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "mode", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Mode), + annotation: permModeToString(uint32(stat.Mode)), + replace: true, + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "ino", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Ino), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "size", + t: ArgTypeInt, + raw: uintptr(stat.Size), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "blocks", + t: ArgTypeInt, + raw: uintptr(stat.Blocks), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "attributes_mask", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Attributes_mask), + }) + + // - + + obj.Properties = append(obj.Properties, Arg{ + name: "atime", + t: ArgTypeObject, + obj: convertStatXTimestamp(stat.Atime), + }) + obj.Properties = append(obj.Properties, Arg{ + name: "btime", + t: ArgTypeObject, + obj: convertStatXTimestamp(stat.Btime), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "ctime", + t: ArgTypeObject, + obj: convertStatXTimestamp(stat.Ctime), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "mtime", + t: ArgTypeObject, + obj: convertStatXTimestamp(stat.Mtime), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "rdev_major", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Rdev_major), + }) + + obj.Properties = append(obj.Properties, Arg{ + name: "rdev_minor", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Rdev_minor), + }) + obj.Properties = append(obj.Properties, Arg{ + name: "dev_major", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Dev_major), + }) + obj.Properties = append(obj.Properties, Arg{ + name: "dev_minor", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Dev_minor), + }) + obj.Properties = append(obj.Properties, Arg{ + name: "mnt_id", + t: ArgTypeUnsignedInt, + raw: uintptr(stat.Mnt_id), + }) + + return &obj +} + +func convertStatXTimestamp(t unix.StatxTimestamp) *Object { + return &Object{ + Name: "statx_timestamp", + Properties: []Arg{ + { + name: "sec", + t: ArgTypeUnsignedInt, + raw: uintptr(t.Sec), + }, + { + name: "nsec", + t: ArgTypeUnsignedInt, + raw: uintptr(t.Nsec), + }, + }, + } +} diff --git a/tracer/types_string.go b/tracer/types_string.go new file mode 100644 index 0000000..a836a51 --- /dev/null +++ b/tracer/types_string.go @@ -0,0 +1,72 @@ +package tracer + +import ( + "fmt" + "syscall" + "unsafe" +) + +/* + NOTE: an array of strings in C (at least a char *argv[]) is an array of pointers to strings, terminated by a NULL pointer. +*/ + +func init() { + registerTypeHandler(argTypeStringArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + var items []Arg + var offset uintptr + size := unsafe.Sizeof(uintptr(0)) + + for { + mem, err := readSize(pid, raw+offset, size) + if err != nil { + return err + } + address := uintptr(decodeUint(mem)) + if address == 0 { + break + } + str, err := readString(pid, address) + if err != nil { + return err + } + items = append(items, Arg{ + t: ArgTypeData, + raw: address, + data: []byte(str), + }) + offset += size + } + arg.t = ArgTypeArray + arg.array = items + return nil + }) + registerTypeHandler(argTypeString, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + str, err := readString(pid, raw) + if err != nil { + return err + } + arg.data = []byte(str) + arg.t = ArgTypeData + return nil + }) +} + +func readString(pid int, addr uintptr) (string, error) { + var output string + if addr == 0 { + return output, nil + } + data := make([]byte, 1) + for { + if _, err := syscall.PtracePeekData(pid, addr, data); err != nil { + return "", fmt.Errorf("read of 0x%x failed: %w", addr, err) + } + if data[0] == 0 { + break + } + output += string(data) + addr++ + } + return output, nil +} diff --git a/tracer/types_sysinfo.go b/tracer/types_sysinfo.go new file mode 100644 index 0000000..a89dd3e --- /dev/null +++ b/tracer/types_sysinfo.go @@ -0,0 +1,114 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeSysinfo, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Sysinfo_t{})) + if err != nil { + return err + } + + var sysinfo unix.Sysinfo_t + if err := decodeStruct(rawVal, &sysinfo); err != nil { + return err + } + + arg.obj, err = convertSysinfo(&sysinfo, pid) + if err != nil { + return err + } + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertLoadsToArray(loads [3]uint64) []Arg { + var loadsArray []Arg + for _, load := range loads { + loadsArray = append(loadsArray, Arg{ + name: "load", + t: ArgTypeUnsignedLong, + raw: uintptr(load), + }) + } + return loadsArray +} + +func convertSysinfo(sysinfo *unix.Sysinfo_t, _ int) (*Object, error) { + return &Object{ + Name: "sysinfo", + Properties: []Arg{ + { + name: "uptime", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Uptime), + }, + { + name: "loads", + t: ArgTypeArray, + array: convertLoadsToArray(sysinfo.Loads), + }, + { + name: "totalram", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Totalram), + }, + { + name: "freeram", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Freeram), + }, + { + name: "sharedram", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Sharedram), + }, + { + name: "bufferram", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Bufferram), + }, + { + name: "totalswap", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Totalswap), + }, + { + name: "freeswap", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Freeswap), + }, + { + name: "procs", + t: ArgTypeUnsignedInt, + raw: uintptr(sysinfo.Procs), + }, + { + name: "totalhigh", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Totalhigh), + }, + { + name: "freehigh", + t: ArgTypeUnsignedLong, + raw: uintptr(sysinfo.Freehigh), + }, + { + name: "mem_unit", + t: ArgTypeUnsignedInt, + raw: uintptr(sysinfo.Unit), + }, + }, + }, nil +} diff --git a/tracer/types_timespec.go b/tracer/types_timespec.go new file mode 100644 index 0000000..24cd553 --- /dev/null +++ b/tracer/types_timespec.go @@ -0,0 +1,89 @@ +package tracer + +import ( + "fmt" + "reflect" + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeTimespec, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Timespec{})) + if err != nil { + return err + } + + var timeVal unix.Timespec + if err := decodeStruct(rawTimeVal, &timeVal); err != nil { + return err + } + + arg.obj = convertTimeSpec(&timeVal) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) + registerTypeHandler(argTypeTimespecArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + + var size int + + switch metadata.LenSource { + case LenSourceFixed: + size = metadata.FixedCount + default: + return fmt.Errorf("unsupported count_from: %d", metadata.LenSource) + } + + rawVals, err := readSize(pid, raw, uintptr(size)*unsafe.Sizeof(unix.Timespec{})) + if err != nil { + return err + } + + vals := make([]unix.Timespec, size) + if err := decodeAnonymous(reflect.ValueOf(&vals).Elem(), rawVals); err != nil { + return err + } + + for _, val := range vals { + arg.array = append(arg.array, Arg{ + t: ArgTypeObject, + obj: convertTimeSpec(&val), + }) + } + } + + arg.t = ArgTypeArray + return nil + }) +} + +func convertTimeSpec(u *unix.Timespec) *Object { + if u == nil { + return nil + } + return &Object{ + Name: "timespec", + Properties: []Arg{ + { + name: "sec", + t: ArgTypeUnsignedLong, + raw: uintptr(u.Sec), + }, + { + name: "usec", + t: ArgTypeUnsignedLong, + raw: uintptr(u.Nsec), + }, + }, + } +} diff --git a/tracer/types_timeval.go b/tracer/types_timeval.go new file mode 100644 index 0000000..ee637df --- /dev/null +++ b/tracer/types_timeval.go @@ -0,0 +1,86 @@ +package tracer + +import ( + "fmt" + "reflect" + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeTimeval, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Timeval{})) + if err != nil { + return err + } + + var timeVal unix.Timeval + if err := decodeStruct(rawTimeVal, &timeVal); err != nil { + return err + } + + arg.obj = convertTimeVal(&timeVal) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) + registerTypeHandler(argTypeTimevalArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + + var size int + + switch metadata.LenSource { + case LenSourceFixed: + size = metadata.FixedCount + default: + return fmt.Errorf("unsupported count_from: %d", metadata.LenSource) + } + + rawVals, err := readSize(pid, raw, uintptr(size)*unsafe.Sizeof(unix.Timeval{})) + if err != nil { + return err + } + + vals := make([]unix.Timeval, size) + if err := decodeAnonymous(reflect.ValueOf(&vals).Elem(), rawVals); err != nil { + return err + } + + for _, val := range vals { + arg.array = append(arg.array, Arg{ + t: ArgTypeObject, + obj: convertTimeVal(&val), + }) + } + } + + arg.t = ArgTypeArray + return nil + }) +} + +func convertTimeVal(u *unix.Timeval) *Object { + return &Object{ + Name: "timeval", + Properties: []Arg{ + { + name: "sec", + t: ArgTypeUnsignedLong, + raw: uintptr(u.Sec), + }, + { + name: "usec", + t: ArgTypeUnsignedLong, + raw: uintptr(u.Usec), + }, + }, + } +} diff --git a/tracer/types_timex.go b/tracer/types_timex.go new file mode 100644 index 0000000..0fc6526 --- /dev/null +++ b/tracer/types_timex.go @@ -0,0 +1,140 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeTimex, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(unix.Timex{})) + if err != nil { + return err + } + + var tx unix.Timex + if err := decodeStruct(mem, &tx); err != nil { + return err + } + + arg.obj = convertTimex(&tx) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertTimex(tx *unix.Timex) *Object { + return &Object{ + Name: "timezone", + Properties: []Arg{ + { + name: "modes", + t: ArgTypeInt, + raw: uintptr(tx.Modes), + }, + { + name: "offset", + t: ArgTypeInt, + raw: uintptr(tx.Offset), + }, + { + name: "freq", + t: ArgTypeInt, + raw: uintptr(tx.Freq), + }, + { + name: "maxerror", + t: ArgTypeInt, + raw: uintptr(tx.Maxerror), + }, + { + name: "esterror", + t: ArgTypeInt, + raw: uintptr(tx.Esterror), + }, + { + name: "status", + t: ArgTypeInt, + raw: uintptr(tx.Status), + }, + { + name: "constant", + t: ArgTypeInt, + raw: uintptr(tx.Constant), + }, + { + name: "precision", + t: ArgTypeInt, + raw: uintptr(tx.Precision), + }, + { + name: "tolerance", + t: ArgTypeInt, + raw: uintptr(tx.Tolerance), + }, + { + name: "time", + t: ArgTypeObject, + obj: convertTimeVal(&tx.Time), + }, + { + name: "tick", + t: ArgTypeInt, + raw: uintptr(tx.Tick), + }, + { + name: "ppsfreq", + t: ArgTypeInt, + raw: uintptr(tx.Ppsfreq), + }, + { + name: "jitter", + t: ArgTypeInt, + raw: uintptr(tx.Jitter), + }, + { + name: "shift", + t: ArgTypeInt, + raw: uintptr(tx.Shift), + }, + { + name: "stabil", + t: ArgTypeInt, + raw: uintptr(tx.Stabil), + }, + { + name: "jitcnt", + t: ArgTypeInt, + raw: uintptr(tx.Jitcnt), + }, + { + name: "calcnt", + t: ArgTypeInt, + raw: uintptr(tx.Calcnt), + }, + { + name: "errcnt", + t: ArgTypeInt, + raw: uintptr(tx.Errcnt), + }, + { + name: "stbcnt", + t: ArgTypeInt, + raw: uintptr(tx.Stbcnt), + }, + { + name: "tai", + t: ArgTypeInt, + raw: uintptr(tx.Tai), + }, + }, + } +} diff --git a/tracer/types_timezone.go b/tracer/types_timezone.go new file mode 100644 index 0000000..a1b54ed --- /dev/null +++ b/tracer/types_timezone.go @@ -0,0 +1,53 @@ +package tracer + +import ( + "unsafe" +) + +type timezone struct { + Minuteswest int32 + Dsttime int32 +} + +func init() { + registerTypeHandler(argTypeTimezone, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + mem, err := readSize(pid, raw, unsafe.Sizeof(timezone{})) + if err != nil { + return err + } + + var tz timezone + if err := decodeStruct(mem, &tz); err != nil { + return err + } + + arg.obj = convertTimezone(&tz) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertTimezone(tz *timezone) *Object { + return &Object{ + Name: "timezone", + Properties: []Arg{ + { + name: "minuteswest", + t: ArgTypeInt, + raw: uintptr(tz.Minuteswest), + }, + { + name: "dsttime", + t: ArgTypeInt, + raw: uintptr(tz.Dsttime), + }, + }, + } +} diff --git a/tracer/types_tms.go b/tracer/types_tms.go new file mode 100644 index 0000000..a290931 --- /dev/null +++ b/tracer/types_tms.go @@ -0,0 +1,59 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeTms, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Tms{})) + if err != nil { + return err + } + + var times unix.Tms + if err := decodeStruct(rawVal, ×); err != nil { + return err + } + + arg.obj = convertTimes(×) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertTimes(times *unix.Tms) *Object { + return &Object{ + Name: "times", + Properties: []Arg{ + { + name: "utime", + t: ArgTypeUnsignedLong, + raw: uintptr(times.Utime), + }, + { + name: "stime", + t: ArgTypeUnsignedLong, + raw: uintptr(times.Stime), + }, + { + name: "cutime", + t: ArgTypeUnsignedLong, + raw: uintptr(times.Cutime), + }, + { + name: "cstime", + t: ArgTypeUnsignedLong, + raw: uintptr(times.Cstime), + }, + }, + } +} diff --git a/tracer/types_uname.go b/tracer/types_uname.go new file mode 100644 index 0000000..d8c444d --- /dev/null +++ b/tracer/types_uname.go @@ -0,0 +1,79 @@ +package tracer + +import ( + "syscall" + "unsafe" +) + +func init() { + registerTypeHandler(argTypeUname, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + if raw > 0 { + data, err := readSize(pid, raw, unsafe.Sizeof(syscall.Utsname{})) + if err != nil { + return err + } + + var uname syscall.Utsname + if err := decodeStruct(data, &uname); err != nil { + return err + } + + arg.obj = convertUname(&uname) + arg.t = ArgTypeObject + } else { + arg.t = ArgTypeAddress + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func getUtsField(f [65]int8) []byte { + var str []byte + for i := 0; i < len(f); i++ { + if f[i] == 0 { + break + } + str = append(str, byte(f[i])) + } + return str +} + +func convertUname(uname *syscall.Utsname) *Object { + return &Object{ + Name: "uname", + Properties: []Arg{ + { + name: "sysname", + t: ArgTypeData, + data: getUtsField(uname.Sysname), + }, + { + name: "nodename", + t: ArgTypeData, + data: getUtsField(uname.Nodename), + }, + { + name: "release", + t: ArgTypeData, + data: getUtsField(uname.Release), + }, + { + name: "version", + t: ArgTypeData, + data: getUtsField(uname.Version), + }, + { + name: "machine", + t: ArgTypeData, + data: getUtsField(uname.Machine), + }, + { + name: "domainname", + t: ArgTypeData, + data: getUtsField(uname.Domainname), + }, + }, + } +} diff --git a/tracer/types_userdesc.go b/tracer/types_userdesc.go new file mode 100644 index 0000000..99a195c --- /dev/null +++ b/tracer/types_userdesc.go @@ -0,0 +1,61 @@ +package tracer + +import ( + "unsafe" +) + +type ldt struct { + EntryNumber uint32 + BaseAddr uint32 + Limit uint32 +} + +func init() { + registerTypeHandler(argTypeUserDesc, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(ldt{})) + if err != nil { + return err + } + + var table ldt + if err := decodeStruct(rawVal, &table); err != nil { + return err + } + + arg.obj = convertLDT(table) + if err != nil { + return err + } + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertLDT(table ldt) *Object { + return &Object{ + Name: "ldt", + Properties: []Arg{ + { + name: "entry_number", + t: ArgTypeUnsignedLong, + raw: uintptr(table.EntryNumber), + }, + { + name: "base_addr", + t: ArgTypeUnsignedLong, + raw: uintptr(table.BaseAddr), + }, + { + name: "limit", + t: ArgTypeUnsignedLong, + raw: uintptr(table.Limit), + }, + }, + } +} diff --git a/tracer/types_ustat.go b/tracer/types_ustat.go new file mode 100644 index 0000000..660710c --- /dev/null +++ b/tracer/types_ustat.go @@ -0,0 +1,79 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeUstat, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + rawVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Ustat_t{})) + if err != nil { + return err + } + + var stat unix.Ustat_t + if err := decodeStruct(rawVal, &stat); err != nil { + return err + } + + arg.obj, err = convertUstat(&stat, pid) + if err != nil { + return err + } + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertUstat(stat *unix.Ustat_t, _ int) (*Object, error) { + + var fname []byte + for _, b := range stat.Fname { + if b == 0 { + break + } + fname = append(fname, byte(b)) + } + + var fpack []byte + for _, b := range stat.Fpack { + if b == 0 { + break + } + fpack = append(fpack, byte(b)) + } + + return &Object{ + Name: "ustat", + Properties: []Arg{ + { + name: "tfree", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Tfree), + }, + { + name: "tinode", + t: ArgTypeUnsignedLong, + raw: uintptr(stat.Tinode), + }, + { + name: "fname", + t: ArgTypeData, + data: fname, + }, + { + name: "fpack", + t: ArgTypeData, + data: fpack, + }, + }, + }, nil +} diff --git a/tracer/types_utimbuf.go b/tracer/types_utimbuf.go new file mode 100644 index 0000000..431bfd6 --- /dev/null +++ b/tracer/types_utimbuf.go @@ -0,0 +1,50 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeUtimbuf, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Utimbuf{})) + if err != nil { + return err + } + + var timeVal unix.Utimbuf + if err := decodeStruct(rawTimeVal, &timeVal); err != nil { + return err + } + + arg.obj = convertTimeBuf(&timeVal) + arg.t = ArgTypeObject + } else { + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} + +func convertTimeBuf(u *unix.Utimbuf) *Object { + return &Object{ + Name: "timbuf", + Properties: []Arg{ + { + name: "actime", + t: ArgTypeUnsignedLong, + raw: uintptr(u.Actime), + }, + { + name: "modtime", + t: ArgTypeUnsignedLong, + raw: uintptr(u.Modtime), + }, + }, + } +} diff --git a/tracer/types_waitstatus.go b/tracer/types_waitstatus.go new file mode 100644 index 0000000..3d0130c --- /dev/null +++ b/tracer/types_waitstatus.go @@ -0,0 +1,30 @@ +package tracer + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +func init() { + registerTypeHandler(argTypeWaitStatus, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { + + if raw > 0 { + // read the raw C struct from the process memory + bytes, err := readSize(pid, raw, unsafe.Sizeof(unix.WaitStatus(0))) + if err != nil { + return err + } + + status := uintptr(decodeUint(bytes)) + + arg.raw = status + arg.t = ArgTypeUnsignedInt + } else { + arg.t = ArgTypeAddress + arg.annotation = "NULL" + arg.replace = true + } + return nil + }) +} diff --git a/ux.png b/ux.png new file mode 100644 index 0000000..a43f399 Binary files /dev/null and b/ux.png differ