aboutsummaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorKumar Kartikeya Dwivedi <memxor@gmail.com>2026-09-18 01:32:16 +0200
committerEduard Zingerman <eddyz87@gmail.com>2026-09-17 18:01:43 -0700
commit71919742c83c32afcccf06a7a28e28f3f55f21a2 (patch)
tree379b27e315ed45f62900c0d32527fbb45c3ec32c /include/linux
parent3440505aca926e726a26c0fd30356454334322bd (diff)
bpf: Assign lock identity to callback map values
A nested bpf_for_each_map_elem() callback can unlock a different element of the same map: static long inner(void *map, int *key, struct value *v, struct value **outer_value) { bpf_spin_lock(&v->lock); bpf_spin_unlock(&(*outer_value)->lock); return 0; } static long outer(void *map, int *key, struct value *v, void *ctx) { bpf_for_each_map_elem(map, inner, &v, 0); return 0; } Both callback values currently have ID zero and the same map_ptr. process_spin_lock() compares those two fields, so it accepts the unlock even though the two callbacks can receive different map elements. Assign a fresh ID to every callback map value in the for-each, timer/workqueue, and task-work constructors. Copies of one callback argument retain its ID, so locking and unlocking through that argument continues to work. Distinct callbacks also get distinct IDs for single-element arrays, including inner arrays sharing inner_map_meta. Preserve map_uid for every inner-map lookup and compare it through check_ids() during state pruning. This preserves relationships between maps, keys, and values while allowing equivalent states with different lookup IDs to match. It avoids field-specific rules for when an inner map needs an identity. Move map_uid out of the metadata union and next to the other IDs, so register comparisons can use the existing memcmp() ranges and remap the IDs separately. Clear it when resetting a register or converting a map lookup result to a socket pointer. Shrink frameno to u8, which is enough for MAX_CALL_FRAMES, to make room without growing bpf_reg_state. Fixes: d0d78c1df9b1 ("bpf: Allow locking bpf_spin_lock global variables") Reported-by: Nicholas Carlini <npc@anthropic.com> Suggested-by: Nicholas Carlini <npc@anthropic.com> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://patch.msgid.link/20260917233222.2542500-9-memxor@gmail.com Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/bpf_verifier.h25
1 files changed, 13 insertions, 12 deletions
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index bba5a727c651..a7202b44ab10 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -46,17 +46,11 @@ struct bpf_reg_state {
/* valid when type == PTR_TO_PACKET */
int range;
- /* valid when type == CONST_PTR_TO_MAP | PTR_TO_MAP_VALUE |
- * PTR_TO_MAP_VALUE_OR_NULL
+ /*
+ * For CONST_PTR_TO_MAP, PTR_TO_MAP_KEY, PTR_TO_MAP_VALUE and
+ * PTR_TO_INSN.
*/
- struct {
- struct bpf_map *map_ptr;
- /* To distinguish map lookups from outer map
- * the map_uid is non-zero for registers
- * pointing to inner maps.
- */
- u32 map_uid;
- };
+ struct bpf_map *map_ptr;
/* for PTR_TO_BTF_ID */
struct {
@@ -155,13 +149,20 @@ struct bpf_reg_state {
* gets parent_id set to the dynptr's id.
*/
u32 parent_id;
- /* Inside the callee two registers can be both PTR_TO_STACK like
+ /*
+ * Distinguishes inner-map lookups and their keys and values. Zero for
+ * other registers. Kept outside the metadata union for ID remapping
+ * during state comparisons.
+ */
+ u32 map_uid;
+ /*
+ * Inside the callee two registers can be both PTR_TO_STACK like
* R1=fp-8 and R2=fp-8, but one of them points to this function stack
* while another to the caller's stack. To differentiate them 'frameno'
* is used which is an index in bpf_verifier_state->frame[] array
* pointing to bpf_func_state.
*/
- u32 frameno;
+ u8 frameno;
/* if (!precise && SCALAR_VALUE) min/max/tnum don't affect safety */
bool precise;
};