thread: Hold a task_struct reference across kthread_stop() - #35
Open
antoniovazquezblanco wants to merge 1 commit into
Open
thread: Hold a task_struct reference across kthread_stop()#35antoniovazquezblanco wants to merge 1 commit into
antoniovazquezblanco wants to merge 1 commit into
Conversation
ipts_thread_runner() signals thread->done and then returns, so the kthread
exits and gets reaped as soon as the thread function is done. ipts_thread_stop()
waits for that completion and only then calls kthread_stop(), by which point
nothing holds a reference to the task_struct any more. If the stopping task is
preempted in that window, kthread_stop() operates on freed memory.
kthread_stop()'s documentation states the requirement:
If threadfn() may call kthread_exit() itself, the caller must ensure
task_struct can't go away.
which is always the case here, since the thread function returns on its own
rather than waiting for kthread_should_stop(). Waiting for the completion first
is what makes the wrapper safe with respect to MEI commands issued during
shutdown, but it also guarantees the thread has already exited, so the wrapper
hits this race by design rather than avoiding it.
Observed on a Surface Pro 4 (EDS v1) while iptsd switched the digitizer into
multitouch mode, which calls ipts_control_restart():
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x6a/0x90, CPU#3: iptsd/395
kthread_stop+0x18b/0x190
ipts_thread_stop+0x32/0x60 [ipts]
ipts_receiver_stop+0x22/0x40 [ipts]
_ipts_control_stop.part.0+0x29/0x50 [ipts]
ipts_control_restart+0x21/0x40 [ipts]
ipts_eds1_raw_request+0x49/0x90 [ipts]
refcount_t: underflow; use-after-free.
WARNING: kernel/fork.c:784 at __put_task_struct+0x129/0x1a0, CPU#2: libinput-device/406
Split kthread_run() into kthread_create() + wake_up_process() so that a
reference can be taken while the thread is still guaranteed to exist, and
release it with kthread_stop_put().
While at it, stop leaving an error pointer in thread->thread when thread
creation fails. ipts_thread_stop() only tests for NULL, so it would hand that
error pointer to kthread_stop().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ipts_thread_runner() signals thread->done and then returns, so the kthread exits and gets reaped as soon as the thread function is done. ipts_thread_stop() waits for that completion and only then calls kthread_stop(), by which point nothing holds a reference to the task_struct any more. If the stopping task is preempted in that window, kthread_stop() operates on freed memory.
kthread_stop()'s documentation states the requirement:
which is always the case here, since the thread function returns on its own rather than waiting for kthread_should_stop(). Waiting for the completion first is what makes the wrapper safe with respect to MEI commands issued during shutdown, but it also guarantees the thread has already exited, so the wrapper hits this race by design rather than avoiding it.
Observed on a Surface Pro 4 (EDS v1) while iptsd switched the digitizer into multitouch mode, which calls ipts_control_restart():
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x6a/0x90, CPU#3: iptsd/395
kthread_stop+0x18b/0x190
ipts_thread_stop+0x32/0x60 [ipts]
ipts_receiver_stop+0x22/0x40 [ipts]
_ipts_control_stop.part.0+0x29/0x50 [ipts]
ipts_control_restart+0x21/0x40 [ipts]
ipts_eds1_raw_request+0x49/0x90 [ipts]
refcount_t: underflow; use-after-free.
WARNING: kernel/fork.c:784 at __put_task_struct+0x129/0x1a0, CPU#2: libinput-device/406
Split kthread_run() into kthread_create() + wake_up_process() so that a reference can be taken while the thread is still guaranteed to exist, and release it with kthread_stop_put().
While at it, stop leaving an error pointer in thread->thread when thread creation fails. ipts_thread_stop() only tests for NULL, so it would hand that error pointer to kthread_stop().