-Fixes first of all the getcred on IPv6 and V4. The

copy's were incorrect and so was the locking.
-A bug was also found that would create a race and
 panic when an abort arrived on a socket being read
 from.
-Also fix the reader to get MSG_TRUNC when a partial
 delivery is aborted.
-Also addresses a couple of coverity caught error path
 memory leaks and a couple of other valid complaints
Approved by:	gnn
This commit is contained in:
Randall Stewart 2006-11-08 00:21:13 +00:00
parent 46bcfc7375
commit 03b0b02163
10 changed files with 167 additions and 177 deletions

View File

@ -1274,6 +1274,7 @@ sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
sizeof(struct in_addr));
} else {
/* invalid family! */
SCTP_FREE(aa);
return (-1);
}
aa->sent = 0; /* clear sent flag */

View File

@ -257,6 +257,7 @@ sctp_build_readq_entry(struct sctp_tcb *stcb,
read_queue_e->port_from = stcb->rport;
read_queue_e->do_not_ref_stcb = 0;
read_queue_e->end_added = 0;
read_queue_e->pdapi_aborted = 0;
failed_build:
return (read_queue_e);
}
@ -293,6 +294,7 @@ sctp_build_readq_entry_chk(struct sctp_tcb *stcb,
read_queue_e->port_from = stcb->rport;
read_queue_e->do_not_ref_stcb = 0;
read_queue_e->end_added = 0;
read_queue_e->pdapi_aborted = 0;
failed_build:
return (read_queue_e);
}

View File

@ -582,6 +582,7 @@ sctp_handle_shutdown(struct sctp_shutdown_chunk *cp,
*/
SCTP_INP_READ_LOCK(stcb->sctp_ep);
stcb->asoc.control_pdapi->end_added = 1;
stcb->asoc.control_pdapi->pdapi_aborted = 1;
if (stcb->asoc.control_pdapi->tail_mbuf) {
stcb->asoc.control_pdapi->tail_mbuf->m_flags |= M_EOR;
}
@ -673,6 +674,7 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp,
*/
SCTP_INP_READ_LOCK(stcb->sctp_ep);
stcb->asoc.control_pdapi->end_added = 1;
stcb->asoc.control_pdapi->pdapi_aborted = 1;
if (stcb->asoc.control_pdapi->tail_mbuf) {
stcb->asoc.control_pdapi->tail_mbuf->m_flags |= M_EOR;
}
@ -1823,6 +1825,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
sig = (uint8_t *) sctp_m_getptr(m_sig, 0, SCTP_SIGNATURE_SIZE, (uint8_t *) & tmp_sig);
if (sig == NULL) {
/* couldn't find signature */
sctp_m_freem(m_sig);
return (NULL);
}
/* compare the received digest with the computed digest */

View File

@ -9870,6 +9870,9 @@ sctp_lower_sosend(struct socket *so,
}
mm = sctp_copy_resume(sp, uio, srcv, max_len, user_marks_eor, &error, &sndout, &new_tail);
if ((mm == NULL) || error) {
if (mm) {
sctp_m_freem(mm);
}
goto out;
}
/* Update the mbuf and count */

View File

@ -1769,8 +1769,10 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
*/
/* got to be root to get at low ports */
if (ntohs(lport) < IPPORT_RESERVED) {
if (p && (error = priv_check(p,
PRIV_NETINET_RESERVEDPORT))) {
if (p && (error =
priv_check(p,
PRIV_NETINET_RESERVEDPORT)
)) {
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_WUNLOCK();
@ -3290,46 +3292,30 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
if (sq->stcb == stcb) {
sq->do_not_ref_stcb = 1;
sq->sinfo_cumtsn = stcb->asoc.cumulative_tsn;
if ((from_inpcbfree == 0) && so) {
/*
* Only if we have a socket lock do
* we do this
*/
if ((sq->held_length) ||
(sq->end_added == 0) ||
((sq->length == 0) && (sq->end_added == 0))) {
/* Held for PD-API */
sq->held_length = 0;
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PDAPIEVNT)) {
/*
* need to change to
* PD-API aborted
*/
stcb->asoc.control_pdapi = sq;
sctp_notify_partial_delivery_indication(stcb,
SCTP_PARTIAL_DELIVERY_ABORTED, 1);
stcb->asoc.control_pdapi = NULL;
} else {
/*
* need to get the
* reader to remove
* it
*/
sq->length = 0;
if (sq->data) {
struct mbuf *m;
m = sq->data;
while (m) {
sctp_sbfree(sq, stcb, &stcb->sctp_socket->so_rcv, m);
m = sctp_m_free(m);
}
sq->data = NULL;
sq->tail_mbuf = NULL;
}
}
/*
* If there is no end, there never will be
* now.
*/
if (sq->end_added == 0) {
/* Held for PD-API clear that. */
sq->pdapi_aborted = 1;
sq->held_length = 0;
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PDAPIEVNT)) {
/*
* Need to add a PD-API
* aborted indication.
* Setting the control_pdapi
* assures that it will be
* added right after this
* msg.
*/
stcb->asoc.control_pdapi = sq;
sctp_notify_partial_delivery_indication(stcb,
SCTP_PARTIAL_DELIVERY_ABORTED, 1);
stcb->asoc.control_pdapi = NULL;
}
}
/* Add an end to wake them */
sq->end_added = 1;
cnt++;
}

View File

@ -149,16 +149,6 @@ struct sctp_copy_all {
int cnt_failed;
};
union sctp_sockstore {
#ifdef AF_INET
struct sockaddr_in sin;
#endif
#ifdef AF_INET6
struct sockaddr_in6 sin6;
#endif
struct sockaddr sa;
};
struct sctp_nets {
TAILQ_ENTRY(sctp_nets) sctp_next; /* next link */
@ -371,6 +361,7 @@ struct sctp_queued_to_read { /* sinfo structure Pluse more */
uint16_t port_from;
uint8_t do_not_ref_stcb;
uint8_t end_added;
uint8_t pdapi_aborted;
};
/* This data structure will be on the outbound

View File

@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
typedef uint32_t sctp_assoc_t;
@ -871,6 +872,17 @@ struct sctpstat {
#define SCTP_STAT_DECR_COUNTER64(_x) SCTP_STAT_DECR(_x)
#define SCTP_STAT_DECR_GAUGE32(_x) SCTP_STAT_DECR(_x)
union sctp_sockstore {
#ifdef AF_INET
struct sockaddr_in sin;
#endif
#ifdef AF_INET6
struct sockaddr_in6 sin6;
#endif
struct sockaddr sa;
};
/*
* Kernel defined for sctp_send
*/

View File

@ -82,6 +82,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_asconf.h>
#include <netinet/sctp_timer.h>
#include <netinet/sctp_auth.h>
#ifdef IPSEC
#include <netinet6/ipsec.h>
#include <netkey/key.h>
@ -483,11 +484,12 @@ sctp_ctlinput(cmd, sa, vip)
static int
sctp_getcred(SYSCTL_HANDLER_ARGS)
{
struct xucred xuc;
struct sockaddr_in addrs[2];
struct sctp_inpcb *inp;
struct sctp_nets *net;
struct sctp_tcb *stcb;
int error, s;
int error;
/*
* XXXRW: Other instances of getcred use SUSER_ALLOWJAIL, as socket
@ -502,7 +504,6 @@ sctp_getcred(SYSCTL_HANDLER_ARGS)
if (error)
return (error);
s = splnet();
stcb = sctp_findassociation_addr_sa(sintosa(&addrs[0]),
sintosa(&addrs[1]),
&inp, &net, 1);
@ -511,15 +512,29 @@ sctp_getcred(SYSCTL_HANDLER_ARGS)
/* reduce ref-count */
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
goto cred_can_cont;
}
error = ENOENT;
goto out;
}
error = SYSCTL_OUT(req, inp->sctp_socket->so_cred, sizeof(struct ucred));
SCTP_TCB_UNLOCK(stcb);
/*
* We use the write lock here, only since in the error leg we need
* it. If we used RLOCK, then we would have to
* wlock/decr/unlock/rlock. Which in theory could create a hole.
* Better to use higher wlock.
*/
SCTP_INP_WLOCK(inp);
cred_can_cont:
error = cr_canseesocket(req->td->td_ucred, inp->sctp_socket);
if (error) {
SCTP_INP_WUNLOCK(inp);
goto out;
}
cru2x(inp->sctp_socket->so_cred, &xuc);
SCTP_INP_WUNLOCK(inp);
error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
out:
splx(s);
return (error);
}
@ -3384,6 +3399,7 @@ sctp_optsset(struct socket *so,
if (sctp_auth_add_hmacid(hmaclist, (uint16_t) hmacid)) {
/* invalid HMACs were found */ ;
error = EINVAL;
sctp_free_hmaclist(hmaclist);
goto sctp_set_hmac_done;
}
}
@ -4655,8 +4671,10 @@ sctp_accept(struct socket *so, struct sockaddr **addr)
sin6->sin6_port = ((struct sockaddr_in6 *)&store)->sin6_port;
sin6->sin6_addr = ((struct sockaddr_in6 *)&store)->sin6_addr;
if ((error = sa6_recoverscope(sin6)) != 0)
if ((error = sa6_recoverscope(sin6)) != 0) {
SCTP_FREE_SONAME(sin6);
return (error);
}
*addr = (struct sockaddr *)sin6;
}
/* Wake any delayed sleep action */
@ -4725,6 +4743,10 @@ sctp_ingetaddr(struct socket *so, struct sockaddr **addr)
SCTP_TCB_LOCK(stcb);
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
sin_a = (struct sockaddr_in *)&net->ro._l_addr;
if (sin_a == NULL)
/* this will make coverity happy */
continue;
if (sin_a->sin_family == AF_INET) {
fnd = 1;
break;

View File

@ -377,7 +377,12 @@ sctp_log_lock(struct sctp_inpcb *inp, struct sctp_tcb *stcb, uint8_t from)
sctp_clog[sctp_cwnd_log_at].time_event = sctp_get_time_of_event();
sctp_clog[sctp_cwnd_log_at].from = (uint8_t) from;
sctp_clog[sctp_cwnd_log_at].event_type = (uint8_t) SCTP_LOG_LOCK_EVENT;
sctp_clog[sctp_cwnd_log_at].x.lock.sock = (void *)inp->sctp_socket;
if (inp) {
sctp_clog[sctp_cwnd_log_at].x.lock.sock = (void *)inp->sctp_socket;
} else {
sctp_clog[sctp_cwnd_log_at].x.lock.sock = (void *)NULL;
}
sctp_clog[sctp_cwnd_log_at].x.lock.inp = (void *)inp;
if (stcb) {
sctp_clog[sctp_cwnd_log_at].x.lock.tcb_lock = mtx_owned(&stcb->tcb_mtx);
@ -2575,7 +2580,6 @@ sctp_notify_assoc_change(uint32_t event, struct sctp_tcb *stcb,
struct mbuf *m_notify;
struct sctp_assoc_change *sac;
struct sctp_queued_to_read *control;
int locked = 0;
/*
* First if we are are going down dump everything we can to the
@ -2589,58 +2593,6 @@ sctp_notify_assoc_change(uint32_t event, struct sctp_tcb *stcb,
/* If the socket is gone we are out of here */
return;
}
if ((event == SCTP_COMM_LOST) || (event == SCTP_SHUTDOWN_COMP)) {
if (stcb->asoc.control_pdapi) {
/*
* we were in the middle of a PD-API verify its
* there.
*/
SCTP_INP_READ_LOCK(stcb->sctp_ep);
locked = 1;
TAILQ_FOREACH(control, &stcb->sctp_ep->read_queue, next) {
if (control == stcb->asoc.control_pdapi) {
/* Yep its here, notify them */
if (event == SCTP_COMM_LOST) {
/*
* Abort/broken we had a
* real PD-API aborted
*/
if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_PDAPIEVNT)) {
/*
* hmm.. don't want
* a notify if
* held_lenght is
* set,they may be
* stuck. clear and
* wake.
*/
if (control->held_length) {
control->held_length = 0;
control->end_added = 1;
}
} else {
sctp_notify_partial_delivery_indication(stcb, event, 1);
}
} else {
/* implicit EOR on EOF */
control->held_length = 0;
control->end_added = 1;
}
SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
locked = 0;
/* wake him up */
control->do_not_ref_stcb = 1;
stcb->asoc.control_pdapi = NULL;
sorwakeup(stcb->sctp_socket);
break;
}
}
if (locked)
SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
}
}
/*
* For TCP model AND UDP connected sockets we will send an error up
* when an ABORT comes in.
@ -2936,13 +2888,15 @@ sctp_notify_adaptation_layer(struct sctp_tcb *stcb,
&stcb->sctp_socket->so_rcv, 1);
}
/* This always must be called with the read-queue LOCKED in the INP */
void
sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb,
uint32_t error, int no_lock)
uint32_t error, int nolock)
{
struct mbuf *m_notify;
struct sctp_pdapi_event *pdapi;
struct sctp_queued_to_read *control;
struct sockbuf *sb;
if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_PDAPIEVNT))
/* event not enabled */
@ -2965,62 +2919,44 @@ sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb,
m_notify->m_pkthdr.rcvif = 0;
m_notify->m_len = sizeof(struct sctp_pdapi_event);
m_notify->m_next = NULL;
if (stcb->asoc.control_pdapi != NULL) {
/* we will do some substitution */
control = stcb->asoc.control_pdapi;
if (no_lock == 0)
SCTP_INP_READ_LOCK(stcb->sctp_ep);
if (control->data == NULL) {
control->data = control->tail_mbuf = m_notify;
control->held_length = 0;
control->length = m_notify->m_len;
control->end_added = 1;
sctp_sballoc(stcb,
&stcb->sctp_socket->so_rcv,
m_notify);
} else if (control->end_added == 0) {
struct mbuf *m = NULL;
m = control->data;
while (m) {
sctp_sbfree(control, stcb,
&stcb->sctp_socket->so_rcv, m);
m = sctp_m_free(m);
}
control->data = NULL;
control->length = m_notify->m_len;
control->data = control->tail_mbuf = m_notify;
control->held_length = 0;
control->end_added = 1;
sctp_sballoc(stcb, &stcb->sctp_socket->so_rcv, m);
} else {
/* Hmm .. should not happen */
control->end_added = 1;
stcb->asoc.control_pdapi = NULL;
goto add_to_end;
}
if (no_lock == 0)
SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination,
0, 0, 0, 0, 0, 0,
m_notify);
if (control == NULL) {
/* no memory */
sctp_m_freem(m_notify);
return;
}
control->length = m_notify->m_len;
/* not that we need this */
control->tail_mbuf = m_notify;
control->held_length = 0;
control->length = 0;
if (nolock == 0) {
SCTP_INP_READ_LOCK(stcb->sctp_ep);
}
sb = &stcb->sctp_socket->so_rcv;
#ifdef SCTP_SB_LOGGING
sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBALLOC, m_notify->m_len);
#endif
sctp_sballoc(stcb, sb, m_notify);
#ifdef SCTP_SB_LOGGING
sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0);
#endif
atomic_add_int(&control->length, m_notify->m_len);
control->end_added = 1;
if (stcb->asoc.control_pdapi)
TAILQ_INSERT_AFTER(&stcb->sctp_ep->read_queue, stcb->asoc.control_pdapi, control, next);
else {
/* we really should not see this case */
TAILQ_INSERT_TAIL(&stcb->sctp_ep->read_queue, control, next);
}
if (nolock == 0) {
SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
}
if (stcb->sctp_ep && stcb->sctp_socket) {
/* This should always be the case */
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
} else {
/* append to socket */
add_to_end:
control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination,
0, 0, 0, 0, 0, 0,
m_notify);
if (control == NULL) {
/* no memory */
sctp_m_freem(m_notify);
return;
}
control->length = m_notify->m_len;
/* not that we need this */
control->tail_mbuf = m_notify;
sctp_add_to_readq(stcb->sctp_ep, stcb,
control,
&stcb->sctp_socket->so_rcv, 1);
}
}
@ -3150,10 +3086,14 @@ void
sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb,
uint32_t error, void *data)
{
if (stcb == NULL) {
/* unlikely but */
return;
}
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) ||
(stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET)
) {
) {
/* No notifications up when we are in a no socket state */
return;
}
@ -3829,6 +3769,13 @@ sctp_add_to_readq(struct sctp_inpcb *inp,
*/
struct mbuf *m, *prev = NULL;
if (inp == NULL) {
/* Gak, TSNH!! */
#ifdef INVARIENTS
panic("Gak, inp NULL on add_to_readq");
#endif
return;
}
SCTP_INP_READ_LOCK(inp);
m = control->data;
control->held_length = 0;
@ -5032,6 +4979,8 @@ sctp_sorecvmsg(struct socket *so,
if (control->end_added == 1) {
/* he aborted, or is done i.e.did a shutdown */
out_flags |= MSG_EOR;
if (control->pdapi_aborted)
out_flags |= MSG_TRUNC;
goto done_with_control;
}
if (so->so_rcv.sb_cc > held_length) {
@ -5143,6 +5092,8 @@ sctp_sorecvmsg(struct socket *so,
* shutdown
*/
out_flags |= MSG_EOR;
if (control->pdapi_aborted)
out_flags |= MSG_TRUNC;
goto done_with_control;
}
if (so->so_rcv.sb_cc > held_length) {

View File

@ -476,11 +476,12 @@ sctp6_ctlinput(cmd, pktdst, d)
static int
sctp6_getcred(SYSCTL_HANDLER_ARGS)
{
struct xucred xuc;
struct sockaddr_in6 addrs[2];
struct sctp_inpcb *inp;
struct sctp_nets *net;
struct sctp_tcb *stcb;
int error, s;
int error;
/*
* XXXRW: Other instances of getcred use SUSER_ALLOWJAIL, as socket
@ -499,26 +500,38 @@ sctp6_getcred(SYSCTL_HANDLER_ARGS)
error = SYSCTL_IN(req, addrs, sizeof(addrs));
if (error)
return (error);
s = splnet();
stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[0]),
sin6tosa(&addrs[1]),
&inp, &net, 1);
if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) {
error = ENOENT;
if (inp) {
if ((inp != NULL) && (stcb == NULL)) {
/* reduce ref-count */
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
goto cred_can_cont;
}
error = ENOENT;
goto out;
}
error = SYSCTL_OUT(req, inp->sctp_socket->so_cred,
sizeof(struct ucred));
SCTP_TCB_UNLOCK(stcb);
/*
* We use the write lock here, only since in the error leg we need
* it. If we used RLOCK, then we would have to
* wlock/decr/unlock/rlock. Which in theory could create a hole.
* Better to use higher wlock.
*/
SCTP_INP_WLOCK(inp);
cred_can_cont:
error = cr_canseesocket(req->td->td_ucred, inp->sctp_socket);
if (error) {
SCTP_INP_WUNLOCK(inp);
goto out;
}
cru2x(inp->sctp_socket->so_cred, &xuc);
SCTP_INP_WUNLOCK(inp);
error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
out:
splx(s);
return (error);
}
@ -1177,6 +1190,10 @@ sctp6_getaddr(struct socket *so, struct sockaddr **addr)
sin_a6 = NULL;
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
if (sin_a6 == NULL)
/* this will make coverity happy */
continue;
if (sin_a6->sin6_family == AF_INET6) {
fnd = 1;
break;
@ -1217,8 +1234,10 @@ sctp6_getaddr(struct socket *so, struct sockaddr **addr)
}
SCTP_INP_RUNLOCK(inp);
/* Scoping things for v6 */
if ((error = sa6_recoverscope(sin6)) != 0)
if ((error = sa6_recoverscope(sin6)) != 0) {
SCTP_FREE_SONAME(sin6);
return (error);
}
(*addr) = (struct sockaddr *)sin6;
return (0);
}