diff options
Diffstat (limited to 'net/unix')
| -rw-r--r-- | net/unix/af_unix.c | 53 | ||||
| -rw-r--r-- | net/unix/garbage.c | 49 |
2 files changed, 73 insertions, 29 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 015606b54d9b..eb90f77bb0e2 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * NET4: Implementation of BSD Unix domain sockets. | 2 | * NET4: Implementation of BSD Unix domain sockets. |
| 3 | * | 3 | * |
| 4 | * Authors: Alan Cox, <alan.cox@linux.org> | 4 | * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> |
| 5 | * | 5 | * |
| 6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License | 7 | * modify it under the terms of the GNU General Public License |
| @@ -711,28 +711,30 @@ static struct sock *unix_find_other(struct net *net, | |||
| 711 | int type, unsigned hash, int *error) | 711 | int type, unsigned hash, int *error) |
| 712 | { | 712 | { |
| 713 | struct sock *u; | 713 | struct sock *u; |
| 714 | struct nameidata nd; | 714 | struct path path; |
| 715 | int err = 0; | 715 | int err = 0; |
| 716 | 716 | ||
| 717 | if (sunname->sun_path[0]) { | 717 | if (sunname->sun_path[0]) { |
| 718 | err = path_lookup(sunname->sun_path, LOOKUP_FOLLOW, &nd); | 718 | struct inode *inode; |
| 719 | err = kern_path(sunname->sun_path, LOOKUP_FOLLOW, &path); | ||
| 719 | if (err) | 720 | if (err) |
| 720 | goto fail; | 721 | goto fail; |
| 721 | err = vfs_permission(&nd, MAY_WRITE); | 722 | inode = path.dentry->d_inode; |
| 723 | err = inode_permission(inode, MAY_WRITE); | ||
| 722 | if (err) | 724 | if (err) |
| 723 | goto put_fail; | 725 | goto put_fail; |
| 724 | 726 | ||
| 725 | err = -ECONNREFUSED; | 727 | err = -ECONNREFUSED; |
| 726 | if (!S_ISSOCK(nd.path.dentry->d_inode->i_mode)) | 728 | if (!S_ISSOCK(inode->i_mode)) |
| 727 | goto put_fail; | 729 | goto put_fail; |
| 728 | u = unix_find_socket_byinode(net, nd.path.dentry->d_inode); | 730 | u = unix_find_socket_byinode(net, inode); |
| 729 | if (!u) | 731 | if (!u) |
| 730 | goto put_fail; | 732 | goto put_fail; |
| 731 | 733 | ||
| 732 | if (u->sk_type == type) | 734 | if (u->sk_type == type) |
| 733 | touch_atime(nd.path.mnt, nd.path.dentry); | 735 | touch_atime(path.mnt, path.dentry); |
| 734 | 736 | ||
| 735 | path_put(&nd.path); | 737 | path_put(&path); |
| 736 | 738 | ||
| 737 | err=-EPROTOTYPE; | 739 | err=-EPROTOTYPE; |
| 738 | if (u->sk_type != type) { | 740 | if (u->sk_type != type) { |
| @@ -753,7 +755,7 @@ static struct sock *unix_find_other(struct net *net, | |||
| 753 | return u; | 755 | return u; |
| 754 | 756 | ||
| 755 | put_fail: | 757 | put_fail: |
| 756 | path_put(&nd.path); | 758 | path_put(&path); |
| 757 | fail: | 759 | fail: |
| 758 | *error=err; | 760 | *error=err; |
| 759 | return NULL; | 761 | return NULL; |
| @@ -1300,14 +1302,23 @@ static void unix_destruct_fds(struct sk_buff *skb) | |||
| 1300 | sock_wfree(skb); | 1302 | sock_wfree(skb); |
| 1301 | } | 1303 | } |
| 1302 | 1304 | ||
| 1303 | 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) |
| 1304 | { | 1306 | { |
| 1305 | 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 | |||
| 1306 | for (i=scm->fp->count-1; i>=0; i--) | 1318 | for (i=scm->fp->count-1; i>=0; i--) |
| 1307 | unix_inflight(scm->fp->fp[i]); | 1319 | unix_inflight(scm->fp->fp[i]); |
| 1308 | UNIXCB(skb).fp = scm->fp; | ||
| 1309 | skb->destructor = unix_destruct_fds; | 1320 | skb->destructor = unix_destruct_fds; |
| 1310 | scm->fp = NULL; | 1321 | return 0; |
| 1311 | } | 1322 | } |
| 1312 | 1323 | ||
| 1313 | /* | 1324 | /* |
| @@ -1366,8 +1377,11 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
| 1366 | goto out; | 1377 | goto out; |
| 1367 | 1378 | ||
| 1368 | memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); | 1379 | memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); |
| 1369 | if (siocb->scm->fp) | 1380 | if (siocb->scm->fp) { |
| 1370 | unix_attach_fds(siocb->scm, skb); | 1381 | err = unix_attach_fds(siocb->scm, skb); |
| 1382 | if (err) | ||
| 1383 | goto out_free; | ||
| 1384 | } | ||
| 1371 | unix_get_secdata(siocb->scm, skb); | 1385 | unix_get_secdata(siocb->scm, skb); |
| 1372 | 1386 | ||
| 1373 | skb_reset_transport_header(skb); | 1387 | skb_reset_transport_header(skb); |
| @@ -1536,8 +1550,13 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
| 1536 | size = min_t(int, size, skb_tailroom(skb)); | 1550 | size = min_t(int, size, skb_tailroom(skb)); |
| 1537 | 1551 | ||
| 1538 | memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); | 1552 | memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); |
| 1539 | if (siocb->scm->fp) | 1553 | if (siocb->scm->fp) { |
| 1540 | unix_attach_fds(siocb->scm, skb); | 1554 | err = unix_attach_fds(siocb->scm, skb); |
| 1555 | if (err) { | ||
| 1556 | kfree_skb(skb); | ||
| 1557 | goto out_err; | ||
| 1558 | } | ||
| 1559 | } | ||
| 1541 | 1560 | ||
| 1542 | if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) { | 1561 | if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) { |
| 1543 | kfree_skb(skb); | 1562 | kfree_skb(skb); |
| @@ -2211,7 +2230,7 @@ static int unix_net_init(struct net *net) | |||
| 2211 | #endif | 2230 | #endif |
| 2212 | error = 0; | 2231 | error = 0; |
| 2213 | out: | 2232 | out: |
| 2214 | return 0; | 2233 | return error; |
| 2215 | } | 2234 | } |
| 2216 | 2235 | ||
| 2217 | static void unix_net_exit(struct net *net) | 2236 | static void unix_net_exit(struct net *net) |
diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 2a27b84f740b..6d4a9a8de5ef 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c | |||
| @@ -186,8 +186,17 @@ static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *), | |||
| 186 | */ | 186 | */ |
| 187 | struct sock *sk = unix_get_socket(*fp++); | 187 | struct sock *sk = unix_get_socket(*fp++); |
| 188 | if (sk) { | 188 | if (sk) { |
| 189 | hit = true; | 189 | struct unix_sock *u = unix_sk(sk); |
| 190 | func(unix_sk(sk)); | 190 | |
| 191 | /* | ||
| 192 | * Ignore non-candidates, they could | ||
| 193 | * have been added to the queues after | ||
| 194 | * starting the garbage collection | ||
| 195 | */ | ||
| 196 | if (u->gc_candidate) { | ||
| 197 | hit = true; | ||
| 198 | func(u); | ||
| 199 | } | ||
| 191 | } | 200 | } |
| 192 | } | 201 | } |
| 193 | if (hit && hitlist != NULL) { | 202 | if (hit && hitlist != NULL) { |
| @@ -249,11 +258,11 @@ static void inc_inflight_move_tail(struct unix_sock *u) | |||
| 249 | { | 258 | { |
| 250 | atomic_long_inc(&u->inflight); | 259 | atomic_long_inc(&u->inflight); |
| 251 | /* | 260 | /* |
| 252 | * If this is still a candidate, move it to the end of the | 261 | * If this still might be part of a cycle, move it to the end |
| 253 | * list, so that it's checked even if it was already passed | 262 | * of the list, so that it's checked even if it was already |
| 254 | * over | 263 | * passed over |
| 255 | */ | 264 | */ |
| 256 | if (u->gc_candidate) | 265 | if (u->gc_maybe_cycle) |
| 257 | list_move_tail(&u->link, &gc_candidates); | 266 | list_move_tail(&u->link, &gc_candidates); |
| 258 | } | 267 | } |
| 259 | 268 | ||
| @@ -267,6 +276,7 @@ void unix_gc(void) | |||
| 267 | struct unix_sock *next; | 276 | struct unix_sock *next; |
| 268 | struct sk_buff_head hitlist; | 277 | struct sk_buff_head hitlist; |
| 269 | struct list_head cursor; | 278 | struct list_head cursor; |
| 279 | LIST_HEAD(not_cycle_list); | ||
| 270 | 280 | ||
| 271 | spin_lock(&unix_gc_lock); | 281 | spin_lock(&unix_gc_lock); |
| 272 | 282 | ||
| @@ -282,10 +292,14 @@ void unix_gc(void) | |||
| 282 | * | 292 | * |
| 283 | * Holding unix_gc_lock will protect these candidates from | 293 | * Holding unix_gc_lock will protect these candidates from |
| 284 | * being detached, and hence from gaining an external | 294 | * being detached, and hence from gaining an external |
| 285 | * reference. This also means, that since there are no | 295 | * reference. Since there are no possible receivers, all |
| 286 | * possible receivers, the receive queues of these sockets are | 296 | * buffers currently on the candidates' queues stay there |
| 287 | * static during the GC, even though the dequeue is done | 297 | * during the garbage collection. |
| 288 | * before the detach without atomicity guarantees. | 298 | * |
| 299 | * We also know that no new candidate can be added onto the | ||
| 300 | * receive queues. Other, non candidate sockets _can_ be | ||
| 301 | * added to queue, so we must make sure only to touch | ||
| 302 | * candidates. | ||
| 289 | */ | 303 | */ |
| 290 | list_for_each_entry_safe(u, next, &gc_inflight_list, link) { | 304 | list_for_each_entry_safe(u, next, &gc_inflight_list, link) { |
| 291 | long total_refs; | 305 | long total_refs; |
| @@ -299,6 +313,7 @@ void unix_gc(void) | |||
| 299 | if (total_refs == inflight_refs) { | 313 | if (total_refs == inflight_refs) { |
| 300 | list_move_tail(&u->link, &gc_candidates); | 314 | list_move_tail(&u->link, &gc_candidates); |
| 301 | u->gc_candidate = 1; | 315 | u->gc_candidate = 1; |
| 316 | u->gc_maybe_cycle = 1; | ||
| 302 | } | 317 | } |
| 303 | } | 318 | } |
| 304 | 319 | ||
| @@ -325,14 +340,24 @@ void unix_gc(void) | |||
| 325 | list_move(&cursor, &u->link); | 340 | list_move(&cursor, &u->link); |
| 326 | 341 | ||
| 327 | if (atomic_long_read(&u->inflight) > 0) { | 342 | if (atomic_long_read(&u->inflight) > 0) { |
| 328 | list_move_tail(&u->link, &gc_inflight_list); | 343 | list_move_tail(&u->link, ¬_cycle_list); |
| 329 | u->gc_candidate = 0; | 344 | u->gc_maybe_cycle = 0; |
| 330 | scan_children(&u->sk, inc_inflight_move_tail, NULL); | 345 | scan_children(&u->sk, inc_inflight_move_tail, NULL); |
| 331 | } | 346 | } |
| 332 | } | 347 | } |
| 333 | list_del(&cursor); | 348 | list_del(&cursor); |
| 334 | 349 | ||
| 335 | /* | 350 | /* |
| 351 | * not_cycle_list contains those sockets which do not make up a | ||
| 352 | * cycle. Restore these to the inflight list. | ||
| 353 | */ | ||
| 354 | while (!list_empty(¬_cycle_list)) { | ||
| 355 | u = list_entry(not_cycle_list.next, struct unix_sock, link); | ||
| 356 | u->gc_candidate = 0; | ||
| 357 | list_move_tail(&u->link, &gc_inflight_list); | ||
| 358 | } | ||
| 359 | |||
| 360 | /* | ||
| 336 | * Now gc_candidates contains only garbage. Restore original | 361 | * Now gc_candidates contains only garbage. Restore original |
| 337 | * inflight counters for these as well, and remove the skbuffs | 362 | * inflight counters for these as well, and remove the skbuffs |
| 338 | * which are creating the cycle(s). | 363 | * which are creating the cycle(s). |
