Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

locking/lockdep: Avoid potential access of invalid memory in lock_class

It was found that reading /proc/lockdep after a lockdep splat may
potentially cause an access to freed memory if lockdep_unregister_key()
is called after the splat but before access to /proc/lockdep [1]. This
is due to the fact that graph_lock() call in lockdep_unregister_key()
fails after the clearing of debug_locks by the splat process.

After lockdep_unregister_key() is called, the lock_name may be freed
but the corresponding lock_class structure still have a reference to
it. That invalid memory pointer will then be accessed when /proc/lockdep
is read by a user and a use-after-free (UAF) error will be reported if
KASAN is enabled.

To fix this problem, lockdep_unregister_key() is now modified to always
search for a matching key irrespective of the debug_locks state and
zap the corresponding lock class if a matching one is found.

[1] https://lore.kernel.org/lkml/77f05c15-81b6-bddd-9650-80d5f23fe330@i-love.sakura.ne.jp/

Fixes: 8b39adbee805 ("locking/lockdep: Make lockdep_unregister_key() honor 'debug_locks' again")
Reported-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Signed-off-by: Waiman Long <longman@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Link: https://lkml.kernel.org/r/20220103023558.1377055-1-longman@redhat.com

authored by

Waiman Long and committed by
Peter Zijlstra
61cc4534 e204193b

+15 -9
+15 -9
kernel/locking/lockdep.c
··· 6287 6287 lockdep_reset_lock_reg(lock); 6288 6288 } 6289 6289 6290 - /* Unregister a dynamically allocated key. */ 6290 + /* 6291 + * Unregister a dynamically allocated key. 6292 + * 6293 + * Unlike lockdep_register_key(), a search is always done to find a matching 6294 + * key irrespective of debug_locks to avoid potential invalid access to freed 6295 + * memory in lock_class entry. 6296 + */ 6291 6297 void lockdep_unregister_key(struct lock_class_key *key) 6292 6298 { 6293 6299 struct hlist_head *hash_head = keyhashentry(key); ··· 6308 6302 return; 6309 6303 6310 6304 raw_local_irq_save(flags); 6311 - if (!graph_lock()) 6312 - goto out_irq; 6305 + lockdep_lock(); 6313 6306 6314 - pf = get_pending_free(); 6315 6307 hlist_for_each_entry_rcu(k, hash_head, hash_entry) { 6316 6308 if (k == key) { 6317 6309 hlist_del_rcu(&k->hash_entry); ··· 6317 6313 break; 6318 6314 } 6319 6315 } 6320 - WARN_ON_ONCE(!found); 6321 - __lockdep_free_key_range(pf, key, 1); 6322 - call_rcu_zapped(pf); 6323 - graph_unlock(); 6324 - out_irq: 6316 + WARN_ON_ONCE(!found && debug_locks); 6317 + if (found) { 6318 + pf = get_pending_free(); 6319 + __lockdep_free_key_range(pf, key, 1); 6320 + call_rcu_zapped(pf); 6321 + } 6322 + lockdep_unlock(); 6325 6323 raw_local_irq_restore(flags); 6326 6324 6327 6325 /* Wait until is_dynamic_key() has finished accessing k->hash_entry. */