SCTP: update sctp_getsockopt helpers to allow oversized buffers
I noted the other day while looking at a bug that was ostensibly
in some perl networking library, that we strictly avoid allowing getsockopt
operations to complete if we pass in oversized buffers. This seems to make
libraries like Perl::NET malfunction since it seems to allocate oversized
buffers for use in several operations. It also seems to be out of line with
the way udp, tcp and ip getsockopt routines handle buffer input (since the
*optlen pointer in both an input and an output and gets set to the length
of the data that we copy into the buffer). This patch brings our getsockopt
helpers into line with other protocols, and allows us to accept oversized
buffers for our getsockopt operations. Tested by me with good results.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Sridhar Samudrala <sri@us.ibm.com>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 6edaaa0..c1f239a 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -3375,12 +3375,13 @@
sctp_assoc_t associd;
int retval = 0;
- if (len != sizeof(status)) {
+ if (len < sizeof(status)) {
retval = -EINVAL;
goto out;
}
- if (copy_from_user(&status, optval, sizeof(status))) {
+ len = sizeof(status);
+ if (copy_from_user(&status, optval, len)) {
retval = -EFAULT;
goto out;
}
@@ -3452,12 +3453,13 @@
struct sctp_transport *transport;
int retval = 0;
- if (len != sizeof(pinfo)) {
+ if (len < sizeof(pinfo)) {
retval = -EINVAL;
goto out;
}
- if (copy_from_user(&pinfo, optval, sizeof(pinfo))) {
+ len = sizeof(pinfo);
+ if (copy_from_user(&pinfo, optval, len)) {
retval = -EFAULT;
goto out;
}
@@ -3523,8 +3525,11 @@
static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
int __user *optlen)
{
- if (len != sizeof(struct sctp_event_subscribe))
+ if (len < sizeof(struct sctp_event_subscribe))
return -EINVAL;
+ len = sizeof(struct sctp_event_subscribe);
+ if (put_user(len, optlen))
+ return -EFAULT;
if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
return -EFAULT;
return 0;
@@ -3546,9 +3551,12 @@
/* Applicable to UDP-style socket only */
if (sctp_style(sk, TCP))
return -EOPNOTSUPP;
- if (len != sizeof(int))
+ if (len < sizeof(int))
return -EINVAL;
- if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
+ len = sizeof(int);
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
return -EFAULT;
return 0;
}
@@ -3599,8 +3607,9 @@
int retval = 0;
struct sctp_association *asoc;
- if (len != sizeof(sctp_peeloff_arg_t))
+ if (len < sizeof(sctp_peeloff_arg_t))
return -EINVAL;
+ len = sizeof(sctp_peeloff_arg_t);
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
@@ -3628,6 +3637,8 @@
/* Return the fd mapped to the new socket. */
peeloff.sd = retval;
+ if (put_user(len, optlen))
+ return -EFAULT;
if (copy_to_user(optval, &peeloff, len))
retval = -EFAULT;
@@ -3736,9 +3747,9 @@
struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk);
- if (len != sizeof(struct sctp_paddrparams))
+ if (len < sizeof(struct sctp_paddrparams))
return -EINVAL;
-
+ len = sizeof(struct sctp_paddrparams);
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
@@ -3837,9 +3848,11 @@
struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk);
- if (len != sizeof(struct sctp_assoc_value))
+ if (len < sizeof(struct sctp_assoc_value))
return - EINVAL;
+ len = sizeof(struct sctp_assoc_value);
+
if (copy_from_user(¶ms, optval, len))
return -EFAULT;
@@ -3888,8 +3901,11 @@
*/
static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
- if (len != sizeof(struct sctp_initmsg))
+ if (len < sizeof(struct sctp_initmsg))
return -EINVAL;
+ len = sizeof(struct sctp_initmsg);
+ if (put_user(len, optlen))
+ return -EFAULT;
if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
return -EFAULT;
return 0;
@@ -3904,7 +3920,7 @@
struct list_head *pos;
int cnt = 0;
- if (len != sizeof(sctp_assoc_t))
+ if (len < sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -3940,10 +3956,12 @@
struct sctp_sock *sp = sctp_sk(sk);
int addrlen;
- if (len != sizeof(struct sctp_getaddrs_old))
+ if (len < sizeof(struct sctp_getaddrs_old))
return -EINVAL;
- if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+ len = sizeof(struct sctp_getaddrs_old);
+
+ if (copy_from_user(&getaddrs, optval, len))
return -EFAULT;
if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -3966,7 +3984,9 @@
if (cnt >= getaddrs.addr_num) break;
}
getaddrs.addr_num = cnt;
- if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &getaddrs, len))
return -EFAULT;
return 0;
@@ -4037,7 +4057,7 @@
rwlock_t *addr_lock;
int cnt = 0;
- if (len != sizeof(sctp_assoc_t))
+ if (len < sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -4179,10 +4199,11 @@
void *buf;
int bytes_copied = 0;
- if (len != sizeof(struct sctp_getaddrs_old))
+ if (len < sizeof(struct sctp_getaddrs_old))
return -EINVAL;
- if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+ len = sizeof(struct sctp_getaddrs_old);
+ if (copy_from_user(&getaddrs, optval, len))
return -EFAULT;
if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -4254,7 +4275,7 @@
/* copy the leading structure back to user */
getaddrs.addr_num = cnt;
- if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+ if (copy_to_user(optval, &getaddrs, len))
err = -EFAULT;
error:
@@ -4282,7 +4303,7 @@
void *addrs;
void *buf;
- if (len <= sizeof(struct sctp_getaddrs))
+ if (len < sizeof(struct sctp_getaddrs))
return -EINVAL;
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
@@ -4379,10 +4400,12 @@
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
- if (len != sizeof(struct sctp_prim))
+ if (len < sizeof(struct sctp_prim))
return -EINVAL;
- if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
+ len = sizeof(struct sctp_prim);
+
+ if (copy_from_user(&prim, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
@@ -4398,7 +4421,9 @@
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
(union sctp_addr *)&prim.ssp_addr);
- if (copy_to_user(optval, &prim, sizeof(struct sctp_prim)))
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &prim, len))
return -EFAULT;
return 0;
@@ -4415,10 +4440,15 @@
{
struct sctp_setadaptation adaptation;
- if (len != sizeof(struct sctp_setadaptation))
+ if (len < sizeof(struct sctp_setadaptation))
return -EINVAL;
+ len = sizeof(struct sctp_setadaptation);
+
adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
+
+ if (put_user(len, optlen))
+ return -EFAULT;
if (copy_to_user(optval, &adaptation, len))
return -EFAULT;
@@ -4452,9 +4482,12 @@
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
- if (len != sizeof(struct sctp_sndrcvinfo))
+ if (len < sizeof(struct sctp_sndrcvinfo))
return -EINVAL;
- if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo)))
+
+ len = sizeof(struct sctp_sndrcvinfo);
+
+ if (copy_from_user(&info, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
@@ -4475,7 +4508,9 @@
info.sinfo_timetolive = sp->default_timetolive;
}
- if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo)))
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &info, len))
return -EFAULT;
return 0;
@@ -4526,10 +4561,12 @@
struct sctp_rtoinfo rtoinfo;
struct sctp_association *asoc;
- if (len != sizeof (struct sctp_rtoinfo))
+ if (len < sizeof (struct sctp_rtoinfo))
return -EINVAL;
- if (copy_from_user(&rtoinfo, optval, sizeof (struct sctp_rtoinfo)))
+ len = sizeof(struct sctp_rtoinfo);
+
+ if (copy_from_user(&rtoinfo, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
@@ -4581,11 +4618,12 @@
struct list_head *pos;
int cnt = 0;
- if (len != sizeof (struct sctp_assocparams))
+ if (len < sizeof (struct sctp_assocparams))
return -EINVAL;
- if (copy_from_user(&assocparams, optval,
- sizeof (struct sctp_assocparams)))
+ len = sizeof(struct sctp_assocparams);
+
+ if (copy_from_user(&assocparams, optval, len))
return -EFAULT;
asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
@@ -4671,9 +4709,11 @@
struct sctp_sock *sp;
struct sctp_association *asoc;
- if (len != sizeof(struct sctp_assoc_value))
+ if (len < sizeof(struct sctp_assoc_value))
return -EINVAL;
+ len = sizeof(struct sctp_assoc_value);
+
if (copy_from_user(¶ms, optval, len))
return -EFAULT;