History

lockdep: fix oops in processing workqueue

Under memory load, on x86_64, with lockdep enabled, the workqueue's process_one_work() has been seen to oops in __lock_acquire(), barfing on a 0xffffffff00000000 pointer in the lockdep_map's class_cache[]. Because it's permissible to free a work_struct from its callout function, the map used is an onstack copy of the map given in the work_struct: and that copy is made without any locking. Surprisingly, gcc (4.5.1 in Hugh's case) uses "rep movsl" rather than "rep movsq" for that structure copy: which might race with a workqueue user's wait_on_work() doing lock_map_acquire() on the source of the copy, putting a pointer into the class_cache[], but only in time for the top half of that pointer to be copied to the destination map. Boom when process_one_work() subsequently does lock_map_acquire() on its onstack copy of the lockdep_map. Fix this, and a similar instance in call_timer_fn(), with a lockdep_copy_map() function which additionally NULLs the class_cache[]. Note: this oops was actually seen on 3.4-next, where flush_work() newly does the racing lock_map_acquire(); but Tejun points out that 3.4 and earlier are already vulnerable to the same through wait_on_work(). * Patch orginally from Peter. Hugh modified it a bit and wrote the description.

Peter Zijlstra committed 14 years ago

Commit 4d82a1d1 parent 544ecf33 files · +24 −2Browse files

Showing the changes to kernel/workqueue.c (1 of 3 files). Show all

Unified · Split

kernel/workqueue.c+3−1
@@ -1818,7 +1818,9 @@ __acquires(&gcwq->lock) · 1817 unmodified lines
18181818 * lock freed" warnings as well as problems when looking into
18191819 * work->lockdep_map, make a copy and use that here.
18201820 */
1821− struct lockdep_map lockdep_map = work->lockdep_map;
1821+ struct lockdep_map lockdep_map;
1822+
1823+ lockdep_copy_map(&lockdep_map, &work->lockdep_map);
18221824 #endif
18231825 /*
18241826 * A single work shouldn't be executed concurrently by