A deadlock can occur when the NFC raw socket is closed while the driver is
processing a command.

Following is the call graph of the affected situation:

send data via raw_sock:
-------------
rawsock_tx_work
  sock_hold                                             => socket refcnt++
  nfc_data_exchange                                     => cb = 
rawsock_data_exchange_complete
    ops->im_transceive = pn533_transceive               => arg->cb = db = 
rawsock_data_exchange_complete
      pn533_send_data_async                             => cb = 
pn533_data_exchange_complete
        __pn533_send_async                              => cmd->complete_cb = 
cb = pn533_data_exchange_complete
          if_ops->send_frame_async

response:
--------
pn533_recv_response
  queue_work(priv->wq, &priv->cmd_complete_work)

pn533_wq_cmd_complete
  pn533_send_async_complete
    cmd->complete_cb() = pn533_data_exchange_complete()
      arg->cb() = rawsock_data_exchange_complete()
        sock_put                                                         => 
socket refcnt-- => If the corresponding socket gets closed in the meantime 
socket will be destructed
          sk_free
            __sk_free
              sk->sk_destruct = rawsock_destruct
                nfc_deactivate_target
                  ops->deactivate_target = pn533_deactivate_target
                    pn533_send_cmd_sync
                      pn533_send_cmd_async
                        __pn533_send_async
                          list_add_tail(&cmd->queue, &dev->cmd_queue)    => add 
to command list because a command is currently processed
                        wait_for_completion                              => the 
workqueue thread waits here because it is the one processing the commands => 
deadlock

To fix the deadlock pn533_deactivate_target is changed to issue the
PN533_CMD_IN_RELEASE command in async mode. This way nothing blocks and the
release command is executed after the current command.

Signed-off-by: Michael Thalmeier <[email protected]>
---
 drivers/nfc/pn533.c | 40 ++++++++++++++++++++++++++++++----------
 1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
index a85830f..074f1e4 100644
--- a/drivers/nfc/pn533.c
+++ b/drivers/nfc/pn533.c
@@ -2263,12 +2263,35 @@ static int pn533_activate_target(struct nfc_dev 
*nfc_dev,
        return 0;
 }
 
+static int pn533_deactivate_target_complete(struct pn533 *dev, void *arg,
+                            struct sk_buff *resp)
+{
+       int rc = 0;
+
+       dev_dbg(&dev->interface->dev, "%s\n", __func__);
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+
+               nfc_err(&dev->interface->dev, "Target release error %d\n", rc);
+
+               return rc;
+       }
+
+       rc = resp->data[0] & PN533_CMD_RET_MASK;
+       if (rc != PN533_CMD_RET_SUCCESS)
+               nfc_err(&dev->interface->dev,
+                       "Error 0x%x when releasing the target\n", rc);
+
+       dev_kfree_skb(resp);
+       return rc;
+}
+
 static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
                                    struct nfc_target *target, u8 mode)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        struct sk_buff *skb;
-       struct sk_buff *resp;
        int rc;
 
        dev_dbg(&dev->interface->dev, "%s\n", __func__);
@@ -2287,16 +2310,13 @@ static void pn533_deactivate_target(struct nfc_dev 
*nfc_dev,
 
        *skb_put(skb, 1) = 1; /* TG*/
 
-       resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_RELEASE, skb);
-       if (IS_ERR(resp))
-               return;
-
-       rc = resp->data[0] & PN533_CMD_RET_MASK;
-       if (rc != PN533_CMD_RET_SUCCESS)
-               nfc_err(&dev->interface->dev,
-                       "Error 0x%x when releasing the target\n", rc);
+       rc = pn533_send_cmd_async(dev, PN533_CMD_IN_RELEASE, skb,
+                                 pn533_deactivate_target_complete, NULL);
+       if (rc < 0) {
+               dev_kfree_skb(skb);
+               nfc_err(&dev->interface->dev, "Target release error %d\n", rc);
+       }
 
-       dev_kfree_skb(resp);
        return;
 }
 
-- 
2.5.5

Reply via email to