git: 5ac91821f5d7 - main - sctp: get rid of stcb send lock
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 28 Mar 2022 23:52:28 UTC
The branch main has been updated by tuexen: URL: https://cgit.FreeBSD.org/src/commit/?id=5ac91821f5d7dd701752ba76041720d240a507c5 commit 5ac91821f5d7dd701752ba76041720d240a507c5 Author: Michael Tuexen <tuexen@FreeBSD.org> AuthorDate: 2022-03-28 23:50:17 +0000 Commit: Michael Tuexen <tuexen@FreeBSD.org> CommitDate: 2022-03-28 23:50:17 +0000 sctp: get rid of stcb send lock Just use the stcb lock instead to simplify locking. Reported by: syzbot+d00b202063150f85b110@syzkaller.appspotmail.com Reported by: syzbot+87f268a0a6d2d6383306@syzkaller.appspotmail.com MFC after: 3 days --- sys/netinet/sctp_input.c | 11 +- sys/netinet/sctp_lock_bsd.h | 22 -- sys/netinet/sctp_output.c | 556 ++++++++++++++++++---------------------- sys/netinet/sctp_pcb.c | 12 +- sys/netinet/sctp_ss_functions.c | 70 +++-- sys/netinet/sctp_sysctl.c | 46 ++-- sys/netinet/sctp_timer.c | 4 +- sys/netinet/sctp_uio.h | 2 +- sys/netinet/sctp_usrreq.c | 4 - sys/netinet/sctputil.c | 12 +- 10 files changed, 333 insertions(+), 406 deletions(-) diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 222b69102bef..91f55af7f598 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -179,6 +179,8 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb, int so_locked) struct sctp_stream_queue_pending *sp; struct sctp_association *asoc; + SCTP_TCB_LOCK_ASSERT(stcb); + /* * This function returns if any stream has true unsent data on it. * Note that as it looks through it will clean up any places that @@ -186,7 +188,6 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb, int so_locked) */ asoc = &stcb->asoc; unsent_data = 0; - SCTP_TCB_SEND_LOCK(stcb); if (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { /* Check to see if some data queued */ for (i = 0; i < stcb->asoc.streamoutcnt; i++) { @@ -234,7 +235,6 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb, int so_locked) } } } - SCTP_TCB_SEND_UNLOCK(stcb); return (unsent_data); } @@ -246,6 +246,8 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb) struct sctp_nets *lnet; unsigned int i; + SCTP_TCB_LOCK_ASSERT(stcb); + init = &cp->init; asoc = &stcb->asoc; /* save off parameters */ @@ -263,7 +265,6 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb) } } } - SCTP_TCB_SEND_LOCK(stcb); if (asoc->pre_open_streams > ntohs(init->num_inbound_streams)) { unsigned int newcnt; struct sctp_stream_out *outs; @@ -323,7 +324,6 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb) /* cut back the count */ asoc->pre_open_streams = newcnt; } - SCTP_TCB_SEND_UNLOCK(stcb); asoc->streamoutcnt = asoc->pre_open_streams; if (asoc->strmout) { for (i = 0; i < asoc->streamoutcnt; i++) { @@ -1808,8 +1808,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); /* send up all the data */ - SCTP_TCB_SEND_LOCK(stcb); - sctp_report_all_outbound(stcb, 0, SCTP_SO_LOCKED); for (i = 0; i < stcb->asoc.streamoutcnt; i++) { stcb->asoc.strmout[i].chunks_on_queues = 0; @@ -1896,7 +1894,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, */ LIST_INSERT_HEAD(head, stcb, sctp_asocs); - SCTP_TCB_SEND_UNLOCK(stcb); SCTP_INP_WUNLOCK(stcb->sctp_ep); SCTP_INP_INFO_WUNLOCK(); asoc->total_flight = 0; diff --git a/sys/netinet/sctp_lock_bsd.h b/sys/netinet/sctp_lock_bsd.h index cd20a730e5b8..2087db337a37 100644 --- a/sys/netinet/sctp_lock_bsd.h +++ b/sys/netinet/sctp_lock_bsd.h @@ -337,28 +337,6 @@ __FBSDID("$FreeBSD$"); #define SCTP_ASOC_CREATE_LOCK_CONTENDED(_inp) \ ((_inp)->inp_create_mtx.mtx_lock & MTX_CONTESTED) -#define SCTP_TCB_SEND_LOCK_INIT(_tcb) do { \ - mtx_init(&(_tcb)->tcb_send_mtx, "sctp-send-tcb", "tcbs", \ - MTX_DEF | MTX_DUPOK); \ -} while (0) - -#define SCTP_TCB_SEND_LOCK_DESTROY(_tcb) do { \ - mtx_destroy(&(_tcb)->tcb_send_mtx); \ -} while (0) - -#define SCTP_TCB_SEND_LOCK(_tcb) do { \ - mtx_lock(&(_tcb)->tcb_send_mtx); \ -} while (0) - -#define SCTP_TCB_SEND_UNLOCK(_tcb) do { \ - mtx_unlock(&(_tcb)->tcb_send_mtx); \ -} while (0) - -#define SCTP_TCB_SEND_LOCK_ASSERT(_tcb) do { \ - KASSERT(mtx_owned(&(_tcb)->tcb_send_mtx), \ - ("Don't own TCB send lock")); \ -} while (0) - /* * For the majority of things (once we have found the association) we will * lock the actual association mutex. This will protect all the assoiciation diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index a46264cd3efe..2bc6ec9628cc 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -6318,13 +6318,15 @@ static int sctp_msg_append(struct sctp_tcb *stcb, struct sctp_nets *net, struct mbuf *m, - struct sctp_sndrcvinfo *srcv, int hold_stcb_lock) + struct sctp_sndrcvinfo *srcv) { int error = 0; struct mbuf *at; struct sctp_stream_queue_pending *sp = NULL; struct sctp_stream_out *strm; + SCTP_TCB_LOCK_ASSERT(stcb); + /* * Given an mbuf chain, put it into the association send queue and * place it on the wheel @@ -6396,18 +6398,12 @@ sctp_msg_append(struct sctp_tcb *stcb, sctp_auth_key_acquire(stcb, sp->auth_keyid); sp->holds_key_ref = 1; } - if (hold_stcb_lock == 0) { - SCTP_TCB_SEND_LOCK(stcb); - } strm = &stcb->asoc.strmout[srcv->sinfo_stream]; sctp_snd_sb_alloc(stcb, sp->length); atomic_add_int(&stcb->asoc.stream_queue_cnt, 1); TAILQ_INSERT_TAIL(&strm->outqueue, sp, next); stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc, strm, sp); m = NULL; - if (hold_stcb_lock == 0) { - SCTP_TCB_SEND_UNLOCK(stcb); - } out_now: if (m) { sctp_m_freem(m); @@ -6656,9 +6652,8 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, atomic_subtract_int(&stcb->asoc.refcnt, 1); goto no_chunk_output; } else { - if (m) { - ret = sctp_msg_append(stcb, net, m, - &ca->sndrcv, 1); + if (m != NULL) { + ret = sctp_msg_append(stcb, net, m, &ca->sndrcv); } asoc = &stcb->asoc; if (ca->sndrcv.sinfo_flags & SCTP_EOF) { @@ -7166,7 +7161,6 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, int leading; uint8_t rcv_flags = 0; uint8_t some_taken; - uint8_t send_lock_up = 0; SCTP_TCB_LOCK_ASSERT(stcb); asoc = &stcb->asoc; @@ -7174,10 +7168,6 @@ one_more_time: /* sa_ignore FREED_MEMORY */ sp = TAILQ_FIRST(&strq->outqueue); if (sp == NULL) { - if (send_lock_up == 0) { - SCTP_TCB_SEND_LOCK(stcb); - send_lock_up = 1; - } sp = TAILQ_FIRST(&strq->outqueue); if (sp) { goto one_more_time; @@ -7191,10 +7181,6 @@ one_more_time: strq->last_msg_incomplete = 0; } to_move = 0; - if (send_lock_up) { - SCTP_TCB_SEND_UNLOCK(stcb); - send_lock_up = 0; - } goto out_of; } if ((sp->msg_is_complete) && (sp->length == 0)) { @@ -7206,16 +7192,11 @@ one_more_time: */ if ((sp->put_last_out == 0) && (sp->discard_rest == 0)) { SCTP_PRINTF("Gak, put out entire msg with NO end!-1\n"); - SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d send_lock:%d\n", + SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d\n", sp->sender_all_done, sp->length, sp->msg_is_complete, - sp->put_last_out, - send_lock_up); - } - if (send_lock_up == 0) { - SCTP_TCB_SEND_LOCK(stcb); - send_lock_up = 1; + sp->put_last_out); } atomic_subtract_int(&asoc->stream_queue_cnt, 1); TAILQ_REMOVE(&strq->outqueue, sp, next); @@ -7234,11 +7215,6 @@ one_more_time: sp->data = NULL; } sctp_free_a_strmoq(stcb, sp, so_locked); - /* we can't be locked to it */ - if (send_lock_up) { - SCTP_TCB_SEND_UNLOCK(stcb); - send_lock_up = 0; - } /* back to get the next msg */ goto one_more_time; } else { @@ -7258,10 +7234,6 @@ one_more_time: to_move = 0; goto out_of; } else if (sp->discard_rest) { - if (send_lock_up == 0) { - SCTP_TCB_SEND_LOCK(stcb); - send_lock_up = 1; - } /* Whack down the size */ atomic_subtract_int(&stcb->asoc.total_output_queue_size, sp->length); if ((stcb->sctp_socket != NULL) && @@ -7282,7 +7254,6 @@ one_more_time: } } some_taken = sp->some_taken; -re_look: length = sp->length; if (sp->msg_is_complete) { /* The message is complete */ @@ -7307,31 +7278,9 @@ re_look: } } else { to_move = sctp_can_we_split_this(stcb, length, space_left, frag_point, eeor_mode); - if (to_move) { - /*- - * We use a snapshot of length in case it - * is expanding during the compare. - */ - uint32_t llen; - - llen = length; - if (to_move >= llen) { - to_move = llen; - if (send_lock_up == 0) { - /*- - * We are taking all of an incomplete msg - * thus we need a send lock. - */ - SCTP_TCB_SEND_LOCK(stcb); - send_lock_up = 1; - if (sp->msg_is_complete) { - /* - * the sender finished the - * msg - */ - goto re_look; - } - } + if (to_move > 0) { + if (to_move >= length) { + to_move = length; } if (sp->some_taken == 0) { rcv_flags |= SCTP_DATA_FIRST_FRAG; @@ -7370,10 +7319,6 @@ re_look: if (to_move >= length) { /* we think we can steal the whole thing */ - if ((sp->sender_all_done == 0) && (send_lock_up == 0)) { - SCTP_TCB_SEND_LOCK(stcb); - send_lock_up = 1; - } if (to_move < sp->length) { /* bail, it changed */ goto dont_do_it; @@ -7467,10 +7412,6 @@ dont_do_it: * all the data if there is no leading space, so we * must put the data back and restore. */ - if (send_lock_up == 0) { - SCTP_TCB_SEND_LOCK(stcb); - send_lock_up = 1; - } if (sp->data == NULL) { /* unsteal the data */ sp->data = chk->data; @@ -7579,8 +7520,8 @@ dont_do_it: * previous loop prior to padding. */ -#ifdef SCTP_ASOCLOG_OF_TSNS SCTP_TCB_LOCK_ASSERT(stcb); +#ifdef SCTP_ASOCLOG_OF_TSNS if (asoc->tsn_out_at >= SCTP_TSN_LOG_SIZE) { asoc->tsn_out_at = 0; asoc->tsn_out_wrapped = 1; @@ -7638,16 +7579,11 @@ dont_do_it: /* All done pull and kill the message */ if (sp->put_last_out == 0) { SCTP_PRINTF("Gak, put out entire msg with NO end!-2\n"); - SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d send_lock:%d\n", + SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d\n", sp->sender_all_done, sp->length, sp->msg_is_complete, - sp->put_last_out, - send_lock_up); - } - if (send_lock_up == 0) { - SCTP_TCB_SEND_LOCK(stcb); - send_lock_up = 1; + sp->put_last_out); } atomic_subtract_int(&asoc->stream_queue_cnt, 1); TAILQ_REMOVE(&strq->outqueue, sp, next); @@ -7672,9 +7608,6 @@ dont_do_it: TAILQ_INSERT_TAIL(&asoc->send_queue, chk, sctp_next); asoc->send_queue_cnt++; out_of: - if (send_lock_up) { - SCTP_TCB_SEND_UNLOCK(stcb); - } return (to_move); } @@ -12081,6 +12014,8 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, int can_send_out_req = 0; uint32_t seq; + SCTP_TCB_LOCK_ASSERT(stcb); + asoc = &stcb->asoc; if (asoc->stream_reset_outstanding) { /*- @@ -12184,7 +12119,6 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, * Ok now we proceed with copying the old out stuff and * initializing the new stuff. */ - SCTP_TCB_SEND_LOCK(stcb); stcb->asoc.ss_functions.sctp_ss_clear(stcb, &stcb->asoc, false); for (i = 0; i < stcb->asoc.streamoutcnt; i++) { TAILQ_INIT(&stcb->asoc.strmout[i].outqueue); @@ -12238,7 +12172,6 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, } stcb->asoc.strm_realoutsize = stcb->asoc.streamoutcnt + adding_o; SCTP_FREE(oldstream, SCTP_M_STRMO); - SCTP_TCB_SEND_UNLOCK(stcb); } skip_stuff: if ((add_stream & 1) && (adding_o > 0)) { @@ -12489,7 +12422,7 @@ int sctp_lower_sosend(struct socket *so, struct sockaddr *addr, struct uio *uio, - struct mbuf *i_pak, + struct mbuf *top, struct mbuf *control, int flags, struct sctp_sndrcvinfo *srcv @@ -12500,7 +12433,6 @@ sctp_lower_sosend(struct socket *so, struct epoch_tracker et; ssize_t sndlen = 0, max_len, local_add_more; int error; - struct mbuf *top = NULL; int queue_only = 0, queue_only_for_init = 0; bool free_cnt_applied = false; int un_sent; @@ -12516,10 +12448,10 @@ sctp_lower_sosend(struct socket *so, int user_marks_eor; bool create_lock_applied = false; int nagle_applies = 0; - int some_on_control = 0; - int got_all_of_the_send = 0; + bool some_on_control; + bool got_all_of_the_send = false; bool hold_tcblock = false; - int non_blocking = 0; + bool non_blocking = false; ssize_t local_soresv = 0; uint16_t port; uint16_t sinfo_flags; @@ -12528,47 +12460,35 @@ sctp_lower_sosend(struct socket *so, error = 0; net = NULL; stcb = NULL; - asoc = NULL; t_inp = inp = (struct sctp_inpcb *)so->so_pcb; if (inp == NULL) { - SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; - if (i_pak != NULL) { - SCTP_RELEASE_PKT(i_pak); - } - return (error); + goto out_unlocked; } - if ((uio == NULL) && (i_pak == NULL)) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); - return (EINVAL); + if ((uio == NULL) && (top == NULL)) { + error = EINVAL; + goto out_unlocked; } user_marks_eor = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR); atomic_add_int(&inp->total_sends, 1); if (uio != NULL) { if (uio->uio_resid < 0) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); - return (EINVAL); + error = EINVAL; + goto out_unlocked; } sndlen = uio->uio_resid; } else { - top = SCTP_HEADER_TO_CHAIN(i_pak); - sndlen = SCTP_HEADER_LEN(i_pak); + sndlen = SCTP_HEADER_LEN(top); } SCTPDBG(SCTP_DEBUG_OUTPUT1, "Send called addr:%p send length %zd\n", - (void *)addr, - sndlen); + (void *)addr, sndlen); if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && SCTP_IS_LISTENING(inp)) { - /* The listener can NOT send */ - SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOTCONN); - error = ENOTCONN; + /* The listener can NOT send. */ + error = EINVAL; goto out_unlocked; } - /** - * Pre-screen address, if one is given the sin-len - * must be set correctly! - */ if (addr != NULL) { union sctp_sockstore *raddr = (union sctp_sockstore *)addr; @@ -12576,7 +12496,6 @@ sctp_lower_sosend(struct socket *so, #ifdef INET case AF_INET: if (raddr->sin.sin_len != sizeof(struct sockaddr_in)) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } @@ -12586,7 +12505,6 @@ sctp_lower_sosend(struct socket *so, #ifdef INET6 case AF_INET6: if (raddr->sin6.sin6_len != sizeof(struct sockaddr_in6)) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } @@ -12594,7 +12512,6 @@ sctp_lower_sosend(struct socket *so, break; #endif default: - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EAFNOSUPPORT); error = EAFNOSUPPORT; goto out_unlocked; } @@ -12607,7 +12524,6 @@ sctp_lower_sosend(struct socket *so, sinfo_assoc_id = srcv->sinfo_assoc_id; if (INVALID_SINFO_FLAG(sinfo_flags) || PR_SCTP_INVALID_POLICY(sinfo_flags)) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } @@ -12624,33 +12540,27 @@ sctp_lower_sosend(struct socket *so, sinfo_flags |= SCTP_EOF; } if (sinfo_flags & SCTP_SENDALL) { - /* its a sendall */ error = sctp_sendall(inp, uio, top, srcv); top = NULL; goto out_unlocked; } if ((sinfo_flags & SCTP_ADDR_OVER) && (addr == NULL)) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } - /* now we must find the assoc */ + /* Now we must find the association. */ + SCTP_INP_RLOCK(inp); if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) || (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { - SCTP_INP_RLOCK(inp); stcb = LIST_FIRST(&inp->sctp_asoc_list); if (stcb != NULL) { SCTP_TCB_LOCK(stcb); hold_tcblock = true; - if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { - SCTP_INP_RUNLOCK(inp); - error = ENOTCONN; - goto out_unlocked; - } } SCTP_INP_RUNLOCK(inp); } else if (sinfo_assoc_id > SCTP_ALL_ASSOC) { - stcb = sctp_findassociation_ep_asocid(inp, sinfo_assoc_id, 1); + stcb = sctp_findasoc_ep_asocid_locked(inp, sinfo_assoc_id, 1); + SCTP_INP_RUNLOCK(inp); if (stcb != NULL) { SCTP_TCB_LOCK_ASSERT(stcb); hold_tcblock = true; @@ -12661,9 +12571,8 @@ sctp_lower_sosend(struct socket *so, * increment it, and if we don't find a tcb * decrement it. */ - SCTP_INP_WLOCK(inp); SCTP_INP_INCR_REF(inp); - SCTP_INP_WUNLOCK(inp); + SCTP_INP_RUNLOCK(inp); stcb = sctp_findassociation_ep_addr(&t_inp, addr, &net, NULL, NULL); if (stcb == NULL) { SCTP_INP_WLOCK(inp); @@ -12673,7 +12582,10 @@ sctp_lower_sosend(struct socket *so, SCTP_TCB_LOCK_ASSERT(stcb); hold_tcblock = true; } + } else { + SCTP_INP_RUNLOCK(inp); } + #ifdef INVARIANTS if (stcb != NULL) { SCTP_TCB_LOCK_ASSERT(stcb); @@ -12688,14 +12600,11 @@ sctp_lower_sosend(struct socket *so, create_lock_applied = true; if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) || (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { - /* Should I really unlock ? */ - SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) && (addr->sa_family == AF_INET6)) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } @@ -12719,18 +12628,16 @@ sctp_lower_sosend(struct socket *so, SCTP_ASOC_CREATE_UNLOCK(inp); create_lock_applied = false; } - if (error) { + if (error != 0) { goto out_unlocked; } if (t_inp != inp) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOTCONN); error = ENOTCONN; goto out_unlocked; } } if (stcb == NULL) { if (addr == NULL) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOENT); error = ENOENT; goto out_unlocked; } else { @@ -12743,7 +12650,6 @@ sctp_lower_sosend(struct socket *so, * User asks to abort a non-existent assoc, * or EOF a non-existent assoc with no data */ - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOENT); error = ENOENT; goto out_unlocked; } @@ -12756,7 +12662,8 @@ sctp_lower_sosend(struct socket *so, p, SCTP_INITIALIZE_AUTH_PARAMS); if (stcb == NULL) { - /* Error is setup for us in the call */ + /* error is setup for us in the call. */ + KASSERT(error != 0, ("error is 0 although stcb is NULL")); goto out_unlocked; } SCTP_TCB_LOCK_ASSERT(stcb); @@ -12776,6 +12683,8 @@ sctp_lower_sosend(struct socket *so, SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_6); hold_tcblock = false; stcb = NULL; + KASSERT(error != 0, + ("error is 0 although sctp_process_cmsgs_for_init() indicated an error")); goto out_unlocked; } } @@ -12795,8 +12704,16 @@ sctp_lower_sosend(struct socket *so, KASSERT(hold_tcblock, ("hold_tcblock is false")); SCTP_TCB_LOCK_ASSERT(stcb); asoc = &stcb->asoc; - KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0, - ("Association about to be freed")); + if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) || + (asoc->state & SCTP_STATE_WAS_ABORTED)) { + if (asoc->state & SCTP_STATE_WAS_ABORTED) { + /* XXX: Could also be ECONNABORTED, not enough info. */ + error = ECONNRESET; + } else { + error = ENOTCONN; + } + goto out_unlocked; + } /* Keep the stcb from being freed under our feet. */ atomic_add_int(&asoc->refcnt, 1); free_cnt_applied = true; @@ -12818,7 +12735,6 @@ sctp_lower_sosend(struct socket *so, net = NULL; if ((net == NULL) || ((port != 0) && (port != stcb->rport))) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } @@ -12833,7 +12749,6 @@ sctp_lower_sosend(struct socket *so, if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NO_FRAGMENT)) { if (sndlen > (ssize_t)asoc->smallest_mtu) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EMSGSIZE); error = EMSGSIZE; goto out_unlocked; } @@ -12841,7 +12756,7 @@ sctp_lower_sosend(struct socket *so, if (SCTP_SO_IS_NBIO(so) || (flags & (MSG_NBIO | MSG_DONTWAIT)) != 0 ) { - non_blocking = 1; + non_blocking = true; } /* would we block? */ if (non_blocking) { @@ -12855,11 +12770,12 @@ sctp_lower_sosend(struct socket *so, } if ((SCTP_SB_LIMIT_SND(so) < (amount + inqueue_bytes + asoc->sb_send_resv)) || (asoc->chunks_on_out_queue >= SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue))) { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EWOULDBLOCK); - if (sndlen > (ssize_t)SCTP_SB_LIMIT_SND(so)) + if ((sndlen > (ssize_t)SCTP_SB_LIMIT_SND(so)) && + (user_marks_eor == 0)) { error = EMSGSIZE; - else + } else { error = EWOULDBLOCK; + } goto out_unlocked; } asoc->sb_send_resv += (uint32_t)sndlen; @@ -12867,15 +12783,9 @@ sctp_lower_sosend(struct socket *so, atomic_add_int(&asoc->sb_send_resv, (int)sndlen); } local_soresv = sndlen; - if (asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) { - SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET); - error = ECONNRESET; - goto out_unlocked; - } /* Is the stream no. valid? */ if (srcv->sinfo_stream >= asoc->streamoutcnt) { /* Invalid stream number */ - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } @@ -12889,27 +12799,18 @@ sctp_lower_sosend(struct socket *so, } else { error = EINVAL; } - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, error); goto out_unlocked; } if ((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) || (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED)) { queue_only = 1; } - /* we are now done with all control */ - if (control) { - sctp_m_freem(control); - control = NULL; - } if ((SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_SENT) || (SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED) || (SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_ACK_SENT) || (asoc->state & SCTP_STATE_SHUTDOWN_PENDING)) { - if (sinfo_flags & SCTP_ABORT) { - ; - } else { - SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET); - error = ECONNRESET; + if ((sinfo_flags & SCTP_ABORT) == 0) { + error = EPIPE; goto out_unlocked; } } @@ -12923,6 +12824,8 @@ sctp_lower_sosend(struct socket *so, SCTP_TCB_LOCK_ASSERT(stcb); KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0, ("Association about to be freed")); + KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0, + ("Association was aborted")); /* Are we aborting? */ if (sinfo_flags & SCTP_ABORT) { @@ -12933,12 +12836,11 @@ sctp_lower_sosend(struct socket *so, SCTP_STAT_INCR(sctps_sends_with_abort); if ((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) || (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED)) { - /* It has to be up before we abort */ - /* how big is the user initiated abort? */ - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); + /* It has to be up before we abort. */ error = EINVAL; goto out; } + /* How big is the user initiated abort? */ if (top != NULL) { struct mbuf *cntm; @@ -12953,17 +12855,14 @@ sctp_lower_sosend(struct socket *so, tot_out = sndlen; tot_demand = (tot_out + sizeof(struct sctp_paramhdr)); if (tot_demand > SCTP_DEFAULT_ADD_MORE) { - /* To big */ - SCTP_LTRACE_ERR_RET(NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, EMSGSIZE); error = EMSGSIZE; - goto out; + goto out_unlocked; } mm = sctp_get_mbuf_for_msg((unsigned int)tot_demand, 0, M_NOWAIT, 1, MT_DATA); } if (mm == NULL) { - SCTP_LTRACE_ERR_RET(NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOMEM); error = ENOMEM; - goto out; + goto out_unlocked; } max_out = asoc->smallest_mtu - sizeof(struct sctp_paramhdr); max_out -= sizeof(struct sctp_abort_msg); @@ -12981,8 +12880,18 @@ sctp_lower_sosend(struct socket *so, error = uiomove((caddr_t)ph, (int)tot_out, uio); SCTP_TCB_LOCK(stcb); hold_tcblock = true; - if (asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) { + if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) || + (asoc->state & SCTP_STATE_WAS_ABORTED)) { sctp_m_freem(mm); + if (asoc->state & SCTP_STATE_WAS_ABORTED) { + /* + * XXX: Could also be ECONNABORTED, + * not enough info. + */ + error = ECONNRESET; + } else { + error = ENOTCONN; + } goto out_unlocked; } if (error != 0) { @@ -12993,6 +12902,7 @@ sctp_lower_sosend(struct socket *so, */ sctp_m_freem(mm); mm = NULL; + error = 0; } } else { if (sndlen != 0) { @@ -13022,6 +12932,8 @@ sctp_lower_sosend(struct socket *so, SCTP_TCB_LOCK_ASSERT(stcb); KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0, ("Association about to be freed")); + KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0, + ("Association was aborted")); /* Calculate the maximum we can send */ inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); @@ -13033,22 +12945,20 @@ sctp_lower_sosend(struct socket *so, /* Unless E_EOR mode is on, we must make a send FIT in one call. */ if ((user_marks_eor == 0) && (sndlen > (ssize_t)SCTP_SB_LIMIT_SND(stcb->sctp_socket))) { - /* It will NEVER fit */ - SCTP_LTRACE_ERR_RET(NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, EMSGSIZE); + /* It will NEVER fit. */ error = EMSGSIZE; goto out_unlocked; } - if ((uio == NULL) && user_marks_eor) { + if ((uio == NULL) && (user_marks_eor != 0)) { /*- * We do not support eeor mode for * sending with mbuf chains (like sendfile). */ - SCTP_LTRACE_ERR_RET(NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out_unlocked; } - if (user_marks_eor) { + if (user_marks_eor != 0) { local_add_more = (ssize_t)min(SCTP_SB_LIMIT_SND(so), SCTP_BASE_SYSCTL(sctp_add_more_threshold)); } else { /*- @@ -13060,13 +12970,12 @@ sctp_lower_sosend(struct socket *so, if (non_blocking) { goto skip_preblock; } - if (((max_len <= local_add_more) && - ((ssize_t)SCTP_SB_LIMIT_SND(so) >= local_add_more)) || + if (((max_len <= local_add_more) && ((ssize_t)SCTP_SB_LIMIT_SND(so) >= local_add_more)) || (max_len == 0) || ((asoc->chunks_on_out_queue + asoc->stream_queue_cnt) >= SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue))) { - /* No room right now ! */ - SOCKBUF_LOCK(&so->so_snd); + /* No room right now! */ inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); + SOCKBUF_LOCK(&so->so_snd); while ((SCTP_SB_LIMIT_SND(so) < (inqueue_bytes + local_add_more)) || ((asoc->stream_queue_cnt + asoc->chunks_on_out_queue) >= SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue))) { SCTPDBG(SCTP_DEBUG_OUTPUT1, "pre_block limit:%u <(inq:%d + %zd) || (%d+%d > %d)\n", @@ -13084,29 +12993,41 @@ sctp_lower_sosend(struct socket *so, SCTP_TCB_UNLOCK(stcb); hold_tcblock = false; error = sbwait(&so->so_snd); - SCTP_TCB_LOCK(stcb); - hold_tcblock = true; - if (asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) { - SOCKBUF_UNLOCK(&so->so_snd); - goto out_unlocked; - } - stcb->block_entry = NULL; if (error || so->so_error || be.error) { if (error == 0) { - if (so->so_error) + if (so->so_error != 0) { error = so->so_error; - if (be.error) { + } + if (be.error != 0) { error = be.error; } } SOCKBUF_UNLOCK(&so->so_snd); goto out_unlocked; } + SOCKBUF_UNLOCK(&so->so_snd); + SCTP_TCB_LOCK(stcb); + hold_tcblock = true; + if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) || + (asoc->state & SCTP_STATE_WAS_ABORTED)) { + if (asoc->state & SCTP_STATE_WAS_ABORTED) { + /* + * XXX: Could also be ECONNABORTED, + * not enough info. + */ + error = ECONNRESET; + } else { + error = ENOTCONN; + } + goto out_unlocked; + } + stcb->block_entry = NULL; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) { sctp_log_block(SCTP_BLOCK_LOG_OUTOF_BLK, asoc, asoc->total_output_queue_size); } inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb)); + SOCKBUF_LOCK(&so->so_snd); } if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) { max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes; @@ -13122,6 +13043,8 @@ skip_preblock: SCTP_TCB_LOCK_ASSERT(stcb); KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0, ("Association about to be freed")); + KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0, + ("Association was aborted")); /* * sndlen covers for mbuf case uio_resid covers for the non-mbuf @@ -13129,42 +13052,48 @@ skip_preblock: */ if (sndlen == 0) { if (sinfo_flags & SCTP_EOF) { - got_all_of_the_send = 1; + got_all_of_the_send = true; goto dataless_eof; } else { - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out; } } + if (top == NULL) { struct sctp_stream_queue_pending *sp; struct sctp_stream_out *strm; uint32_t sndout; - /* - * XXX: This will change soon, when the TCP send lock is - * retired. - */ - SCTP_TCB_UNLOCK(stcb); - hold_tcblock = false; - SCTP_TCB_SEND_LOCK(stcb); if ((asoc->stream_locked) && (asoc->stream_locked_on != srcv->sinfo_stream)) { - SCTP_TCB_SEND_UNLOCK(stcb); - SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); error = EINVAL; goto out; } strm = &asoc->strmout[srcv->sinfo_stream]; if (strm->last_msg_incomplete == 0) { do_a_copy_in: - SCTP_TCB_SEND_UNLOCK(stcb); + SCTP_TCB_UNLOCK(stcb); + hold_tcblock = false; sp = sctp_copy_it_in(stcb, asoc, srcv, uio, net, max_len, user_marks_eor, &error); - if (error) { + SCTP_TCB_LOCK(stcb); + hold_tcblock = true; + if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) || + (asoc->state & SCTP_STATE_WAS_ABORTED)) { + if (asoc->state & SCTP_STATE_WAS_ABORTED) { + /* + * XXX: Could also be ECONNABORTED, + * not enough info. + */ + error = ECONNRESET; + } else { + error = ENOTCONN; + } + goto out; + } + if (error != 0) { goto out; } - SCTP_TCB_SEND_LOCK(stcb); /* The out streams might be reallocated. */ strm = &asoc->strmout[srcv->sinfo_stream]; if (sp->msg_is_complete) { @@ -13202,74 +13131,79 @@ skip_preblock: #endif goto do_a_copy_in; } *** 1040 LINES SKIPPED ***