From 03b0b021637c91bc292b2cd7a0bf65a226905e66 Mon Sep 17 00:00:00 2001 From: Randall Stewart Date: Wed, 8 Nov 2006 00:21:13 +0000 Subject: [PATCH] -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 --- sys/netinet/sctp_asconf.c | 1 + sys/netinet/sctp_indata.c | 2 + sys/netinet/sctp_input.c | 3 + sys/netinet/sctp_output.c | 3 + sys/netinet/sctp_pcb.c | 66 ++++++-------- sys/netinet/sctp_structs.h | 11 +-- sys/netinet/sctp_uio.h | 12 +++ sys/netinet/sctp_usrreq.c | 34 +++++-- sys/netinet/sctputil.c | 173 +++++++++++++----------------------- sys/netinet6/sctp6_usrreq.c | 39 +++++--- 10 files changed, 167 insertions(+), 177 deletions(-) diff --git a/sys/netinet/sctp_asconf.c b/sys/netinet/sctp_asconf.c index 72efc5e2780c..7b44da5356e9 100644 --- a/sys/netinet/sctp_asconf.c +++ b/sys/netinet/sctp_asconf.c @@ -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 */ diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index bbb26e92045b..2da8f8b6c0fa 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -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); } diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 1545e8cb2d45..ac8940cd7277 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -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 */ diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 094e6657e4ba..76c2d6176993 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -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 */ diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 24c2ba5ed0fe..fb74b444e987 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -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++; } diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index c60f04aa906d..6472c2f2e113 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -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 diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h index 8532b4f80bae..6bd26b35cd42 100644 --- a/sys/netinet/sctp_uio.h +++ b/sys/netinet/sctp_uio.h @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #endif #include #include +#include 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 */ diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 25d639f678c8..95762b8e43cc 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -82,6 +82,7 @@ __FBSDID("$FreeBSD$"); #include #include #include + #ifdef IPSEC #include #include @@ -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; diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 45ebbe56fac7..927e444dfe89 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -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) { diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c index 34836587705b..8f8b62aab299 100644 --- a/sys/netinet6/sctp6_usrreq.c +++ b/sys/netinet6/sctp6_usrreq.c @@ -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); }