Skip to content

Commit

Permalink
Separate out rule optimizers from main syscall rendering.
Browse files Browse the repository at this point in the history
This does an optimization pass over `RuleSet`s prior to rendering anything,
rather than optimizing at the last minute for each syscall.

PiperOrigin-RevId: 581671748
  • Loading branch information
EtiennePerot authored and gvisor-bot committed Nov 12, 2023
1 parent a69a483 commit c46ffac
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 11 deletions.
34 changes: 26 additions & 8 deletions pkg/seccomp/seccomp.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,15 +330,32 @@ type BuildStats struct {
// BPF bytecode optimizations).
BuildDuration time.Duration

// OptimizeDuration is the amount of time it took to run BPF bytecode
// RuleOptimizeDuration is the amount of time it took to run SyscallRule
// optimizations.
OptimizeDuration time.Duration
RuleOptimizeDuration time.Duration

// BPFOptimizeDuration is the amount of time it took to run BPF bytecode
// optimizations.
BPFOptimizeDuration time.Duration
}

// 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, options ProgramOptions) ([]bpf.Instruction, BuildStats, error) {
start := time.Now()

// Make a copy of the syscall rules and optimize them.
rulesCopy := make([]RuleSet, len(rules))
for i, rs := range rules {
rulesMap := MakeSyscallRules(make(map[uintptr]SyscallRule, len(rs.Rules.rules)))
for sysno, rule := range rs.Rules.rules {
rulesMap.rules[sysno] = optimizeSyscallRule(rule)
}
rs.Rules = rulesMap
rulesCopy[i] = rs
}
ruleOptimizeDuration := time.Since(start)

program := &syscallProgram{
program: bpf.NewProgramBuilder(),
}
Expand All @@ -350,7 +367,7 @@ func BuildProgram(rules []RuleSet, options ProgramOptions) ([]bpf.Instruction, B
badArchLabel := label("badarch")
program.Stmt(bpf.Ld|bpf.Abs|bpf.W, seccompDataOffsetArch)
program.IfNot(bpf.Jmp|bpf.Jeq|bpf.K, LINUX_AUDIT_ARCH, badArchLabel)
if err := buildIndex(rules, program); err != nil {
if err := buildIndex(rulesCopy, program); err != nil {
return nil, BuildStats{}, err
}

Expand All @@ -367,16 +384,17 @@ func BuildProgram(rules []RuleSet, options ProgramOptions) ([]bpf.Instruction, B
return nil, BuildStats{}, err
}
beforeOpt := len(insns)
buildDuration := time.Since(start)
buildDuration := time.Since(start) - ruleOptimizeDuration
insns = bpf.Optimize(insns)
optimizeDuration := time.Since(start) - buildDuration
bpfOptimizeDuration := time.Since(start) - ruleOptimizeDuration - buildDuration
afterOpt := len(insns)
log.Debugf("Seccomp program optimized from %d to %d instructions; took %v to build and %v to optimize", beforeOpt, afterOpt, buildDuration, optimizeDuration)
log.Debugf("Seccomp program optimized from %d to %d instructions; took %v to build and %v to optimize", beforeOpt, afterOpt, buildDuration, bpfOptimizeDuration)
return insns, BuildStats{
SizeBeforeOptimizations: beforeOpt,
SizeAfterOptimizations: afterOpt,
BuildDuration: buildDuration,
OptimizeDuration: optimizeDuration,
RuleOptimizeDuration: ruleOptimizeDuration,
BPFOptimizeDuration: bpfOptimizeDuration,
}, nil
}

Expand Down Expand Up @@ -532,7 +550,7 @@ func buildBSTProgram(n *node, rng knownRange, rules []RuleSet, program *syscallP
// check the next rule set. We need to ensure
// that at the very end, we insert a direct
// jump label for the unmatched case.
optimizeSyscallRule(rule).Render(program, ruleSetLabelSet)
rule.Render(program, ruleSetLabelSet)
ruleSetFrag.MustHaveJumpedTo(ruleSetLabelSet.Matched(), ruleSetLabelSet.Mismatched())
program.Label(ruleSetLabelSet.Matched())
program.Ret(rs.Action)
Expand Down
5 changes: 3 additions & 2 deletions runsc/boot/filter/dumpfilter/dumpfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ func main() {
log.Infof("Size before optimizations: %d", stats.SizeBeforeOptimizations)
log.Infof("Size after optimizations: %d", stats.SizeAfterOptimizations)
log.Infof("Build duration: %v", stats.BuildDuration)
log.Infof("Optimization passes duration: %v", stats.OptimizeDuration)
log.Infof("Total duration: %v", stats.BuildDuration+stats.OptimizeDuration)
log.Infof("Rule optimization passes duration: %v", stats.RuleOptimizeDuration)
log.Infof("BPF optimization passes duration: %v", stats.BPFOptimizeDuration)
log.Infof("Total duration: %v", stats.BuildDuration+stats.RuleOptimizeDuration+stats.BPFOptimizeDuration)
switch *output {
case "fancy":
dump, err := bpf.DecodeInstructions(insns)
Expand Down
4 changes: 3 additions & 1 deletion test/secbench/secbench.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ func RunBench(b *testing.B, bn secbenchdef.Bench) {
b.Fatalf("program does not compile: %v", err)
}
b.ReportMetric(float64(bn.BuildStats.BuildDuration.Nanoseconds()), "build-ns")
b.ReportMetric(float64(bn.BuildStats.OptimizeDuration.Nanoseconds()), "opt-ns")
b.ReportMetric(float64(bn.BuildStats.RuleOptimizeDuration.Nanoseconds()), "ruleopt-ns")
b.ReportMetric(float64(bn.BuildStats.BPFOptimizeDuration.Nanoseconds()), "bpfopt-ns")
b.ReportMetric(float64((bn.BuildStats.RuleOptimizeDuration + bn.BuildStats.BPFOptimizeDuration).Nanoseconds()), "opt-ns")
b.ReportMetric(float64(bn.BuildStats.SizeBeforeOptimizations), "gen-instr")
b.ReportMetric(float64(bn.BuildStats.SizeAfterOptimizations), "opt-instr")
b.ReportMetric(float64(bn.BuildStats.SizeBeforeOptimizations)/float64(bn.BuildStats.SizeAfterOptimizations), "compression-ratio")
Expand Down

0 comments on commit c46ffac

Please sign in to comment.