@@ -50,7 +50,13 @@ struct bpf_reg_state {
s32 off;
union {
/* valid when type == PTR_TO_PACKET */
- int range;
+ struct {
+ int range;
+ /* To distinguish packet pointers backed by different
+ * packets, to prevent pkt pointer comparisons.
+ */
+ u32 pkt_uid;
+ };
/* valid when type == CONST_PTR_TO_MAP | PTR_TO_MAP_VALUE |
* PTR_TO_MAP_VALUE_OR_NULL
@@ -431,6 +431,12 @@ static bool type_is_pkt_pointer(enum bpf_reg_type type)
type == PTR_TO_PACKET_META;
}
+static bool type_is_pkt_pointer_any(enum bpf_reg_type type)
+{
+ return type_is_pkt_pointer(type) ||
+ type == PTR_TO_PACKET_END;
+}
+
static bool type_is_sk_pointer(enum bpf_reg_type type)
{
return type == PTR_TO_SOCKET ||
@@ -861,6 +867,8 @@ static void print_verifier_state(struct bpf_verifier_env *env,
verbose_a("off=%d", reg->off);
if (type_is_pkt_pointer(t))
verbose_a("r=%d", reg->range);
+ if (type_is_pkt_pointer_any(t) && reg->pkt_uid)
+ verbose_a("pkt_uid=%d", reg->pkt_uid);
else if (base_type(t) == CONST_PTR_TO_MAP ||
base_type(t) == PTR_TO_MAP_KEY ||
base_type(t) == PTR_TO_MAP_VALUE)
@@ -1394,8 +1402,7 @@ static bool reg_is_pkt_pointer(const struct bpf_reg_state *reg)
static bool reg_is_pkt_pointer_any(const struct bpf_reg_state *reg)
{
- return reg_is_pkt_pointer(reg) ||
- reg->type == PTR_TO_PACKET_END;
+ return type_is_pkt_pointer_any(reg->type);
}
/* Unmodified PTR_TO_PACKET[_META,_END] register from ctx access. */
@@ -6575,14 +6582,17 @@ static void release_reg_references(struct bpf_verifier_env *env,
struct bpf_reg_state *regs = state->regs, *reg;
int i;
- for (i = 0; i < MAX_BPF_REG; i++)
- if (regs[i].ref_obj_id == ref_obj_id)
+ for (i = 0; i < MAX_BPF_REG; i++) {
+ if (regs[i].ref_obj_id == ref_obj_id ||
+ (reg_is_pkt_pointer_any(®s[i]) && regs[i].pkt_uid == ref_obj_id))
mark_reg_unknown(env, regs, i);
+ }
bpf_for_each_spilled_reg(i, state, reg) {
if (!reg)
continue;
- if (reg->ref_obj_id == ref_obj_id)
+ if (reg->ref_obj_id == ref_obj_id ||
+ (reg_is_pkt_pointer_any(reg) && reg->pkt_uid == ref_obj_id))
__mark_reg_unknown(env, reg);
}
}
@@ -8200,7 +8210,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
if (reg_is_pkt_pointer(ptr_reg)) {
dst_reg->id = ++env->id_gen;
/* something was added to pkt_ptr, set range to zero */
- memset(&dst_reg->raw, 0, sizeof(dst_reg->raw));
+ dst_reg->range = 0;
}
break;
case BPF_SUB:
@@ -8260,7 +8270,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
dst_reg->id = ++env->id_gen;
/* something was added to pkt_ptr, set range to zero */
if (smin_val < 0)
- memset(&dst_reg->raw, 0, sizeof(dst_reg->raw));
+ dst_reg->range = 0;
}
break;
case BPF_AND:
@@ -9287,7 +9297,8 @@ static void __find_good_pkt_pointers(struct bpf_func_state *state,
for (i = 0; i < MAX_BPF_REG; i++) {
reg = &state->regs[i];
- if (reg->type == type && reg->id == dst_reg->id)
+ if (reg->type == type && reg->id == dst_reg->id &&
+ reg->pkt_uid == dst_reg->pkt_uid)
/* keep the maximum range already checked */
reg->range = max(reg->range, new_range);
}
@@ -9295,7 +9306,8 @@ static void __find_good_pkt_pointers(struct bpf_func_state *state,
bpf_for_each_spilled_reg(i, state, reg) {
if (!reg)
continue;
- if (reg->type == type && reg->id == dst_reg->id)
+ if (reg->type == type && reg->id == dst_reg->id &&
+ reg->pkt_uid == dst_reg->pkt_uid)
reg->range = max(reg->range, new_range);
}
}
@@ -9910,6 +9922,14 @@ static void mark_ptr_or_null_regs(struct bpf_verifier_state *vstate, u32 regno,
__mark_ptr_or_null_regs(vstate->frame[i], id, is_null);
}
+static bool is_bad_pkt_comparison(const struct bpf_reg_state *dst_reg,
+ const struct bpf_reg_state *src_reg)
+{
+ if (!reg_is_pkt_pointer_any(dst_reg) || !reg_is_pkt_pointer_any(src_reg))
+ return false;
+ return dst_reg->pkt_uid != src_reg->pkt_uid;
+}
+
static bool try_match_pkt_pointers(const struct bpf_insn *insn,
struct bpf_reg_state *dst_reg,
struct bpf_reg_state *src_reg,
@@ -9923,6 +9943,9 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn,
if (BPF_CLASS(insn->code) == BPF_JMP32)
return false;
+ if (is_bad_pkt_comparison(dst_reg, src_reg))
+ return false;
+
switch (BPF_OP(insn->code)) {
case BPF_JGT:
if ((dst_reg->type == PTR_TO_PACKET &&
@@ -10220,11 +10243,17 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
mark_ptr_or_null_regs(other_branch, insn->dst_reg,
opcode == BPF_JEQ);
} else if (!try_match_pkt_pointers(insn, dst_reg, ®s[insn->src_reg],
- this_branch, other_branch) &&
- is_pointer_value(env, insn->dst_reg)) {
- verbose(env, "R%d pointer comparison prohibited\n",
- insn->dst_reg);
- return -EACCES;
+ this_branch, other_branch)) {
+ if (is_pointer_value(env, insn->dst_reg)) {
+ verbose(env, "R%d pointer comparison prohibited\n",
+ insn->dst_reg);
+ return -EACCES;
+ }
+ if (is_bad_pkt_comparison(dst_reg, ®s[insn->src_reg])) {
+ verbose(env, "R%d, R%d pkt pointer comparison prohibited\n",
+ insn->dst_reg, insn->src_reg);
+ return -EACCES;
+ }
}
if (env->log.level & BPF_LOG_LEVEL)
print_insn_state(env, this_branch->frame[this_branch->curframe]);
@@ -11514,6 +11543,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
/* id relations must be preserved */
if (rold->id && !check_ids(rold->id, rcur->id, idmap))
return false;
+ if (rold->pkt_uid && !check_ids(rold->pkt_uid, rcur->pkt_uid, idmap))
+ return false;
/* new val must satisfy old val knowledge */
return range_within(rold, rcur) &&
tnum_in(rold->var_off, rcur->var_off);