Skip to content

Commit

Permalink
nfc: pn533: Fix buggy cleanup order
Browse files Browse the repository at this point in the history
[ Upstream commit b8cedb7 ]

When removing the pn533 device (i2c or USB), there is a logic error. The
original code first cancels the worker (flush_delayed_work) and then
destroys the workqueue (destroy_workqueue), leaving the timer the last
one to be deleted (del_timer). This result in a possible race condition
in a multi-core preempt-able kernel. That is, if the cleanup
(pn53x_common_clean) is concurrently run with the timer handler
(pn533_listen_mode_timer), the timer can queue the poll_work to the
already destroyed workqueue, causing use-after-free.

This patch reorder the cleanup: it uses the del_timer_sync to make sure
the handler is finished before the routine will destroy the workqueue.
Note that the timer cannot be activated by the worker again.

static void pn533_wq_poll(struct work_struct *work)
...
 rc = pn533_send_poll_frame(dev);
 if (rc)
   return;

 if (cur_mod->len == 0 && dev->poll_mod_count > 1)
   mod_timer(&dev->listen_timer, ...);

That is, the mod_timer can be called only when pn533_send_poll_frame()
returns no error, which is impossible because the device is detaching
and the lower driver should return ENODEV code.

Signed-off-by: Lin Ma <linma@zju.edu.cn>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
f0rm2l1n authored and gregkh committed Jun 6, 2022
1 parent 7e18fd1 commit 4965149
Showing 1 changed file with 3 additions and 2 deletions.
5 changes: 3 additions & 2 deletions drivers/nfc/pn533/pn533.c
Original file line number Diff line number Diff line change
Expand Up @@ -2789,13 +2789,14 @@ void pn53x_common_clean(struct pn533 *priv)
{
struct pn533_cmd *cmd, *n;

/* delete the timer before cleanup the worker */
del_timer_sync(&priv->listen_timer);

flush_delayed_work(&priv->poll_work);
destroy_workqueue(priv->wq);

skb_queue_purge(&priv->resp_q);

del_timer(&priv->listen_timer);

list_for_each_entry_safe(cmd, n, &priv->cmd_queue, queue) {
list_del(&cmd->queue);
kfree(cmd);
Expand Down

0 comments on commit 4965149

Please sign in to comment.