Re: [PATCH bpf-next v3 4/5] bpf: simplify the bpf_is_reg64()
[email protected] Sun, 02 Aug 2026 21:26:59 +0000
| Newsgroups | org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Critical] bpf_is_reg64() fails to identify BPF_ADDR_SPACE_CAST operations with imm == 1U << 16 as 32-bit when the arena has BPF_F_NO_USER_CONV set, leading to missing zero-extension and possible sandbox escape. - [Critical] Unconditional overwrite of `zext_dst` destroys manual overrides for `PTR_TO_ARENA` arithmetic, leading to missing zero-extensions and arbitrary kernel memory access. -- --- Patch [4]: [PATCH bpf-next v3 4/5] bpf: simplify the bpf_is_reg64() --- commit 4ece654ab9bc1fb0f5d2f2cc247cae7c94f2cb89 Author: Eduard Zingerman <[email protected]> bpf: simplify the bpf_is_reg64() This commit simplifies bpf_is_reg64() by removing unused parameters, removing BPF_JMP instructions handling, and streamlining conditions. It also moves the function to kernel/bpf/fixups.c. > diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c > index 6125598d16a84..7b3510d46b377 100644 > --- a/kernel/bpf/fixups.c > +++ b/kernel/bpf/fixups.c [ ... ] > @@ -44,6 +44,49 @@ static int insn_def_regno(const struct bpf_insn *insn) > } > } > > +/* > + * For use only in combination with insn_def_regno() >= 0. > + * Returns TRUE if the destination register operates on 64-bit, > + * otherwise return FALSE. > + */ > +static bool bpf_is_reg64(struct bpf_insn *insn) > +{ > + u8 class = BPF_CLASS(insn->code); > + u8 mode = BPF_MODE(insn->code); > + u8 size = BPF_SIZE(insn->code); > + u8 op = BPF_OP(insn->code); > + bool mode_mem; > + > + /* subregister endiness swap */ > + if ((class == BPF_ALU || class == BPF_ALU64) && op == BPF_END && insn->imm != 64) > + return false; > + > + /* w0 += 1 */ > + if (class == BPF_ALU && op != BPF_END) > + return false; > + > + /* cast from as(1) to as(0), PTR_TO_ARENA is 32-bit */ > + if (insn->code == (BPF_ALU64 | BPF_MOV | BPF_X) && > + insn->off == BPF_ADDR_SPACE_CAST && insn->imm == 1) > + return false; [Severity: Critical] Does this logic miss identifying BPF_ADDR_SPACE_CAST operations with imm == 1U << 16 as 32-bit? If the arena has BPF_F_NO_USER_CONV set, bpf_do_misc_fixups() rewrites all BPF_ADDR_SPACE_CAST instructions (including imm == 1U << 16) into a 32-bit MOV to explicitly clear the upper 32 bits. However, since this falls through and evaluates to 64-bit, the static liveness analysis evaluates zext_dst to false. When bpf_opt_subreg_zext_lo32_rnd_hi32() runs, it skips inserting a BPF_ZEXT_REG zero-extension. Could this leave the upper 32 bits un-zeroed on architectures requiring explicit zero-extension for 32-bit operations (e.g., s390x) or that sign-extend them (e.g., riscv64), leading to out-of-bounds memory accesses? > + > + /* non 64-bit, non signed extended loads */ > + mode_mem = mode == BPF_MEM || mode == BPF_PROBE_MEM || mode == BPF_PROBE_MEM32; > + if (class == BPF_LDX && mode_mem && size != BPF_DW) > + return false; > + > + /* atomics, see insn_def_regno() */ > + if (class == BPF_STX && size != BPF_DW) > + return false; > + > + /* both LD_IND and LD_ABS return 32-bit data. */ > + if (class == BPF_LD && (mode == BPF_IND || mode == BPF_ABS)) > + return false; > + > + /* Conservatively return true at default. */ > + return true; > +} > + > +/* > + * Return the 32-bit subregister defined by INSN, or -1 if INSN does not > + * explicitly define a 32-bit value. > + */ > +int bpf_insn_def32(struct bpf_insn *insn) > +{ > + int dst_reg = insn_def_regno(insn); > + > + if (dst_reg < 0 || bpf_is_reg64(insn)) > + return -1; [Severity: Critical] Would this unconditional behavior inadvertently overwrite manual overrides for PTR_TO_ARENA arithmetic? During verifier simulation, adjust_reg_min_max_vals() manually forces aux->zext_dst = true on arena arithmetic to ensure zero-extension is applied by the JIT. However, the static liveness pass in bpf_compute_live_registers() runs afterwards and does an unconditional assignment based on def32. Because bpf_is_reg64() evaluates 64-bit ALU operations as 64-bit, def32 here becomes -1. Could this cause the liveness pass to blindly clear the manually set zext_dst flag to false? If bpf_do_misc_fixups() then rewrites the instruction to a 32-bit operation, it might miss the necessary zero extensions, potentially allowing an escape from the arena boundaries. > + > + return dst_reg; > +} -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=4