Skip to content

Commit

Permalink
llc: fix sk_buff refcounting in llc_conn_state_process()
Browse files Browse the repository at this point in the history
If llc_conn_state_process() sees that llc_conn_service() put the skb on
a list, it will drop one fewer references to it.  This is wrong because
the current behavior is that llc_conn_service() never consumes a
reference to the skb.

The code also makes the number of skb references being dropped
conditional on which of ind_prim and cfm_prim are nonzero, yet neither
of these affects how many references are *acquired*.  So there is extra
code that tries to fix this up by sometimes taking another reference.

Remove the unnecessary/broken refcounting logic and instead just add an
skb_get() before the only two places where an extra reference is
actually consumed.

Fixes: 1da177e ("Linux-2.6.12-rc2")
Signed-off-by: Eric Biggers <[email protected]>
Signed-off-by: Jakub Kicinski <[email protected]>
  • Loading branch information
ebiggers authored and Jakub Kicinski committed Oct 8, 2019
1 parent fc8d5db commit 36453c8
Showing 1 changed file with 6 additions and 27 deletions.
33 changes: 6 additions & 27 deletions net/llc/llc_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,34 +64,19 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
struct llc_sock *llc = llc_sk(skb->sk);
struct llc_conn_state_ev *ev = llc_conn_ev(skb);

/*
* We have to hold the skb, because llc_conn_service will kfree it in
* the sending path and we need to look at the skb->cb, where we encode
* llc_conn_state_ev.
*/
skb_get(skb);
ev->ind_prim = ev->cfm_prim = 0;
/*
* Send event to state machine
*/
rc = llc_conn_service(skb->sk, skb);
if (unlikely(rc != 0)) {
printk(KERN_ERR "%s: llc_conn_service failed\n", __func__);
goto out_kfree_skb;
}

if (unlikely(!ev->ind_prim && !ev->cfm_prim)) {
/* indicate or confirm not required */
if (!skb->next)
goto out_kfree_skb;
goto out_skb_put;
}

if (unlikely(ev->ind_prim && ev->cfm_prim)) /* Paranoia */
skb_get(skb);

switch (ev->ind_prim) {
case LLC_DATA_PRIM:
skb_get(skb);
llc_save_primitive(sk, skb, LLC_DATA_PRIM);
if (unlikely(sock_queue_rcv_skb(sk, skb))) {
/*
Expand All @@ -108,6 +93,7 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
* skb->sk pointing to the newly created struct sock in
* llc_conn_handler. -acme
*/
skb_get(skb);
skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_state_change(sk);
break;
Expand All @@ -123,7 +109,6 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
sk->sk_state_change(sk);
}
}
kfree_skb(skb);
sock_put(sk);
break;
case LLC_RESET_PRIM:
Expand All @@ -132,14 +117,11 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
* RESET is not being notified to upper layers for now
*/
printk(KERN_INFO "%s: received a reset ind!\n", __func__);
kfree_skb(skb);
break;
default:
if (ev->ind_prim) {
if (ev->ind_prim)
printk(KERN_INFO "%s: received unknown %d prim!\n",
__func__, ev->ind_prim);
kfree_skb(skb);
}
/* No indication */
break;
}
Expand Down Expand Up @@ -181,15 +163,12 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
printk(KERN_INFO "%s: received a reset conf!\n", __func__);
break;
default:
if (ev->cfm_prim) {
if (ev->cfm_prim)
printk(KERN_INFO "%s: received unknown %d prim!\n",
__func__, ev->cfm_prim);
break;
}
goto out_skb_put; /* No confirmation */
/* No confirmation */
break;
}
out_kfree_skb:
kfree_skb(skb);
out_skb_put:
kfree_skb(skb);
return rc;
Expand Down

0 comments on commit 36453c8

Please sign in to comment.