Skip to content

Commit

Permalink
syscalls: add haveBPFToBPFCalls feature probe
Browse files Browse the repository at this point in the history
When trying to load a program containing bpf2bpf calls on a kernel that
doesn't support it, the we bubble up the verifier error to the caller:

  program xdp_prog: load program: invalid argument: unreachable insn 28

This is clear, but doesn't allow skipping tests when loading e.g. loader.c
on older kernels due to it containing bpf2bpf calls.

Implement a feature probe for bpf2bpf, return an unsupported error when
trying to load a program with multiple subprogs on an older kernel.

Update the test suite to skip unsupported features instead of hardcoding
a skip on 4.16 and earlier.

Signed-off-by: Timo Beckers <timo@isovalent.com>
  • Loading branch information
ti-mo committed May 4, 2022
1 parent e8ed3bd commit 63c0b0a
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 17 deletions.
3 changes: 1 addition & 2 deletions internal/btf/core_reloc_test.go
Expand Up @@ -90,8 +90,6 @@ func TestCORERelocationRead(t *testing.T) {
}
defer tgt.Close()

testutils.SkipOnOldKernel(t, "4.16", "bpf2bpf calls")

for _, progSpec := range spec.Programs {
t.Run(progSpec.Name, func(t *testing.T) {
if _, err := tgt.Seek(0, io.SeekStart); err != nil {
Expand All @@ -101,6 +99,7 @@ func TestCORERelocationRead(t *testing.T) {
prog, err := ebpf.NewProgramWithOptions(progSpec, ebpf.ProgramOptions{
TargetBTF: tgt,
})
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal("Load program:", err)
}
Expand Down
4 changes: 4 additions & 0 deletions linker.go
Expand Up @@ -169,6 +169,10 @@ func fixupAndValidate(insns asm.Instructions) error {
return fmt.Errorf("instruction %d: map %s: %w", iter.Index, ins.Reference(), asm.ErrUnsatisfiedMapReference)
}

if ins.IsFunctionReference() && haveBPFToBPFCalls() != nil {
return fmt.Errorf("kernel does not support bpf2bpf function calls: %w", haveBPFToBPFCalls())
}

fixupProbeReadKernel(ins)
}

Expand Down
6 changes: 2 additions & 4 deletions linker_test.go
Expand Up @@ -42,9 +42,8 @@ func TestFindReferences(t *testing.T) {
t.Fatal(err)
}

testutils.SkipOnOldKernel(t, "4.16", "bpf2bpf calls")

prog, err := NewProgram(progs["entrypoint"])
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
Expand All @@ -66,8 +65,6 @@ func TestForwardFunctionDeclaration(t *testing.T) {
t.Fatal(err)
}

testutils.SkipOnOldKernel(t, "4.16", "bpf2bpf calls")

if coll.ByteOrder != internal.NativeEndian {
return
}
Expand All @@ -76,6 +73,7 @@ func TestForwardFunctionDeclaration(t *testing.T) {

// This program calls an unimplemented forward function declaration.
_, err = NewProgram(spec)
testutils.SkipIfNotSupported(t, err)
if !errors.Is(err, asm.ErrUnsatisfiedProgramReference) {
t.Fatal("Expected an error wrapping ErrUnsatisfiedProgramReference, got:", err)
}
Expand Down
1 change: 1 addition & 0 deletions prog_test.go
Expand Up @@ -374,6 +374,7 @@ func TestProgramWithUnsatisfiedMap(t *testing.T) {
progSpec.ByteOrder = nil

_, err = NewProgram(progSpec)
testutils.SkipIfNotSupported(t, err)
if !errors.Is(err, asm.ErrUnsatisfiedMapReference) {
t.Fatal("Expected an error wrapping asm.ErrUnsatisfiedMapReference, got", err)
}
Expand Down
46 changes: 35 additions & 11 deletions syscalls.go
Expand Up @@ -38,6 +38,21 @@ func invalidBPFObjNameChar(char rune) bool {
}
}

func progLoad(insns asm.Instructions, license string) (*sys.FD, error) {
buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
return nil, err
}
bytecode := buf.Bytes()

return sys.ProgLoad(&sys.ProgLoadAttr{
ProgType: sys.ProgType(Kprobe),
License: sys.NewStringPointer("GPL"),
Insns: sys.NewSlicePointer(bytecode),
InsnCnt: uint32(len(bytecode) / asm.InstructionSize),
})
}

var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() error {
_, err := sys.MapCreate(&sys.MapCreateAttr{
MapType: sys.MapType(ArrayOfMaps),
Expand Down Expand Up @@ -226,21 +241,30 @@ var haveProbeReadKernel = internal.FeatureTest("bpf_probe_read_kernel", "5.5", f
asm.FnProbeReadKernel.Call(),
asm.Return(),
}
buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
return err
}
bytecode := buf.Bytes()

fd, err := sys.ProgLoad(&sys.ProgLoadAttr{
ProgType: sys.ProgType(Kprobe),
License: sys.NewStringPointer("GPL"),
Insns: sys.NewSlicePointer(bytecode),
InsnCnt: uint32(len(bytecode) / asm.InstructionSize),
})
fd, err := progLoad(insns, "GPL")
if err != nil {
return internal.ErrNotSupported
}
_ = fd.Close()
return nil
})

var haveBPFToBPFCalls = internal.FeatureTest("bpf2bpf calls", "4.16", func() error {
insns := asm.Instructions{
asm.Call.Label("prog2").WithSymbol("prog1"),
asm.Return(),
asm.Mov.Imm(asm.R0, 0).WithSymbol("prog2"),
asm.Return(),
}

fd, err := progLoad(insns, "MIT")
if errors.Is(err, unix.EINVAL) {
return internal.ErrNotSupported
}
if err != nil {
return err
}
_ = fd.Close()
return nil
})
4 changes: 4 additions & 0 deletions syscalls_test.go
Expand Up @@ -54,3 +54,7 @@ func TestHaveInnerMaps(t *testing.T) {
func TestHaveProbeReadKernel(t *testing.T) {
testutils.CheckFeatureTest(t, haveProbeReadKernel)
}

func TestHaveBPFToBPFCalls(t *testing.T) {
testutils.CheckFeatureTest(t, haveBPFToBPFCalls)
}

0 comments on commit 63c0b0a

Please sign in to comment.