aboutsummaryrefslogtreecommitdiffhomepage
path: root/sandbox/seccomp/seccomp.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-07-01 20:23:33 +0900
committerOphestra <cat@gensokyo.uk>2025-07-01 22:11:32 +0900
commit1a8840bebc673672235b6e10b1b9386f24751757 (patch)
treed1e6772bfd685e2162d047e3640bd3ee55c1a1f7 /sandbox/seccomp/seccomp.go
parent1fb453dffe4c83866fedfa4590be30ec65e815ff (diff)
sandbox/seccomp: resolve rules natively
This enables loading syscall filter policies from external cross-platform config files. This also removes a significant amount of C code. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'sandbox/seccomp/seccomp.go')
-rw-r--r--sandbox/seccomp/seccomp.go151
1 files changed, 43 insertions, 108 deletions
diff --git a/sandbox/seccomp/seccomp.go b/sandbox/seccomp/seccomp.go
index c9a201e2..adbe4d08 100644
--- a/sandbox/seccomp/seccomp.go
+++ b/sandbox/seccomp/seccomp.go
@@ -1,125 +1,60 @@
-// Package seccomp provides filter presets and high level wrappers around libseccomp.
+// Package seccomp provides high level wrappers around libseccomp.
package seccomp
-/*
-#cgo linux pkg-config: --static libseccomp
-
-#include "seccomp-build.h"
-*/
-import "C"
-
import (
- "errors"
- "fmt"
+ "os"
"runtime"
- "syscall"
- "unsafe"
+ "sync"
)
-// LibraryError represents a libseccomp error.
-type LibraryError struct {
- Prefix string
- Seccomp syscall.Errno
- Errno error
-}
+type exporter struct {
+ presets FilterPreset
+ flags PrepareFlag
+ r, w *os.File
-func (e *LibraryError) Error() string {
- if e.Seccomp == 0 {
- if e.Errno == nil {
- panic("invalid libseccomp error")
- }
- return fmt.Sprintf("%s: %s", e.Prefix, e.Errno)
- }
- if e.Errno == nil {
- return fmt.Sprintf("%s: %s", e.Prefix, e.Seccomp)
- }
- return fmt.Sprintf("%s: %s (%s)", e.Prefix, e.Seccomp, e.Errno)
+ prepareOnce sync.Once
+ prepareErr error
+ closeOnce sync.Once
+ closeErr error
+ exportErr <-chan error
}
-func (e *LibraryError) Is(err error) bool {
- if e == nil {
- return err == nil
- }
- if ef, ok := err.(*LibraryError); ok {
- return *e == *ef
- }
- return (e.Seccomp != 0 && errors.Is(err, e.Seccomp)) ||
- (e.Errno != nil && errors.Is(err, e.Errno))
-}
+func (e *exporter) prepare() error {
+ e.prepareOnce.Do(func() {
+ if r, w, err := os.Pipe(); err != nil {
+ e.prepareErr = err
+ return
+ } else {
+ e.r, e.w = r, w
+ }
-var resPrefix = [...]string{
- 0: "",
- 1: "seccomp_init failed",
- 2: "seccomp_arch_add failed",
- 3: "seccomp_arch_add failed (multiarch)",
- 4: "internal libseccomp failure",
- 5: "seccomp_rule_add failed",
- 6: "seccomp_export_bpf failed",
- 7: "seccomp_load failed",
+ ec := make(chan error, 1)
+ go func(fd uintptr) {
+ ec <- preparePreset(int(fd), e.presets, e.flags)
+ close(ec)
+ _ = e.closeWrite()
+ runtime.KeepAlive(e.w)
+ }(e.w.Fd())
+ e.exportErr = ec
+ runtime.SetFinalizer(e, (*exporter).closeWrite)
+ })
+ return e.prepareErr
}
-type FilterOpts = C.hakurei_filter_opts
-
-const (
- filterVerbose FilterOpts = C.HAKUREI_VERBOSE
- // FilterExt are project-specific extensions.
- FilterExt FilterOpts = C.HAKUREI_EXT
- // FilterDenyNS denies namespace setup syscalls.
- FilterDenyNS FilterOpts = C.HAKUREI_DENY_NS
- // FilterDenyTTY denies faking input.
- FilterDenyTTY FilterOpts = C.HAKUREI_DENY_TTY
- // FilterDenyDevel denies development-related syscalls.
- FilterDenyDevel FilterOpts = C.HAKUREI_DENY_DEVEL
- // FilterMultiarch allows multiarch/emulation.
- FilterMultiarch FilterOpts = C.HAKUREI_MULTIARCH
- // FilterLinux32 sets PER_LINUX32.
- FilterLinux32 FilterOpts = C.HAKUREI_LINUX32
- // FilterCan allows AF_CAN.
- FilterCan FilterOpts = C.HAKUREI_CAN
- // FilterBluetooth allows AF_BLUETOOTH.
- FilterBluetooth FilterOpts = C.HAKUREI_BLUETOOTH
-)
-
-func buildFilter(fd int, opts FilterOpts) error {
- var (
- arch C.uint32_t = 0
- multiarch C.uint32_t = 0
- )
- switch runtime.GOARCH {
- case "386":
- arch = C.SCMP_ARCH_X86
- case "amd64":
- arch = C.SCMP_ARCH_X86_64
- multiarch = C.SCMP_ARCH_X86
- case "arm":
- arch = C.SCMP_ARCH_ARM
- case "arm64":
- arch = C.SCMP_ARCH_AARCH64
- multiarch = C.SCMP_ARCH_ARM
- }
+func (e *exporter) closeWrite() error {
+ e.closeOnce.Do(func() {
+ if e.w == nil {
+ panic("closeWrite called on invalid exporter")
+ }
+ e.closeErr = e.w.Close()
- // this removes repeated transitions between C and Go execution
- // when producing log output via hakurei_println and CPrintln is nil
- if fp := printlnP.Load(); fp != nil {
- opts |= filterVerbose
- }
+ // no need for a finalizer anymore
+ runtime.SetFinalizer(e, nil)
+ })
- var ret C.int
- res, err := C.hakurei_build_filter(&ret, C.int(fd), arch, multiarch, opts)
- if prefix := resPrefix[res]; prefix != "" {
- return &LibraryError{
- prefix,
- -syscall.Errno(ret),
- err,
- }
- }
- return err
+ return e.closeErr
}
-// only used for testing
-func syscallResolveName(s string) (trap int) {
- v := C.CString(s)
- trap = int(C.seccomp_syscall_resolve_name(v))
- C.free(unsafe.Pointer(v))
- return
+func newExporter(presets FilterPreset, flags PrepareFlag) *exporter {
+ return &exporter{presets: presets, flags: flags}
}