diff --git a/seccomp.go b/seccomp.go index dd28106..b982c84 100644 --- a/seccomp.go +++ b/seccomp.go @@ -27,6 +27,9 @@ import ( int seccomp_precompute(scmp_filter_ctx ctx) { return -EOPNOTSUPP; } +int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf, size_t *len) { + return -EOPNOTSUPP; +} #endif */ import "C" @@ -1242,6 +1245,30 @@ func (f *ScmpFilter) ExportBPF(file *os.File) error { return nil } +// ExportBPFMem is similar to [ExportBPF], except the data is written into +// a memory and returned as []byte. +func (f *ScmpFilter) ExportBPFMem() ([]byte, error) { + f.lock.Lock() + defer f.lock.Unlock() + + if !f.valid { + return nil, errBadFilter + } + + var len C.size_t + // Get the size required. + if retCode := C.seccomp_export_bpf_mem(f.filterCtx, unsafe.Pointer(nil), &len); retCode < 0 { + return nil, errRc(retCode) + } + // Get the data. + buf := make([]byte, int(len)) + if retCode := C.seccomp_export_bpf_mem(f.filterCtx, unsafe.Pointer(&buf[0]), &len); retCode < 0 { + return nil, errRc(retCode) + } + + return buf, nil +} + // Userspace Notification API // GetNotifFd returns the userspace notification file descriptor associated with the given diff --git a/seccomp_test.go b/seccomp_test.go index cfd17e4..aac516f 100644 --- a/seccomp_test.go +++ b/seccomp_test.go @@ -3,8 +3,10 @@ package seccomp import ( + "bytes" "errors" "fmt" + "io" "os" "os/exec" "strings" @@ -773,6 +775,64 @@ func subprocessCreateActKillProcessFilter(t *testing.T) { } } +func TestExportBPF(t *testing.T) { + execInSubprocess(t, subprocessExportBPF) +} + +func subprocessExportBPF(t *testing.T) { + filter, err := NewFilter(ActAllow) + if err != nil { + t.Fatalf("Error creating filter: %s", err) + } + defer filter.Release() + + call, err := GetSyscallFromName("getpid") + if err != nil { + t.Fatalf("Error getting syscall number of getpid: %s", err) + } + + err = filter.AddRule(call, ActErrno.SetReturnCode(42)) + if err != nil { + t.Fatalf("Error adding rule: %s", err) + } + + file, err := os.Create(t.TempDir() + "/bpf") + if err != nil { + t.Fatal(err) + } + defer file.Close() + + err = filter.ExportBPF(file) + if err != nil { + t.Fatalf("ExportBPF: %v", err) + } + + if _, err := file.Seek(0, io.SeekStart); err != nil { + t.Fatal(err) + } + contents, err := io.ReadAll(file) + if err != nil { + t.Fatal(err) + } + t.Logf("ExportBPF: size %d", len(contents)) + + expErr := error(nil) + // ExportBPFMem needs seccomp 2.6.0. + if checkAPI(t.Name(), 0, 2, 6, 0) != nil { + expErr = syscall.EOPNOTSUPP + } + contentsMem, err := filter.ExportBPFMem() + if err != expErr { + t.Errorf("ExportBPFMem: want %v, got %v", expErr, err) + } + if err == nil { + t.Logf("ExportBPFMem: size %d", len(contents)) + if !bytes.Equal(contents, contentsMem) { + t.Errorf("Got different data from ExportBPF and ExportBPFMem (%v != %v)", contents, contentsMem) + } + } +} + // // Seccomp notification tests //