seccomp.BuildProgram: Add ProgramOptions struct.

This is just a refactoring, but the intention of this `struct` is to add
other useful options for the program build, such as the list of expected
"hottest" syscalls by frequency.

The interface is a bit awkward, because of the need for two entry points
(one which didn't have any way to set the default actions), and because the
zero value of `linux.BPFAction` is a valid (and common) "default action":
killing the program (`linux.SECCOMP_RET_KILL_THREAD`).
We also can't use `*linux.BPFAction`, as `linux.SECCOMP_RET_*` are constants.
So the struct fields use functions that "resolve" to an action.
This reads fairly well in the call sites (`DefaultAction: Return(action)`),
at the cost of slightly convoluted logic in `seccomp.go`.

PiperOrigin-RevId: 581477348
This commit is contained in:
Etienne Perot
2023-11-11 00:44:40 -08:00
committed by gVisor bot
parent b4bf726395
commit 62175dea49
12 changed files with 155 additions and 73 deletions
+30 -13
View File
@@ -52,29 +52,24 @@ const (
// making it possible for the process to continue running after a violation.
// However, it will leave a SECCOMP audit event trail behind. In any case, the
// syscall is still blocked from executing.
func Install(rules SyscallRules, denyRules SyscallRules) error {
defaultAction, err := defaultAction()
if err != nil {
return err
}
func Install(rules SyscallRules, denyRules SyscallRules, options ProgramOptions) error {
// *** DEBUG TIP ***
// If you suspect the process is getting killed due to a seccomp violation, uncomment the line
// below to get a panic stack trace when there is a violation.
// defaultAction = linux.BPFAction(linux.SECCOMP_RET_TRAP)
// options.DefaultAction = Return(linux.BPFAction(linux.SECCOMP_RET_TRAP))
log.Infof("Installing seccomp filters for %d syscalls (action=%v)", rules.Size(), defaultAction)
log.Infof("Installing seccomp filters for %d syscalls (action=%v)", rules.Size(), options.DefaultAction)
instrs, _, err := BuildProgram([]RuleSet{
{
Rules: denyRules,
Action: defaultAction,
Action: options.DefaultAction,
},
{
Rules: rules,
Action: linux.SECCOMP_RET_ALLOW,
},
}, defaultAction, defaultAction)
}, options)
if log.IsLogging(log.Debug) {
programStr, errDecode := bpf.DecodeInstructions(instrs)
if errDecode != nil {
@@ -303,6 +298,28 @@ func (m matchedValue) LoadLow32Bits() {
m.program.Stmt(bpf.Ld|bpf.Abs|bpf.W, m.dataOffsetLow)
}
// ProgramOptions configure a seccomp program.
type ProgramOptions struct {
// DefaultAction is the action returned when none of the rules match.
DefaultAction linux.BPFAction
// BadArchAction is the action returned when the architecture of the
// syscall structure input doesn't match the one the program expects.
BadArchAction linux.BPFAction
}
// DefaultProgramOptions returns the default program options.
func DefaultProgramOptions() ProgramOptions {
action, err := defaultAction()
if err != nil {
panic(fmt.Sprintf("cannot determine default seccomp action: %v", err))
}
return ProgramOptions{
DefaultAction: action,
BadArchAction: action,
}
}
// BuildStats contains information about seccomp program generation.
type BuildStats struct {
// SizeBeforeOptimizations and SizeAfterOptimizations correspond to the
@@ -320,7 +337,7 @@ type BuildStats struct {
// BuildProgram builds a BPF program from the given map of actions to matching
// SyscallRules. The single generated program covers all provided RuleSets.
func BuildProgram(rules []RuleSet, defaultAction, badArchAction linux.BPFAction) ([]bpf.Instruction, BuildStats, error) {
func BuildProgram(rules []RuleSet, options ProgramOptions) ([]bpf.Instruction, BuildStats, error) {
start := time.Now()
program := &syscallProgram{
program: bpf.NewProgramBuilder(),
@@ -339,11 +356,11 @@ func BuildProgram(rules []RuleSet, defaultAction, badArchAction linux.BPFAction)
// Default label if none of the rules matched:
program.Label(defaultLabel)
program.Ret(defaultAction)
program.Ret(options.DefaultAction)
// Label if the architecture didn't match:
program.Label(badArchLabel)
program.Ret(badArchAction)
program.Ret(options.BadArchAction)
insns, err := program.program.Instructions()
if err != nil {
+94 -50
View File
@@ -70,12 +70,11 @@ func TestBasic(t *testing.T) {
}
for _, test := range []struct {
name string
ruleSets []RuleSet
wantPanic bool
defaultAction linux.BPFAction
badArchAction linux.BPFAction
specs []spec
name string
ruleSets []RuleSet
wantPanic bool
options ProgramOptions
specs []spec
}{
{
name: "Single syscall",
@@ -85,8 +84,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "syscall allowed",
@@ -119,8 +120,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_TRAP,
},
},
defaultAction: linux.SECCOMP_RET_KILL_THREAD,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_KILL_THREAD,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "allowed (1a)",
@@ -156,8 +159,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "allowed (1)",
@@ -211,8 +216,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "arch (123)",
@@ -231,8 +238,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "action trap",
@@ -254,8 +263,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "allowed",
@@ -286,8 +297,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "match first rule",
@@ -335,8 +348,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "hit first rule",
@@ -381,8 +396,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "argument allowed (all match)",
@@ -427,8 +444,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "arg allowed",
@@ -475,8 +494,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "high 32bits greater",
@@ -518,8 +539,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "arg allowed",
@@ -564,8 +587,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "high 32bits greater",
@@ -607,8 +632,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "arg allowed (both greater)",
@@ -658,8 +685,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "high 32bits greater",
@@ -701,8 +730,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "arg allowed",
@@ -752,8 +783,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "high 32bits greater",
@@ -796,8 +829,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "arg allowed",
@@ -846,8 +881,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "arg allowed (low order mandatory bit)",
@@ -913,8 +950,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "zero allowed",
@@ -975,8 +1014,10 @@ func TestBasic(t *testing.T) {
Action: linux.SECCOMP_RET_ALLOW,
},
},
defaultAction: linux.SECCOMP_RET_TRAP,
badArchAction: linux.SECCOMP_RET_KILL_THREAD,
options: ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
},
specs: []spec{
{
desc: "allowed",
@@ -1001,7 +1042,7 @@ func TestBasic(t *testing.T) {
t.Helper()
}()
var err error
instrs, _, err = BuildProgram(test.ruleSets, test.defaultAction, test.badArchAction)
instrs, _, err = BuildProgram(test.ruleSets, test.options)
if err != nil {
t.Fatalf("BuildProgram() got error: %v", err)
}
@@ -1052,7 +1093,10 @@ func TestRandom(t *testing.T) {
Rules: syscallRules,
Action: linux.SECCOMP_RET_ALLOW,
},
}, linux.SECCOMP_RET_TRAP, linux.SECCOMP_RET_KILL_THREAD)
}, ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
})
if err != nil {
t.Fatalf("buildProgram() got error: %v", err)
}
+1 -1
View File
@@ -107,7 +107,7 @@ func main() {
})
}
if err := seccomp.Install(syscalls, seccomp.NewSyscallRules()); err != nil {
if err := seccomp.Install(syscalls, seccomp.NewSyscallRules(), seccomp.DefaultProgramOptions()); err != nil {
fmt.Printf("Failed to install seccomp: %v\n", err)
os.Exit(1)
}
+4 -1
View File
@@ -791,7 +791,10 @@ func seccompMmapRules(m *machine) {
Action: linux.SECCOMP_RET_TRAP,
},
}
instrs, _, err := seccomp.BuildProgram(rules, linux.SECCOMP_RET_ALLOW, linux.SECCOMP_RET_ALLOW)
instrs, _, err := seccomp.BuildProgram(rules, seccomp.ProgramOptions{
DefaultAction: linux.SECCOMP_RET_ALLOW,
BadArchAction: linux.SECCOMP_RET_ALLOW,
})
if err != nil {
panic(fmt.Sprintf("failed to build rules: %v", err))
}
@@ -114,7 +114,10 @@ func attachedThread(flags uintptr, defaultAction linux.BPFAction) (*thread, erro
})
}
rules = appendArchSeccompRules(rules, defaultAction)
instrs, _, err := seccomp.BuildProgram(rules, defaultAction, defaultAction)
instrs, _, err := seccomp.BuildProgram(rules, seccomp.ProgramOptions{
DefaultAction: defaultAction,
BadArchAction: defaultAction,
})
if err != nil {
return nil, err
}
@@ -130,7 +130,10 @@ func attachedThread(flags uintptr, defaultAction linux.BPFAction) (*thread, erro
rules = append(rules, ruleSet)
rules = appendArchSeccompRules(rules)
}
instrs, _, err := seccomp.BuildProgram(rules, defaultAction, defaultAction)
instrs, _, err := seccomp.BuildProgram(rules, seccomp.ProgramOptions{
DefaultAction: defaultAction,
BadArchAction: defaultAction,
})
if err != nil {
return nil, err
}
+4 -1
View File
@@ -146,7 +146,10 @@ func sysmsgThreadRules(stubStart uintptr) []bpf.Instruction {
Action: linux.SECCOMP_RET_ALLOW,
},
}...)
instrs, _, err := seccomp.BuildProgram(rules, linux.SECCOMP_RET_TRAP, linux.SECCOMP_RET_TRAP)
instrs, _, err := seccomp.BuildProgram(rules, seccomp.ProgramOptions{
DefaultAction: linux.SECCOMP_RET_TRAP,
BadArchAction: linux.SECCOMP_RET_TRAP,
})
if err != nil {
panic(fmt.Sprintf("failed to build rules for sysmsg threads: %v", err))
}
+4 -1
View File
@@ -49,7 +49,10 @@ func main() {
Rules: rules,
Action: linux.SECCOMP_RET_ALLOW,
},
}, linux.SECCOMP_RET_ERRNO, linux.SECCOMP_RET_ERRNO)
}, seccomp.ProgramOptions{
DefaultAction: linux.SECCOMP_RET_ERRNO,
BadArchAction: linux.SECCOMP_RET_ERRNO,
})
if err != nil {
log.Warningf("%v", err)
os.Exit(1)
+1 -1
View File
@@ -79,7 +79,7 @@ func Rules(opt Options) (seccomp.SyscallRules, seccomp.SyscallRules) {
// Install seccomp filters based on the given platform.
func Install(opt Options) error {
rules, denyRules := Rules(opt)
return seccomp.Install(rules, denyRules)
return seccomp.Install(rules, denyRules, seccomp.DefaultProgramOptions())
}
// Report writes a warning message to the log.
+1 -1
View File
@@ -53,7 +53,7 @@ func Install(opt Options) error {
// when not enabled.
s.Merge(instrumentationFilters())
return seccomp.Install(s, seccomp.DenyNewExecMappings)
return seccomp.Install(s, seccomp.DenyNewExecMappings, seccomp.DefaultProgramOptions())
}
// report writes a warning message to the log.
+4 -1
View File
@@ -51,7 +51,10 @@ func BuildProgram(s *specs.LinuxSeccomp) (bpf.Program, error) {
return bpf.Program{}, fmt.Errorf("invalid seccomp rules: %w", err)
}
instrs, _, err := seccomp.BuildProgram(ruleset, defaultAction, killThreadAction)
instrs, _, err := seccomp.BuildProgram(ruleset, seccomp.ProgramOptions{
DefaultAction: defaultAction,
BadArchAction: killThreadAction,
})
if err != nil {
return bpf.Program{}, fmt.Errorf("building seccomp program: %w", err)
}
+4 -1
View File
@@ -49,7 +49,10 @@ func BenchFromSyscallRules(b *testing.B, name string, profile secbenchdef.Profil
Rules: rules,
Action: linux.SECCOMP_RET_ALLOW,
},
}, linux.SECCOMP_RET_ERRNO, linux.SECCOMP_RET_ERRNO)
}, seccomp.ProgramOptions{
DefaultAction: linux.SECCOMP_RET_ERRNO,
BadArchAction: linux.SECCOMP_RET_ERRNO,
})
if err != nil {
b.Fatalf("BuildProgram() failed: %v", err)
}