Skip to content

Commit d25adbe

Browse files
lxindavem330
authored andcommitted
sctp: fix an use-after-free issue in sctp_sock_dump
Commit 86fdb34 ("sctp: ensure ep is not destroyed before doing the dump") tried to fix an use-after-free issue by checking !sctp_sk(sk)->ep with holding sock and sock lock. But Paolo noticed that endpoint could be destroyed in sctp_rcv without sock lock protection. It means the use-after-free issue still could be triggered when sctp_rcv put and destroy ep after sctp_sock_dump checks !ep, although it's pretty hard to reproduce. I could reproduce it by mdelay in sctp_rcv while msleep in sctp_close and sctp_sock_dump long time. This patch is to add another param cb_done to sctp_for_each_transport and dump ep->assocs with holding tsp after jumping out of transport's traversal in it to avoid this issue. It can also improve sctp diag dump to make it run faster, as no need to save sk into cb->args[5] and keep calling sctp_for_each_transport any more. This patch is also to use int * instead of int for the pos argument in sctp_for_each_transport, which could make postion increment only in sctp_for_each_transport and no need to keep changing cb->args[2] in sctp_sock_filter and sctp_sock_dump any more. Fixes: 86fdb34 ("sctp: ensure ep is not destroyed before doing the dump") Reported-by: Paolo Abeni <[email protected]> Signed-off-by: Xin Long <[email protected]> Acked-by: Marcelo Ricardo Leitner <[email protected]> Acked-by: Neil Horman <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5023a6d commit d25adbe

File tree

3 files changed

+36
-39
lines changed

3 files changed

+36
-39
lines changed

include/net/sctp/sctp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
127127
const union sctp_addr *laddr,
128128
const union sctp_addr *paddr, void *p);
129129
int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
130-
struct net *net, int pos, void *p);
130+
int (*cb_done)(struct sctp_transport *, void *),
131+
struct net *net, int *pos, void *p);
131132
int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), void *p);
132133
int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc,
133134
struct sctp_info *info);

net/sctp/sctp_diag.c

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -279,19 +279,19 @@ static int sctp_tsp_dump_one(struct sctp_transport *tsp, void *p)
279279
return err;
280280
}
281281

282-
static int sctp_sock_dump(struct sock *sk, void *p)
282+
static int sctp_sock_dump(struct sctp_transport *tsp, void *p)
283283
{
284+
struct sctp_endpoint *ep = tsp->asoc->ep;
284285
struct sctp_comm_param *commp = p;
286+
struct sock *sk = ep->base.sk;
285287
struct sk_buff *skb = commp->skb;
286288
struct netlink_callback *cb = commp->cb;
287289
const struct inet_diag_req_v2 *r = commp->r;
288290
struct sctp_association *assoc;
289291
int err = 0;
290292

291293
lock_sock(sk);
292-
if (!sctp_sk(sk)->ep)
293-
goto release;
294-
list_for_each_entry(assoc, &sctp_sk(sk)->ep->asocs, asocs) {
294+
list_for_each_entry(assoc, &ep->asocs, asocs) {
295295
if (cb->args[4] < cb->args[1])
296296
goto next;
297297

@@ -327,40 +327,30 @@ static int sctp_sock_dump(struct sock *sk, void *p)
327327
cb->args[4]++;
328328
}
329329
cb->args[1] = 0;
330-
cb->args[2]++;
331330
cb->args[3] = 0;
332331
cb->args[4] = 0;
333332
release:
334333
release_sock(sk);
335-
sock_put(sk);
336334
return err;
337335
}
338336

339-
static int sctp_get_sock(struct sctp_transport *tsp, void *p)
337+
static int sctp_sock_filter(struct sctp_transport *tsp, void *p)
340338
{
341339
struct sctp_endpoint *ep = tsp->asoc->ep;
342340
struct sctp_comm_param *commp = p;
343341
struct sock *sk = ep->base.sk;
344-
struct netlink_callback *cb = commp->cb;
345342
const struct inet_diag_req_v2 *r = commp->r;
346343
struct sctp_association *assoc =
347344
list_entry(ep->asocs.next, struct sctp_association, asocs);
348345

349346
/* find the ep only once through the transports by this condition */
350347
if (tsp->asoc != assoc)
351-
goto out;
348+
return 0;
352349

353350
if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family)
354-
goto out;
355-
356-
sock_hold(sk);
357-
cb->args[5] = (long)sk;
351+
return 0;
358352

359353
return 1;
360-
361-
out:
362-
cb->args[2]++;
363-
return 0;
364354
}
365355

366356
static int sctp_ep_dump(struct sctp_endpoint *ep, void *p)
@@ -503,12 +493,8 @@ static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
503493
if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)))
504494
goto done;
505495

506-
next:
507-
cb->args[5] = 0;
508-
sctp_for_each_transport(sctp_get_sock, net, cb->args[2], &commp);
509-
510-
if (cb->args[5] && !sctp_sock_dump((struct sock *)cb->args[5], &commp))
511-
goto next;
496+
sctp_for_each_transport(sctp_sock_filter, sctp_sock_dump,
497+
net, (int *)&cb->args[2], &commp);
512498

513499
done:
514500
cb->args[1] = cb->args[4];

net/sctp/socket.c

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4658,29 +4658,39 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
46584658
EXPORT_SYMBOL_GPL(sctp_transport_lookup_process);
46594659

46604660
int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *),
4661-
struct net *net, int pos, void *p) {
4661+
int (*cb_done)(struct sctp_transport *, void *),
4662+
struct net *net, int *pos, void *p) {
46624663
struct rhashtable_iter hti;
4663-
void *obj;
4664-
int err;
4665-
4666-
err = sctp_transport_walk_start(&hti);
4667-
if (err)
4668-
return err;
4664+
struct sctp_transport *tsp;
4665+
int ret;
46694666

4670-
obj = sctp_transport_get_idx(net, &hti, pos + 1);
4671-
for (; !IS_ERR_OR_NULL(obj); obj = sctp_transport_get_next(net, &hti)) {
4672-
struct sctp_transport *transport = obj;
4667+
again:
4668+
ret = sctp_transport_walk_start(&hti);
4669+
if (ret)
4670+
return ret;
46734671

4674-
if (!sctp_transport_hold(transport))
4672+
tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
4673+
for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
4674+
if (!sctp_transport_hold(tsp))
46754675
continue;
4676-
err = cb(transport, p);
4677-
sctp_transport_put(transport);
4678-
if (err)
4676+
ret = cb(tsp, p);
4677+
if (ret)
46794678
break;
4679+
(*pos)++;
4680+
sctp_transport_put(tsp);
46804681
}
46814682
sctp_transport_walk_stop(&hti);
46824683

4683-
return err;
4684+
if (ret) {
4685+
if (cb_done && !cb_done(tsp, p)) {
4686+
(*pos)++;
4687+
sctp_transport_put(tsp);
4688+
goto again;
4689+
}
4690+
sctp_transport_put(tsp);
4691+
}
4692+
4693+
return ret;
46844694
}
46854695
EXPORT_SYMBOL_GPL(sctp_for_each_transport);
46864696

0 commit comments

Comments
 (0)