forked from cilium/ebpf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprog_linux_test.go
137 lines (110 loc) · 3.16 KB
/
prog_linux_test.go
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package ebpf
import (
"fmt"
"math"
"runtime"
"slices"
"testing"
"time"
"github.com/go-quicktest/qt"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/testutils"
"github.com/cilium/ebpf/internal/unix"
)
func TestProgramTestRunInterrupt(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.0", "EINTR from BPF_PROG_TEST_RUN")
prog := createBasicProgram(t)
var (
tgid = unix.Getpid()
tidChan = make(chan int, 1)
exit = make(chan struct{})
errs = make(chan error, 1)
timeout = time.After(5 * time.Second)
)
defer close(exit)
go func() {
runtime.LockOSThread()
defer func() {
// Wait for the test to allow us to unlock the OS thread, to
// ensure that we don't send SIGUSR1 to the wrong thread.
<-exit
runtime.UnlockOSThread()
}()
tidChan <- unix.Gettid()
// Block this thread in the BPF syscall, so that we can
// trigger EINTR by sending a signal.
opts := RunOptions{
Data: internal.EmptyBPFContext,
Repeat: math.MaxInt32,
Reset: func() {
// We don't know how long finishing the
// test run would take, so flag that we've seen
// an interruption and abort the goroutine.
close(errs)
runtime.Goexit()
},
}
_, _, err := prog.run(&opts)
errs <- err
}()
tid := <-tidChan
for {
err := unix.Tgkill(tgid, tid, unix.SIGUSR1)
if err != nil {
t.Fatal("Can't send signal to goroutine thread:", err)
}
select {
case err, ok := <-errs:
if !ok {
return
}
testutils.SkipIfNotSupported(t, err)
if err == nil {
t.Fatal("testRun wasn't interrupted")
}
t.Fatal("testRun returned an error:", err)
case <-timeout:
t.Fatal("Timed out trying to interrupt the goroutine")
default:
}
}
}
func TestProgramVerifierLogLinux(t *testing.T) {
check := func(t *testing.T, err error) {
t.Helper()
var ve *internal.VerifierError
qt.Assert(t, qt.ErrorAs(err, &ve))
loglen := len(fmt.Sprintf("%+v", ve))
qt.Assert(t, qt.IsTrue(loglen > minVerifierLogSize),
qt.Commentf("Log buffer didn't grow past minimum, got %d bytes", loglen))
}
// Generate a base program of sufficient size whose verifier log does not fit
// in the minimum buffer size. Stay under 4096 insn limit of older kernels.
var base asm.Instructions
for i := 0; i < 4093; i++ {
base = append(base, asm.Mov.Reg(asm.R0, asm.R1))
}
// Touch R10 (read-only frame pointer) to reliably force a verifier error.
invalid := slices.Clone(base)
invalid = append(invalid, asm.Mov.Reg(asm.R10, asm.R0))
invalid = append(invalid, asm.Return())
valid := slices.Clone(base)
valid = append(valid, asm.Return())
// Start out with testing against the invalid program.
spec := &ProgramSpec{
Type: SocketFilter,
License: "MIT",
Instructions: invalid,
}
_, err := newProgram(t, spec, nil)
check(t, err)
// Run tests against a valid program from here on out.
spec.Instructions = valid
// Explicitly request verifier log for a valid program and a start size.
prog := mustNewProgram(t, spec, &ProgramOptions{
LogLevel: LogLevelInstruction,
LogSizeStart: minVerifierLogSize * 2,
})
qt.Assert(t, qt.IsTrue(len(prog.VerifierLog) > minVerifierLogSize))
}