Commit 423852f8 authored by Xin Long's avatar Xin Long Committed by David S. Miller

sctp: check stream reset info len before making reconf chunk

Now when resetting stream, if both in and out flags are set, the info
len can reach:
  sizeof(struct sctp_strreset_outreq) + SCTP_MAX_STREAM(65535) +
  sizeof(struct sctp_strreset_inreq)  + SCTP_MAX_STREAM(65535)
even without duplicated stream no, this value is far greater than the
chunk's max size.

_sctp_make_chunk doesn't do any check for this, which would cause the
skb it allocs is huge, syzbot even reported a crash due to this.

This patch is to check stream reset info len before making reconf
chunk and return EINVAL if the len exceeds chunk's capacity.

Thanks Marcelo and Neil for making this clear.

v1->v2:
  - move the check into sctp_send_reset_streams instead.

Fixes: cc16f00f ("sctp: add support for generating stream reconf ssn reset request chunk")
Reported-by: default avatarDmitry Vyukov <dvyukov@google.com>
Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cea0cc80
...@@ -3594,8 +3594,8 @@ struct sctp_chunk *sctp_make_strreset_req( ...@@ -3594,8 +3594,8 @@ struct sctp_chunk *sctp_make_strreset_req(
__u16 stream_num, __be16 *stream_list, __u16 stream_num, __be16 *stream_list,
bool out, bool in) bool out, bool in)
{ {
__u16 stream_len = stream_num * sizeof(__u16);
struct sctp_strreset_outreq outreq; struct sctp_strreset_outreq outreq;
__u16 stream_len = stream_num * 2;
struct sctp_strreset_inreq inreq; struct sctp_strreset_inreq inreq;
struct sctp_chunk *retval; struct sctp_chunk *retval;
__u16 outlen, inlen; __u16 outlen, inlen;
......
...@@ -282,16 +282,32 @@ int sctp_send_reset_streams(struct sctp_association *asoc, ...@@ -282,16 +282,32 @@ int sctp_send_reset_streams(struct sctp_association *asoc,
str_nums = params->srs_number_streams; str_nums = params->srs_number_streams;
str_list = params->srs_stream_list; str_list = params->srs_stream_list;
if (out && str_nums) if (str_nums) {
int param_len = 0;
if (out) {
for (i = 0; i < str_nums; i++) for (i = 0; i < str_nums; i++)
if (str_list[i] >= stream->outcnt) if (str_list[i] >= stream->outcnt)
goto out; goto out;
if (in && str_nums) param_len = str_nums * sizeof(__u16) +
sizeof(struct sctp_strreset_outreq);
}
if (in) {
for (i = 0; i < str_nums; i++) for (i = 0; i < str_nums; i++)
if (str_list[i] >= stream->incnt) if (str_list[i] >= stream->incnt)
goto out; goto out;
param_len += str_nums * sizeof(__u16) +
sizeof(struct sctp_strreset_inreq);
}
if (param_len > SCTP_MAX_CHUNK_LEN -
sizeof(struct sctp_reconf_chunk))
goto out;
}
nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL); nstr_list = kcalloc(str_nums, sizeof(__be16), GFP_KERNEL);
if (!nstr_list) { if (!nstr_list) {
retval = -ENOMEM; retval = -ENOMEM;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment