Skip to content

Commit

Permalink
NFC: llcp: Fix Rx memory leak
Browse files Browse the repository at this point in the history
The reference count bump on the llcp Rx path is leading to a memory leak
whenever we're not receiving an I frame.
We fix that by removing the refcount bump (drivers must not free their
received skb) and using it only in the I frame path, when the frame is
actually queued. In that case, the skb will only be freed when someone
fetches it from userspace. in all other cases, LLCP received frames will
be freed when leaving the Rx work queue.

Reported-by: Eric Lapuyade <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
  • Loading branch information
Samuel Ortiz committed Jan 9, 2013
1 parent 2593c2c commit 1727cf9
Showing 1 changed file with 25 additions and 10 deletions.
35 changes: 25 additions & 10 deletions net/nfc/llcp/llcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -782,8 +782,14 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,

/* There is no sequence with UI frames */
skb_pull(skb, LLCP_HEADER_SIZE);
if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
pr_err("receive queue is full\n");
if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
/*
* UI frames will be freed from the socket layer, so we
* need to keep them alive until someone receives them.
*/
skb_get(skb);
} else {
pr_err("Receive queue is full\n");
kfree_skb(skb);
}

Expand Down Expand Up @@ -977,8 +983,14 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
pr_err("Received out of sequence I PDU\n");

skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE);
if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
pr_err("receive queue is full\n");
if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
/*
* I frames will be freed from the socket layer, so we
* need to keep them alive until someone receives them.
*/
skb_get(skb);
} else {
pr_err("Receive queue is full\n");
kfree_skb(skb);
}
}
Expand Down Expand Up @@ -1299,6 +1311,13 @@ static void nfc_llcp_rx_work(struct work_struct *work)
local->rx_pending = NULL;
}

static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
{
local->rx_pending = skb;
del_timer(&local->link_timer);
schedule_work(&local->rx_work);
}

void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
{
struct nfc_llcp_local *local = (struct nfc_llcp_local *) data;
Expand All @@ -1309,9 +1328,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
return;
}

local->rx_pending = skb_get(skb);
del_timer(&local->link_timer);
schedule_work(&local->rx_work);
__nfc_llcp_recv(local, skb);
}

int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
Expand All @@ -1322,9 +1339,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
if (local == NULL)
return -ENODEV;

local->rx_pending = skb_get(skb);
del_timer(&local->link_timer);
schedule_work(&local->rx_work);
__nfc_llcp_recv(local, skb);

return 0;
}
Expand Down

0 comments on commit 1727cf9

Please sign in to comment.