mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-03 12:35:02 +00:00
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 MFC after: 1 week
This commit is contained in:
parent
e9a2e4d1d2
commit
bb995f2ef0
@ -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;
|
||||
@ -12899,6 +12834,92 @@ sctp_lower_sosend(struct socket *so,
|
||||
goto out_unlocked;
|
||||
}
|
||||
|
||||
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"));
|
||||
|
||||
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,
|
||||
@ -12910,7 +12931,6 @@ sctp_lower_sosend(struct socket *so,
|
||||
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 @@ sctp_lower_sosend(struct socket *so,
|
||||
* 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;
|
||||
|
Loading…
Reference in New Issue
Block a user