aboutsummaryrefslogtreecommitdiffhomepage
path: root/helper/seccomp/export_test.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-02-03 18:10:29 +0900
committerOphestra <cat@gensokyo.uk>2025-02-03 19:43:03 +0900
commit5b7b3fa9a4d8ca4ad08d6648752e28ee66f0ffae (patch)
tree8f8790e0114e33bc19efeed9a8902404f5b0e61f /helper/seccomp/export_test.go
parentd58fb8c6eee659e4a71c4d69bfb8539199b431bd (diff)
helper/seccomp: implement reader interface via pipe
This also does not require the libc tmpfile call. BPF programs emitted by libseccomp seems to be deterministic. The tests would catch regressions as it verifies the program against known good output backed by manual testing. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'helper/seccomp/export_test.go')
-rw-r--r--helper/seccomp/export_test.go123
1 files changed, 123 insertions, 0 deletions
diff --git a/helper/seccomp/export_test.go b/helper/seccomp/export_test.go
new file mode 100644
index 00000000..bbafa3b1
--- /dev/null
+++ b/helper/seccomp/export_test.go
@@ -0,0 +1,123 @@
+package seccomp_test
+
+import (
+ "crypto/sha512"
+ "errors"
+ "io"
+ "slices"
+ "syscall"
+ "testing"
+
+ "git.gensokyo.uk/security/fortify/helper/seccomp"
+ "git.gensokyo.uk/security/fortify/internal/fmsg"
+)
+
+func TestExport(t *testing.T) {
+ testCases := []struct {
+ name string
+ opts seccomp.SyscallOpts
+ want []byte
+ wantErr bool
+ }{
+ {"compat", 0, []byte{
+ 0x95, 0xec, 0x69, 0xd0, 0x17, 0x73, 0x3e, 0x07,
+ 0x21, 0x60, 0xe0, 0xda, 0x80, 0xfd, 0xeb, 0xec,
+ 0xdf, 0x27, 0xae, 0x81, 0x66, 0xf5, 0xe2, 0xa7,
+ 0x31, 0x27, 0x0c, 0x98, 0xea, 0x2d, 0x29, 0x46,
+ 0xcb, 0x52, 0x31, 0x02, 0x90, 0x63, 0x66, 0x8a,
+ 0xf2, 0x15, 0x87, 0x91, 0x55, 0xda, 0x21, 0xac,
+ 0xa7, 0x9b, 0x07, 0x0e, 0x04, 0xc0, 0xee, 0x9a,
+ 0xcd, 0xf5, 0x8f, 0x55, 0xcf, 0xa8, 0x15, 0xa5,
+ }, false},
+ {"base", seccomp.FlagExt, []byte{
+ 0xdc, 0x7f, 0x2e, 0x1c, 0x5e, 0x82, 0x9b, 0x79,
+ 0xeb, 0xb7, 0xef, 0xc7, 0x59, 0x15, 0x0f, 0x54,
+ 0xa8, 0x3a, 0x75, 0xc8, 0xdf, 0x6f, 0xee, 0x4d,
+ 0xce, 0x5d, 0xad, 0xc4, 0x73, 0x6c, 0x58, 0x5d,
+ 0x4d, 0xee, 0xbf, 0xeb, 0x3c, 0x79, 0x69, 0xaf,
+ 0x3a, 0x07, 0x7e, 0x90, 0xb7, 0x7b, 0xb4, 0x74,
+ 0x1d, 0xb0, 0x5d, 0x90, 0x99, 0x7c, 0x86, 0x59,
+ 0xb9, 0x58, 0x91, 0x20, 0x6a, 0xc9, 0x95, 0x2d,
+ }, false},
+ {"everything", seccomp.FlagExt |
+ seccomp.FlagDenyNS | seccomp.FlagDenyTTY | seccomp.FlagDenyDevel |
+ seccomp.FlagMultiarch | seccomp.FlagLinux32 | seccomp.FlagCan |
+ seccomp.FlagBluetooth, []byte{
+ 0xe9, 0x9d, 0xd3, 0x45, 0xe1, 0x95, 0x41, 0x34,
+ 0x73, 0xd3, 0xcb, 0xee, 0x07, 0xb4, 0xed, 0x57,
+ 0xb9, 0x08, 0xbf, 0xa8, 0x9e, 0xa2, 0x07, 0x2f,
+ 0xe9, 0x34, 0x82, 0x84, 0x7f, 0x50, 0xb5, 0xb7,
+ 0x58, 0xda, 0x17, 0xe7, 0x4c, 0xa2, 0xbb, 0xc0,
+ 0x08, 0x13, 0xde, 0x49, 0xa2, 0xb9, 0xbf, 0x83,
+ 0x4c, 0x02, 0x4e, 0xd4, 0x88, 0x50, 0xbe, 0x69,
+ 0xb6, 0x8a, 0x9a, 0x4c, 0x5f, 0x53, 0xa9, 0xdb,
+ }, false},
+ {"strict", seccomp.FlagExt |
+ seccomp.FlagDenyNS | seccomp.FlagDenyTTY | seccomp.FlagDenyDevel, []byte{
+ 0xe8, 0x80, 0x29, 0x8d, 0xf2, 0xbd, 0x67, 0x51,
+ 0xd0, 0x04, 0x0f, 0xc2, 0x1b, 0xc0, 0xed, 0x4c,
+ 0x00, 0xf9, 0x5d, 0xc0, 0xd7, 0xba, 0x50, 0x6c,
+ 0x24, 0x4d, 0x8b, 0x8c, 0xf6, 0x86, 0x6d, 0xba,
+ 0x8e, 0xf4, 0xa3, 0x32, 0x96, 0xf2, 0x87, 0xb6,
+ 0x6c, 0xcc, 0xc1, 0xd7, 0x8e, 0x97, 0x02, 0x65,
+ 0x97, 0xf8, 0x4c, 0xc7, 0xde, 0xc1, 0x57, 0x3e,
+ 0x14, 0x89, 0x60, 0xfb, 0xd3, 0x5c, 0xd7, 0x35,
+ }, false},
+ {"strict compat", 0 |
+ seccomp.FlagDenyNS | seccomp.FlagDenyTTY | seccomp.FlagDenyDevel, []byte{
+ 0x39, 0x87, 0x1b, 0x93, 0xff, 0xaf, 0xc8, 0xb9,
+ 0x79, 0xfc, 0xed, 0xc0, 0xb0, 0xc3, 0x7b, 0x9e,
+ 0x03, 0x92, 0x2f, 0x5b, 0x02, 0x74, 0x8d, 0xc5,
+ 0xc3, 0xc1, 0x7c, 0x92, 0x52, 0x7f, 0x6e, 0x02,
+ 0x2e, 0xde, 0x1f, 0x48, 0xbf, 0xf5, 0x92, 0x46,
+ 0xea, 0x45, 0x2c, 0x0d, 0x1d, 0xe5, 0x48, 0x27,
+ 0x80, 0x8b, 0x1a, 0x6f, 0x84, 0xf3, 0x2b, 0xbd,
+ 0xe1, 0xaa, 0x02, 0xae, 0x30, 0xee, 0xdc, 0xfa,
+ }, false},
+ }
+
+ buf := make([]byte, 8)
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ seccomp.CPrintln = fmsg.Println
+ t.Cleanup(func() { seccomp.CPrintln = nil })
+
+ e := seccomp.New(tc.opts)
+ digest := sha512.New()
+
+ if _, err := io.CopyBuffer(digest, e, buf); (err != nil) != tc.wantErr {
+ t.Errorf("Exporter: error = %v, wantErr %v", err, tc.wantErr)
+ return
+ }
+ if err := e.Close(); err != nil {
+ t.Errorf("Close: error = %v", err)
+ return
+ }
+ if got := digest.Sum(nil); slices.Compare(got, tc.want) != 0 {
+ t.Fatalf("Export() hash = %x, want %x",
+ got, tc.want)
+ return
+ }
+ })
+ }
+
+ t.Run("close without use", func(t *testing.T) {
+ e := seccomp.New(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 := seccomp.New(0)
+ if _, err := e.Read(make([]byte, 0)); err != nil {
+ t.Errorf("Read: error = %v", err)
+ return
+ }
+ if err := e.Close(); err == nil || err.Error() != "seccomp_export_bpf failed: operation canceled" {
+ t.Errorf("Close: error = %v", err)
+ return
+ }
+ })
+}