aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXin Long <lucien.xin@gmail.com>2017-11-17 01:11:11 -0500
committerDavid S. Miller <davem@davemloft.net>2017-11-17 20:32:41 -0500
commitecca8f88da5c4260cc2bccfefd2a24976704c366 (patch)
tree763c7eece0fe70691f21ce60d1f6b3ef8e6759b5
parentd35ef8f846c72d84bfccf239c248c84f79c3a7e8 (diff)
sctp: set frag_point in sctp_setsockopt_maxseg correctly
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 <marcelo.leitner@gmail.com> Reported-by: Hangbin Liu <liuhangbin@gmail.com> Signed-off-by: Xin Long <lucien.xin@gmail.com> Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/sctp/sctp.h3
-rw-r--r--net/sctp/socket.c29
2 files changed, 21 insertions, 11 deletions
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index d7d8cba01469..749a42882437 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -444,7 +444,8 @@ static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu)
444 if (asoc->user_frag) 444 if (asoc->user_frag)
445 frag = min_t(int, frag, asoc->user_frag); 445 frag = min_t(int, frag, asoc->user_frag);
446 446
447 frag = SCTP_TRUNC4(min_t(int, frag, SCTP_MAX_CHUNK_LEN)); 447 frag = SCTP_TRUNC4(min_t(int, frag, SCTP_MAX_CHUNK_LEN -
448 sizeof(struct sctp_data_chunk)));
448 449
449 return frag; 450 return frag;
450} 451}
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 4c0a77292792..3204a9b29407 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -3140,9 +3140,9 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsign
3140 */ 3140 */
3141static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen) 3141static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen)
3142{ 3142{
3143 struct sctp_sock *sp = sctp_sk(sk);
3143 struct sctp_assoc_value params; 3144 struct sctp_assoc_value params;
3144 struct sctp_association *asoc; 3145 struct sctp_association *asoc;
3145 struct sctp_sock *sp = sctp_sk(sk);
3146 int val; 3146 int val;
3147 3147
3148 if (optlen == sizeof(int)) { 3148 if (optlen == sizeof(int)) {
@@ -3158,26 +3158,35 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
3158 if (copy_from_user(&params, optval, optlen)) 3158 if (copy_from_user(&params, optval, optlen))
3159 return -EFAULT; 3159 return -EFAULT;
3160 val = params.assoc_value; 3160 val = params.assoc_value;
3161 } else 3161 } else {
3162 return -EINVAL; 3162 return -EINVAL;
3163 }
3163 3164
3164 if ((val != 0) && ((val < 8) || (val > SCTP_MAX_CHUNK_LEN))) 3165 if (val) {
3165 return -EINVAL; 3166 int min_len, max_len;
3166 3167
3167 asoc = sctp_id2assoc(sk, params.assoc_id); 3168 min_len = SCTP_DEFAULT_MINSEGMENT - sp->pf->af->net_header_len;
3168 if (!asoc && params.assoc_id && sctp_style(sk, UDP)) 3169 min_len -= sizeof(struct sctphdr) +
3169 return -EINVAL; 3170 sizeof(struct sctp_data_chunk);
3171
3172 max_len = SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_data_chunk);
3170 3173
3174 if (val < min_len || val > max_len)
3175 return -EINVAL;
3176 }
3177
3178 asoc = sctp_id2assoc(sk, params.assoc_id);
3171 if (asoc) { 3179 if (asoc) {
3172 if (val == 0) { 3180 if (val == 0) {
3173 val = asoc->pathmtu; 3181 val = asoc->pathmtu - sp->pf->af->net_header_len;
3174 val -= sp->pf->af->net_header_len;
3175 val -= sizeof(struct sctphdr) + 3182 val -= sizeof(struct sctphdr) +
3176 sizeof(struct sctp_data_chunk); 3183 sizeof(struct sctp_data_chunk);
3177 } 3184 }
3178 asoc->user_frag = val; 3185 asoc->user_frag = val;
3179 asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu); 3186 asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
3180 } else { 3187 } else {
3188 if (params.assoc_id && sctp_style(sk, UDP))
3189 return -EINVAL;
3181 sp->user_frag = val; 3190 sp->user_frag = val;
3182 } 3191 }
3183 3192