aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean Tranchetti <stranche@codeaurora.org>2019-02-07 15:33:21 -0500
committerSteffen Klassert <steffen.klassert@secunet.com>2019-02-12 04:36:42 -0500
commitfc2d5cfdcfe2ab76b263d91429caa22451123085 (patch)
tree7c4734b77fbf909198e64d2e5cbbd7d93916b74f
parentf75a2804da391571563c4b6b29e7797787332673 (diff)
af_key: unconditionally clone on broadcast
Attempting to avoid cloning the skb when broadcasting by inflating the refcount with sock_hold/sock_put while under RCU lock is dangerous and violates RCU principles. It leads to subtle race conditions when attempting to free the SKB, as we may reference sockets that have already been freed by the stack. Unable to handle kernel paging request at virtual address 6b6b6b6b6b6c4b [006b6b6b6b6b6c4b] address between user and kernel address ranges Internal error: Oops: 96000004 [#1] PREEMPT SMP task: fffffff78f65b380 task.stack: ffffff8049a88000 pc : sock_rfree+0x38/0x6c lr : skb_release_head_state+0x6c/0xcc Process repro (pid: 7117, stack limit = 0xffffff8049a88000) Call trace: sock_rfree+0x38/0x6c skb_release_head_state+0x6c/0xcc skb_release_all+0x1c/0x38 __kfree_skb+0x1c/0x30 kfree_skb+0xd0/0xf4 pfkey_broadcast+0x14c/0x18c pfkey_sendmsg+0x1d8/0x408 sock_sendmsg+0x44/0x60 ___sys_sendmsg+0x1d0/0x2a8 __sys_sendmsg+0x64/0xb4 SyS_sendmsg+0x34/0x4c el0_svc_naked+0x34/0x38 Kernel panic - not syncing: Fatal exception Suggested-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: Sean Tranchetti <stranche@codeaurora.org> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
-rw-r--r--net/key/af_key.c40
1 files changed, 15 insertions, 25 deletions
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 637030f43b67..5651c29cb5bd 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -196,30 +196,22 @@ static int pfkey_release(struct socket *sock)
196 return 0; 196 return 0;
197} 197}
198 198
199static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2, 199static int pfkey_broadcast_one(struct sk_buff *skb, gfp_t allocation,
200 gfp_t allocation, struct sock *sk) 200 struct sock *sk)
201{ 201{
202 int err = -ENOBUFS; 202 int err = -ENOBUFS;
203 203
204 sock_hold(sk); 204 if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf)
205 if (*skb2 == NULL) { 205 return err;
206 if (refcount_read(&skb->users) != 1) { 206
207 *skb2 = skb_clone(skb, allocation); 207 skb = skb_clone(skb, allocation);
208 } else { 208
209 *skb2 = skb; 209 if (skb) {
210 refcount_inc(&skb->users); 210 skb_set_owner_r(skb, sk);
211 } 211 skb_queue_tail(&sk->sk_receive_queue, skb);
212 } 212 sk->sk_data_ready(sk);
213 if (*skb2 != NULL) { 213 err = 0;
214 if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
215 skb_set_owner_r(*skb2, sk);
216 skb_queue_tail(&sk->sk_receive_queue, *skb2);
217 sk->sk_data_ready(sk);
218 *skb2 = NULL;
219 err = 0;
220 }
221 } 214 }
222 sock_put(sk);
223 return err; 215 return err;
224} 216}
225 217
@@ -234,7 +226,6 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
234{ 226{
235 struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); 227 struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
236 struct sock *sk; 228 struct sock *sk;
237 struct sk_buff *skb2 = NULL;
238 int err = -ESRCH; 229 int err = -ESRCH;
239 230
240 /* XXX Do we need something like netlink_overrun? I think 231 /* XXX Do we need something like netlink_overrun? I think
@@ -253,7 +244,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
253 * socket. 244 * socket.
254 */ 245 */
255 if (pfk->promisc) 246 if (pfk->promisc)
256 pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk); 247 pfkey_broadcast_one(skb, GFP_ATOMIC, sk);
257 248
258 /* the exact target will be processed later */ 249 /* the exact target will be processed later */
259 if (sk == one_sk) 250 if (sk == one_sk)
@@ -268,7 +259,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
268 continue; 259 continue;
269 } 260 }
270 261
271 err2 = pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk); 262 err2 = pfkey_broadcast_one(skb, GFP_ATOMIC, sk);
272 263
273 /* Error is cleared after successful sending to at least one 264 /* Error is cleared after successful sending to at least one
274 * registered KM */ 265 * registered KM */
@@ -278,9 +269,8 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
278 rcu_read_unlock(); 269 rcu_read_unlock();
279 270
280 if (one_sk != NULL) 271 if (one_sk != NULL)
281 err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk); 272 err = pfkey_broadcast_one(skb, allocation, one_sk);
282 273
283 kfree_skb(skb2);
284 kfree_skb(skb); 274 kfree_skb(skb);
285 return err; 275 return err;
286} 276}