From: Tianci Cao Extend ALU variant validation to accept off == 1 and off == 2 as UHMUL and SHMUL, respectively, for BPF_ALU64 MUL instructions. Both offsets remain reserved for BPF_ALU MUL. All other MUL offsets remain invalid. Reject HMUL instructions when the program is offloaded to hardware or when JIT compilation is requested but the JIT does not support HMUL. Return -EOPNOTSUPP with a verifier log message in both cases. UHMUL and SHMUL remain available through the interpreter when JIT compilation is disabled. For scalar tracking, compute the exact high-half result when both operands are constants. Mark the destination unknown when either operand is variable until range analysis for these variants is added. Keep the existing scalar analysis for ordinary low-half MUL unchanged. Reject UHMUL and SHMUL when either operand is PTR_TO_ARENA. Arena arithmetic can be rewritten from BPF_ALU64 to BPF_ALU to enforce its 32-bit pointer representation, but such a rewrite cannot preserve the semantics of variants that are only defined for BPF_ALU64. Link: https://lore.kernel.org/bpf/CAADnVQ+2O2zgdy11+vxE6dOp+3Br4zZBysHF-Hne7cP+eeucnQ@mail.gmail.com/ Suggested-by: Alexei Starovoitov Signed-off-by: Tianci Cao Co-developed-by: Yazhou Tang Signed-off-by: Yazhou Tang Co-developed-by: Shenghao Yuan Signed-off-by: Shenghao Yuan --- kernel/bpf/verifier.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 4f9d7b3de442..ac7974038eaa 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -16664,6 +16664,24 @@ static void scalar_min_max_mul(struct bpf_reg_state *dst_reg, cnum64_from_srange(smin, smax)); } +static void scalar_min_max_uhmul(struct bpf_reg_state *dst_reg, + struct bpf_reg_state *src_reg) +{ + u64 dst = reg_const_value(dst_reg, false); + u64 src = reg_const_value(src_reg, false); + + ___mark_reg_known(dst_reg, bpf_uhmul64(dst, src)); +} + +static void scalar_min_max_shmul(struct bpf_reg_state *dst_reg, + struct bpf_reg_state *src_reg) +{ + s64 dst = reg_const_value(dst_reg, false); + s64 src = reg_const_value(src_reg, false); + + ___mark_reg_known(dst_reg, bpf_shmul64(dst, src)); +} + static void scalar32_min_max_udiv(struct bpf_reg_state *dst_reg, struct bpf_reg_state *src_reg) { @@ -17364,6 +17382,25 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, reg_step_reset(dst_reg); break; case BPF_MUL: + /* + * Range analysis for UHMUL/SHMUL is not implemented yet. + * Compute an exact result only when both operands are constants. + * Ordinary low-half MUL continues to use the analysis below. + */ + if (bpf_insn_is_hmul(insn) && + (!is_reg_const(dst_reg, false) || + !is_reg_const(&src_reg, false))) { + __mark_reg_unknown(env, dst_reg); + break; + } + if (!alu32 && insn->off == BPF_UHMUL) { + scalar_min_max_uhmul(dst_reg, &src_reg); + break; + } + if (!alu32 && insn->off == BPF_SHMUL) { + scalar_min_max_shmul(dst_reg, &src_reg); + break; + } scalar_step_mul(dst_reg, &src_reg); dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off); scalar32_min_max_mul(dst_reg, &src_reg); @@ -17499,6 +17536,11 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, /* Case where at least one operand is an arena. */ if (dst_reg->type == PTR_TO_ARENA || (src_reg && src_reg->type == PTR_TO_ARENA)) { + if (bpf_insn_is_hmul(insn)) { + verbose(env, "UHMUL/SHMUL with arena pointers are not supported\n"); + return -EOPNOTSUPP; + } + if (dst_reg->type != PTR_TO_ARENA) *dst_reg = *src_reg; @@ -21197,6 +21239,8 @@ static int fd_array_get_map_idx(struct bpf_verifier_env *env, u32 idx) static bool is_valid_alu_variant(const struct bpf_insn *insn) { switch (BPF_OP(insn->code)) { + case BPF_MUL: + return insn->off == 0 || bpf_insn_is_hmul(insn); case BPF_DIV: case BPF_MOD: /* off == 1 selects SDIV/SMOD. */ @@ -21206,6 +21250,17 @@ static bool is_valid_alu_variant(const struct bpf_insn *insn) } } +/* The interpreter supports uhmul and shmul. JITs opt in. */ +static int check_hmul_jit(struct bpf_verifier_env *env) +{ + if (bpf_prog_is_offloaded(env->prog->aux) || + (env->prog->jit_requested && !bpf_jit_supports_hmul())) { + verbose(env, "JIT doesn't support uhmul and shmul\n"); + return -EOPNOTSUPP; + } + return 0; +} + static int check_alu_fields(struct bpf_verifier_env *env, struct bpf_insn *insn) { u8 class = BPF_CLASS(insn->code); @@ -21267,6 +21322,8 @@ static int check_alu_fields(struct bpf_verifier_env *env, struct bpf_insn *insn) verbose(env, "BPF_ALU uses reserved fields\n"); return -EINVAL; } + if (bpf_insn_is_hmul(insn)) + return check_hmul_jit(env); return 0; default: verbose(env, "invalid BPF_ALU opcode %x\n", opcode); -- 2.56.0