From a63137f21b83d44e315998779fd91e1ae1c2e06e Mon Sep 17 00:00:00 2001 From: Arthur Fabre Date: Fri, 28 Aug 2026 13:23:20 +0200 Subject: [PATCH] C: avoid undefined behavior for bit shifts >= 32 The C standard says, for bit shifts: > If the value of the right operand [...] is greater than or equal to the width of the promoted left operand, the behavior is undefined. But in our generated C code, we allow shifts for the full value of RegX. Do the same thing the kernel does: mask it with & 31 to avoid any undefined behavior. --- c.go | 5 +++++ insn_test.go | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/c.go b/c.go index 1d82de2..1367db2 100644 --- a/c.go +++ b/c.go @@ -187,6 +187,11 @@ func insnToC(insn instruction, blk *block) ([]string, error) { case bpf.ALUOpConstant: return stat("a %s= %d;", aluToCOp[i.Op], i.Val) case bpf.ALUOpX: + // Shifts >=32 are undefined behavior in C. + // https://elixir.bootlin.com/linux/v7.2/source/kernel/bpf/core.c#L1899 + if i.Op == bpf.ALUOpShiftLeft || i.Op == bpf.ALUOpShiftRight { + return stat("a %s= (x & 31);", aluToCOp[i.Op]) + } return stat("a %s= x;", aluToCOp[i.Op]) case bpf.NegateA: return stat("a = -a;") diff --git a/insn_test.go b/insn_test.go index 5931c14..f7193c2 100644 --- a/insn_test.go +++ b/insn_test.go @@ -526,10 +526,15 @@ func checkAlu(t *testing.T, op bpf.ALUOp, a, b, res uint32) { } checkBackends(t, constFilter, []byte{}, match) + checkAluX(t, op, a, b, res) +} + +func checkAluX(t *testing.T, op bpf.ALUOp, a, x, res uint32) { + t.Helper() xFilter := []bpf.Instruction{ bpf.LoadConstant{Dst: bpf.RegA, Val: a}, - bpf.LoadConstant{Dst: bpf.RegX, Val: b}, + bpf.LoadConstant{Dst: bpf.RegX, Val: x}, bpf.ALUOpX{Op: op}, @@ -623,6 +628,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) + checkAluX(t, bpf.ALUOpShiftLeft, 1, 32, 1) } func TestALUShiftRight(t *testing.T) { @@ -631,6 +637,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) + checkAluX(t, bpf.ALUOpShiftRight, 0x80000000, 32, 0x80000000) } func TestALUMod(t *testing.T) {