Skip to content

Commit bcbd94f

Browse files
Mikulas Patockasnitm
Mikulas Patocka
authored andcommitted
dm crypt: fix a possible hang due to race condition on exit
A kernel thread executes __set_current_state(TASK_INTERRUPTIBLE), __add_wait_queue, spin_unlock_irq and then tests kthread_should_stop(). It is possible that the processor reorders memory accesses so that kthread_should_stop() is executed before __set_current_state(). If such reordering happens, there is a possible race on thread termination: CPU 0: calls kthread_should_stop() it tests KTHREAD_SHOULD_STOP bit, returns false CPU 1: calls kthread_stop(cc->write_thread) sets the KTHREAD_SHOULD_STOP bit calls wake_up_process on the kernel thread, that sets the thread state to TASK_RUNNING CPU 0: sets __set_current_state(TASK_INTERRUPTIBLE) spin_unlock_irq(&cc->write_thread_wait.lock) schedule() - and the process is stuck and never terminates, because the state is TASK_INTERRUPTIBLE and wake_up_process on CPU 1 already terminated Fix this race condition by using a new flag DM_CRYPT_EXIT_THREAD to signal that the kernel thread should exit. The flag is set and tested while holding cc->write_thread_wait.lock, so there is no possibility of racy access to the flag. Also, remove the unnecessary set_task_state(current, TASK_RUNNING) following the schedule() call. When the process was woken up, its state was already set to TASK_RUNNING. Other kernel code also doesn't set the state to TASK_RUNNING following schedule() (for example, do_wait_for_common in completion.c doesn't do it). Fixes: dc26762 ("dm crypt: offload writes to thread") Signed-off-by: Mikulas Patocka <[email protected]> Cc: [email protected] # v4.0+ Signed-off-by: Mike Snitzer <[email protected]>
1 parent 43e43c9 commit bcbd94f

File tree

1 file changed

+13
-9
lines changed

1 file changed

+13
-9
lines changed

drivers/md/dm-crypt.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ struct iv_tcw_private {
112112
* and encrypts / decrypts at the same time.
113113
*/
114114
enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
115-
DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD };
115+
DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD,
116+
DM_CRYPT_EXIT_THREAD};
116117

117118
/*
118119
* The fields in here must be read only after initialization.
@@ -1203,20 +1204,18 @@ static int dmcrypt_write(void *data)
12031204
if (!RB_EMPTY_ROOT(&cc->write_tree))
12041205
goto pop_from_list;
12051206

1207+
if (unlikely(test_bit(DM_CRYPT_EXIT_THREAD, &cc->flags))) {
1208+
spin_unlock_irq(&cc->write_thread_wait.lock);
1209+
break;
1210+
}
1211+
12061212
__set_current_state(TASK_INTERRUPTIBLE);
12071213
__add_wait_queue(&cc->write_thread_wait, &wait);
12081214

12091215
spin_unlock_irq(&cc->write_thread_wait.lock);
12101216

1211-
if (unlikely(kthread_should_stop())) {
1212-
set_task_state(current, TASK_RUNNING);
1213-
remove_wait_queue(&cc->write_thread_wait, &wait);
1214-
break;
1215-
}
1216-
12171217
schedule();
12181218

1219-
set_task_state(current, TASK_RUNNING);
12201219
spin_lock_irq(&cc->write_thread_wait.lock);
12211220
__remove_wait_queue(&cc->write_thread_wait, &wait);
12221221
goto continue_locked;
@@ -1531,8 +1530,13 @@ static void crypt_dtr(struct dm_target *ti)
15311530
if (!cc)
15321531
return;
15331532

1534-
if (cc->write_thread)
1533+
if (cc->write_thread) {
1534+
spin_lock_irq(&cc->write_thread_wait.lock);
1535+
set_bit(DM_CRYPT_EXIT_THREAD, &cc->flags);
1536+
wake_up_locked(&cc->write_thread_wait);
1537+
spin_unlock_irq(&cc->write_thread_wait.lock);
15351538
kthread_stop(cc->write_thread);
1539+
}
15361540

15371541
if (cc->io_queue)
15381542
destroy_workqueue(cc->io_queue);

0 commit comments

Comments
 (0)