Skip to content

Commit 3d781ff

Browse files
author
Alexei Starovoitov
committed
Merge branch 'refactor-verifier-object-relationship-tracking'
Amery Hung says: ==================== Refactor verifier object relationship tracking Hi all, This patchset cleans up dynptr handling, refactors object relationship tracking in the verifier by introducing parent_id and folding ref_obj_id into id, and fixes dynptr use-after-free bugs where file/skb dynptrs are not invalidated when the parent referenced object is freed. * Motivation * In BPF qdisc programs, an skb can be freed through kfuncs. However, since dynptr does not track the parent referenced object (e.g., skb), the verifier does not invalidate the dynptr after the skb is freed, resulting in use-after-free. The same issue also affects file dynptr. The figure below shows the current state of object tracking. The verifier tracks objects using three fields: id for nullness tracking, ref_obj_id for lifetime tracking, and dynptr_id for tracking the parent dynptr of a slice (PTR_TO_MEM only). While dynptr_id links slices to their parent dynptr, there is no field that links a dynptr back to its parent skb. When the skb is freed via release_reference(ref_obj_id=1), only objects with ref_obj_id=1 are invalidated. Since skb dynptr is non-referenced (ref_obj_id=0), the dynptr and its derived slices remain accessible. Current: object (id, ref_obj_id, dynptr_id) id = unique id of the object (for nullness tracking) ref_obj_id = id of the referenced object (for lifetime tracking) dynptr_id = id of the parent dynptr (only for PTR_TO_MEM slices) skb (0,1,0) ^^ ! No link from dynptr to skb ! |+------------------------------+ | bpf_dynptr_clone | dynptr A (2,0,0) dynptr C (4,0,0) ^ ^ bpf_dynptr_slice | | | | slice B (3,0,2) slice D (5,0,4) * Why not simply use ref_obj_id to track the parent? * A natural first approach is to link dynptr to its parent by sharing the parent's ref_obj_id and propagating it to slices. Now, releasing the skb via release_reference(ref_obj_id=1) correctly invalidates all derived objects. Attempted fix: share parent's ref_obj_id skb (0,1,0) ^^ || |+------------------------------+ | bpf_dynptr_clone | dynptr A (2,1,0) dynptr C (4,1,0) ^ ^ bpf_dynptr_slice | | | | slice B (3,1,2) slice D (5,1,4) However, this approach does not generalize to all dynptr types. Referenced dynptrs such as file dynptr acquire their own ref_obj_id to track the dynptr's lifetime. Since ref_obj_id is already used for the dynptr's own reference, it cannot also be used to point to the parent file object. While it is possible to add specialized handling for individual dynptr types [0], it adds complexity and does not generalize. An alternative approach is to avoid introducing a new field and instead repurpose ref_obj_id as parent_id by folding lifetime tracking into id [1]. In this design, each object is represented as (id, ref_obj_id) where id is used for both nullness and lifetime tracking, and ref_obj_id tracks the parent object's id. Attempted: object (id, ref_obj_id) id = id of the object (for nullness and lifetime tracking) ref_obj_id = id of the parent object ' = id is referenced skb (1',0) ^^ || bpf_dynptr_from_skb |+------------------------------+ | bpf_dynptr_clone(A, C) | dynptr A (2,1') dynptr C (4,1') ^ ^ bpf_dynptr_slice | | | | slice B (3,2) slice D (5,4) However, this design cannot express the relationship between referenced socket pointers and their casted counterparts. After pointer casting, the original and casted pointers need the same lifetime (same ref_obj_id in the current design) but different nullness (different id). The casted pointer may be NULL even if the original is valid. With id serving as the only field for both nullness and lifetime, and ref_obj_id repurposed as parent, there is no way to express "different identity, same lifetime." Referenced socket pointer (expressed using current design): C = ptr_casting_function(A) ptr A (1,1,0) ptr C (2,1,0) ^ ^ | | ptr C may be NULL even if ptr A is valid but they have the same lifetime * New Design: parent_id with branch splitting and intermediate reference * The patchset folds ref_obj_id into id and adds parent_id to bpf_reg_state (patch 5). A child object's parent_id points to the parent object's id. This replaces the PTR_TO_MEM-specific dynptr_id. Whether a register is referenced is determined by checking if its id appears in the reference array via reg_is_referenced() rather than reading a dedicated ref_obj_id field. Pointer casting: The challenge with pointer casting is that a cast result may be NULL even when the source is valid, requiring distinct identity but shared lifetime. This is solved using branch splitting: when a helper like bpf_sk_fullsock() is called with a referenced pointer, the verifier pushes an explicit NULL branch and assigns the cast result the same id as the source. Since the cast may return NULL for a non-NULL input, the NULL case is explored as a separate verifier branch. This allows releasing any of the original or cast pointers to invalidate all others, while avoiding the need for a separate tracking mechanism. Referenced dynptrs: The challenge with referenced dynptrs is that clones of a referenced dynptr have the same lifetime but different identities. When a referenced dynptr is overwritten, only slices derived from it will be invalidated. To solve this, the verifier creates an intermediate reference. This reference serves as a shared lifetime anchor for the dynptr and all its clones. All clones share the same parent_id but get unique ids for independent slice tracking. Releasing a referenced dynptr releases the intermediate reference, which in turn invalidates all clones and their derived slices. If the parent object is released while the intermediate reference still exists, it is reported as a leaked reference. Release cascading: When releasing an object, release_reference() performs a stack-based DFS to invalidate all descendants. It walks the object tree via parent_id links, invalidating registers and dynptr stack slots. Child references encountered during traversal are reported as leaked references. parent_id is also added to bpf_reference_state to enable intermediate reference. When acquiring a reference, a parent_id can be specified to link the new reference to an existing one (e.g., file dynptr's intermediate reference has parent_id linking to the file's reference). Final: object (id, parent_id) id = unique id of the object (for nullness and lifetime tracking) parent_id = id of the parent object (for object relationship tracking) I = intermediate reference serving as lifetime anchor in acquired_refs ' = id is referenced (appears in reference array) skb (1',0) ^^ || bpf_dynptr_from_skb |+------------------------------+ | bpf_dynptr_clone(A, C) | dynptr A (2,1') dynptr C (4,1') ^ ^ bpf_dynptr_slice | | | | slice B (3,2) slice D (5,4) * Preserving reg->id after null-check * For parent_id tracking to work, child objects need to refer to the parent's id. This requires two preparatory changes: assigning reg->id when reading referenced kptrs from program context (patch 3), and preserving reg->id of pointer objects after null-check (patch 4). Previously, null-check would clear reg->id, making it impossible for children to reference the parent afterward. The latter causes a slight increase in verified states for some programs. One selftest object sees +19 states (+5.01%). For Meta BPF objects, the increase is also minor, with the largest being +34 states (+3.63%). * Object relationship in different scenarios (for reference) * The figures below show how the final design handles all four combinations of referenced/non-referenced dynptr with referenced/non-referenced parent. (1) Non-referenced dynptr with referenced parent (e.g., skb in Qdisc): skb (1',0) ^^ || bpf_dynptr_from_skb |+------------------------------+ | bpf_dynptr_clone(A, C) | dynptr A (2,1') dynptr C (4,1') dynptr A and C live independently (2) Non-referenced dynptr with non-referenced parent (e.g., skb in TC, always valid): bpf_dynptr_from_skb bpf_dynptr_clone(A, C) dynptr A (1,0) dynptr C (2,0) dynptr A and C live independently (3) Referenced dynptr with referenced parent: file (1',0) ^ bpf_dynptr_from_file | I (2',1') <-- intermediate reference ^^ || |+-------------------------------+ | bpf_dynptr_clone(A, C) | dynptr A (3,2') dynptr C (4,2') dynptr A and C have the same lifetime Releasing either dynptr releases I, invalidating both. Releasing file (1') detects I as a leaked reference. (4) Referenced dynptr with non-referenced parent: bpf_ringbuf_reserve_dynptr I (1',0) <-- intermediate reference ^^ || |+--------------------------------+ | bpf_dynptr_clone(A, C) | dynptr A (2,1') dynptr C (3,1') dynptr A and C have the same lifetime [0] https://lore.kernel.org/bpf/20250414161443.1146103-2-memxor@gmail.com/ [1] https://github.com/ameryhung/bpf/commits/obj_relationship_v2_no_parent_id/ Changelog: v5 -> v6 - Squash "bpf: Fold ref_obj_id into id and introduce virtual references" (v5 patch 9) into "bpf: Refactor object relationship tracking and fix dynptr UAF bug" (now patch 5). ref_obj_id is removed in the same patch that introduces parent_id, eliminating the intermediate state where both coexist (Eduard) - Drop virtual references for pointer casting. Instead, cast results reuse the source pointer's id and use branch splitting to explore the NULL case as a separate verifier branch. This avoids adding virtual reference infrastructure for a case that can be handled more simply (Eduard, Andrii) - Address nit from Eduard Link: https://lore.kernel.org/bpf/20260519181314.2731658-1-ameryhung@gmail.com/ v4 -> v5 - Add patch 9 folding ref_obj_id into id and introducing virtual references for pointer casting and referenced dynptr clones (Eduard, Andrii) - Add patch 10 fixing dynptr ref counting to scan all call frames instead of only the current frame (Eduard) - Add utility function validate_ref_obj() (Eduard) Link: https://lore.kernel.org/bpf/20260506142709.2298255-1-ameryhung@gmail.com/ v3 -> v4 - Add patch 1 clean up mark_stack_slot_obj_read() and callers (to address v3 ignoring err returned from mark_dynptr_read) (Andrii) - Fix release_reference() and move the logic allowing destroying a referenced object when refcnt > 1 from destroy_if_stack_slots_dynptr() to release_reference() (Mykyta) - Add patch 7 introducing ref_obj_desc and unifying ref_obj handling (to address Eduard's concern about unclear meta->{id,ref_obj_id} initialization/use and confusing function arguments of process_dynptr_func()) - Add patch 8 unifying release_regno handling so that bpf_kptr_xchg also use release_reference() Link: https://lore.kernel.org/bpf/20260421221016.2967924-1-ameryhung@gmail.com/ v2 -> v3 - Rebase to bpf-next/master - Update veristat numbers - Update commit msg to explain multiple dropped checks (Mykyta, Andrii) - Reuse idmap as idstack in release_reference() and check for duplicate id (Mykyta, Andrii) - Change to use RUN_TEST for qdisc dynptr selftest (Eduard) Link: https://lore.kernel.org/bpf/20260307064439.3247440-1-ameryhung@gmail.com/ v1 -> v2 - Redesign: Use object (id, ref_obj_id, parent_id) instead of (id, ref_obj_id) as it cannot express ptr casting without introducing specialized code to handle the case - Use stack-based DFS to release objects to avoid recursion (Andrii) - Keep reg->id after null check - Add dynptr cleanup - Fix dynptr kfunc arg type determination - Add a file dynptr UAF selftest Link: https://lore.kernel.org/bpf/20260202214817.2853236-1-ameryhung@gmail.com/ --- ==================== Link: https://patch.msgid.link/20260529014936.2811085-1-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 868d43c + 60c7c3b commit 3d781ff

30 files changed

Lines changed: 957 additions & 745 deletions

include/linux/bpf.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ struct bpf_insn_access_aux {
10621062
struct {
10631063
struct btf *btf;
10641064
u32 btf_id;
1065-
u32 ref_obj_id;
1065+
u32 ref_id;
10661066
};
10671067
};
10681068
struct bpf_verifier_log *log; /* for verbose logs */
@@ -1631,7 +1631,7 @@ struct bpf_ctx_arg_aux {
16311631
enum bpf_reg_type reg_type;
16321632
struct btf *btf;
16331633
u32 btf_id;
1634-
u32 ref_obj_id;
1634+
u32 ref_id;
16351635
bool refcounted;
16361636
};
16371637

include/linux/bpf_verifier.h

Lines changed: 47 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ struct bpf_reg_state {
6666

6767
struct { /* for PTR_TO_MEM | PTR_TO_MEM_OR_NULL */
6868
u32 mem_size;
69-
u32 dynptr_id; /* for dynptr slices */
7069
};
7170

7271
/* For dynptr stack slots */
@@ -148,46 +147,14 @@ struct bpf_reg_state {
148147
#define BPF_ADD_CONST32 (1U << 30)
149148
#define BPF_ADD_CONST (BPF_ADD_CONST64 | BPF_ADD_CONST32)
150149
u32 id;
151-
/* PTR_TO_SOCKET and PTR_TO_TCP_SOCK could be a ptr returned
152-
* from a pointer-cast helper, bpf_sk_fullsock() and
153-
* bpf_tcp_sock().
154-
*
155-
* Consider the following where "sk" is a reference counted
156-
* pointer returned from "sk = bpf_sk_lookup_tcp();":
157-
*
158-
* 1: sk = bpf_sk_lookup_tcp();
159-
* 2: if (!sk) { return 0; }
160-
* 3: fullsock = bpf_sk_fullsock(sk);
161-
* 4: if (!fullsock) { bpf_sk_release(sk); return 0; }
162-
* 5: tp = bpf_tcp_sock(fullsock);
163-
* 6: if (!tp) { bpf_sk_release(sk); return 0; }
164-
* 7: bpf_sk_release(sk);
165-
* 8: snd_cwnd = tp->snd_cwnd; // verifier will complain
166-
*
167-
* After bpf_sk_release(sk) at line 7, both "fullsock" ptr and
168-
* "tp" ptr should be invalidated also. In order to do that,
169-
* the reg holding "fullsock" and "sk" need to remember
170-
* the original refcounted ptr id (i.e. sk_reg->id) in ref_obj_id
171-
* such that the verifier can reset all regs which have
172-
* ref_obj_id matching the sk_reg->id.
173-
*
174-
* sk_reg->ref_obj_id is set to sk_reg->id at line 1.
175-
* sk_reg->id will stay as NULL-marking purpose only.
176-
* After NULL-marking is done, sk_reg->id can be reset to 0.
177-
*
178-
* After "fullsock = bpf_sk_fullsock(sk);" at line 3,
179-
* fullsock_reg->ref_obj_id is set to sk_reg->ref_obj_id.
180-
*
181-
* After "tp = bpf_tcp_sock(fullsock);" at line 5,
182-
* tp_reg->ref_obj_id is set to fullsock_reg->ref_obj_id
183-
* which is the same as sk_reg->ref_obj_id.
184-
*
185-
* From the verifier perspective, if sk, fullsock and tp
186-
* are not NULL, they are the same ptr with different
187-
* reg->type. In particular, bpf_sk_release(tp) is also
188-
* allowed and has the same effect as bpf_sk_release(sk).
150+
/*
151+
* Tracks the parent object this register was derived from.
152+
* Used for cascading invalidation: when the parent object is
153+
* released or invalidated, all registers with matching parent_id
154+
* are also invalidated. For example, a slice from bpf_dynptr_data()
155+
* gets parent_id set to the dynptr's id.
189156
*/
190-
u32 ref_obj_id;
157+
u32 parent_id;
191158
/* Inside the callee two registers can be both PTR_TO_STACK like
192159
* R1=fp-8 and R2=fp-8, but one of them points to this function stack
193160
* while another to the caller's stack. To differentiate them 'frameno'
@@ -364,10 +331,14 @@ struct bpf_reference_state {
364331
* is used purely to inform the user of a reference leak.
365332
*/
366333
int insn_idx;
367-
/* Use to keep track of the source object of a lock, to ensure
368-
* it matches on unlock.
369-
*/
370-
void *ptr;
334+
union {
335+
/* For REF_TYPE_PTR */
336+
int parent_id;
337+
/* Use to keep track of the source object of a lock, to ensure
338+
* it matches on unlock.
339+
*/
340+
void *ptr;
341+
};
371342
};
372343

373344
struct bpf_retval_range {
@@ -585,23 +556,26 @@ bpf_get_spilled_stack_arg(int slot, struct bpf_func_state *frame)
585556
iter < frame->out_stack_arg_cnt; \
586557
iter++, reg = bpf_get_spilled_stack_arg(iter, frame))
587558

588-
#define bpf_for_each_reg_in_vstate_mask(__vst, __state, __reg, __mask, __expr) \
559+
#define bpf_for_each_reg_in_vstate_mask(__vst, __state, __reg, __stack, __mask, __expr) \
589560
({ \
590561
struct bpf_verifier_state *___vstate = __vst; \
591562
int ___i, ___j; \
592563
for (___i = 0; ___i <= ___vstate->curframe; ___i++) { \
593564
struct bpf_reg_state *___regs; \
594565
__state = ___vstate->frame[___i]; \
595566
___regs = __state->regs; \
567+
__stack = NULL; \
596568
for (___j = 0; ___j < MAX_BPF_REG; ___j++) { \
597569
__reg = &___regs[___j]; \
598570
(void)(__expr); \
599571
} \
600572
bpf_for_each_spilled_reg(___j, __state, __reg, __mask) { \
601573
if (!__reg) \
602574
continue; \
575+
__stack = &__state->stack[___j]; \
603576
(void)(__expr); \
604577
} \
578+
__stack = NULL; \
605579
bpf_for_each_spilled_stack_arg(___j, __state, __reg) { \
606580
if (!__reg) \
607581
continue; \
@@ -611,8 +585,13 @@ bpf_get_spilled_stack_arg(int slot, struct bpf_func_state *frame)
611585
})
612586

613587
/* Invoke __expr over regsiters in __vst, setting __state and __reg */
614-
#define bpf_for_each_reg_in_vstate(__vst, __state, __reg, __expr) \
615-
bpf_for_each_reg_in_vstate_mask(__vst, __state, __reg, 1 << STACK_SPILL, __expr)
588+
#define bpf_for_each_reg_in_vstate(__vst, __state, __reg, __expr) \
589+
({ \
590+
struct bpf_stack_state * ___stack; \
591+
(void)___stack; \
592+
bpf_for_each_reg_in_vstate_mask(__vst, __state, __reg, ___stack,\
593+
1 << STACK_SPILL, __expr); \
594+
})
616595

617596
/* linked list of verifier states used to prune search */
618597
struct bpf_verifier_state_list {
@@ -1438,6 +1417,25 @@ struct bpf_map_desc {
14381417
int uid;
14391418
};
14401419

1420+
/* The last initialized dynptr; Populated by process_dynptr_func() */
1421+
struct bpf_dynptr_desc {
1422+
enum bpf_dynptr_type type;
1423+
u32 id;
1424+
u32 parent_id;
1425+
};
1426+
1427+
/*
1428+
* The last seen rereferenced object; Updated by update_ref_obj() when a register refers to a
1429+
* referenced object. Used when the helper or kfunc is casting a referenced object, returning
1430+
* allocated memory derived from referenced object or creating a dynptr with a referenced
1431+
* object as parent.
1432+
*/
1433+
struct ref_obj_desc {
1434+
u32 id;
1435+
u32 parent_id;
1436+
u8 cnt;
1437+
};
1438+
14411439
struct bpf_kfunc_call_arg_meta {
14421440
/* In parameters */
14431441
struct btf *btf;
@@ -1446,7 +1444,6 @@ struct bpf_kfunc_call_arg_meta {
14461444
const struct btf_type *func_proto;
14471445
const char *func_name;
14481446
/* Out parameters */
1449-
u32 ref_obj_id;
14501447
u8 release_regno;
14511448
bool r0_rdonly;
14521449
u32 ret_btf_id;
@@ -1478,16 +1475,13 @@ struct bpf_kfunc_call_arg_meta {
14781475
struct {
14791476
struct btf_field *field;
14801477
} arg_rbtree_root;
1481-
struct {
1482-
enum bpf_dynptr_type type;
1483-
u32 id;
1484-
u32 ref_obj_id;
1485-
} initialized_dynptr;
14861478
struct {
14871479
u8 spi;
14881480
u8 frameno;
14891481
} iter;
14901482
struct bpf_map_desc map;
1483+
struct bpf_dynptr_desc dynptr;
1484+
struct ref_obj_desc ref_obj;
14911485
u64 mem_size;
14921486
};
14931487

kernel/bpf/btf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6957,7 +6957,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
69576957
info->reg_type = ctx_arg_info->reg_type;
69586958
info->btf = ctx_arg_info->btf ? : btf_vmlinux;
69596959
info->btf_id = ctx_arg_info->btf_id;
6960-
info->ref_obj_id = ctx_arg_info->ref_obj_id;
6960+
info->ref_id = ctx_arg_info->ref_id;
69616961
return true;
69626962
}
69636963
}

kernel/bpf/fixups.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
870870
case PTR_TO_BTF_ID:
871871
case PTR_TO_BTF_ID | PTR_UNTRUSTED:
872872
/* PTR_TO_BTF_ID | MEM_ALLOC always has a valid lifetime, unlike
873-
* PTR_TO_BTF_ID, and an active ref_obj_id, but the same cannot
873+
* PTR_TO_BTF_ID, and an active referenced id, but the same cannot
874874
* be said once it is marked PTR_UNTRUSTED, hence we must handle
875875
* any faults for loads into such types. BPF_WRITE is disallowed
876876
* for this case.

kernel/bpf/helpers.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4957,7 +4957,7 @@ BTF_ID_FLAGS(func, bpf_stream_print_stack, KF_IMPLICIT_ARGS)
49574957
BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_IMPLICIT_ARGS)
49584958
BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_IMPLICIT_ARGS)
49594959
BTF_ID_FLAGS(func, bpf_dynptr_from_file)
4960-
BTF_ID_FLAGS(func, bpf_dynptr_file_discard)
4960+
BTF_ID_FLAGS(func, bpf_dynptr_file_discard, KF_RELEASE)
49614961
BTF_ID_FLAGS(func, bpf_timer_cancel_async)
49624962
BTF_KFUNCS_END(common_btf_ids)
49634963

kernel/bpf/log.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -665,8 +665,8 @@ static void print_reg_state(struct bpf_verifier_env *env,
665665
verbose_a("id=%d", reg->id & ~BPF_ADD_CONST);
666666
if (reg->id & BPF_ADD_CONST)
667667
verbose(env, "%+d", reg->delta);
668-
if (reg->ref_obj_id)
669-
verbose_a("ref_obj_id=%d", reg->ref_obj_id);
668+
if (reg->parent_id)
669+
verbose_a("parent_id=%d", reg->parent_id);
670670
if (type_is_non_owning_ref(reg->type))
671671
verbose_a("%s", "non_own_ref");
672672
if (type_is_map_ptr(t)) {
@@ -768,21 +768,19 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie
768768
verbose(env, "=dynptr_%s(", dynptr_type_str(reg->dynptr.type));
769769
if (reg->id)
770770
verbose_a("id=%d", reg->id);
771-
if (reg->ref_obj_id)
772-
verbose_a("ref_id=%d", reg->ref_obj_id);
773-
if (reg->dynptr_id)
774-
verbose_a("dynptr_id=%d", reg->dynptr_id);
771+
if (reg->parent_id)
772+
verbose_a("parent_id=%d", reg->parent_id);
775773
verbose(env, ")");
776774
break;
777775
case STACK_ITER:
778-
/* only main slot has ref_obj_id set; skip others */
779-
if (!reg->ref_obj_id)
776+
/* only main slot has id set; skip others */
777+
if (!reg->id)
780778
continue;
781779

782-
verbose(env, " fp%d=iter_%s(ref_id=%d,state=%s,depth=%u)",
780+
verbose(env, " fp%d=iter_%s(id=%d,state=%s,depth=%u)",
783781
(-i - 1) * BPF_REG_SIZE,
784782
iter_type_str(reg->iter.btf, reg->iter.btf_id),
785-
reg->ref_obj_id, iter_state_str(reg->iter.state),
783+
reg->id, iter_state_str(reg->iter.state),
786784
reg->iter.depth);
787785
break;
788786
case STACK_MISC:

kernel/bpf/states.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ static bool regs_exact(const struct bpf_reg_state *rold,
489489
{
490490
return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
491491
check_ids(rold->id, rcur->id, idmap) &&
492-
check_ids(rold->ref_obj_id, rcur->ref_obj_id, idmap);
492+
check_ids(rold->parent_id, rcur->parent_id, idmap);
493493
}
494494

495495
enum exact_level {
@@ -614,7 +614,7 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
614614
range_within(rold, rcur) &&
615615
tnum_in(rold->var_off, rcur->var_off) &&
616616
check_ids(rold->id, rcur->id, idmap) &&
617-
check_ids(rold->ref_obj_id, rcur->ref_obj_id, idmap);
617+
check_ids(rold->parent_id, rcur->parent_id, idmap);
618618
case PTR_TO_PACKET_META:
619619
case PTR_TO_PACKET:
620620
/* We must have at least as much range as the old ptr
@@ -794,7 +794,8 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
794794
cur_reg = &cur->stack[spi].spilled_ptr;
795795
if (old_reg->dynptr.type != cur_reg->dynptr.type ||
796796
old_reg->dynptr.first_slot != cur_reg->dynptr.first_slot ||
797-
!check_ids(old_reg->ref_obj_id, cur_reg->ref_obj_id, idmap))
797+
!check_ids(old_reg->id, cur_reg->id, idmap) ||
798+
!check_ids(old_reg->parent_id, cur_reg->parent_id, idmap))
798799
return false;
799800
break;
800801
case STACK_ITER:
@@ -810,13 +811,13 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
810811
old_reg->iter.btf_id != cur_reg->iter.btf_id ||
811812
old_reg->iter.state != cur_reg->iter.state ||
812813
/* ignore {old_reg,cur_reg}->iter.depth, see above */
813-
!check_ids(old_reg->ref_obj_id, cur_reg->ref_obj_id, idmap))
814+
!check_ids(old_reg->id, cur_reg->id, idmap))
814815
return false;
815816
break;
816817
case STACK_IRQ_FLAG:
817818
old_reg = &old->stack[spi].spilled_ptr;
818819
cur_reg = &cur->stack[spi].spilled_ptr;
819-
if (!check_ids(old_reg->ref_obj_id, cur_reg->ref_obj_id, idmap) ||
820+
if (!check_ids(old_reg->id, cur_reg->id, idmap) ||
820821
old_reg->irq.kfunc_class != cur_reg->irq.kfunc_class)
821822
return false;
822823
break;

0 commit comments

Comments
 (0)