]> git.ipfire.org Git - thirdparty/kernel/stable.git/commit
staging: comedi: comedi_test: fix timer race conditions
authorIan Abbott <abbotti@mev.co.uk>
Wed, 7 Sep 2016 15:35:32 +0000 (11:35 -0400)
committerSasha Levin <alexander.levin@verizon.com>
Wed, 7 Sep 2016 18:37:59 +0000 (14:37 -0400)
commit871499e342fc0fe42e7dbef00fb680bef217c1c5
tree46c9d25615f44f479f5b0d7b3566b8f6935f7873
parent3b60b86aec06fbae1142ccc4e55b39b529ae2a25
staging: comedi: comedi_test: fix timer race conditions

[ commit 403fe7f34e3327ddac2e06a15e76a293d613381e upstream.

  Patch is cut down a bit from upstream patch, as upstream patch fixed
  parts that don't exist in this version.  Also, some identifiers were
  renamed, so patch description has been edited accordingly
  -- Ian Abbott
]

Commit 73e0e4dfed4c ("staging: comedi: comedi_test: fix timer lock-up")
fixed a lock-up in the timer routine `waveform_ai_interrupt()` caused by
commit 240512474424 ("staging: comedi: comedi_test: use
comedi_handle_events()").  However, it introduced a race condition that
can result in the timer routine misbehaving, such as accessing freed
memory or dereferencing a NULL pointer.

73e0... changed the timer routine to do nothing unless a
`WAVEFORM_AI_RUNNING` flag was set, and changed `waveform_ai_cancel()`
to clear the flag and replace a call to `del_timer_sync()` with a call
to `del_timer()`.  `waveform_ai_cancel()` may be called from the timer
routine itself (via `comedi_handle_events()`), or from `do_cancel()`.
(`do_cancel()` is called as a result of a file operation (usually a
`COMEDI_CANCEL` ioctl command, or a release), or during device removal.)
When called from `do_cancel()`, the call to `waveform_ai_cancel()` is
followed by a call to `do_become_nonbusy()`, which frees up stuff for
the current asynchronous command under the assumption that it is now
safe to do so.  The race condition occurs when the timer routine
`waveform_ai_interrupt()` checks the `WAVEFORM_AI_RUNNING` flag just
before it is cleared by `waveform_ai_cancel()`, and is still running
during the call to `do_become_nonbusy()`.  In particular, it can lead to
a NULL pointer dereference because `do_become_nonbusy()` frees
`async->cmd.chanlist` and sets it to `NULL`, but
`waveform_ai_interrupt()` dereferences it.

Fix the race by calling `del_timer_sync()` instead of `del_timer()` in
`waveform_ai_cancel()` when not in an interrupt context.  The only time
`waveform_ai_cancel()` is called in an interrupt context is when it is
called from the timer routine itself, via `comedi_handle_events()`.

There is no longer any need for the `WAVEFORM_AI_RUNNING` flag, so get
rid of it.

Fixes: 73e0e4dfed4c ("staging: comedi: comedi_test: fix timer lock-up")
Reported-by: Éric Piel <piel@delmic.com>
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Cc: Éric Piel <piel@delmic.com>
Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
drivers/staging/comedi/drivers/comedi_test.c