• Mikulas Patocka's avatar
    dm crypt: fix a possible hang due to race condition on exit · bcbd94ff
    Mikulas Patocka authored
    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: dc267621 ("dm crypt: offload writes to thread")
    Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
    Cc: stable@vger.kernel.org # v4.0+
    Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
    bcbd94ff
dm-crypt.c 50.4 KB