Skip to content

Commit 29e224b

Browse files
Sean TranchettiGerrit - the friendly Code Review server
authored andcommitted
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 CRs-Fixed: 2251019 Change-Id: Ib3b01f941a34a7df61fe9445f746b7df33f4656a Signed-off-by: Sean Tranchetti <[email protected]>
1 parent 6804981 commit 29e224b

File tree

1 file changed

+15
-25
lines changed

1 file changed

+15
-25
lines changed

net/key/af_key.c

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -196,30 +196,22 @@ static int pfkey_release(struct socket *sock)
196196
return 0;
197197
}
198198

199-
static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
200-
gfp_t allocation, struct sock *sk)
199+
static int pfkey_broadcast_one(struct sk_buff *skb, gfp_t allocation,
200+
struct sock *sk)
201201
{
202202
int err = -ENOBUFS;
203203

204-
sock_hold(sk);
205-
if (*skb2 == NULL) {
206-
if (atomic_read(&skb->users) != 1) {
207-
*skb2 = skb_clone(skb, allocation);
208-
} else {
209-
*skb2 = skb;
210-
atomic_inc(&skb->users);
211-
}
212-
}
213-
if (*skb2 != NULL) {
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-
}
204+
if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf)
205+
return err;
206+
207+
skb = skb_clone(skb, allocation);
208+
209+
if (skb) {
210+
skb_set_owner_r(skb, sk);
211+
skb_queue_tail(&sk->sk_receive_queue, skb);
212+
sk->sk_data_ready(sk);
213+
err = 0;
221214
}
222-
sock_put(sk);
223215
return err;
224216
}
225217

@@ -234,7 +226,6 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
234226
{
235227
struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
236228
struct sock *sk;
237-
struct sk_buff *skb2 = NULL;
238229
int err = -ESRCH;
239230

240231
/* 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,
253244
* socket.
254245
*/
255246
if (pfk->promisc)
256-
pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk);
247+
pfkey_broadcast_one(skb, GFP_ATOMIC, sk);
257248

258249
/* the exact target will be processed later */
259250
if (sk == one_sk)
@@ -268,7 +259,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
268259
continue;
269260
}
270261

271-
err2 = pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk);
262+
err2 = pfkey_broadcast_one(skb, GFP_ATOMIC, sk);
272263

273264
/* Error is cleared after successful sending to at least one
274265
* registered KM */
@@ -278,9 +269,8 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
278269
rcu_read_unlock();
279270

280271
if (one_sk != NULL)
281-
err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk);
272+
err = pfkey_broadcast_one(skb, allocation, one_sk);
282273

283-
kfree_skb(skb2);
284274
kfree_skb(skb);
285275
return err;
286276
}

0 commit comments

Comments
 (0)