aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Tuexen <tuexen@FreeBSD.org>2022-08-08 10:53:42 +0000
committerMichael Tuexen <tuexen@FreeBSD.org>2023-02-01 23:32:26 +0000
commitcb15bf432533997e47fad2c4375c19e6826f4b2b (patch)
tree7aec7fb5f91baac9fb75e6ab9024bd9c1c2517ee
parentec4646fe25fe590db7a354992fdf1f2cd7ae9eeb (diff)
downloadsrc-cb15bf432533997e47fad2c4375c19e6826f4b2b.tar.gz
src-cb15bf432533997e47fad2c4375c19e6826f4b2b.zip
sctp: improve handling of send() calls with no user data`
In particular, don't report EAGAIN on send() calls with no user data, which might trigger a KASSERT in asyc IO. Reported by: syzbot+3b4dc5d1d63e9bd01eda@syzkaller.appspotmail.com (cherry picked from commit bb995f2ef0e75e2e9cc784fc36fc3eb02d3e3113)
-rw-r--r--sys/netinet/sctp_output.c186
1 files changed, 98 insertions, 88 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 6736f7e9f948..6ea139795be0 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -6342,6 +6342,18 @@ sctp_msg_append(struct sctp_tcb *stcb,
error = EINVAL;
goto out_now;
}
+ if ((stcb->asoc.strmout[srcv->sinfo_stream].state != SCTP_STREAM_OPEN) &&
+ (stcb->asoc.strmout[srcv->sinfo_stream].state != SCTP_STREAM_OPENING)) {
+ /*
+ * Can't queue any data while stream reset is underway.
+ */
+ if (stcb->asoc.strmout[srcv->sinfo_stream].state > SCTP_STREAM_OPEN) {
+ error = EAGAIN;
+ } else {
+ error = EINVAL;
+ }
+ goto out_now;
+ }
/* Now can we send this? */
if ((SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_SENT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
@@ -12724,83 +12736,6 @@ sctp_lower_sosend(struct socket *so,
sinfo_flags |= SCTP_EOF;
}
}
- if (sinfo_flags & SCTP_ADDR_OVER) {
- if (addr != NULL) {
- net = sctp_findnet(stcb, addr);
- } else {
- net = NULL;
- }
- if ((net == NULL) ||
- ((port != 0) && (port != stcb->rport))) {
- error = EINVAL;
- goto out_unlocked;
- }
- } else {
- if (asoc->alternate != NULL) {
- net = asoc->alternate;
- } else {
- net = asoc->primary_destination;
- }
- }
- sinfo_stream = sndrcvninfo->sinfo_stream;
- /* Is the stream no. valid? */
- if (sinfo_stream >= asoc->streamoutcnt) {
- /* Invalid stream number */
- error = EINVAL;
- goto out_unlocked;
- }
- if ((asoc->strmout[sinfo_stream].state != SCTP_STREAM_OPEN) &&
- (asoc->strmout[sinfo_stream].state != SCTP_STREAM_OPENING)) {
- /*
- * Can't queue any data while stream reset is underway.
- */
- if (asoc->strmout[sinfo_stream].state > SCTP_STREAM_OPEN) {
- error = EAGAIN;
- } else {
- error = EINVAL;
- }
- goto out_unlocked;
- }
- if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NO_FRAGMENT)) {
- if (sndlen > (ssize_t)asoc->smallest_mtu) {
- error = EMSGSIZE;
- goto out_unlocked;
- }
- }
- atomic_add_int(&stcb->total_sends, 1);
- if (SCTP_SO_IS_NBIO(so) || (flags & (MSG_NBIO | MSG_DONTWAIT)) != 0) {
- non_blocking = true;
- }
- if (non_blocking) {
- ssize_t amount;
-
- inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
- if (user_marks_eor == 0) {
- amount = sndlen;
- } else {
- amount = 1;
- }
- 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))) {
- if ((sndlen > (ssize_t)SCTP_SB_LIMIT_SND(so)) &&
- (user_marks_eor == 0)) {
- error = EMSGSIZE;
- } else {
- error = EWOULDBLOCK;
- }
- goto out_unlocked;
- }
- }
- atomic_add_int(&asoc->sb_send_resv, (int)sndlen);
- local_soresv = sndlen;
-
- KASSERT(stcb != NULL, ("stcb is NULL"));
- 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) {
struct mbuf *mm;
@@ -12906,11 +12841,96 @@ sctp_lower_sosend(struct socket *so,
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
+ if (sinfo_flags & SCTP_ADDR_OVER) {
+ if (addr != NULL) {
+ net = sctp_findnet(stcb, addr);
+ } else {
+ net = NULL;
+ }
+ if ((net == NULL) ||
+ ((port != 0) && (port != stcb->rport))) {
+ error = EINVAL;
+ goto out_unlocked;
+ }
+ } else {
+ if (asoc->alternate != NULL) {
+ net = asoc->alternate;
+ } else {
+ net = asoc->primary_destination;
+ }
+ }
+ if (sndlen == 0) {
+ if (sinfo_flags & SCTP_EOF) {
+ got_all_of_the_send = true;
+ goto dataless_eof;
+ } else {
+ error = EINVAL;
+ goto out_unlocked;
+ }
+ }
+ if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NO_FRAGMENT)) {
+ if (sndlen > (ssize_t)asoc->smallest_mtu) {
+ error = EMSGSIZE;
+ goto out_unlocked;
+ }
+ }
+ sinfo_stream = sndrcvninfo->sinfo_stream;
+ /* Is the stream no. valid? */
+ if (sinfo_stream >= asoc->streamoutcnt) {
+ /* Invalid stream number */
+ error = EINVAL;
+ goto out_unlocked;
+ }
+ if ((asoc->strmout[sinfo_stream].state != SCTP_STREAM_OPEN) &&
+ (asoc->strmout[sinfo_stream].state != SCTP_STREAM_OPENING)) {
+ /*
+ * Can't queue any data while stream reset is underway.
+ */
+ if (asoc->strmout[sinfo_stream].state > SCTP_STREAM_OPEN) {
+ error = EAGAIN;
+ } else {
+ error = EINVAL;
+ }
+ goto out_unlocked;
+ }
+ atomic_add_int(&stcb->total_sends, 1);
+ if (SCTP_SO_IS_NBIO(so) || (flags & (MSG_NBIO | MSG_DONTWAIT)) != 0) {
+ non_blocking = true;
+ }
+ if (non_blocking) {
+ ssize_t amount;
+
+ inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
+ if (user_marks_eor == 0) {
+ amount = sndlen;
+ } else {
+ amount = 1;
+ }
+ 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))) {
+ if ((sndlen > (ssize_t)SCTP_SB_LIMIT_SND(so)) &&
+ (user_marks_eor == 0)) {
+ error = EMSGSIZE;
+ } else {
+ error = EWOULDBLOCK;
+ }
+ goto out_unlocked;
+ }
+ }
+ atomic_add_int(&asoc->sb_send_resv, (int)sndlen);
+ local_soresv = sndlen;
+
+ KASSERT(stcb != NULL, ("stcb is NULL"));
+ 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"));
+
/* Ok, we will attempt a msgsnd :> */
if (p != NULL) {
p->td_ru.ru_msgsnd++;
}
-
/* Calculate the maximum we can send */
inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) {
@@ -13013,16 +13033,6 @@ skip_preblock:
* sndlen covers for mbuf case uio_resid covers for the non-mbuf
* case NOTE: uio will be null when top/mbuf is passed
*/
- if (sndlen == 0) {
- if (sinfo_flags & SCTP_EOF) {
- got_all_of_the_send = true;
- goto dataless_eof;
- } else {
- error = EINVAL;
- goto out;
- }
- }
-
if (top == NULL) {
struct sctp_stream_queue_pending *sp;
struct sctp_stream_out *strm;