Skip to content

Commit

Permalink
sctp: set frag_point in sctp_setsockopt_maxseg correctly
Browse files Browse the repository at this point in the history
commit ecca8f8 upstream.

Now in sctp_setsockopt_maxseg user_frag or frag_point can be set with
val >= 8 and val <= SCTP_MAX_CHUNK_LEN. But both checks are incorrect.

val >= 8 means frag_point can even be less than SCTP_DEFAULT_MINSEGMENT.
Then in sctp_datamsg_from_user(), when it's value is greater than cookie
echo len and trying to bundle with cookie echo chunk, the first_len will
overflow.

The worse case is when it's value is equal as cookie echo len, first_len
becomes 0, it will go into a dead loop for fragment later on. In Hangbin
syzkaller testing env, oom was even triggered due to consecutive memory
allocation in that loop.

Besides, SCTP_MAX_CHUNK_LEN is the max size of the whole chunk, it should
deduct the data header for frag_point or user_frag check.

This patch does a proper check with SCTP_DEFAULT_MINSEGMENT subtracting
the sctphdr and datahdr, SCTP_MAX_CHUNK_LEN subtracting datahdr when
setting frag_point via sockopt. It also improves sctp_setsockopt_maxseg
codes.

Suggested-by: Marcelo Ricardo Leitner <[email protected]>
Reported-by: Hangbin Liu <[email protected]>
Signed-off-by: Xin Long <[email protected]>
Acked-by: Marcelo Ricardo Leitner <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
lxin authored and gregkh committed Feb 25, 2018
1 parent 8555288 commit 2e67122
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 11 deletions.
3 changes: 2 additions & 1 deletion include/net/sctp/sctp.h
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@ static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu)
if (asoc->user_frag)
frag = min_t(int, frag, asoc->user_frag);

frag = SCTP_TRUNC4(min_t(int, frag, SCTP_MAX_CHUNK_LEN));
frag = SCTP_TRUNC4(min_t(int, frag, SCTP_MAX_CHUNK_LEN -
sizeof(struct sctp_data_chunk)));

return frag;
}
Expand Down
29 changes: 19 additions & 10 deletions net/sctp/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -3125,9 +3125,9 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsign
*/
static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen)
{
struct sctp_sock *sp = sctp_sk(sk);
struct sctp_assoc_value params;
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
int val;

if (optlen == sizeof(int)) {
Expand All @@ -3143,26 +3143,35 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
val = params.assoc_value;
} else
} else {
return -EINVAL;
}

if ((val != 0) && ((val < 8) || (val > SCTP_MAX_CHUNK_LEN)))
return -EINVAL;
if (val) {
int min_len, max_len;

asoc = sctp_id2assoc(sk, params.assoc_id);
if (!asoc && params.assoc_id && sctp_style(sk, UDP))
return -EINVAL;
min_len = SCTP_DEFAULT_MINSEGMENT - sp->pf->af->net_header_len;
min_len -= sizeof(struct sctphdr) +
sizeof(struct sctp_data_chunk);

max_len = SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_data_chunk);

if (val < min_len || val > max_len)
return -EINVAL;
}

asoc = sctp_id2assoc(sk, params.assoc_id);
if (asoc) {
if (val == 0) {
val = asoc->pathmtu;
val -= sp->pf->af->net_header_len;
val = asoc->pathmtu - sp->pf->af->net_header_len;
val -= sizeof(struct sctphdr) +
sizeof(struct sctp_data_chunk);
sizeof(struct sctp_data_chunk);
}
asoc->user_frag = val;
asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
} else {
if (params.assoc_id && sctp_style(sk, UDP))
return -EINVAL;
sp->user_frag = val;
}

Expand Down

0 comments on commit 2e67122

Please sign in to comment.