Skip to contentSkip to conversation

Some sockopt optlen fixes

PATCH In review Marcelo Ricardo Leitner 3 participants 6 messages Jan 8, 2018 – Jan 10, 2018

Subsystems (3)
lore
Share & tools
lore

Marcelo Ricardo Leitner

author maintainer
#0 lore raw

cover Some sockopt optlen fixes

Hangbin Liu reported that some SCTP sockopt are allowing the user to get the kernel to allocate really large buffers by not having a ceiling on optlen.

This patchset address this issue (in patch 2), replace an GFP_ATOMIC that isn't needed and avoid calculating the option size multiple times in some setsockopt.

Marcelo Ricardo Leitner (3):
sctp: GFP_ATOMIC is not needed in sctp_setsockopt_events
sctp: add a ceiling to optlen in some sockopts
sctp: make use of pre-calculated len

 net/sctp/socket.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)
Signature · 1 line
2.14.3
author maintainer
#1 lore raw

PATCH 1/3 sctp: GFP_ATOMIC is not needed in sctp_setsockopt_events

So replace it with GFP_USER and also add __GFP_NOWARN.

---
 net/sctp/socket.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sctp/socket.c +1 -1
index b4fb6e4886d264f302fc67b8d822cd8bdb5f3f41..54c046783a89e76c9909ee85c83e6be38ada41a7 100644
@@ -2277,7 +2277,7 @@ static int sctp_setsockopt_events(struct sock *sk, char __user *optval,
 
 		if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
 			event = sctp_ulpevent_make_sender_dry_event(asoc,
-					GFP_ATOMIC);
+					GFP_USER | __GFP_NOWARN);
 			if (!event)
 				return -ENOMEM;
 
Signature · 1 line
2.14.3
author maintainer
#2 lore raw

PATCH 2/3 sctp: add a ceiling to optlen in some sockopts

In reply to Marcelo Ricardo Leitner

Hangbin Liu reported that some sockopt calls could cause the kernel to log a warning on memory allocation failure if the user supplied a large optlen value. That is because some of them called memdup_user() without a ceiling on optlen, allowing it to try to allocate really large buffers.

This patch adds a ceiling by limiting optlen to the maximum allowed that would still make sense for these sockopt.

---
 net/sctp/socket.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
net/sctp/socket.c +10 -0
index 54c046783a89e76c9909ee85c83e6be38ada41a7..022b94f11fd8ac0d3b839b16dfc14f86abf2324f 100644
@@ -3498,6 +3498,8 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk,
 
 	if (optlen < sizeof(struct sctp_hmacalgo))
 		return -EINVAL;
+	optlen = min_t(unsigned int, optlen, sizeof(struct sctp_hmacalgo) +
+					     SCTP_AUTH_NUM_HMACS * sizeof(u16));
 
 	hmacs = memdup_user(optval, optlen);
 	if (IS_ERR(hmacs))
@@ -3536,6 +3538,11 @@ static int sctp_setsockopt_auth_key(struct sock *sk,
 
 	if (optlen <= sizeof(struct sctp_authkey))
 		return -EINVAL;
+	/* authkey->sca_keylength is u16, so optlen can't be bigger than
+	 * this.
+	 */
+	optlen = min_t(unsigned int, optlen, USHRT_MAX +
+					     sizeof(struct sctp_authkey));
 
 	authkey = memdup_user(optval, optlen);
 	if (IS_ERR(authkey))
@@ -3893,6 +3900,9 @@ static int sctp_setsockopt_reset_streams(struct sock *sk,
 
 	if (optlen < sizeof(*params))
 		return -EINVAL;
+	/* srs_number_streams is u16, so optlen can't be bigger than this. */
+	optlen = min_t(unsigned int, optlen, USHRT_MAX +
+					     sizeof(__u16) * sizeof(*params));
 
 	params = memdup_user(optval, optlen);
 	if (IS_ERR(params))
Signature · 1 line
2.14.3
author maintainer
#3 lore raw

PATCH 3/3 sctp: make use of pre-calculated len

In reply to Marcelo Ricardo Leitner

Some sockopt handling functions were calculating the length of the buffer to be written to userspace and then calculating it again when actually writing the buffer, which could lead to some write not using an up-to-date length.

This patch updates such places to just make use of the len variable.

Also, replace some sizeof(type) to sizeof(var).

---
 net/sctp/socket.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)
net/sctp/socket.c +10 -6
index 022b94f11fd8ac0d3b839b16dfc14f86abf2324f..9b01e994f66108a12abc31f452482f1de8749d84 100644
@@ -5025,7 +5025,7 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv
 	len = sizeof(int);
 	if (put_user(len, optlen))
 		return -EFAULT;
-	if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
+	if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
 		return -EFAULT;
 	return 0;
 }
@@ -5655,6 +5655,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
 		err = -EFAULT;
 		goto out;
 	}
+	/* XXX: We should have accounted for sizeof(struct sctp_getaddrs) too,
+	 * but we can't change it anymore.
+	 */
 	if (put_user(bytes_copied, optlen))
 		err = -EFAULT;
 out:
@@ -6091,7 +6094,7 @@ static int sctp_getsockopt_maxseg(struct sock *sk, int len,
 		params.assoc_id = 0;
 	} else if (len >= sizeof(struct sctp_assoc_value)) {
 		len = sizeof(struct sctp_assoc_value);
-		if (copy_from_user(&params, optval, sizeof(params)))
+		if (copy_from_user(&params, optval, len))
 			return -EFAULT;
 	} else
 		return -EINVAL;
@@ -6261,7 +6264,9 @@ static int sctp_getsockopt_active_key(struct sock *sk, int len,
 
 	if (len < sizeof(struct sctp_authkeyid))
 		return -EINVAL;
-	if (copy_from_user(&val, optval, sizeof(struct sctp_authkeyid)))
+
+	len = sizeof(struct sctp_authkeyid);
+	if (copy_from_user(&val, optval, len))
 		return -EFAULT;
 
 	asoc = sctp_id2assoc(sk, val.scact_assoc_id);
@@ -6273,7 +6278,6 @@ static int sctp_getsockopt_active_key(struct sock *sk, int len,
 	else
 		val.scact_keynumber = ep->active_key_id;
 
-	len = sizeof(struct sctp_authkeyid);
 	if (put_user(len, optlen))
 		return -EFAULT;
 	if (copy_to_user(optval, &val, len))
@@ -6299,7 +6303,7 @@ static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
 	if (len < sizeof(struct sctp_authchunks))
 		return -EINVAL;
 
-	if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
+	if (copy_from_user(&val, optval, sizeof(val)))
 		return -EFAULT;
 
 	to = p->gauth_chunks;
@@ -6344,7 +6348,7 @@ static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
 	if (len < sizeof(struct sctp_authchunks))
 		return -EINVAL;
 
-	if (copy_from_user(&val, optval, sizeof(struct sctp_authchunks)))
+	if (copy_from_user(&val, optval, sizeof(val)))
 		return -EFAULT;
 
 	to = p->gauth_chunks;
Signature · 1 line
2.14.3
Acked-by on the v1 series
#4 lore raw
In reply to Marcelo Ricardo Leitner
Quoted 8 lines · Marcelo Ricardo Leitner

Hangbin Liu reported that some SCTP sockopt are allowing the user to get the kernel to allocate really large buffers by not having a ceiling on optlen.

This patchset address this issue (in patch 2), replace an GFP_ATOMIC that isn't needed and avoid calculating the option size multiple times in some setsockopt.

Quoted 12 lines

Marcelo Ricardo Leitner (3):
sctp: GFP_ATOMIC is not needed in sctp_setsockopt_events
sctp: add a ceiling to optlen in some sockopts
sctp: make use of pre-calculated len

 net/sctp/socket.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)
Signature · 1 line
2.14.3

Series

maintainer
#5 lore raw
In reply to Marcelo Ricardo Leitner

From: Marcelo Ricardo Leitner <[email protected]>
Date: Mon, 8 Jan 2018 19:02:26 -0200

Quoted 7 lines

Hangbin Liu reported that some SCTP sockopt are allowing the user to get the kernel to allocate really large buffers by not having a ceiling on optlen.

This patchset address this issue (in patch 2), replace an GFP_ATOMIC that isn't needed and avoid calculating the option size multiple times in some setsockopt.

Series applied, thank you.