| Summary: | non-privileged user opening routing socket causes resource leak | ||
|---|---|---|---|
| Product: | Base System | Reporter: | Richard Andrades <richard> |
| Component: | kern | Assignee: | freebsd-bugs (Nobody) <bugs> |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | Unspecified | ||
| Hardware: | Any | ||
| OS: | Any | ||
State Changed From-To: open->closed fix committed in -CURRENT. MFC after 4.4-RELEASE. |
If a routing socket is opened without root permissions, the socket() call fails. But the kernel fails to free the socket structure which it already allocated at the begining of the system call. This causes a resource leak in the socket table. If this happens often enough, the system runs out of socket descriptors and is unable to start any new network operations. Fix: Note: I checked the latest version and this bug has not yet been fixed. FILE: src/sys/net/rtsock.c Function: rts_attach() ---Begin code fragment---------------------------------------- MALLOC(rp, struct rawcb *, sizeof *rp, M_PCB, M_WAITOK); /* XXX */ if (rp == 0) return ENOBUFS; bzero(rp, sizeof *rp); /* * The splnet() is necessary to block protocols from sending * error notifications (like RTM_REDIRECT or RTM_LOSING) while * this PCB is extant but incompletely initialized. * Probably we should try to do more of this work beforehand and * eliminate the spl. */ s = splnet(); so->so_pcb = (caddr_t)rp; error = raw_usrreqs.pru_attach(so, proto, p); /* NOTE: Will return error if not root */ rp = sotorawcb(so); if (error) { /* Begin BUG FIX */ so->so_pcb = 0; /* Otherwise the socket won't be freed */ /* End BUG FIX */ splx(s); free(rp, M_PCB); return error; } ---End code fragment------------------------------------------------ Explanation: ************ In the function socreate() in the file src/sys/kern/uipc_socket.c there is the following code fragment: ----------------------------- error = (*prp->pr_usrreqs->pru_attach)(so, proto, p); /* NOTE: This goes to rts_attach() */ if (error) { so->so_state |= SS_NOFDREF; sofree(so); return (error); } ------------------------------------ Looking at the function sofree() in the same file, we find: ------------------------------------- void sofree(so) register struct socket *so; { struct socket *head = so->so_head; if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) return; ----------------------------------- So if rts_attach() fails to reset the so_pcb member, sofree() will not free the socket. How-To-Repeat: Run: --------------------------------------- main() { for(;;) socket(AF_ROUTE, SOCK_RAW, 0); } --------------------------------------- as a non-root user.