> diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
> index 0981791014eb..943a0f315cf2 100644
> --- a/arch/x86/net/bpf_jit_comp.c
> +++ b/arch/x86/net/bpf_jit_comp.c
> @@ -2300,41 +2300,50 @@ static int do_jit(struct bpf_verifier_env *env,
> struct bpf_prog *bpf_prog, int *
> EMIT_LFENCE();
> break;
>
> - /* ST: *(u8*)(dst_reg + off) = imm */
> case BPF_ST | BPF_MEM | BPF_B:
[ ... ]
> case BPF_ST | BPF_MEM | BPF_DW:
> - if (dst_reg == BPF_REG_PARAMS && insn->off == -8) {
> - /* Arg 6: store immediate in r9 register */
> - emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31,
> (u32)imm32);
> + switch (BPF_SIZE(insn->code)) {
[ ... ]
> + case BPF_DW:
> + if (dst_reg == BPF_REG_PARAMS &&
> + insn->off == -8) {
> + /* Arg 6: store immediate in r9
> register */
> + emit_mov_imm64(&prog, X86_REG_R9,
> + imm32 >> 31, (u32)imm32);
> + break;
> + }
> + EMIT2(add_1mod(0x48, dst_reg), 0xC7);
> break;
> }
> - EMIT2(add_1mod(0x48, dst_reg), 0xC7);
>
> -st: insn_off = insn->off;
> + insn_off = insn->off;
Has the scope of the Arg 6 break changed here?
In the original code the BPF_DW handling lived directly under the case
label, and its break left the whole outer instruction switch:
case BPF_ST | BPF_MEM | BPF_DW:
if (dst_reg == BPF_REG_PARAMS && insn->off == -8) {
/* Arg 6: store immediate in r9 register */
emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31,
(u32)imm32);
break;
}
EMIT2(add_1mod(0x48, dst_reg), 0xC7);
st: insn_off = insn->off;
...
So for the Arg 6 case it emitted exactly the movabs into r9 and stopped,
never reaching the st: addressing and immediate emission.
After the refactor, the Arg 6 branch lives inside the inner switch on
BPF_SIZE(), so its break only exits that inner switch:
case BPF_DW:
if (dst_reg == BPF_REG_PARAMS &&
insn->off == -8) {
emit_mov_imm64(&prog, X86_REG_R9,
imm32 >> 31, (u32)imm32);
break;
}
EMIT2(add_1mod(0x48, dst_reg), 0xC7);
break;
}
insn_off = insn->off;
if (dst_reg == BPF_REG_PARAMS) {
insn_off = outgoing_arg_base - outgoing_rsp -
insn_off - 16;
dst_reg = BPF_REG_FP;
}
if (is_imm8(insn_off))
EMIT2(add_1reg(0x40, dst_reg), insn_off);
else
EMIT1_off32(add_1reg(0x80, dst_reg), insn_off);
EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code)));
After emitting the movabs into r9, does control now fall through into
the common insn_off block and emit an additional ModRM/displacement byte
sequence plus a 4-byte immediate that do not belong to any instruction?
Would those extra bytes corrupt the emitted machine code stream for the
Arg 6 path, since dst_reg == BPF_REG_PARAMS with insn->off == -8 is a
reachable case used to pass the 6th outgoing call argument in r9?
Could the original break be preserved here, for example by breaking out
of the outer switch (or skipping the insn_off block) once the Arg 6
movabs has been emitted?
The commit message describes this as grouping the BPF_ST instructions
"in a single block of fall-through cases", implying no behavioral change,
but the Arg 6 path appears to gain extra emitted bytes.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/26978380520