container/seccomp: remove export pipe
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m11s
Test / Sandbox (race detector) (push) Successful in 4m2s
Test / Hpkg (push) Successful in 4m19s
Test / Hakurei (race detector) (push) Successful in 4m47s
Test / Hakurei (push) Successful in 2m13s
Test / Flake checks (push) Successful in 1m32s
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m11s
Test / Sandbox (race detector) (push) Successful in 4m2s
Test / Hpkg (push) Successful in 4m19s
Test / Hakurei (race detector) (push) Successful in 4m47s
Test / Hakurei (push) Successful in 2m13s
Test / Flake checks (push) Successful in 1m32s
This was only useful when wrapping bwrap. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
7638a44fa6
commit
123d7fbfd5
@ -1,6 +1,7 @@
|
|||||||
package seccomp_test
|
package seccomp_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
|
||||||
"hakurei.app/container/bits"
|
"hakurei.app/container/bits"
|
||||||
@ -12,18 +13,18 @@ type (
|
|||||||
seccomp.ExportFlag
|
seccomp.ExportFlag
|
||||||
bits.FilterPreset
|
bits.FilterPreset
|
||||||
}
|
}
|
||||||
bpfLookup map[bpfPreset][]byte
|
bpfLookup map[bpfPreset][sha512.Size]byte
|
||||||
)
|
)
|
||||||
|
|
||||||
func toHash(s string) []byte {
|
func toHash(s string) [sha512.Size]byte {
|
||||||
if len(s) != 128 {
|
if len(s) != sha512.Size*2 {
|
||||||
panic("bad sha512 string length")
|
panic("bad sha512 string length")
|
||||||
}
|
}
|
||||||
if v, err := hex.DecodeString(s); err != nil {
|
if v, err := hex.DecodeString(s); err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
} else if len(v) != 64 {
|
} else if len(v) != sha512.Size {
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
} else {
|
} else {
|
||||||
return v
|
return ([sha512.Size]byte)(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,16 @@
|
|||||||
|
|
||||||
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
|
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
|
||||||
int32_t hakurei_export_filter(int *ret_p, int fd, uint32_t arch,
|
int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
|
||||||
uint32_t multiarch,
|
uint32_t arch, uint32_t multiarch,
|
||||||
struct hakurei_syscall_rule *rules,
|
struct hakurei_syscall_rule *rules,
|
||||||
size_t rules_sz, hakurei_export_flag flags) {
|
size_t rules_sz, hakurei_export_flag flags) {
|
||||||
int i;
|
int i;
|
||||||
int last_allowed_family;
|
int last_allowed_family;
|
||||||
int disallowed;
|
int disallowed;
|
||||||
struct hakurei_syscall_rule *rule;
|
struct hakurei_syscall_rule *rule;
|
||||||
|
void *buf;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
int32_t res = 0; /* refer to resPrefix for message */
|
int32_t res = 0; /* refer to resPrefix for message */
|
||||||
|
|
||||||
@ -108,14 +110,26 @@ int32_t hakurei_export_filter(int *ret_p, int fd, uint32_t arch,
|
|||||||
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1,
|
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1,
|
||||||
SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1));
|
SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1));
|
||||||
|
|
||||||
if (fd < 0) {
|
if (allocate_p == 0) {
|
||||||
*ret_p = seccomp_load(ctx);
|
*ret_p = seccomp_load(ctx);
|
||||||
if (*ret_p != 0) {
|
if (*ret_p != 0) {
|
||||||
res = 7;
|
res = 7;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*ret_p = seccomp_export_bpf(ctx, fd);
|
*ret_p = seccomp_export_bpf_mem(ctx, NULL, &len);
|
||||||
|
if (*ret_p != 0) {
|
||||||
|
res = 6;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = hakurei_scmp_allocate(allocate_p, len);
|
||||||
|
if (buf == NULL) {
|
||||||
|
res = 4;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret_p = seccomp_export_bpf_mem(ctx, buf, &len);
|
||||||
if (*ret_p != 0) {
|
if (*ret_p != 0) {
|
||||||
res = 6;
|
res = 6;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -18,7 +18,8 @@ struct hakurei_syscall_rule {
|
|||||||
struct scmp_arg_cmp *arg;
|
struct scmp_arg_cmp *arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
int32_t hakurei_export_filter(int *ret_p, int fd, uint32_t arch,
|
extern void *hakurei_scmp_allocate(uintptr_t f, size_t len);
|
||||||
uint32_t multiarch,
|
int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
|
||||||
|
uint32_t arch, uint32_t multiarch,
|
||||||
struct hakurei_syscall_rule *rules,
|
struct hakurei_syscall_rule *rules,
|
||||||
size_t rules_sz, hakurei_export_flag flags);
|
size_t rules_sz, hakurei_export_flag flags);
|
@ -3,7 +3,7 @@ package seccomp
|
|||||||
/*
|
/*
|
||||||
#cgo linux pkg-config: --static libseccomp
|
#cgo linux pkg-config: --static libseccomp
|
||||||
|
|
||||||
#include <libseccomp-helper.h>
|
#include "libseccomp-helper.h"
|
||||||
#include <sys/personality.h>
|
#include <sys/personality.h>
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
@ -11,23 +11,21 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/cgo"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// ErrInvalidRules is returned for a zero-length rules slice.
|
||||||
PER_LINUX = C.PER_LINUX
|
var ErrInvalidRules = errors.New("invalid native rules slice")
|
||||||
PER_LINUX32 = C.PER_LINUX32
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidRules = errors.New("invalid native rules slice")
|
|
||||||
)
|
|
||||||
|
|
||||||
// LibraryError represents a libseccomp error.
|
// LibraryError represents a libseccomp error.
|
||||||
type LibraryError struct {
|
type LibraryError struct {
|
||||||
|
// User facing description of the libseccomp function returning the error.
|
||||||
Prefix string
|
Prefix string
|
||||||
|
// Negated errno value returned by libseccomp.
|
||||||
Seccomp syscall.Errno
|
Seccomp syscall.Errno
|
||||||
|
// Global errno value on return.
|
||||||
Errno error
|
Errno error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +54,9 @@ func (e *LibraryError) Is(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// ScmpSyscall represents a syscall number passed to libseccomp via [NativeRule.Syscall].
|
||||||
ScmpSyscall = C.int
|
ScmpSyscall = C.int
|
||||||
|
// ScmpErrno represents an errno value passed to libseccomp via [NativeRule.Errno].
|
||||||
ScmpErrno = C.int
|
ScmpErrno = C.int
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -88,12 +88,23 @@ var resPrefix = [...]string{
|
|||||||
3: "seccomp_arch_add failed (multiarch)",
|
3: "seccomp_arch_add failed (multiarch)",
|
||||||
4: "internal libseccomp failure",
|
4: "internal libseccomp failure",
|
||||||
5: "seccomp_rule_add failed",
|
5: "seccomp_rule_add failed",
|
||||||
6: "seccomp_export_bpf failed",
|
6: "seccomp_export_bpf_mem failed",
|
||||||
7: "seccomp_load failed",
|
7: "seccomp_load failed",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export streams filter contents to fd, or installs it to the current process if fd < 0.
|
// cbAllocateBuffer is the function signature for the function handle passed to hakurei_export_filter
|
||||||
func Export(fd int, rules []NativeRule, flags ExportFlag) error {
|
// which allocates the buffer that the resulting bpf program is copied into, and writes its slice header
|
||||||
|
// to a value held by the caller.
|
||||||
|
type cbAllocateBuffer = func(len C.size_t) (buf unsafe.Pointer)
|
||||||
|
|
||||||
|
//export hakurei_scmp_allocate
|
||||||
|
func hakurei_scmp_allocate(f C.uintptr_t, len C.size_t) (buf unsafe.Pointer) {
|
||||||
|
return cgo.Handle(f).Value().(cbAllocateBuffer)(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeFilter generates a bpf program from a slice of [NativeRule] and writes the resulting byte slice to p.
|
||||||
|
// The filter is installed to the current process if p is nil.
|
||||||
|
func makeFilter(rules []NativeRule, flags ExportFlag, p *[]byte) error {
|
||||||
if len(rules) == 0 {
|
if len(rules) == 0 {
|
||||||
return ErrInvalidRules
|
return ErrInvalidRules
|
||||||
}
|
}
|
||||||
@ -117,33 +128,56 @@ func Export(fd int, rules []NativeRule, flags ExportFlag) error {
|
|||||||
|
|
||||||
var ret C.int
|
var ret C.int
|
||||||
|
|
||||||
var rulesPinner runtime.Pinner
|
var scmpPinner runtime.Pinner
|
||||||
for i := range rules {
|
for i := range rules {
|
||||||
rule := &rules[i]
|
rule := &rules[i]
|
||||||
rulesPinner.Pin(rule)
|
scmpPinner.Pin(rule)
|
||||||
if rule.Arg != nil {
|
if rule.Arg != nil {
|
||||||
rulesPinner.Pin(rule.Arg)
|
scmpPinner.Pin(rule.Arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res, err := C.hakurei_export_filter(
|
|
||||||
&ret, C.int(fd),
|
var allocateP cgo.Handle
|
||||||
|
if p != nil {
|
||||||
|
allocateP = cgo.NewHandle(func(len C.size_t) (buf unsafe.Pointer) {
|
||||||
|
// this is so the slice header gets a Go pointer
|
||||||
|
*p = make([]byte, len)
|
||||||
|
|
||||||
|
buf = unsafe.Pointer(unsafe.SliceData(*p))
|
||||||
|
scmpPinner.Pin(buf)
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := C.hakurei_scmp_make_filter(
|
||||||
|
&ret, C.uintptr_t(allocateP),
|
||||||
arch, multiarch,
|
arch, multiarch,
|
||||||
(*C.struct_hakurei_syscall_rule)(unsafe.Pointer(&rules[0])),
|
(*C.struct_hakurei_syscall_rule)(unsafe.Pointer(&rules[0])),
|
||||||
C.size_t(len(rules)),
|
C.size_t(len(rules)),
|
||||||
flags,
|
flags,
|
||||||
)
|
)
|
||||||
rulesPinner.Unpin()
|
scmpPinner.Unpin()
|
||||||
|
if p != nil {
|
||||||
|
allocateP.Delete()
|
||||||
|
}
|
||||||
|
|
||||||
if prefix := resPrefix[res]; prefix != "" {
|
if prefix := resPrefix[res]; prefix != "" {
|
||||||
return &LibraryError{
|
return &LibraryError{prefix, syscall.Errno(-ret), err}
|
||||||
prefix,
|
|
||||||
-syscall.Errno(ret),
|
|
||||||
err,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export generates a bpf program from a slice of [NativeRule].
|
||||||
|
// Errors returned by libseccomp is wrapped in [LibraryError].
|
||||||
|
func Export(rules []NativeRule, flags ExportFlag) (data []byte, err error) {
|
||||||
|
err = makeFilter(rules, flags, &data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load generates a bpf program from a slice of [NativeRule] and enforces it on the current process.
|
||||||
|
// Errors returned by libseccomp is wrapped in [LibraryError].
|
||||||
|
func Load(rules []NativeRule, flags ExportFlag) error { return makeFilter(rules, flags, nil) }
|
||||||
|
|
||||||
// ScmpCompare is the equivalent of scmp_compare;
|
// ScmpCompare is the equivalent of scmp_compare;
|
||||||
// Comparison operators
|
// Comparison operators
|
||||||
type ScmpCompare = C.enum_scmp_compare
|
type ScmpCompare = C.enum_scmp_compare
|
||||||
@ -184,7 +218,15 @@ type ScmpArgCmp struct {
|
|||||||
DatumA, DatumB ScmpDatum
|
DatumA, DatumB ScmpDatum
|
||||||
}
|
}
|
||||||
|
|
||||||
// only used for testing
|
const (
|
||||||
|
// PersonaLinux is passed in a [ScmpDatum] for filtering calls to syscall.SYS_PERSONALITY.
|
||||||
|
PersonaLinux = C.PER_LINUX
|
||||||
|
// PersonaLinux32 is passed in a [ScmpDatum] for filtering calls to syscall.SYS_PERSONALITY.
|
||||||
|
PersonaLinux32 = C.PER_LINUX32
|
||||||
|
)
|
||||||
|
|
||||||
|
// syscallResolveName resolves a syscall number by name via seccomp_syscall_resolve_name.
|
||||||
|
// This function is only for testing the lookup tables and included here for convenience.
|
||||||
func syscallResolveName(s string) (trap int) {
|
func syscallResolveName(s string) (trap int) {
|
||||||
v := C.CString(s)
|
v := C.CString(s)
|
||||||
trap = int(C.seccomp_syscall_resolve_name(v))
|
trap = int(C.seccomp_syscall_resolve_name(v))
|
||||||
|
@ -3,8 +3,6 @@ package seccomp_test
|
|||||||
import (
|
import (
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"slices"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -12,6 +10,67 @@ import (
|
|||||||
. "hakurei.app/container/seccomp"
|
. "hakurei.app/container/seccomp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestLibraryError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
sample *LibraryError
|
||||||
|
want string
|
||||||
|
wantIs bool
|
||||||
|
compare error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"full",
|
||||||
|
&LibraryError{Prefix: "seccomp_export_bpf failed", Seccomp: syscall.ECANCELED, Errno: syscall.EBADF},
|
||||||
|
"seccomp_export_bpf failed: operation canceled (bad file descriptor)",
|
||||||
|
true,
|
||||||
|
&LibraryError{Prefix: "seccomp_export_bpf failed", Seccomp: syscall.ECANCELED, Errno: syscall.EBADF},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"errno only",
|
||||||
|
&LibraryError{Prefix: "seccomp_init failed", Errno: syscall.ENOMEM},
|
||||||
|
"seccomp_init failed: cannot allocate memory",
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seccomp only",
|
||||||
|
&LibraryError{Prefix: "internal libseccomp failure", Seccomp: syscall.EFAULT},
|
||||||
|
"internal libseccomp failure: bad address",
|
||||||
|
true,
|
||||||
|
syscall.EFAULT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if errors.Is(tc.sample, tc.compare) != tc.wantIs {
|
||||||
|
t.Errorf("errors.Is(%#v, %#v) did not return %v",
|
||||||
|
tc.sample, tc.compare, tc.wantIs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := tc.sample.Error(); got != tc.want {
|
||||||
|
t.Errorf("Error: %q, want %q",
|
||||||
|
got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("invalid", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
wantPanic := "invalid libseccomp error"
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != wantPanic {
|
||||||
|
t.Errorf("panic: %q, want %q", r, wantPanic)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
_ = new(LibraryError).Error()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestExport(t *testing.T) {
|
func TestExport(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -38,61 +97,34 @@ func TestExport(t *testing.T) {
|
|||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
e := New(Preset(tc.presets, tc.flags), tc.flags)
|
|
||||||
want := bpfExpected[bpfPreset{tc.flags, tc.presets}]
|
want := bpfExpected[bpfPreset{tc.flags, tc.presets}]
|
||||||
digest := sha512.New()
|
if data, err := Export(Preset(tc.presets, tc.flags), tc.flags); (err != nil) != tc.wantErr {
|
||||||
|
t.Errorf("Export: error = %v, wantErr %v", err, tc.wantErr)
|
||||||
if _, err := io.Copy(digest, e); (err != nil) != tc.wantErr {
|
|
||||||
t.Errorf("Exporter: error = %v, wantErr %v", err, tc.wantErr)
|
|
||||||
return
|
return
|
||||||
}
|
} else if got := sha512.Sum512(data); got != want {
|
||||||
if err := e.Close(); err != nil {
|
t.Fatalf("Export: hash = %x, want %x", got, want)
|
||||||
t.Errorf("Close: error = %v", err)
|
|
||||||
}
|
|
||||||
if got := digest.Sum(nil); !slices.Equal(got, want) {
|
|
||||||
t.Fatalf("Export: hash = %x, want %x",
|
|
||||||
got, want)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("close without use", func(t *testing.T) {
|
|
||||||
e := New(Preset(0, 0), 0)
|
|
||||||
if err := e.Close(); !errors.Is(err, syscall.EINVAL) {
|
|
||||||
t.Errorf("Close: error = %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("close partial read", func(t *testing.T) {
|
|
||||||
e := New(Preset(0, 0), 0)
|
|
||||||
if _, err := e.Read(nil); err != nil {
|
|
||||||
t.Errorf("Read: error = %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// the underlying implementation uses buffered io, so the outcome of this is nondeterministic;
|
|
||||||
// that is not harmful however, so both outcomes are checked for here
|
|
||||||
if err := e.Close(); err != nil &&
|
|
||||||
(!errors.Is(err, syscall.ECANCELED) || !errors.Is(err, syscall.EBADF)) {
|
|
||||||
t.Errorf("Close: error = %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkExport(b *testing.B) {
|
func BenchmarkExport(b *testing.B) {
|
||||||
buf := make([]byte, 8)
|
const exportFlags = AllowMultiarch | AllowCAN | AllowBluetooth
|
||||||
|
const presetFlags = PresetExt | PresetDenyNS | PresetDenyTTY | PresetDenyDevel | PresetLinux32
|
||||||
|
var want = bpfExpected[bpfPreset{exportFlags, presetFlags}]
|
||||||
|
|
||||||
for b.Loop() {
|
for b.Loop() {
|
||||||
e := New(
|
data, err := Export(Preset(presetFlags, exportFlags), exportFlags)
|
||||||
Preset(PresetExt|PresetDenyNS|PresetDenyTTY|PresetDenyDevel|PresetLinux32,
|
|
||||||
AllowMultiarch|AllowCAN|AllowBluetooth),
|
b.StopTimer()
|
||||||
AllowMultiarch|AllowCAN|AllowBluetooth)
|
if err != nil {
|
||||||
if _, err := io.CopyBuffer(io.Discard, e, buf); err != nil {
|
b.Fatalf("Export: error = %v", err)
|
||||||
b.Fatalf("cannot export: %v", err)
|
|
||||||
}
|
}
|
||||||
if err := e.Close(); err != nil {
|
if got := sha512.Sum512(data); got != want {
|
||||||
b.Fatalf("cannot close exporter: %v", err)
|
b.Fatalf("Export: hash = %x, want %x", got, want)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
b.StartTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Preset(presets bits.FilterPreset, flags ExportFlag) (rules []NativeRule) {
|
func Preset(presets bits.FilterPreset, flags ExportFlag) (rules []NativeRule) {
|
||||||
allowedPersonality := PER_LINUX
|
allowedPersonality := PersonaLinux
|
||||||
if presets&bits.PresetLinux32 != 0 {
|
if presets&bits.PresetLinux32 != 0 {
|
||||||
allowedPersonality = PER_LINUX32
|
allowedPersonality = PersonaLinux32
|
||||||
}
|
}
|
||||||
presetDevelFinal := presetDevel(ScmpDatum(allowedPersonality))
|
presetDevelFinal := presetDevel(ScmpDatum(allowedPersonality))
|
||||||
|
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
package seccomp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"hakurei.app/helper/proc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New returns an inactive Encoder instance.
|
|
||||||
func New(rules []NativeRule, flags ExportFlag) *Encoder { return &Encoder{newExporter(rules, flags)} }
|
|
||||||
|
|
||||||
// Load loads a filter into the kernel.
|
|
||||||
func Load(rules []NativeRule, flags ExportFlag) error { return Export(-1, rules, flags) }
|
|
||||||
|
|
||||||
/*
|
|
||||||
An Encoder writes a BPF program to an output stream.
|
|
||||||
|
|
||||||
Methods of Encoder are not safe for concurrent use.
|
|
||||||
|
|
||||||
An Encoder must not be copied after first use.
|
|
||||||
*/
|
|
||||||
type Encoder struct{ *exporter }
|
|
||||||
|
|
||||||
func (e *Encoder) Read(p []byte) (n int, err error) {
|
|
||||||
if err = e.prepare(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return e.r.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) Close() error {
|
|
||||||
if e.r == nil {
|
|
||||||
return syscall.EINVAL
|
|
||||||
}
|
|
||||||
|
|
||||||
// this hangs if the cgo thread fails to exit
|
|
||||||
return errors.Join(e.closeWrite(), <-e.exportErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFile returns an instance of exporter implementing [proc.File].
|
|
||||||
func NewFile(rules []NativeRule, flags ExportFlag) proc.File {
|
|
||||||
return &File{rules: rules, flags: flags}
|
|
||||||
}
|
|
||||||
|
|
||||||
// File implements [proc.File] and provides access to the read end of exporter pipe.
|
|
||||||
type File struct {
|
|
||||||
rules []NativeRule
|
|
||||||
flags ExportFlag
|
|
||||||
proc.BaseFile
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) ErrCount() int { return 2 }
|
|
||||||
func (f *File) Fulfill(ctx context.Context, dispatchErr func(error)) error {
|
|
||||||
e := newExporter(f.rules, f.flags)
|
|
||||||
if err := e.prepare(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.Set(e.r)
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case err := <-e.exportErr:
|
|
||||||
dispatchErr(nil)
|
|
||||||
dispatchErr(err)
|
|
||||||
case <-ctx.Done():
|
|
||||||
dispatchErr(e.closeWrite())
|
|
||||||
dispatchErr(<-e.exportErr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
// Package seccomp provides high level wrappers around libseccomp.
|
|
||||||
package seccomp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type exporter struct {
|
|
||||||
rules []NativeRule
|
|
||||||
flags ExportFlag
|
|
||||||
r, w *os.File
|
|
||||||
|
|
||||||
prepareOnce sync.Once
|
|
||||||
prepareErr error
|
|
||||||
closeOnce sync.Once
|
|
||||||
closeErr error
|
|
||||||
exportErr <-chan error
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
ec := make(chan error, 1)
|
|
||||||
go func(fd uintptr) {
|
|
||||||
ec <- Export(int(fd), e.rules, e.flags)
|
|
||||||
close(ec)
|
|
||||||
_ = e.closeWrite()
|
|
||||||
runtime.KeepAlive(e.w)
|
|
||||||
}(e.w.Fd())
|
|
||||||
e.exportErr = ec
|
|
||||||
runtime.SetFinalizer(e, (*exporter).closeWrite)
|
|
||||||
})
|
|
||||||
return e.prepareErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *exporter) closeWrite() error {
|
|
||||||
e.closeOnce.Do(func() {
|
|
||||||
if e.w == nil {
|
|
||||||
panic("closeWrite called on invalid exporter")
|
|
||||||
}
|
|
||||||
e.closeErr = e.w.Close()
|
|
||||||
|
|
||||||
// no need for a finalizer anymore
|
|
||||||
runtime.SetFinalizer(e, nil)
|
|
||||||
})
|
|
||||||
|
|
||||||
return e.closeErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func newExporter(rules []NativeRule, flags ExportFlag) *exporter {
|
|
||||||
return &exporter{rules: rules, flags: flags}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package seccomp_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"hakurei.app/container/seccomp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLibraryError(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
sample *seccomp.LibraryError
|
|
||||||
want string
|
|
||||||
wantIs bool
|
|
||||||
compare error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"full",
|
|
||||||
&seccomp.LibraryError{Prefix: "seccomp_export_bpf failed", Seccomp: syscall.ECANCELED, Errno: syscall.EBADF},
|
|
||||||
"seccomp_export_bpf failed: operation canceled (bad file descriptor)",
|
|
||||||
true,
|
|
||||||
&seccomp.LibraryError{Prefix: "seccomp_export_bpf failed", Seccomp: syscall.ECANCELED, Errno: syscall.EBADF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"errno only",
|
|
||||||
&seccomp.LibraryError{Prefix: "seccomp_init failed", Errno: syscall.ENOMEM},
|
|
||||||
"seccomp_init failed: cannot allocate memory",
|
|
||||||
false,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"seccomp only",
|
|
||||||
&seccomp.LibraryError{Prefix: "internal libseccomp failure", Seccomp: syscall.EFAULT},
|
|
||||||
"internal libseccomp failure: bad address",
|
|
||||||
true,
|
|
||||||
syscall.EFAULT,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
if errors.Is(tc.sample, tc.compare) != tc.wantIs {
|
|
||||||
t.Errorf("errors.Is(%#v, %#v) did not return %v",
|
|
||||||
tc.sample, tc.compare, tc.wantIs)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got := tc.sample.Error(); got != tc.want {
|
|
||||||
t.Errorf("Error: %q, want %q",
|
|
||||||
got, tc.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("invalid", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
wantPanic := "invalid libseccomp error"
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != wantPanic {
|
|
||||||
t.Errorf("panic: %q, want %q", r, wantPanic)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
runtime.KeepAlive(new(seccomp.LibraryError).Error())
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user