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
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
|
||||
"hakurei.app/container/bits"
|
||||
@ -12,18 +13,18 @@ type (
|
||||
seccomp.ExportFlag
|
||||
bits.FilterPreset
|
||||
}
|
||||
bpfLookup map[bpfPreset][]byte
|
||||
bpfLookup map[bpfPreset][sha512.Size]byte
|
||||
)
|
||||
|
||||
func toHash(s string) []byte {
|
||||
if len(s) != 128 {
|
||||
func toHash(s string) [sha512.Size]byte {
|
||||
if len(s) != sha512.Size*2 {
|
||||
panic("bad sha512 string length")
|
||||
}
|
||||
if v, err := hex.DecodeString(s); err != nil {
|
||||
panic(err.Error())
|
||||
} else if len(v) != 64 {
|
||||
} else if len(v) != sha512.Size {
|
||||
panic("unreachable")
|
||||
} else {
|
||||
return v
|
||||
return ([sha512.Size]byte)(v)
|
||||
}
|
||||
}
|
||||
|
@ -9,14 +9,16 @@
|
||||
|
||||
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
int32_t hakurei_export_filter(int *ret_p, int fd, uint32_t arch,
|
||||
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,
|
||||
size_t rules_sz, hakurei_export_flag flags) {
|
||||
int i;
|
||||
int last_allowed_family;
|
||||
int disallowed;
|
||||
struct hakurei_syscall_rule *rule;
|
||||
void *buf;
|
||||
size_t len = 0;
|
||||
|
||||
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,
|
||||
SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1));
|
||||
|
||||
if (fd < 0) {
|
||||
if (allocate_p == 0) {
|
||||
*ret_p = seccomp_load(ctx);
|
||||
if (*ret_p != 0) {
|
||||
res = 7;
|
||||
goto out;
|
||||
}
|
||||
} 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) {
|
||||
res = 6;
|
||||
goto out;
|
||||
|
@ -18,7 +18,8 @@ struct hakurei_syscall_rule {
|
||||
struct scmp_arg_cmp *arg;
|
||||
};
|
||||
|
||||
int32_t hakurei_export_filter(int *ret_p, int fd, uint32_t arch,
|
||||
uint32_t multiarch,
|
||||
extern void *hakurei_scmp_allocate(uintptr_t f, size_t len);
|
||||
int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
|
||||
uint32_t arch, uint32_t multiarch,
|
||||
struct hakurei_syscall_rule *rules,
|
||||
size_t rules_sz, hakurei_export_flag flags);
|
@ -3,7 +3,7 @@ package seccomp
|
||||
/*
|
||||
#cgo linux pkg-config: --static libseccomp
|
||||
|
||||
#include <libseccomp-helper.h>
|
||||
#include "libseccomp-helper.h"
|
||||
#include <sys/personality.h>
|
||||
*/
|
||||
import "C"
|
||||
@ -11,23 +11,21 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"runtime/cgo"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
PER_LINUX = C.PER_LINUX
|
||||
PER_LINUX32 = C.PER_LINUX32
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidRules = errors.New("invalid native rules slice")
|
||||
)
|
||||
// ErrInvalidRules is returned for a zero-length rules slice.
|
||||
var ErrInvalidRules = errors.New("invalid native rules slice")
|
||||
|
||||
// LibraryError represents a libseccomp error.
|
||||
type LibraryError struct {
|
||||
// User facing description of the libseccomp function returning the error.
|
||||
Prefix string
|
||||
// Negated errno value returned by libseccomp.
|
||||
Seccomp syscall.Errno
|
||||
// Global errno value on return.
|
||||
Errno error
|
||||
}
|
||||
|
||||
@ -56,7 +54,9 @@ func (e *LibraryError) Is(err error) bool {
|
||||
}
|
||||
|
||||
type (
|
||||
// ScmpSyscall represents a syscall number passed to libseccomp via [NativeRule.Syscall].
|
||||
ScmpSyscall = C.int
|
||||
// ScmpErrno represents an errno value passed to libseccomp via [NativeRule.Errno].
|
||||
ScmpErrno = C.int
|
||||
)
|
||||
|
||||
@ -88,12 +88,23 @@ var resPrefix = [...]string{
|
||||
3: "seccomp_arch_add failed (multiarch)",
|
||||
4: "internal libseccomp failure",
|
||||
5: "seccomp_rule_add failed",
|
||||
6: "seccomp_export_bpf failed",
|
||||
6: "seccomp_export_bpf_mem failed",
|
||||
7: "seccomp_load failed",
|
||||
}
|
||||
|
||||
// Export streams filter contents to fd, or installs it to the current process if fd < 0.
|
||||
func Export(fd int, rules []NativeRule, flags ExportFlag) error {
|
||||
// cbAllocateBuffer is the function signature for the function handle passed to hakurei_export_filter
|
||||
// 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 {
|
||||
return ErrInvalidRules
|
||||
}
|
||||
@ -117,33 +128,56 @@ func Export(fd int, rules []NativeRule, flags ExportFlag) error {
|
||||
|
||||
var ret C.int
|
||||
|
||||
var rulesPinner runtime.Pinner
|
||||
var scmpPinner runtime.Pinner
|
||||
for i := range rules {
|
||||
rule := &rules[i]
|
||||
rulesPinner.Pin(rule)
|
||||
scmpPinner.Pin(rule)
|
||||
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,
|
||||
(*C.struct_hakurei_syscall_rule)(unsafe.Pointer(&rules[0])),
|
||||
C.size_t(len(rules)),
|
||||
flags,
|
||||
)
|
||||
rulesPinner.Unpin()
|
||||
scmpPinner.Unpin()
|
||||
if p != nil {
|
||||
allocateP.Delete()
|
||||
}
|
||||
|
||||
if prefix := resPrefix[res]; prefix != "" {
|
||||
return &LibraryError{
|
||||
prefix,
|
||||
-syscall.Errno(ret),
|
||||
err,
|
||||
}
|
||||
return &LibraryError{prefix, syscall.Errno(-ret), 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;
|
||||
// Comparison operators
|
||||
type ScmpCompare = C.enum_scmp_compare
|
||||
@ -184,7 +218,15 @@ type ScmpArgCmp struct {
|
||||
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) {
|
||||
v := C.CString(s)
|
||||
trap = int(C.seccomp_syscall_resolve_name(v))
|
||||
|
@ -3,8 +3,6 @@ package seccomp_test
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"io"
|
||||
"slices"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
@ -12,6 +10,67 @@ import (
|
||||
. "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) {
|
||||
t.Parallel()
|
||||
|
||||
@ -38,61 +97,34 @@ func TestExport(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := New(Preset(tc.presets, tc.flags), tc.flags)
|
||||
want := bpfExpected[bpfPreset{tc.flags, tc.presets}]
|
||||
digest := sha512.New()
|
||||
|
||||
if _, err := io.Copy(digest, e); (err != nil) != tc.wantErr {
|
||||
t.Errorf("Exporter: error = %v, wantErr %v", err, tc.wantErr)
|
||||
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)
|
||||
return
|
||||
}
|
||||
if err := e.Close(); err != nil {
|
||||
t.Errorf("Close: error = %v", err)
|
||||
}
|
||||
if got := digest.Sum(nil); !slices.Equal(got, want) {
|
||||
t.Fatalf("Export: hash = %x, want %x",
|
||||
got, want)
|
||||
} else if got := sha512.Sum512(data); got != want {
|
||||
t.Fatalf("Export: hash = %x, want %x", got, want)
|
||||
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) {
|
||||
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() {
|
||||
e := New(
|
||||
Preset(PresetExt|PresetDenyNS|PresetDenyTTY|PresetDenyDevel|PresetLinux32,
|
||||
AllowMultiarch|AllowCAN|AllowBluetooth),
|
||||
AllowMultiarch|AllowCAN|AllowBluetooth)
|
||||
if _, err := io.CopyBuffer(io.Discard, e, buf); err != nil {
|
||||
b.Fatalf("cannot export: %v", err)
|
||||
data, err := Export(Preset(presetFlags, exportFlags), exportFlags)
|
||||
|
||||
b.StopTimer()
|
||||
if err != nil {
|
||||
b.Fatalf("Export: error = %v", err)
|
||||
}
|
||||
if err := e.Close(); err != nil {
|
||||
b.Fatalf("cannot close exporter: %v", err)
|
||||
if got := sha512.Sum512(data); got != want {
|
||||
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) {
|
||||
allowedPersonality := PER_LINUX
|
||||
allowedPersonality := PersonaLinux
|
||||
if presets&bits.PresetLinux32 != 0 {
|
||||
allowedPersonality = PER_LINUX32
|
||||
allowedPersonality = PersonaLinux32
|
||||
}
|
||||
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