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
This commit is contained in:
truckman 2002-08-21 11:57:12 +00:00
parent 2d2341a634
commit 7199888e8f
5 changed files with 130 additions and 84 deletions

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}

View File

@ -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));