INSN_F_DST_REG_STACK = BIT(1), /* dst_reg is PTR_TO_STACK */
INSN_F_SRC_REG_STACK = BIT(2), /* src_reg is PTR_TO_STACK */
+
+ INSN_F_STACK_ARG_ACCESS = BIT(3),
};
struct bpf_jmp_history_entry {
u32 frame;
u32 reg_masks[MAX_CALL_FRAMES];
u64 stack_masks[MAX_CALL_FRAMES];
+ u8 stack_arg_masks[MAX_CALL_FRAMES];
};
struct bpf_id_pair {
bt->stack_masks[frame] |= 1ull << slot;
}
+static inline void bt_set_frame_stack_arg_slot(struct backtrack_state *bt, u32 frame, u32 slot)
+{
+ bt->stack_arg_masks[frame] |= 1 << slot;
+}
+
static inline bool bt_is_frame_reg_set(struct backtrack_state *bt, u32 frame, u32 reg)
{
return bt->reg_masks[frame] & (1 << reg);
int i;
for (i = 0; i <= bt->frame; i++)
- mask |= bt->reg_masks[i] | bt->stack_masks[i];
+ mask |= bt->reg_masks[i] | bt->stack_masks[i] | bt->stack_arg_masks[i];
return mask == 0;
}
+static inline void bt_clear_frame_stack_arg_slot(struct backtrack_state *bt, u32 frame, u32 slot)
+{
+ bt->stack_arg_masks[frame] &= ~(1 << slot);
+}
+
+static inline bool bt_is_frame_stack_arg_slot_set(struct backtrack_state *bt, u32 frame, u32 slot)
+{
+ return bt->stack_arg_masks[frame] & (1 << slot);
+}
+
static inline int bt_subprog_enter(struct backtrack_state *bt)
{
if (bt->frame == MAX_CALL_FRAMES - 1) {
return bt->stack_masks[bt->frame];
}
+static inline u8 bt_stack_arg_mask(struct backtrack_state *bt)
+{
+ return bt->stack_arg_masks[bt->frame];
+}
+
static inline bool bt_is_reg_set(struct backtrack_state *bt, u32 reg)
{
return bt->reg_masks[bt->frame] & (1 << reg);
return 0;
bt_clear_reg(bt, load_reg);
+ if (hist && hist->flags & INSN_F_STACK_ARG_ACCESS) {
+ spi = hist->spi;
+ /*
+ * Stack arg read: callee reads from r11+off, but
+ * the data lives in the caller's stack_arg_regs.
+ * Set the mask in the caller frame so precision
+ * is marked in the caller's slot at the callee
+ * entry checkpoint.
+ */
+ bt_set_frame_stack_arg_slot(bt, bt->frame - 1, spi);
+ return 0;
+ }
+
/* scalars can only be spilled into stack w/o losing precision.
* Load from any other memory can be zero extended.
* The desire to keep that precision is already indicated
* encountered a case of pointer subtraction.
*/
return -ENOTSUPP;
+
+ if (hist && hist->flags & INSN_F_STACK_ARG_ACCESS) {
+ spi = hist->spi;
+ if (!bt_is_frame_stack_arg_slot_set(bt, bt->frame, spi))
+ return 0;
+ bt_clear_frame_stack_arg_slot(bt, bt->frame, spi);
+ if (class == BPF_STX)
+ bt_set_reg(bt, sreg);
+ return 0;
+ }
+
/* scalars can only be spilled into stack */
if (!hist || !(hist->flags & INSN_F_STACK_ACCESS))
return 0;
bpf_bt_set_frame_reg(bt, bt->frame - 1, i);
}
}
+ if (bt_stack_arg_mask(bt)) {
+ verifier_bug(env,
+ "static subprog leftover stack arg slots %x",
+ bt_stack_arg_mask(bt));
+ return -EFAULT;
+ }
if (bt_subprog_exit(bt))
return -EFAULT;
return 0;
*changed = true;
}
}
+ for (i = 0; i < func->out_stack_arg_cnt; i++) {
+ if (!bt_is_frame_stack_arg_slot_set(bt, fr, i))
+ continue;
+ reg = &func->stack_arg_regs[i];
+ if (reg->type != SCALAR_VALUE || reg->precise) {
+ bt_clear_frame_stack_arg_slot(bt, fr, i);
+ } else {
+ reg->precise = true;
+ *changed = true;
+ }
+ }
if (env->log.level & BPF_LOG_LEVEL2) {
fmt_reg_mask(env->tmp_str_buf, TMP_STR_BUF_LEN,
bt_frame_reg_mask(bt, fr));
return -1;
}
+static int arg_idx_from_argno(argno_t a)
+{
+ return arg_from_argno(a) - 1;
+}
+
static const char *btf_type_name(const struct btf *btf, u32 id)
{
return btf_name_by_offset(btf, btf_type_by_id(btf, id)->name_off);
__mark_reg_known(arg, env->prog->insnsi[env->insn_idx].imm);
}
state->no_stack_arg_load = true;
- return 0;
+ return bpf_push_jmp_history(env, env->cur_state,
+ INSN_F_STACK_ARG_ACCESS, spi, 0, 0);
}
/*
arg = &caller->stack_arg_regs[spi];
cur = vstate->frame[vstate->curframe];
cur->regs[dst_regno] = *arg;
- return 0;
+ return bpf_push_jmp_history(env, env->cur_state,
+ INSN_F_STACK_ARG_ACCESS, spi, 0, 0);
+}
+
+static int mark_stack_arg_precision(struct bpf_verifier_env *env, int arg_idx)
+{
+ struct bpf_func_state *caller = cur_func(env);
+ int spi = arg_idx - MAX_BPF_FUNC_REG_ARGS;
+
+ bt_set_frame_stack_arg_slot(&env->bt, caller->frameno, spi);
+ return mark_chain_precision_batch(env, env->cur_state);
}
static int check_outgoing_stack_args(struct bpf_verifier_env *env, struct bpf_func_state *caller,
}
err = check_helper_mem_access(env, mem_reg, mem_argno, reg_umax(size_reg),
access_type, zero_size_allowed, meta);
- if (!err)
- err = mark_chain_precision(env, reg_from_argno(size_argno));
+ if (!err) {
+ int regno = reg_from_argno(size_argno);
+
+ if (regno >= 0)
+ err = mark_chain_precision(env, regno);
+ else
+ err = mark_stack_arg_precision(env, arg_idx_from_argno(size_argno));
+ }
return err;
}
struct bpf_kfunc_call_arg_meta *meta)
{
const struct btf_type *t;
- u32 arg_idx = arg_from_argno(argno) - 1;
+ u32 arg_idx = arg_idx_from_argno(argno);
int spi, err, i, nr_slots, btf_id;
if (reg->type != PTR_TO_STACK) {