Skip to content

thread: Hold a task_struct reference across kthread_stop() - #35

Open
antoniovazquezblanco wants to merge 1 commit into
linux-surface:masterfrom
antoniovazquezblanco:fix/thread-kthread-stop-uaf
Open

thread: Hold a task_struct reference across kthread_stop()#35
antoniovazquezblanco wants to merge 1 commit into
linux-surface:masterfrom
antoniovazquezblanco:fix/thread-kthread-stop-uaf

Conversation

@antoniovazquezblanco

Copy link
Copy Markdown

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().

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().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant