diff options
Diffstat (limited to 'sys/kern/uipc_usrreq.c')
-rw-r--r-- | sys/kern/uipc_usrreq.c | 107 |
1 files changed, 59 insertions, 48 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index ac93ea1b9c4e..24557b510270 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -357,33 +357,32 @@ unp_pcb_lock2(struct unpcb *unp, struct unpcb *unp2) } static __noinline void -unp_pcb_owned_lock2_slowpath(struct unpcb *unp, struct unpcb **unp2p, int *freed) - +unp_pcb_owned_lock2_slowpath(struct unpcb *unp, struct unpcb **unp2p, + int *freed) { struct unpcb *unp2; unp2 = *unp2p; - unp_pcb_hold((unp2)); - UNP_PCB_UNLOCK((unp)); - UNP_PCB_LOCK((unp2)); - UNP_PCB_LOCK((unp)); - *freed = unp_pcb_rele((unp2)); + unp_pcb_hold(unp2); + UNP_PCB_UNLOCK(unp); + UNP_PCB_LOCK(unp2); + UNP_PCB_LOCK(unp); + *freed = unp_pcb_rele(unp2); if (*freed) *unp2p = NULL; } -#define unp_pcb_owned_lock2(unp, unp2, freed) do { \ - freed = 0; \ - UNP_PCB_LOCK_ASSERT((unp)); \ - UNP_PCB_UNLOCK_ASSERT((unp2)); \ - MPASS(unp != unp2); \ - if (__predict_true(UNP_PCB_TRYLOCK((unp2)))) \ - break; \ - else if ((uintptr_t)(unp2) > (uintptr_t)(unp)) \ - UNP_PCB_LOCK((unp2)); \ - else { \ - unp_pcb_owned_lock2_slowpath((unp), &(unp2), &freed); \ - } \ +#define unp_pcb_owned_lock2(unp, unp2, freed) do { \ + freed = 0; \ + UNP_PCB_LOCK_ASSERT(unp); \ + UNP_PCB_UNLOCK_ASSERT(unp2); \ + MPASS((unp) != (unp2)); \ + if (__predict_true(UNP_PCB_TRYLOCK(unp2))) \ + break; \ + else if ((uintptr_t)(unp2) > (uintptr_t)(unp)) \ + UNP_PCB_LOCK(unp2); \ + else \ + unp_pcb_owned_lock2_slowpath((unp), &(unp2), &freed); \ } while (0) @@ -1175,16 +1174,22 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, unp2->unp_flags &= ~UNP_WANTCRED; control = unp_addsockcred(td, control); } + /* - * Send to paired receive port, and then reduce send buffer - * hiwater marks to maintain backpressure. Wake up readers. + * Send to paired receive port and wake up readers. Don't + * check for space available in the receive buffer if we're + * attaching ancillary data; Unix domain sockets only check + * for space in the sending sockbuf, and that check is + * performed one level up the stack. At that level we cannot + * precisely account for the amount of buffer space used + * (e.g., because control messages are not yet internalized). */ switch (so->so_type) { case SOCK_STREAM: if (control != NULL) { - if (sbappendcontrol_locked(&so2->so_rcv, m, - control)) - control = NULL; + sbappendcontrol_locked(&so2->so_rcv, m, + control); + control = NULL; } else sbappend_locked(&so2->so_rcv, m, flags); break; @@ -1193,14 +1198,8 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, const struct sockaddr *from; from = &sun_noname; - /* - * Don't check for space available in so2->so_rcv. - * Unix domain sockets only check for space in the - * sending sockbuf, and that check is performed one - * level up the stack. - */ if (sbappendaddr_nospacecheck_locked(&so2->so_rcv, - from, m, control)) + from, m, control)) control = NULL; break; } @@ -1609,24 +1608,8 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam, sa = NULL; } - /* - * The connector's (client's) credentials are copied from its - * process structure at the time of connect() (which is now). - */ - cru2x(td->td_ucred, &unp3->unp_peercred); - unp3->unp_flags |= UNP_HAVEPC; + unp_copy_peercred(td, unp3, unp, unp2); - /* - * The receiver's (server's) credentials are copied from the - * unp_peercred member of socket on which the former called - * listen(); uipc_listen() cached that process's credentials - * at that time so we can use them now. - */ - memcpy(&unp->unp_peercred, &unp2->unp_peercred, - sizeof(unp->unp_peercred)); - unp->unp_flags |= UNP_HAVEPC; - if (unp2->unp_flags & UNP_WANTCRED) - unp3->unp_flags |= UNP_WANTCRED; UNP_PCB_UNLOCK(unp2); unp2 = unp3; unp_pcb_owned_lock2(unp2, unp, freed); @@ -1665,6 +1648,27 @@ bad: return (error); } +/* + * Set socket peer credentials at connection time. + * + * The client's PCB credentials are copied from its process structure. The + * server's PCB credentials are copied from the socket on which it called + * listen(2). uipc_listen cached that process's credentials at the time. + */ +void +unp_copy_peercred(struct thread *td, struct unpcb *client_unp, + struct unpcb *server_unp, struct unpcb *listen_unp) +{ + cru2x(td->td_ucred, &client_unp->unp_peercred); + client_unp->unp_flags |= UNP_HAVEPC; + + memcpy(&server_unp->unp_peercred, &listen_unp->unp_peercred, + sizeof(server_unp->unp_peercred)); + server_unp->unp_flags |= UNP_HAVEPC; + if (listen_unp->unp_flags & UNP_WANTCRED) + client_unp->unp_flags |= UNP_WANTCRED; +} + static int unp_connect2(struct socket *so, struct socket *so2, int req) { @@ -2046,6 +2050,13 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags) &fdep[i]->fde_caps); unp_externalize_fp(fdep[i]->fde_file); } + + /* + * The new type indicates that the mbuf data refers to + * kernel resources that may need to be released before + * the mbuf is freed. + */ + m_chtype(*controlp, MT_EXTCONTROL); FILEDESC_XUNLOCK(fdesc); free(fdep[0], M_FILECAPS); } else { |