Skip to content

Commit b89e20a

Browse files
Maarten Lankhorstgregkh
authored andcommitted
devcoredump: Fix circular locking dependency with devcd->mutex.
[ Upstream commit a91c809 ] The original code causes a circular locking dependency found by lockdep. ====================================================== WARNING: possible circular locking dependency detected 6.16.0-rc6-lgci-xe-xe-pw-151626v3+ #1 Tainted: G S U ------------------------------------------------------ xe_fault_inject/5091 is trying to acquire lock: ffff888156815688 ((work_completion)(&(&devcd->del_wk)->work)){+.+.}-{0:0}, at: __flush_work+0x25d/0x660 but task is already holding lock: ffff888156815620 (&devcd->mutex){+.+.}-{3:3}, at: dev_coredump_put+0x3f/0xa0 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (&devcd->mutex){+.+.}-{3:3}: mutex_lock_nested+0x4e/0xc0 devcd_data_write+0x27/0x90 sysfs_kf_bin_write+0x80/0xf0 kernfs_fop_write_iter+0x169/0x220 vfs_write+0x293/0x560 ksys_write+0x72/0xf0 __x64_sys_write+0x19/0x30 x64_sys_call+0x2bf/0x2660 do_syscall_64+0x93/0xb60 entry_SYSCALL_64_after_hwframe+0x76/0x7e -> #1 (kn->active#236){++++}-{0:0}: kernfs_drain+0x1e2/0x200 __kernfs_remove+0xae/0x400 kernfs_remove_by_name_ns+0x5d/0xc0 remove_files+0x54/0x70 sysfs_remove_group+0x3d/0xa0 sysfs_remove_groups+0x2e/0x60 device_remove_attrs+0xc7/0x100 device_del+0x15d/0x3b0 devcd_del+0x19/0x30 process_one_work+0x22b/0x6f0 worker_thread+0x1e8/0x3d0 kthread+0x11c/0x250 ret_from_fork+0x26c/0x2e0 ret_from_fork_asm+0x1a/0x30 -> #0 ((work_completion)(&(&devcd->del_wk)->work)){+.+.}-{0:0}: __lock_acquire+0x1661/0x2860 lock_acquire+0xc4/0x2f0 __flush_work+0x27a/0x660 flush_delayed_work+0x5d/0xa0 dev_coredump_put+0x63/0xa0 xe_driver_devcoredump_fini+0x12/0x20 [xe] devm_action_release+0x12/0x30 release_nodes+0x3a/0x120 devres_release_all+0x8a/0xd0 device_unbind_cleanup+0x12/0x80 device_release_driver_internal+0x23a/0x280 device_driver_detach+0x14/0x20 unbind_store+0xaf/0xc0 drv_attr_store+0x21/0x50 sysfs_kf_write+0x4a/0x80 kernfs_fop_write_iter+0x169/0x220 vfs_write+0x293/0x560 ksys_write+0x72/0xf0 __x64_sys_write+0x19/0x30 x64_sys_call+0x2bf/0x2660 do_syscall_64+0x93/0xb60 entry_SYSCALL_64_after_hwframe+0x76/0x7e other info that might help us debug this: Chain exists of: (work_completion)(&(&devcd->del_wk)->work) --> kn->active#236 --> &devcd->mutex Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&devcd->mutex); lock(kn->active#236); lock(&devcd->mutex); lock((work_completion)(&(&devcd->del_wk)->work)); *** DEADLOCK *** 5 locks held by xe_fault_inject/5091: #0: ffff8881129f9488 (sb_writers#5){.+.+}-{0:0}, at: ksys_write+0x72/0xf0 #1: ffff88810c755078 (&of->mutex#2){+.+.}-{3:3}, at: kernfs_fop_write_iter+0x123/0x220 #2: ffff8881054811a0 (&dev->mutex){....}-{3:3}, at: device_release_driver_internal+0x55/0x280 #3: ffff888156815620 (&devcd->mutex){+.+.}-{3:3}, at: dev_coredump_put+0x3f/0xa0 #4: ffffffff8359e020 (rcu_read_lock){....}-{1:2}, at: __flush_work+0x72/0x660 stack backtrace: CPU: 14 UID: 0 PID: 5091 Comm: xe_fault_inject Tainted: G S U 6.16.0-rc6-lgci-xe-xe-pw-151626v3+ #1 PREEMPT_{RT,(lazy)} Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER Hardware name: Micro-Star International Co., Ltd. MS-7D25/PRO Z690-A DDR4(MS-7D25), BIOS 1.10 12/13/2021 Call Trace: <TASK> dump_stack_lvl+0x91/0xf0 dump_stack+0x10/0x20 print_circular_bug+0x285/0x360 check_noncircular+0x135/0x150 ? register_lock_class+0x48/0x4a0 __lock_acquire+0x1661/0x2860 lock_acquire+0xc4/0x2f0 ? __flush_work+0x25d/0x660 ? mark_held_locks+0x46/0x90 ? __flush_work+0x25d/0x660 __flush_work+0x27a/0x660 ? __flush_work+0x25d/0x660 ? trace_hardirqs_on+0x1e/0xd0 ? __pfx_wq_barrier_func+0x10/0x10 flush_delayed_work+0x5d/0xa0 dev_coredump_put+0x63/0xa0 xe_driver_devcoredump_fini+0x12/0x20 [xe] devm_action_release+0x12/0x30 release_nodes+0x3a/0x120 devres_release_all+0x8a/0xd0 device_unbind_cleanup+0x12/0x80 device_release_driver_internal+0x23a/0x280 ? bus_find_device+0xa8/0xe0 device_driver_detach+0x14/0x20 unbind_store+0xaf/0xc0 drv_attr_store+0x21/0x50 sysfs_kf_write+0x4a/0x80 kernfs_fop_write_iter+0x169/0x220 vfs_write+0x293/0x560 ksys_write+0x72/0xf0 __x64_sys_write+0x19/0x30 x64_sys_call+0x2bf/0x2660 do_syscall_64+0x93/0xb60 ? __f_unlock_pos+0x15/0x20 ? __x64_sys_getdents64+0x9b/0x130 ? __pfx_filldir64+0x10/0x10 ? do_syscall_64+0x1a2/0xb60 ? clear_bhb_loop+0x30/0x80 ? clear_bhb_loop+0x30/0x80 entry_SYSCALL_64_after_hwframe+0x76/0x7e RIP: 0033:0x76e292edd574 Code: c7 00 16 00 00 00 b8 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 80 3d d5 ea 0e 00 00 74 13 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 54 c3 0f 1f 00 55 48 89 e5 48 83 ec 20 48 89 RSP: 002b:00007fffe247a828 EFLAGS: 00000202 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 000076e292edd574 RDX: 000000000000000c RSI: 00006267f6306063 RDI: 000000000000000b RBP: 000000000000000c R08: 000076e292fc4b20 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000202 R12: 00006267f6306063 R13: 000000000000000b R14: 00006267e6859c00 R15: 000076e29322a000 </TASK> xe 0000:03:00.0: [drm] Xe device coredump has been deleted. Fixes: 01daccf ("devcoredump : Serialize devcd_del work") Cc: Mukesh Ojha <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Cc: Johannes Berg <[email protected]> Cc: Rafael J. Wysocki <[email protected]> Cc: Danilo Krummrich <[email protected]> Cc: [email protected] Cc: [email protected] # v6.1+ Signed-off-by: Maarten Lankhorst <[email protected]> Cc: Matthew Brost <[email protected]> Acked-by: Mukesh Ojha <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]> [ replaced disable_delayed_work_sync() with cancel_delayed_work_sync() ] Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 7c691e1 commit b89e20a

File tree

1 file changed

+84
-54
lines changed

1 file changed

+84
-54
lines changed

drivers/base/devcoredump.c

Lines changed: 84 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -26,50 +26,46 @@ struct devcd_entry {
2626
void *data;
2727
size_t datalen;
2828
/*
29-
* Here, mutex is required to serialize the calls to del_wk work between
30-
* user/kernel space which happens when devcd is added with device_add()
31-
* and that sends uevent to user space. User space reads the uevents,
32-
* and calls to devcd_data_write() which try to modify the work which is
33-
* not even initialized/queued from devcoredump.
29+
* There are 2 races for which mutex is required.
3430
*
31+
* The first race is between device creation and userspace writing to
32+
* schedule immediately destruction.
3533
*
34+
* This race is handled by arming the timer before device creation, but
35+
* when device creation fails the timer still exists.
3636
*
37-
* cpu0(X) cpu1(Y)
37+
* To solve this, hold the mutex during device_add(), and set
38+
* init_completed on success before releasing the mutex.
3839
*
39-
* dev_coredump() uevent sent to user space
40-
* device_add() ======================> user space process Y reads the
41-
* uevents writes to devcd fd
42-
* which results into writes to
40+
* That way the timer will never fire until device_add() is called,
41+
* it will do nothing if init_completed is not set. The timer is also
42+
* cancelled in that case.
4343
*
44-
* devcd_data_write()
45-
* mod_delayed_work()
46-
* try_to_grab_pending()
47-
* del_timer()
48-
* debug_assert_init()
49-
* INIT_DELAYED_WORK()
50-
* schedule_delayed_work()
51-
*
52-
*
53-
* Also, mutex alone would not be enough to avoid scheduling of
54-
* del_wk work after it get flush from a call to devcd_free()
55-
* mentioned as below.
56-
*
57-
* disabled_store()
58-
* devcd_free()
59-
* mutex_lock() devcd_data_write()
60-
* flush_delayed_work()
61-
* mutex_unlock()
62-
* mutex_lock()
63-
* mod_delayed_work()
64-
* mutex_unlock()
65-
* So, delete_work flag is required.
44+
* The second race involves multiple parallel invocations of devcd_free(),
45+
* add a deleted flag so only 1 can call the destructor.
6646
*/
6747
struct mutex mutex;
68-
bool delete_work;
48+
bool init_completed, deleted;
6949
struct module *owner;
7050
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
7151
void *data, size_t datalen);
7252
void (*free)(void *data);
53+
/*
54+
* If nothing interferes and device_add() was returns success,
55+
* del_wk will destroy the device after the timer fires.
56+
*
57+
* Multiple userspace processes can interfere in the working of the timer:
58+
* - Writing to the coredump will reschedule the timer to run immediately,
59+
* if still armed.
60+
*
61+
* This is handled by using "if (cancel_delayed_work()) {
62+
* schedule_delayed_work() }", to prevent re-arming after having
63+
* been previously fired.
64+
* - Writing to /sys/class/devcoredump/disabled will destroy the
65+
* coredump synchronously.
66+
* This is handled by using disable_delayed_work_sync(), and then
67+
* checking if deleted flag is set with &devcd->mutex held.
68+
*/
7369
struct delayed_work del_wk;
7470
struct device *failing_dev;
7571
};
@@ -98,14 +94,27 @@ static void devcd_dev_release(struct device *dev)
9894
kfree(devcd);
9995
}
10096

97+
static void __devcd_del(struct devcd_entry *devcd)
98+
{
99+
devcd->deleted = true;
100+
device_del(&devcd->devcd_dev);
101+
put_device(&devcd->devcd_dev);
102+
}
103+
101104
static void devcd_del(struct work_struct *wk)
102105
{
103106
struct devcd_entry *devcd;
107+
bool init_completed;
104108

105109
devcd = container_of(wk, struct devcd_entry, del_wk.work);
106110

107-
device_del(&devcd->devcd_dev);
108-
put_device(&devcd->devcd_dev);
111+
/* devcd->mutex serializes against dev_coredumpm_timeout */
112+
mutex_lock(&devcd->mutex);
113+
init_completed = devcd->init_completed;
114+
mutex_unlock(&devcd->mutex);
115+
116+
if (init_completed)
117+
__devcd_del(devcd);
109118
}
110119

111120
static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
@@ -125,12 +134,12 @@ static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj,
125134
struct device *dev = kobj_to_dev(kobj);
126135
struct devcd_entry *devcd = dev_to_devcd(dev);
127136

128-
mutex_lock(&devcd->mutex);
129-
if (!devcd->delete_work) {
130-
devcd->delete_work = true;
131-
mod_delayed_work(system_wq, &devcd->del_wk, 0);
132-
}
133-
mutex_unlock(&devcd->mutex);
137+
/*
138+
* Although it's tempting to use mod_delayed work here,
139+
* that will cause a reschedule if the timer already fired.
140+
*/
141+
if (cancel_delayed_work(&devcd->del_wk))
142+
schedule_delayed_work(&devcd->del_wk, 0);
134143

135144
return count;
136145
}
@@ -158,11 +167,21 @@ static int devcd_free(struct device *dev, void *data)
158167
{
159168
struct devcd_entry *devcd = dev_to_devcd(dev);
160169

170+
/*
171+
* To prevent a race with devcd_data_write(), cancel work and
172+
* complete manually instead.
173+
*
174+
* We cannot rely on the return value of
175+
* cancel_delayed_work_sync() here, because it might be in the
176+
* middle of a cancel_delayed_work + schedule_delayed_work pair.
177+
*
178+
* devcd->mutex here guards against multiple parallel invocations
179+
* of devcd_free().
180+
*/
181+
cancel_delayed_work_sync(&devcd->del_wk);
161182
mutex_lock(&devcd->mutex);
162-
if (!devcd->delete_work)
163-
devcd->delete_work = true;
164-
165-
flush_delayed_work(&devcd->del_wk);
183+
if (!devcd->deleted)
184+
__devcd_del(devcd);
166185
mutex_unlock(&devcd->mutex);
167186
return 0;
168187
}
@@ -186,12 +205,10 @@ static ssize_t disabled_show(const struct class *class, const struct class_attri
186205
* put_device() <- last reference
187206
* error = fn(dev, data) devcd_dev_release()
188207
* devcd_free(dev, data) kfree(devcd)
189-
* mutex_lock(&devcd->mutex);
190208
*
191209
*
192-
* In the above diagram, It looks like disabled_store() would be racing with parallely
193-
* running devcd_del() and result in memory abort while acquiring devcd->mutex which
194-
* is called after kfree of devcd memory after dropping its last reference with
210+
* In the above diagram, it looks like disabled_store() would be racing with parallelly
211+
* running devcd_del() and result in memory abort after dropping its last reference with
195212
* put_device(). However, this will not happens as fn(dev, data) runs
196213
* with its own reference to device via klist_node so it is not its last reference.
197214
* so, above situation would not occur.
@@ -352,7 +369,7 @@ void dev_coredumpm(struct device *dev, struct module *owner,
352369
devcd->read = read;
353370
devcd->free = free;
354371
devcd->failing_dev = get_device(dev);
355-
devcd->delete_work = false;
372+
devcd->deleted = false;
356373

357374
mutex_init(&devcd->mutex);
358375
device_initialize(&devcd->devcd_dev);
@@ -361,8 +378,14 @@ void dev_coredumpm(struct device *dev, struct module *owner,
361378
atomic_inc_return(&devcd_count));
362379
devcd->devcd_dev.class = &devcd_class;
363380

364-
mutex_lock(&devcd->mutex);
365381
dev_set_uevent_suppress(&devcd->devcd_dev, true);
382+
383+
/* devcd->mutex prevents devcd_del() completing until init finishes */
384+
mutex_lock(&devcd->mutex);
385+
devcd->init_completed = false;
386+
INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
387+
schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
388+
366389
if (device_add(&devcd->devcd_dev))
367390
goto put_device;
368391

@@ -379,13 +402,20 @@ void dev_coredumpm(struct device *dev, struct module *owner,
379402

380403
dev_set_uevent_suppress(&devcd->devcd_dev, false);
381404
kobject_uevent(&devcd->devcd_dev.kobj, KOBJ_ADD);
382-
INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
383-
schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
405+
406+
/*
407+
* Safe to run devcd_del() now that we are done with devcd_dev.
408+
* Alternatively we could have taken a ref on devcd_dev before
409+
* dropping the lock.
410+
*/
411+
devcd->init_completed = true;
384412
mutex_unlock(&devcd->mutex);
385413
return;
386414
put_device:
387-
put_device(&devcd->devcd_dev);
388415
mutex_unlock(&devcd->mutex);
416+
cancel_delayed_work_sync(&devcd->del_wk);
417+
put_device(&devcd->devcd_dev);
418+
389419
put_module:
390420
module_put(owner);
391421
free:

0 commit comments

Comments
 (0)