diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-20 22:43:32 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-20 22:43:32 +0300 |
| commit | 6ca4d5ddacaff05d8bd82a5e9a6dfbb39ac111c9 (patch) | |
| tree | a0b4469a9eb96bfb0b5a09d5f086219782040982 /cmd | |
| parent | 7a9839917461b12c810329ccb8fd3c6de06902d2 (diff) | |
feat: add keyctl ptrace perf_event_open tracing (task 77)
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/ioworkload/scenario_security.go | 134 | ||||
| -rw-r--r-- | cmd/ioworkload/scenario_security_test.go | 38 | ||||
| -rw-r--r-- | cmd/ioworkload/scenarios.go | 1 |
3 files changed, 173 insertions, 0 deletions
diff --git a/cmd/ioworkload/scenario_security.go b/cmd/ioworkload/scenario_security.go new file mode 100644 index 0000000..e9e0fe8 --- /dev/null +++ b/cmd/ioworkload/scenario_security.go @@ -0,0 +1,134 @@ +package main + +import ( + "fmt" + "runtime" + "syscall" + "unsafe" +) + +var keySpecProcessKeyringArg = ^uintptr(1) + +func securityKeysPtracePerf() error { + nr, err := securitySyscallNumbers(runtime.GOARCH) + if err != nil { + return err + } + + // Best-effort probes: these syscalls may fail with EPERM/EACCES depending on + // policy, but the tracepoints are still exercised. + runKeySyscalls(nr) + runPtraceSyscall(nr) + runPerfEventOpenSyscall(nr) + return nil +} + +type securitySyscalls struct { + addKey uintptr + requestKey uintptr + keyctl uintptr + ptrace uintptr + perfEventOpen uintptr +} + +func securitySyscallNumbers(arch string) (securitySyscalls, error) { + switch arch { + case "amd64": + return securitySyscalls{ + addKey: 248, + requestKey: 249, + keyctl: 250, + ptrace: 101, + perfEventOpen: 298, + }, nil + case "arm64": + return securitySyscalls{ + addKey: 217, + requestKey: 218, + keyctl: 219, + ptrace: 117, + perfEventOpen: 241, + }, nil + default: + return securitySyscalls{}, fmt.Errorf("security syscall numbers not defined for GOARCH=%s", arch) + } +} + +func runKeySyscalls(nr securitySyscalls) { + keyType, _ := syscall.BytePtrFromString("user") + desc, _ := syscall.BytePtrFromString("ior-key") + payload := []byte("ior") + + var payloadPtr uintptr + if len(payload) > 0 { + payloadPtr = uintptr(unsafe.Pointer(&payload[0])) + } + + _, _, _ = syscall.Syscall6( + nr.addKey, + uintptr(unsafe.Pointer(keyType)), + uintptr(unsafe.Pointer(desc)), + payloadPtr, + uintptr(len(payload)), + keySpecProcessKeyringArg, + 0, + ) + + _, _, _ = syscall.Syscall6( + nr.requestKey, + uintptr(unsafe.Pointer(keyType)), + uintptr(unsafe.Pointer(desc)), + 0, + keySpecProcessKeyringArg, + 0, + 0, + ) + + _, _, _ = syscall.Syscall6( + nr.keyctl, + 0, + keySpecProcessKeyringArg, + 0, + 0, + 0, + 0, + ) +} + +func runPtraceSyscall(nr securitySyscalls) { + _, _, _ = syscall.Syscall6( + nr.ptrace, + uintptr(syscall.PTRACE_TRACEME), + 0, + 0, + 0, + 0, + 0, + ) +} + +type perfEventAttr struct { + Type uint32 + Size uint32 + Config uint64 +} + +func runPerfEventOpenSyscall(nr securitySyscalls) { + attr := perfEventAttr{ + Type: 1, // PERF_TYPE_SOFTWARE + Size: uint32(unsafe.Sizeof(perfEventAttr{})), + Config: 0, // PERF_COUNT_SW_CPU_CLOCK + } + fd, _, _ := syscall.Syscall6( + nr.perfEventOpen, + uintptr(unsafe.Pointer(&attr)), + 0, + ^uintptr(0), // cpu = -1 + ^uintptr(0), // group_fd = -1 + 0, + 0, + ) + if int64(fd) >= 0 { + _ = syscall.Close(int(fd)) + } +} diff --git a/cmd/ioworkload/scenario_security_test.go b/cmd/ioworkload/scenario_security_test.go new file mode 100644 index 0000000..f1b6152 --- /dev/null +++ b/cmd/ioworkload/scenario_security_test.go @@ -0,0 +1,38 @@ +package main + +import "testing" + +func TestSecuritySyscallNumbers(t *testing.T) { + for _, tc := range []struct { + name string + arch string + wantErr bool + addKey uintptr + ptrace uintptr + perf uintptr + }{ + {name: "amd64", arch: "amd64", addKey: 248, ptrace: 101, perf: 298}, + {name: "arm64", arch: "arm64", addKey: 217, ptrace: 117, perf: 241}, + {name: "unsupported", arch: "riscv64", wantErr: true}, + } { + got, err := securitySyscallNumbers(tc.arch) + if tc.wantErr { + if err == nil { + t.Fatalf("%s: expected error", tc.name) + } + continue + } + if err != nil { + t.Fatalf("%s: unexpected error: %v", tc.name, err) + } + if got.addKey != tc.addKey || got.ptrace != tc.ptrace || got.perfEventOpen != tc.perf { + t.Fatalf( + "%s: unexpected numbers add_key=%d ptrace=%d perf_event_open=%d", + tc.name, + got.addKey, + got.ptrace, + got.perfEventOpen, + ) + } + } +} diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go index 7ca5aa4..a1039e0 100644 --- a/cmd/ioworkload/scenarios.go +++ b/cmd/ioworkload/scenarios.go @@ -111,6 +111,7 @@ var scenarios = map[string]func() error{ "truncate-ftruncate-ebadf": truncateFtruncateEbadf, "pidfd-getfd-success": pidfdGetfdSuccess, "pidfd-getfd-failure": pidfdGetfdFailure, + "security-keys-ptrace-perf": securityKeysPtracePerf, "iouring-setup": iouringSetup, "iouring-enter": iouringEnter, "iouring-register": iouringRegister, |
