From 7199888e8f06576febc8146d51d09b781470e8ce Mon Sep 17 00:00:00 2001 From: truckman Date: Wed, 21 Aug 2002 11:57:12 +0000 Subject: [PATCH] Create new functions in_sockaddr(), in6_sockaddr(), and in6_v4mapsin6_sockaddr() which allocate the appropriate sockaddr_in* structure and initialize it with the address and port information passed as arguments. Use calls to these new functions to replace code that is replicated multiple times in in_setsockaddr(), in_setpeeraddr(), in6_setsockaddr(), in6_setpeeraddr(), in6_mapped_sockaddr(), and in6_mapped_peeraddr(). Inline COMMON_END in tcp_usr_accept() so that we can call in_sockaddr() with temporary copies of the address and port after the PCB is unlocked. Fix the lock violation in tcp6_usr_accept() (caused by calling MALLOC() inside in6_mapped_peeraddr() while the PCB is locked) by changing the implementation of tcp6_usr_accept() to match tcp_usr_accept(). Reviewed by: suz --- sys/netinet/in_pcb.c | 53 +++++++++++------------ sys/netinet/in_pcb.h | 2 + sys/netinet/tcp_usrreq.c | 63 ++++++++++++++++++--------- sys/netinet6/in6_pcb.c | 92 +++++++++++++++++++++++----------------- sys/netinet6/in6_pcb.h | 4 ++ 5 files changed, 130 insertions(+), 84 deletions(-) diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 8284031a200e..9ad3607cf6e0 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -577,6 +577,23 @@ in_pcbdetach(inp) uma_zfree(ipi->ipi_zone, inp); } +struct sockaddr * +in_sockaddr(port, addr_p) + in_port_t port; + struct in_addr *addr_p; +{ + struct sockaddr_in *sin; + + MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, + M_WAITOK | M_ZERO); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_addr = *addr_p; + sin->sin_port = port; + + return (struct sockaddr *)sin; +} + /* * The wrapper function will pass down the pcbinfo for this function to lock. * The socket must have a valid @@ -593,15 +610,8 @@ in_setsockaddr(so, nam, pcbinfo) { int s; register struct inpcb *inp; - register struct sockaddr_in *sin; - - /* - * Do the malloc first in case it blocks. - */ - MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, - M_WAITOK | M_ZERO); - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); + struct in_addr addr; + in_port_t port; s = splnet(); INP_INFO_RLOCK(pcbinfo); @@ -609,17 +619,16 @@ in_setsockaddr(so, nam, pcbinfo) if (!inp) { INP_INFO_RUNLOCK(pcbinfo); splx(s); - free(sin, M_SONAME); return ECONNRESET; } INP_LOCK(inp); - sin->sin_port = inp->inp_lport; - sin->sin_addr = inp->inp_laddr; + port = inp->inp_lport; + addr = inp->inp_laddr; INP_UNLOCK(inp); INP_INFO_RUNLOCK(pcbinfo); splx(s); - *nam = (struct sockaddr *)sin; + *nam = in_sockaddr(port, &addr); return 0; } @@ -634,15 +643,8 @@ in_setpeeraddr(so, nam, pcbinfo) { int s; register struct inpcb *inp; - register struct sockaddr_in *sin; - - /* - * Do the malloc first in case it blocks. - */ - MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, - M_WAITOK | M_ZERO); - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); + struct in_addr addr; + in_port_t port; s = splnet(); INP_INFO_RLOCK(pcbinfo); @@ -650,17 +652,16 @@ in_setpeeraddr(so, nam, pcbinfo) if (!inp) { INP_INFO_RUNLOCK(pcbinfo); splx(s); - free(sin, M_SONAME); return ECONNRESET; } INP_LOCK(inp); - sin->sin_port = inp->inp_fport; - sin->sin_addr = inp->inp_faddr; + port = inp->inp_fport; + addr = inp->inp_faddr; INP_UNLOCK(inp); INP_INFO_RUNLOCK(pcbinfo); splx(s); - *nam = (struct sockaddr *)sin; + *nam = in_sockaddr(port, &addr); return 0; } diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index d4d049cea74a..1654eb99e93b 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -344,6 +344,8 @@ void in_pcbnotifyall(struct inpcbinfo *pcbinfo, struct in_addr, void in_pcbrehash(struct inpcb *); int in_setpeeraddr(struct socket *so, struct sockaddr **nam, struct inpcbinfo *pcbinfo); int in_setsockaddr(struct socket *so, struct sockaddr **nam, struct inpcbinfo *pcbinfo);; +struct sockaddr * + in_sockaddr(in_port_t port, struct in_addr *addr); void in_pcbremlists(struct inpcb *inp); int prison_xinpcb(struct thread *td, struct inpcb *inp); #endif /* _KERNEL */ diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 767fb7d29323..2b8e3fd1a8b9 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -467,8 +467,8 @@ tcp_usr_accept(struct socket *so, struct sockaddr **nam) int error = 0; struct inpcb *inp = NULL; struct tcpcb *tp = NULL; - struct sockaddr_in *sin; - const int inirw = INI_READ; + struct in_addr addr; + in_port_t port = 0; TCPDEBUG0; if (so->so_state & SS_ISDISCONNECTED) { @@ -476,21 +476,12 @@ tcp_usr_accept(struct socket *so, struct sockaddr **nam) goto out; } - /* - * Do the malloc first in case it blocks. - */ - MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, - M_WAITOK | M_ZERO); - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); - s = splnet(); INP_INFO_RLOCK(&tcbinfo); inp = sotoinpcb(so); if (!inp) { INP_INFO_RUNLOCK(&tcbinfo); splx(s); - free(sin, M_SONAME); return (EINVAL); } INP_LOCK(inp); @@ -499,14 +490,20 @@ tcp_usr_accept(struct socket *so, struct sockaddr **nam) TCPDEBUG1(); /* - * We inline in_setpeeraddr here, because we have already done - * the locking and the malloc. + * We inline in_setpeeraddr and COMMON_END here, so that we can + * copy the data of interest and defer the malloc until after we + * release the lock. */ - sin->sin_port = inp->inp_fport; - sin->sin_addr = inp->inp_faddr; - *nam = (struct sockaddr *)sin; + port = inp->inp_fport; + addr = inp->inp_faddr; - COMMON_END(PRU_ACCEPT); +out: TCPDEBUG2(PRU_ACCEPT); + if (tp) + INP_UNLOCK(inp); + splx(s); + if (error == 0) + *nam = in_sockaddr(port, &addr); + return error; } #ifdef INET6 @@ -517,7 +514,10 @@ tcp6_usr_accept(struct socket *so, struct sockaddr **nam) struct inpcb *inp = NULL; int error = 0; struct tcpcb *tp = NULL; - const int inirw = INI_READ; + struct in_addr addr; + struct in6_addr addr6; + in_port_t port = 0; + int v4 = 0; TCPDEBUG0; if (so->so_state & SS_ISDISCONNECTED) { @@ -537,8 +537,31 @@ tcp6_usr_accept(struct socket *so, struct sockaddr **nam) INP_INFO_RUNLOCK(&tcbinfo); tp = intotcpcb(inp); TCPDEBUG1(); - in6_mapped_peeraddr(so, nam); - COMMON_END(PRU_ACCEPT); + /* + * We inline in6_mapped_peeraddr and COMMON_END here, so that we can + * copy the data of interest and defer the malloc until after we + * release the lock. + */ + if (inp->inp_vflag & INP_IPV4) { + v4 = 1; + port = inp->inp_fport; + addr = inp->inp_faddr; + } else { + port = inp->inp_fport; + addr6 = inp->in6p_faddr; + } + +out: TCPDEBUG2(PRU_ACCEPT); + if (tp) + INP_UNLOCK(inp); + splx(s); + if (error == 0) { + if (v4) + *nam = in6_v4mapsin6_sockaddr(port, &addr); + else + *nam = in6_sockaddr(port, &addr6); + } + return error; } #endif /* INET6 */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 083ed0379e8a..2d7baab99b90 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -626,6 +626,50 @@ in6_pcbdetach(inp) uma_zfree(ipi->ipi_zone, inp); } +struct sockaddr * +in6_sockaddr(port, addr_p) + in_port_t port; + struct in6_addr *addr_p; +{ + struct sockaddr_in6 *sin6; + + MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_WAITOK); + bzero(sin6, sizeof *sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_port = port; + sin6->sin6_addr = *addr_p; + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); + else + sin6->sin6_scope_id = 0; /*XXX*/ + if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_addr.s6_addr16[1] = 0; + + return (struct sockaddr *)sin6; +} + +struct sockaddr * +in6_v4mapsin6_sockaddr(port, addr_p) + in_port_t port; + struct in_addr *addr_p; +{ + struct sockaddr_in sin; + struct sockaddr_in6 *sin6_p; + + bzero(&sin, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_port = port; + sin.sin_addr = *addr_p; + + MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME, + M_WAITOK); + in6_sin_2_v4mapsin6(&sin, sin6_p); + + return (struct sockaddr *)sin6_p; +} + /* * The calling convention of in6_setsockaddr() and in6_setpeeraddr() was * modified to match the pru_sockaddr() and pru_peeraddr() entry points @@ -643,34 +687,20 @@ in6_setsockaddr(so, nam) { int s; register struct inpcb *inp; - register struct sockaddr_in6 *sin6; - - /* - * Do the malloc first in case it blocks. - */ - MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_WAITOK); - bzero(sin6, sizeof *sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_len = sizeof(*sin6); + struct in6_addr addr; + in_port_t port; s = splnet(); inp = sotoinpcb(so); if (!inp) { splx(s); - free(sin6, M_SONAME); return EINVAL; } - sin6->sin6_port = inp->inp_lport; - sin6->sin6_addr = inp->in6p_laddr; + port = inp->inp_lport; + addr = inp->in6p_laddr; splx(s); - if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) - sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); - else - sin6->sin6_scope_id = 0; /*XXX*/ - if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) - sin6->sin6_addr.s6_addr16[1] = 0; - *nam = (struct sockaddr *)sin6; + *nam = in6_sockaddr(port, &addr); return 0; } @@ -681,34 +711,20 @@ in6_setpeeraddr(so, nam) { int s; struct inpcb *inp; - register struct sockaddr_in6 *sin6; - - /* - * Do the malloc first in case it blocks. - */ - MALLOC(sin6, struct sockaddr_in6 *, sizeof(*sin6), M_SONAME, M_WAITOK); - bzero((caddr_t)sin6, sizeof (*sin6)); - sin6->sin6_family = AF_INET6; - sin6->sin6_len = sizeof(struct sockaddr_in6); + struct in6_addr addr; + in_port_t port; s = splnet(); inp = sotoinpcb(so); if (!inp) { splx(s); - free(sin6, M_SONAME); return EINVAL; } - sin6->sin6_port = inp->inp_fport; - sin6->sin6_addr = inp->in6p_faddr; + port = inp->inp_fport; + addr = inp->in6p_faddr; splx(s); - if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) - sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); - else - sin6->sin6_scope_id = 0; /*XXX*/ - if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) - sin6->sin6_addr.s6_addr16[1] = 0; - *nam = (struct sockaddr *)sin6; + *nam = in6_sockaddr(port, &addr); return 0; } diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h index 5098a12a892b..6d5c6095ce85 100644 --- a/sys/netinet6/in6_pcb.h +++ b/sys/netinet6/in6_pcb.h @@ -95,6 +95,10 @@ void in6_pcbnotify __P((struct inpcbhead *, struct sockaddr *, struct inpcb *(*)(struct inpcb *, int))); struct inpcb * in6_rtchange __P((struct inpcb *, int)); +struct sockaddr * + in6_sockaddr __P((in_port_t port, struct in6_addr *addr_p)); +struct sockaddr * + in6_v4mapsin6_sockaddr __P((in_port_t port, struct in_addr *addr_p)); int in6_setpeeraddr __P((struct socket *so, struct sockaddr **nam)); int in6_setsockaddr __P((struct socket *so, struct sockaddr **nam)); int in6_mapped_sockaddr __P((struct socket *so, struct sockaddr **nam));