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
}