History

bpf: use check_ids() for active_lock comparison

An update for verifier.c:states_equal()/regsafe() to use check_ids() for active spin lock comparisons. This fixes the issue reported by Kumar Kartikeya Dwivedi in [1] using technique suggested by Edward Cree. W/o this commit the verifier might be tricked to accept the following program working with a map containing spin locks: 0: r9 = map_lookup_elem(...) ; Returns PTR_TO_MAP_VALUE_OR_NULL id=1. 1: r8 = map_lookup_elem(...) ; Returns PTR_TO_MAP_VALUE_OR_NULL id=2. 2: if r9 == 0 goto exit ; r9 -> PTR_TO_MAP_VALUE. 3: if r8 == 0 goto exit ; r8 -> PTR_TO_MAP_VALUE. 4: r7 = ktime_get_ns() ; Unbound SCALAR_VALUE. 5: r6 = ktime_get_ns() ; Unbound SCALAR_VALUE. 6: bpf_spin_lock(r8) ; active_lock.id == 2. 7: if r6 > r7 goto +1 ; No new information about the state ; is derived from this check, thus ; produced verifier states differ only ; in 'insn_idx'. 8: r9 = r8 ; Optionally make r9.id == r8.id. --- checkpoint --- ; Assume is_state_visisted() creates a ; checkpoint here. 9: bpf_spin_unlock(r9) ; (a,b) active_lock.id == 2. ; (a) r9.id == 2, (b) r9.id == 1. 10: exit(0) Consider two verification paths: (a) 0-10 (b) 0-7,9-10 The path (a) is verified first. If checkpoint is created at (8) the (b) would assume that (8) is safe because regsafe() does not compare register ids for registers of type PTR_TO_MAP_VALUE. [1] https://lore.kernel.org/bpf/20221111202719.982118-1-memxor@gmail.com/

Eduard Zingerman committed 3 years ago

Commit 4ea2bb11 parent 7d057941 file · +13 −3Browse files

Showing the changes to kernel (1 of 1 file). Show all

Unified · Split

kernel/bpf/verifier.c+13−3
@@ -13119,7 +13119,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, · 13118 unmodified lines
13119 */13119 */
13120 return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&13120 return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
13121 range_within(rold, rcur) &&13121 range_within(rold, rcur) &&
13122 tnum_in(rold->var_off, rcur->var_off);13122 tnum_in(rold->var_off, rcur->var_off) &&
13123 check_ids(rold->id, rcur->id, idmap);
13123 case PTR_TO_PACKET_META:13124 case PTR_TO_PACKET_META:
13124 case PTR_TO_PACKET:13125 case PTR_TO_PACKET:
13125 if (rcur->type != rold->type)13126 if (rcur->type != rold->type)
@@ -13291,8 +13292,17 @@ static bool states_equal(struct bpf_verifier_env *env, · 165 unmodified lines
13291 if (old->speculative && !cur->speculative)13292 if (old->speculative && !cur->speculative)
13292 return false;13293 return false;
13293 13294
13294 if (old->active_lock.ptr != cur->active_lock.ptr ||13295 if (old->active_lock.ptr != cur->active_lock.ptr)
13295 old->active_lock.id != cur->active_lock.id)13296 return false;
13297
13298 /* Old and cur active_lock's have to be either both present
13299 * or both absent.
13300 */
13301 if (!!old->active_lock.id != !!cur->active_lock.id)
13302 return false;
13303
13304 if (old->active_lock.id &&
13305 !check_ids(old->active_lock.id, cur->active_lock.id, env->idmap_scratch))
13296 return false;13306 return false;
13297 13307
13298 if (old->active_rcu_lock != cur->active_rcu_lock)13308 if (old->active_rcu_lock != cur->active_rcu_lock)