From 4a2e8de6c850499c87c8ca5914bf762502bbb1e7 Mon Sep 17 00:00:00 2001 From: Arthur Fabre Date: Thu, 27 Aug 2026 14:45:56 +0200 Subject: [PATCH] Reject cBPF shifts > 31 cBPF only allows right or left shifts up to 31 bits (as the registers are 32 bits). The kernel rejects programs with bigger shifts. Reject them too: otherwise we emit eBPF code that will also fail to load. --- cbpfc.go | 10 ++++++++++ cbpfc_test.go | 30 ++++++++++++++++++++++++++++++ insn_test.go | 2 ++ 3 files changed, 42 insertions(+) diff --git a/cbpfc.go b/cbpfc.go index f00d3fb..4c67c01 100644 --- a/cbpfc.go +++ b/cbpfc.go @@ -389,6 +389,16 @@ func validateInstructions(insns []bpf.Instruction) error { default: return errors.Errorf("unsupported BPF extension %d: %v", pc, insn) } + + // The kernel rejects shifts by a constant >= 32 : + // https://elixir.bootlin.com/linux/v6.10/source/net/core/filter.c#L516 + case bpf.ALUOpConstant: + switch i.Op { + case bpf.ALUOpShiftLeft, bpf.ALUOpShiftRight: + if i.Val >= 32 { + return errors.Errorf("instruction %d shifts by %d, must be less than 32: %v", pc, i.Val, insn) + } + } } } diff --git a/cbpfc_test.go b/cbpfc_test.go index cdff1b7..b0cf8d4 100644 --- a/cbpfc_test.go +++ b/cbpfc_test.go @@ -545,6 +545,36 @@ func TestDivisionByZeroImm(t *testing.T) { test(t, bpf.ALUOpMod) } +// Shifts greater than 31 +func TestShiftConstantTooLarge(t *testing.T) { + test := func(t *testing.T, op bpf.ALUOp) { + t.Helper() + + // Valid: shifts less than 32 are accepted. + for _, val := range []uint32{0, 1, 31} { + _, err := compile([]bpf.Instruction{ + bpf.ALUOpConstant{Op: op, Val: val}, + bpf.RetA{}, + }, compileOpts{}) + if err != nil { + t.Fatalf("shift by %d rejected: %v", val, err) + } + } + + // Invalid: shifts of 32 or more are rejected. + for _, val := range []uint32{32, 33, 64, 0xFFFFFFFF} { + _, err := compile([]bpf.Instruction{ + bpf.ALUOpConstant{Op: op, Val: val}, + bpf.RetA{}, + }, compileOpts{}) + requireError(t, err, "must be less than 32") + } + } + + test(t, bpf.ALUOpShiftLeft) + test(t, bpf.ALUOpShiftRight) +} + // Division by RegX func TestDivisionByZeroX(t *testing.T) { test := func(t *testing.T, op bpf.ALUOp) { diff --git a/insn_test.go b/insn_test.go index 651717a..8586457 100644 --- a/insn_test.go +++ b/insn_test.go @@ -602,6 +602,7 @@ func TestALUShiftLeft(t *testing.T) { checkAlu(t, bpf.ALUOpShiftLeft, 1, 0, 1) checkAlu(t, bpf.ALUOpShiftLeft, 1, 4, 0x10) + checkAlu(t, bpf.ALUOpShiftLeft, 1, 31, 0x80000000) } func TestALUShiftRight(t *testing.T) { @@ -609,6 +610,7 @@ func TestALUShiftRight(t *testing.T) { checkAlu(t, bpf.ALUOpShiftRight, 0xF0, 4, 0x0F) checkAlu(t, bpf.ALUOpShiftRight, 0xF0, 8, 0) + checkAlu(t, bpf.ALUOpShiftRight, 0x80000000, 31, 1) } func TestALUMod(t *testing.T) {