1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
package bpf
import (
"sort"
"testing"
)
// TestTreeRouting verifies the binary-search tree + leaf groups route every
// allowlisted syscall number to its own leaf. It compiles in distinctLeaves
// mode, where each matched syscall returns its own number, so any misrouting
// (a number reaching the wrong leaf) is detected as a wrong return value.
func TestTreeRouting(t *testing.T) {
for _, system := range testSystems {
prog, err := compile(system, compileOpts{distinctLeaves: true})
if err != nil {
t.Fatalf("compile(%q): %v", system, err)
}
for _, ap := range targetArches[system] {
// Collect the deduped, sorted nrs the compiler actually emits.
nrs := routedNrs(ap)
for _, nr := range nrs {
got, err := Run(prog, SeccompData{Arch: ap.auditArch, Nr: nr})
if err != nil {
t.Fatalf("%s/%s nr=%d: Run: %v", system, ap.name, nr, err)
}
if got != nr {
t.Errorf("%s/%s nr=%d misrouted: leaf returned %d", system, ap.name, nr, got)
}
}
// A number not in the table must reach the default deny (ENOSYS),
// never a syscall leaf.
if got, _ := Run(prog, SeccompData{Arch: ap.auditArch, Nr: 0x7ffffffe}); got != Errno(errENOSYS) {
t.Errorf("%s/%s unknown nr: got 0x%08x, want ENOSYS", system, ap.name, got)
}
}
}
}
// TestNoTrampolinesNeeded asserts the inlined codegen keeps every conditional
// jump within the 8-bit limit for all supported systems, so Compile succeeds
// without any long-jump trampolines (the static invariant that replaced
// auto-trampolining).
func TestNoTrampolinesNeeded(t *testing.T) {
for _, system := range append([]string{}, testSystems...) {
if _, err := Compile(system); err != nil {
t.Errorf("Compile(%q) failed (a conditional jump likely exceeded 255): %v", system, err)
}
}
// Also the single-compat-arch systems.
for _, system := range []string{"i686-linux", "armv7l-linux"} {
if _, err := Compile(system); err != nil {
t.Errorf("Compile(%q): %v", system, err)
}
}
}
// routedNrs returns the sorted, de-duplicated syscall numbers for a profile,
// matching the set the compiler emits leaves for.
func routedNrs(ap archProfile) []uint32 {
var rules []resolvedRule
for _, tname := range ap.tableNames() {
table := syscallTables[tname]
for _, r := range lixRules {
if nr, ok := table[r.name]; ok {
rules = append(rules, resolvedRule{nr: nr, rule: r})
}
}
}
sort.Slice(rules, func(a, b int) bool { return rules[a].nr < rules[b].nr })
rules = dedupByNr(rules)
out := make([]uint32, len(rules))
for i, rr := range rules {
out[i] = rr.nr
}
return out
}
|