diff --git a/seccomp.go b/seccomp.go index e362ec6..dd28106 100644 --- a/seccomp.go +++ b/seccomp.go @@ -17,8 +17,18 @@ import ( "unsafe" ) -// #include -// #include +/* +#include +#include +#include + +// The following functions were added in libseccomp v2.6.0. +#if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 6 +int seccomp_precompute(scmp_filter_ctx ctx) { + return -EOPNOTSUPP; +} +#endif +*/ import "C" // Exported types @@ -818,6 +828,26 @@ func (f *ScmpFilter) RemoveArch(arch ScmpArch) error { return nil } +// Precompute precomputes the seccomp filter for later use by [Load] and +// similar functions. Not only does this improve performance of [Load], +// it also ensures that the seccomp filter can be loaded in an +// async-signal-safe manner if no changes have been made to the filter +// since it was precomputed. +func (f *ScmpFilter) Precompute() error { + f.lock.Lock() + defer f.lock.Unlock() + + if !f.valid { + return errBadFilter + } + + if retCode := C.seccomp_precompute(f.filterCtx); retCode != 0 { + return errRc(retCode) + } + + return nil +} + // Load loads a filter context into the kernel. // Returns an error if the filter context is invalid or the syscall failed. func (f *ScmpFilter) Load() error { diff --git a/seccomp_test.go b/seccomp_test.go index 44b104e..cfd17e4 100644 --- a/seccomp_test.go +++ b/seccomp_test.go @@ -3,6 +3,7 @@ package seccomp import ( + "errors" "fmt" "os" "os/exec" @@ -601,6 +602,18 @@ func TestRuleAddAndLoad(t *testing.T) { } func subprocessRuleAddAndLoad(t *testing.T) { + doSubprocessRuleAddAndLoad(t, false) +} + +func TestRuleAddPrecomputeAndLoad(t *testing.T) { + execInSubprocess(t, subprocessRuleAddPrecomputeAndLoad) +} + +func subprocessRuleAddPrecomputeAndLoad(t *testing.T) { + doSubprocessRuleAddAndLoad(t, true) +} + +func doSubprocessRuleAddAndLoad(t *testing.T, precompute bool) { // Test #1: Add a trivial filter filter1, err := NewFilter(ActAllow) if err != nil { @@ -653,6 +666,19 @@ func subprocessRuleAddAndLoad(t *testing.T) { t.Errorf("Error adding second conditional rule: %s", err) } + if precompute { + expErr := error(nil) + // Precompute needs seccomp 2.6.0 and API level 7. + if checkAPI(t.Name(), 7, 2, 6, 0) != nil { + expErr = syscall.EOPNOTSUPP + } + + err = filter1.Precompute() + if !errors.Is(err, expErr) { + t.Errorf("Precompute: want %v, got %v", expErr, err) + } + } + err = filter1.Load() if err != nil { t.Errorf("Error loading filter: %s", err)