diff options
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r-- | net/unix/af_unix.c | 33 |
1 files changed, 26 insertions, 7 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 4d3c6071b9a..66d5ac4773a 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -1302,14 +1302,23 @@ static void unix_destruct_fds(struct sk_buff *skb) | |||
1302 | sock_wfree(skb); | 1302 | sock_wfree(skb); |
1303 | } | 1303 | } |
1304 | 1304 | ||
1305 | static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) | 1305 | static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) |
1306 | { | 1306 | { |
1307 | int i; | 1307 | int i; |
1308 | |||
1309 | /* | ||
1310 | * Need to duplicate file references for the sake of garbage | ||
1311 | * collection. Otherwise a socket in the fps might become a | ||
1312 | * candidate for GC while the skb is not yet queued. | ||
1313 | */ | ||
1314 | UNIXCB(skb).fp = scm_fp_dup(scm->fp); | ||
1315 | if (!UNIXCB(skb).fp) | ||
1316 | return -ENOMEM; | ||
1317 | |||
1308 | for (i=scm->fp->count-1; i>=0; i--) | 1318 | for (i=scm->fp->count-1; i>=0; i--) |
1309 | unix_inflight(scm->fp->fp[i]); | 1319 | unix_inflight(scm->fp->fp[i]); |
1310 | UNIXCB(skb).fp = scm->fp; | ||
1311 | skb->destructor = unix_destruct_fds; | 1320 | skb->destructor = unix_destruct_fds; |
1312 | scm->fp = NULL; | 1321 | return 0; |
1313 | } | 1322 | } |
1314 | 1323 | ||
1315 | /* | 1324 | /* |
@@ -1334,6 +1343,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
1334 | 1343 | ||
1335 | if (NULL == siocb->scm) | 1344 | if (NULL == siocb->scm) |
1336 | siocb->scm = &tmp_scm; | 1345 | siocb->scm = &tmp_scm; |
1346 | wait_for_unix_gc(); | ||
1337 | err = scm_send(sock, msg, siocb->scm); | 1347 | err = scm_send(sock, msg, siocb->scm); |
1338 | if (err < 0) | 1348 | if (err < 0) |
1339 | return err; | 1349 | return err; |
@@ -1368,8 +1378,11 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
1368 | goto out; | 1378 | goto out; |
1369 | 1379 | ||
1370 | memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); | 1380 | memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); |
1371 | if (siocb->scm->fp) | 1381 | if (siocb->scm->fp) { |
1372 | unix_attach_fds(siocb->scm, skb); | 1382 | err = unix_attach_fds(siocb->scm, skb); |
1383 | if (err) | ||
1384 | goto out_free; | ||
1385 | } | ||
1373 | unix_get_secdata(siocb->scm, skb); | 1386 | unix_get_secdata(siocb->scm, skb); |
1374 | 1387 | ||
1375 | skb_reset_transport_header(skb); | 1388 | skb_reset_transport_header(skb); |
@@ -1481,6 +1494,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
1481 | 1494 | ||
1482 | if (NULL == siocb->scm) | 1495 | if (NULL == siocb->scm) |
1483 | siocb->scm = &tmp_scm; | 1496 | siocb->scm = &tmp_scm; |
1497 | wait_for_unix_gc(); | ||
1484 | err = scm_send(sock, msg, siocb->scm); | 1498 | err = scm_send(sock, msg, siocb->scm); |
1485 | if (err < 0) | 1499 | if (err < 0) |
1486 | return err; | 1500 | return err; |
@@ -1538,8 +1552,13 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
1538 | size = min_t(int, size, skb_tailroom(skb)); | 1552 | size = min_t(int, size, skb_tailroom(skb)); |
1539 | 1553 | ||
1540 | memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); | 1554 | memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); |
1541 | if (siocb->scm->fp) | 1555 | if (siocb->scm->fp) { |
1542 | unix_attach_fds(siocb->scm, skb); | 1556 | err = unix_attach_fds(siocb->scm, skb); |
1557 | if (err) { | ||
1558 | kfree_skb(skb); | ||
1559 | goto out_err; | ||
1560 | } | ||
1561 | } | ||
1543 | 1562 | ||
1544 | if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) { | 1563 | if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) { |
1545 | kfree_skb(skb); | 1564 | kfree_skb(skb); |