Files
mattermost/plugin/rpcplugin/sandbox/seccomp_linux_test.go
Chris f5c8a71698 ABC-22: Plugin sandboxing for linux/amd64 (#8068)
* plugin sandboxing

* remove unused type

* better symlink handling, better remounting, better test, whitespace
fixes, and comment on the remounting

* fix test compile error

* big simplification for getting mount flags

* mask statfs flags to the ones we're interested in
2018-01-15 09:21:06 -08:00

211 lines
5.8 KiB
Go

// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package sandbox
import (
"encoding/binary"
"syscall"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/bpf"
)
func seccompData(nr int32, arch uint32, ip uint64, args ...uint64) []byte {
var buf [64]byte
binary.BigEndian.PutUint32(buf[0:], uint32(nr))
binary.BigEndian.PutUint32(buf[4:], arch)
binary.BigEndian.PutUint64(buf[8:], ip)
for i := 0; i < 6 && i < len(args); i++ {
binary.BigEndian.PutUint64(buf[16+i*8:], args[i])
}
return buf[:]
}
func TestSeccompFilter(t *testing.T) {
for name, tc := range map[string]struct {
Filter []bpf.Instruction
Data []byte
Expected bool
}{
"Allowed": {
Filter: SeccompFilter(0xf00, []SeccompSyscall{
{Syscall: syscall.SYS_READ},
{Syscall: syscall.SYS_WRITE},
}),
Data: seccompData(syscall.SYS_READ, 0xf00, 0),
Expected: true,
},
"AllFail": {
Filter: SeccompFilter(0xf00, []SeccompSyscall{
{
Syscall: syscall.SYS_READ,
Any: []SeccompConditions{
{All: []SeccompCondition{
&SeccompArgHasAnyBit{Arg: 0, Mask: 2},
&SeccompArgHasAnyBit{Arg: 1, Mask: 2},
&SeccompArgHasAnyBit{Arg: 2, Mask: 2},
&SeccompArgHasAnyBit{Arg: 3, Mask: 2},
}},
},
},
{Syscall: syscall.SYS_WRITE},
}),
Data: seccompData(syscall.SYS_READ, 0xf00, 0, 1, 2, 3, 4),
Expected: false,
},
"AllPass": {
Filter: SeccompFilter(0xf00, []SeccompSyscall{
{
Syscall: syscall.SYS_READ,
Any: []SeccompConditions{
{All: []SeccompCondition{
&SeccompArgHasAnyBit{Arg: 0, Mask: 7},
&SeccompArgHasAnyBit{Arg: 1, Mask: 7},
&SeccompArgHasAnyBit{Arg: 2, Mask: 7},
&SeccompArgHasAnyBit{Arg: 3, Mask: 7},
}},
},
},
{Syscall: syscall.SYS_WRITE},
}),
Data: seccompData(syscall.SYS_READ, 0xf00, 0, 1, 2, 3, 4),
Expected: true,
},
"AnyFail": {
Filter: SeccompFilter(0xf00, []SeccompSyscall{
{
Syscall: syscall.SYS_READ,
Any: []SeccompConditions{
{All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 0, Mask: 8}}},
{All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 1, Mask: 8}}},
{All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 2, Mask: 8}}},
{All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 3, Mask: 8}}},
},
},
{Syscall: syscall.SYS_WRITE},
}),
Data: seccompData(syscall.SYS_READ, 0xf00, 0, 1, 2, 3, 4),
Expected: false,
},
"AnyPass": {
Filter: SeccompFilter(0xf00, []SeccompSyscall{
{
Syscall: syscall.SYS_READ,
Any: []SeccompConditions{
{All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 0, Mask: 2}}},
{All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 1, Mask: 2}}},
{All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 2, Mask: 2}}},
{All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 3, Mask: 2}}},
},
},
{Syscall: syscall.SYS_WRITE},
}),
Data: seccompData(syscall.SYS_READ, 0xf00, 0, 1, 2, 3, 4),
Expected: true,
},
"BadArch": {
Filter: SeccompFilter(0xf00, []SeccompSyscall{
{Syscall: syscall.SYS_READ},
{Syscall: syscall.SYS_WRITE},
}),
Data: seccompData(syscall.SYS_MOUNT, 0xf01, 0),
Expected: false,
},
"BadSyscall": {
Filter: SeccompFilter(0xf00, []SeccompSyscall{
{Syscall: syscall.SYS_READ},
{Syscall: syscall.SYS_WRITE},
}),
Data: seccompData(syscall.SYS_MOUNT, 0xf00, 0),
Expected: false,
},
} {
t.Run(name, func(t *testing.T) {
vm, err := bpf.NewVM(tc.Filter)
require.NoError(t, err)
result, err := vm.Run(tc.Data)
require.NoError(t, err)
if tc.Expected {
assert.Equal(t, SECCOMP_RET_ALLOW, result)
} else {
assert.Equal(t, int(SECCOMP_RET_ERRNO|syscall.EPERM), result)
}
})
}
}
func TestSeccompFilter_Conditions(t *testing.T) {
for name, tc := range map[string]struct {
Condition SeccompCondition
Args []uint64
Expected bool
}{
"ArgHasAnyBitFail": {
Condition: SeccompArgHasAnyBit{Arg: 0, Mask: 0x0004},
Args: []uint64{0x0400008000},
Expected: false,
},
"ArgHasAnyBitPass1": {
Condition: SeccompArgHasAnyBit{Arg: 0, Mask: 0x400000004},
Args: []uint64{0x8000008004},
Expected: true,
},
"ArgHasAnyBitPass2": {
Condition: SeccompArgHasAnyBit{Arg: 0, Mask: 0x400000004},
Args: []uint64{0x8400008000},
Expected: true,
},
"ArgHasNoBitsFail1": {
Condition: SeccompArgHasNoBits{Arg: 0, Mask: 0x1100000011},
Args: []uint64{0x0000008007},
Expected: false,
},
"ArgHasNoBitsFail2": {
Condition: SeccompArgHasNoBits{Arg: 0, Mask: 0x1100000011},
Args: []uint64{0x0700008000},
Expected: false,
},
"ArgHasNoBitsPass": {
Condition: SeccompArgHasNoBits{Arg: 0, Mask: 0x400000004},
Args: []uint64{0x8000008000},
Expected: true,
},
"ArgEqualsPass": {
Condition: SeccompArgEquals{Arg: 0, Value: 0x123456789ABCDEF},
Args: []uint64{0x123456789ABCDEF},
Expected: true,
},
"ArgEqualsFail1": {
Condition: SeccompArgEquals{Arg: 0, Value: 0x123456789ABCDEF},
Args: []uint64{0x023456789ABCDEF},
Expected: false,
},
"ArgEqualsFail2": {
Condition: SeccompArgEquals{Arg: 0, Value: 0x123456789ABCDEF},
Args: []uint64{0x123456789ABCDE0},
Expected: false,
},
} {
t.Run(name, func(t *testing.T) {
filter := SeccompFilter(0xf00, []SeccompSyscall{
{
Syscall: 1,
Any: []SeccompConditions{{All: []SeccompCondition{tc.Condition}}},
},
})
vm, err := bpf.NewVM(filter)
require.NoError(t, err)
result, err := vm.Run(seccompData(1, 0xf00, 0, tc.Args...))
require.NoError(t, err)
if tc.Expected {
assert.Equal(t, SECCOMP_RET_ALLOW, result)
} else {
assert.Equal(t, int(SECCOMP_RET_ERRNO|syscall.EPERM), result)
}
})
}
}