Skip to content

Commit f88fcad

Browse files
sys: automatically use BPF tokens available to the process
This commit adds support for BPF tokens. When first attempting to create a map, program or BTF blob, automatically discover all bpffs mounts available to the process and attempt to create tokens until successful. This has the benefit of not hardcoding any paths or passing them via environment variables. Since token info was only added as of 6.17, allow obj_info to fail and always treat EPERM as ErrTokenCapabilities if tokens are in use and info is missing. This avoids having to add corner cases and token plumbing to all existing feature probes and tests. Signed-off-by: Dylan Reimerink <dylan.reimerink@isovalent.com> Co-authored-by: Timo Beckers <timo@isovalent.com>
1 parent 1daaae7 commit f88fcad

12 files changed

Lines changed: 506 additions & 0 deletions

File tree

btf/handle.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ func NewHandleFromRawBTF(btf []byte) (*Handle, error) {
9999
attr.BtfLogLevel = 1
100100
}
101101

102+
if errors.Is(err, sys.ErrTokenCapabilities) {
103+
return nil, fmt.Errorf("load btf: %w", err)
104+
}
105+
102106
if err := haveBTF(); err != nil {
103107
return nil, err
104108
}

btf/handle_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import (
44
"fmt"
55
"testing"
66

7+
"github.com/go-quicktest/qt"
8+
79
"github.com/cilium/ebpf/btf"
10+
"github.com/cilium/ebpf/internal/sys"
811
"github.com/cilium/ebpf/internal/testutils"
12+
"github.com/cilium/ebpf/internal/unix"
913
)
1014

1115
func TestHandleIterator(t *testing.T) {
@@ -83,6 +87,35 @@ func TestParseModuleSplitSpec(t *testing.T) {
8387
}
8488
}
8589

90+
func TestNewHandleFromBTFWithToken(t *testing.T) {
91+
b, err := btf.NewBuilder([]btf.Type{&btf.Int{"example", 4, btf.Unsigned}}, nil)
92+
qt.Assert(t, qt.IsNil(err))
93+
94+
buf, err := b.Marshal(nil, nil)
95+
qt.Assert(t, qt.IsNil(err))
96+
97+
testutils.RunWithToken(t, "no-cmd", testutils.Delegated{
98+
// Random map type we don't use in this test since we need to delegate at
99+
// least one permission.
100+
Cmds: []sys.Cmd{},
101+
Maps: []sys.MapType{sys.BPF_MAP_TYPE_ARRAY},
102+
}, func(t *testing.T) {
103+
h, err := btf.NewHandleFromRawBTF(buf)
104+
testutils.SkipIfNotSupported(t, err)
105+
qt.Assert(t, qt.ErrorIs(err, unix.EPERM))
106+
h.Close()
107+
})
108+
109+
testutils.RunWithToken(t, "success", testutils.Delegated{
110+
Cmds: []sys.Cmd{sys.BPF_BTF_LOAD},
111+
}, func(t *testing.T) {
112+
h, err := btf.NewHandleFromRawBTF(buf)
113+
testutils.SkipIfNotSupported(t, err)
114+
qt.Assert(t, qt.IsNil(err))
115+
h.Close()
116+
})
117+
}
118+
86119
func ExampleHandleIterator() {
87120
it := new(btf.HandleIterator)
88121
defer it.Handle.Close()

internal/sys/syscall_other.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package sys
44

55
import (
6+
"errors"
67
"fmt"
78
"os"
89
"path/filepath"
@@ -24,6 +25,11 @@ func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
2425
defer unmaskProfilerSignal()
2526
}
2627

28+
tok, err := tokenAttr(cmd, attr)
29+
if err != nil {
30+
return 0, fmt.Errorf("apply token attributes: %w", err)
31+
}
32+
2733
for {
2834
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
2935
runtime.KeepAlive(attr)
@@ -39,6 +45,24 @@ func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
3945
err = wrappedErrno{errNo}
4046
}
4147

48+
// Tokens are in use, attempt to enrich EPERM with capability information.
49+
if tok != nil && errors.Is(err, unix.EPERM) {
50+
// Kernels before 6.17 don't have token info, so we can't return accurate
51+
// errors. Make the token error a hint by adding a question mark.
52+
//
53+
// Returning ErrTokenCapabilities here isn't ideal since it's not
54+
// conclusive, but since this behaviour is limited to one LTS release
55+
// (6.12), and only when tokens are enabled, it's better than having to
56+
// account for it in all existing feature probes and tests.
57+
if tok.info == nil {
58+
return r1, fmt.Errorf("%w (%w?)", err, ErrTokenCapabilities)
59+
}
60+
61+
if terr := tok.Capable(cmd, attr); terr != nil {
62+
return r1, fmt.Errorf("%w: %w", terr, err)
63+
}
64+
}
65+
4266
return r1, err
4367
}
4468
}

0 commit comments

Comments
 (0)