diff options
author | Eric Biggers <ebiggers@google.com> | 2019-10-06 17:24:27 -0400 |
---|---|---|
committer | Jakub Kicinski <jakub.kicinski@netronome.com> | 2019-10-08 16:23:05 -0400 |
commit | 36453c852816f19947ca482a595dffdd2efa4965 (patch) | |
tree | f0204d988d31cc5bc60a8e66c208a971dc48917a | |
parent | fc8d5db10cbe1338a52ebc74e7feab9276721774 (diff) |
llc: fix sk_buff refcounting in llc_conn_state_process()
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: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
-rw-r--r-- | net/llc/llc_conn.c | 33 |
1 files changed, 6 insertions, 27 deletions
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 0b0c6f12153b..a79b739eb223 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c | |||
@@ -64,12 +64,6 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) | |||
64 | struct llc_sock *llc = llc_sk(skb->sk); | 64 | struct llc_sock *llc = llc_sk(skb->sk); |
65 | struct llc_conn_state_ev *ev = llc_conn_ev(skb); | 65 | struct llc_conn_state_ev *ev = llc_conn_ev(skb); |
66 | 66 | ||
67 | /* | ||
68 | * We have to hold the skb, because llc_conn_service will kfree it in | ||
69 | * the sending path and we need to look at the skb->cb, where we encode | ||
70 | * llc_conn_state_ev. | ||
71 | */ | ||
72 | skb_get(skb); | ||
73 | ev->ind_prim = ev->cfm_prim = 0; | 67 | ev->ind_prim = ev->cfm_prim = 0; |
74 | /* | 68 | /* |
75 | * Send event to state machine | 69 | * Send event to state machine |
@@ -77,21 +71,12 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) | |||
77 | rc = llc_conn_service(skb->sk, skb); | 71 | rc = llc_conn_service(skb->sk, skb); |
78 | if (unlikely(rc != 0)) { | 72 | if (unlikely(rc != 0)) { |
79 | printk(KERN_ERR "%s: llc_conn_service failed\n", __func__); | 73 | printk(KERN_ERR "%s: llc_conn_service failed\n", __func__); |
80 | goto out_kfree_skb; | ||
81 | } | ||
82 | |||
83 | if (unlikely(!ev->ind_prim && !ev->cfm_prim)) { | ||
84 | /* indicate or confirm not required */ | ||
85 | if (!skb->next) | ||
86 | goto out_kfree_skb; | ||
87 | goto out_skb_put; | 74 | goto out_skb_put; |
88 | } | 75 | } |
89 | 76 | ||
90 | if (unlikely(ev->ind_prim && ev->cfm_prim)) /* Paranoia */ | ||
91 | skb_get(skb); | ||
92 | |||
93 | switch (ev->ind_prim) { | 77 | switch (ev->ind_prim) { |
94 | case LLC_DATA_PRIM: | 78 | case LLC_DATA_PRIM: |
79 | skb_get(skb); | ||
95 | llc_save_primitive(sk, skb, LLC_DATA_PRIM); | 80 | llc_save_primitive(sk, skb, LLC_DATA_PRIM); |
96 | if (unlikely(sock_queue_rcv_skb(sk, skb))) { | 81 | if (unlikely(sock_queue_rcv_skb(sk, skb))) { |
97 | /* | 82 | /* |
@@ -108,6 +93,7 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) | |||
108 | * skb->sk pointing to the newly created struct sock in | 93 | * skb->sk pointing to the newly created struct sock in |
109 | * llc_conn_handler. -acme | 94 | * llc_conn_handler. -acme |
110 | */ | 95 | */ |
96 | skb_get(skb); | ||
111 | skb_queue_tail(&sk->sk_receive_queue, skb); | 97 | skb_queue_tail(&sk->sk_receive_queue, skb); |
112 | sk->sk_state_change(sk); | 98 | sk->sk_state_change(sk); |
113 | break; | 99 | break; |
@@ -123,7 +109,6 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) | |||
123 | sk->sk_state_change(sk); | 109 | sk->sk_state_change(sk); |
124 | } | 110 | } |
125 | } | 111 | } |
126 | kfree_skb(skb); | ||
127 | sock_put(sk); | 112 | sock_put(sk); |
128 | break; | 113 | break; |
129 | case LLC_RESET_PRIM: | 114 | case LLC_RESET_PRIM: |
@@ -132,14 +117,11 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) | |||
132 | * RESET is not being notified to upper layers for now | 117 | * RESET is not being notified to upper layers for now |
133 | */ | 118 | */ |
134 | printk(KERN_INFO "%s: received a reset ind!\n", __func__); | 119 | printk(KERN_INFO "%s: received a reset ind!\n", __func__); |
135 | kfree_skb(skb); | ||
136 | break; | 120 | break; |
137 | default: | 121 | default: |
138 | if (ev->ind_prim) { | 122 | if (ev->ind_prim) |
139 | printk(KERN_INFO "%s: received unknown %d prim!\n", | 123 | printk(KERN_INFO "%s: received unknown %d prim!\n", |
140 | __func__, ev->ind_prim); | 124 | __func__, ev->ind_prim); |
141 | kfree_skb(skb); | ||
142 | } | ||
143 | /* No indication */ | 125 | /* No indication */ |
144 | break; | 126 | break; |
145 | } | 127 | } |
@@ -181,15 +163,12 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) | |||
181 | printk(KERN_INFO "%s: received a reset conf!\n", __func__); | 163 | printk(KERN_INFO "%s: received a reset conf!\n", __func__); |
182 | break; | 164 | break; |
183 | default: | 165 | default: |
184 | if (ev->cfm_prim) { | 166 | if (ev->cfm_prim) |
185 | printk(KERN_INFO "%s: received unknown %d prim!\n", | 167 | printk(KERN_INFO "%s: received unknown %d prim!\n", |
186 | __func__, ev->cfm_prim); | 168 | __func__, ev->cfm_prim); |
187 | break; | 169 | /* No confirmation */ |
188 | } | 170 | break; |
189 | goto out_skb_put; /* No confirmation */ | ||
190 | } | 171 | } |
191 | out_kfree_skb: | ||
192 | kfree_skb(skb); | ||
193 | out_skb_put: | 172 | out_skb_put: |
194 | kfree_skb(skb); | 173 | kfree_skb(skb); |
195 | return rc; | 174 | return rc; |