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>

authored by Neil Horman and committed by Vlad Yasevich 408f22e8 19e6454c

+74 -34
+74 -34
net/sctp/socket.c
··· 3375 3375 sctp_assoc_t associd; 3376 3376 int retval = 0; 3377 3377 3378 - if (len != sizeof(status)) { 3378 + if (len < sizeof(status)) { 3379 3379 retval = -EINVAL; 3380 3380 goto out; 3381 3381 } 3382 3382 3383 - if (copy_from_user(&status, optval, sizeof(status))) { 3383 + len = sizeof(status); 3384 + if (copy_from_user(&status, optval, len)) { 3384 3385 retval = -EFAULT; 3385 3386 goto out; 3386 3387 } ··· 3453 3452 struct sctp_transport *transport; 3454 3453 int retval = 0; 3455 3454 3456 - if (len != sizeof(pinfo)) { 3455 + if (len < sizeof(pinfo)) { 3457 3456 retval = -EINVAL; 3458 3457 goto out; 3459 3458 } 3460 3459 3461 - if (copy_from_user(&pinfo, optval, sizeof(pinfo))) { 3460 + len = sizeof(pinfo); 3461 + if (copy_from_user(&pinfo, optval, len)) { 3462 3462 retval = -EFAULT; 3463 3463 goto out; 3464 3464 } ··· 3525 3523 static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval, 3526 3524 int __user *optlen) 3527 3525 { 3528 - if (len != sizeof(struct sctp_event_subscribe)) 3526 + if (len < sizeof(struct sctp_event_subscribe)) 3529 3527 return -EINVAL; 3528 + len = sizeof(struct sctp_event_subscribe); 3529 + if (put_user(len, optlen)) 3530 + return -EFAULT; 3530 3531 if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len)) 3531 3532 return -EFAULT; 3532 3533 return 0; ··· 3551 3546 /* Applicable to UDP-style socket only */ 3552 3547 if (sctp_style(sk, TCP)) 3553 3548 return -EOPNOTSUPP; 3554 - if (len != sizeof(int)) 3549 + if (len < sizeof(int)) 3555 3550 return -EINVAL; 3556 - if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len)) 3551 + len = sizeof(int); 3552 + if (put_user(len, optlen)) 3553 + return -EFAULT; 3554 + if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int))) 3557 3555 return -EFAULT; 3558 3556 return 0; 3559 3557 } ··· 3607 3599 int retval = 0; 3608 3600 struct sctp_association *asoc; 3609 3601 3610 - if (len != sizeof(sctp_peeloff_arg_t)) 3602 + if (len < sizeof(sctp_peeloff_arg_t)) 3611 3603 return -EINVAL; 3604 + len = sizeof(sctp_peeloff_arg_t); 3612 3605 if (copy_from_user(&peeloff, optval, len)) 3613 3606 return -EFAULT; 3614 3607 ··· 3637 3628 3638 3629 /* Return the fd mapped to the new socket. */ 3639 3630 peeloff.sd = retval; 3631 + if (put_user(len, optlen)) 3632 + return -EFAULT; 3640 3633 if (copy_to_user(optval, &peeloff, len)) 3641 3634 retval = -EFAULT; 3642 3635 ··· 3747 3736 struct sctp_association *asoc = NULL; 3748 3737 struct sctp_sock *sp = sctp_sk(sk); 3749 3738 3750 - if (len != sizeof(struct sctp_paddrparams)) 3739 + if (len < sizeof(struct sctp_paddrparams)) 3751 3740 return -EINVAL; 3752 - 3741 + len = sizeof(struct sctp_paddrparams); 3753 3742 if (copy_from_user(&params, optval, len)) 3754 3743 return -EFAULT; 3755 3744 ··· 3848 3837 struct sctp_association *asoc = NULL; 3849 3838 struct sctp_sock *sp = sctp_sk(sk); 3850 3839 3851 - if (len != sizeof(struct sctp_assoc_value)) 3840 + if (len < sizeof(struct sctp_assoc_value)) 3852 3841 return - EINVAL; 3842 + 3843 + len = sizeof(struct sctp_assoc_value); 3853 3844 3854 3845 if (copy_from_user(&params, optval, len)) 3855 3846 return -EFAULT; ··· 3901 3888 */ 3902 3889 static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen) 3903 3890 { 3904 - if (len != sizeof(struct sctp_initmsg)) 3891 + if (len < sizeof(struct sctp_initmsg)) 3905 3892 return -EINVAL; 3893 + len = sizeof(struct sctp_initmsg); 3894 + if (put_user(len, optlen)) 3895 + return -EFAULT; 3906 3896 if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len)) 3907 3897 return -EFAULT; 3908 3898 return 0; ··· 3920 3904 struct list_head *pos; 3921 3905 int cnt = 0; 3922 3906 3923 - if (len != sizeof(sctp_assoc_t)) 3907 + if (len < sizeof(sctp_assoc_t)) 3924 3908 return -EINVAL; 3925 3909 3926 3910 if (copy_from_user(&id, optval, sizeof(sctp_assoc_t))) ··· 3956 3940 struct sctp_sock *sp = sctp_sk(sk); 3957 3941 int addrlen; 3958 3942 3959 - if (len != sizeof(struct sctp_getaddrs_old)) 3943 + if (len < sizeof(struct sctp_getaddrs_old)) 3960 3944 return -EINVAL; 3961 3945 3962 - if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old))) 3946 + len = sizeof(struct sctp_getaddrs_old); 3947 + 3948 + if (copy_from_user(&getaddrs, optval, len)) 3963 3949 return -EFAULT; 3964 3950 3965 3951 if (getaddrs.addr_num <= 0) return -EINVAL; ··· 3984 3966 if (cnt >= getaddrs.addr_num) break; 3985 3967 } 3986 3968 getaddrs.addr_num = cnt; 3987 - if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) 3969 + if (put_user(len, optlen)) 3970 + return -EFAULT; 3971 + if (copy_to_user(optval, &getaddrs, len)) 3988 3972 return -EFAULT; 3989 3973 3990 3974 return 0; ··· 4057 4037 rwlock_t *addr_lock; 4058 4038 int cnt = 0; 4059 4039 4060 - if (len != sizeof(sctp_assoc_t)) 4040 + if (len < sizeof(sctp_assoc_t)) 4061 4041 return -EINVAL; 4062 4042 4063 4043 if (copy_from_user(&id, optval, sizeof(sctp_assoc_t))) ··· 4199 4179 void *buf; 4200 4180 int bytes_copied = 0; 4201 4181 4202 - if (len != sizeof(struct sctp_getaddrs_old)) 4182 + if (len < sizeof(struct sctp_getaddrs_old)) 4203 4183 return -EINVAL; 4204 4184 4205 - if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old))) 4185 + len = sizeof(struct sctp_getaddrs_old); 4186 + if (copy_from_user(&getaddrs, optval, len)) 4206 4187 return -EFAULT; 4207 4188 4208 4189 if (getaddrs.addr_num <= 0) return -EINVAL; ··· 4275 4254 4276 4255 /* copy the leading structure back to user */ 4277 4256 getaddrs.addr_num = cnt; 4278 - if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) 4257 + if (copy_to_user(optval, &getaddrs, len)) 4279 4258 err = -EFAULT; 4280 4259 4281 4260 error: ··· 4303 4282 void *addrs; 4304 4283 void *buf; 4305 4284 4306 - if (len <= sizeof(struct sctp_getaddrs)) 4285 + if (len < sizeof(struct sctp_getaddrs)) 4307 4286 return -EINVAL; 4308 4287 4309 4288 if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs))) ··· 4400 4379 struct sctp_association *asoc; 4401 4380 struct sctp_sock *sp = sctp_sk(sk); 4402 4381 4403 - if (len != sizeof(struct sctp_prim)) 4382 + if (len < sizeof(struct sctp_prim)) 4404 4383 return -EINVAL; 4405 4384 4406 - if (copy_from_user(&prim, optval, sizeof(struct sctp_prim))) 4385 + len = sizeof(struct sctp_prim); 4386 + 4387 + if (copy_from_user(&prim, optval, len)) 4407 4388 return -EFAULT; 4408 4389 4409 4390 asoc = sctp_id2assoc(sk, prim.ssp_assoc_id); ··· 4421 4398 sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, 4422 4399 (union sctp_addr *)&prim.ssp_addr); 4423 4400 4424 - if (copy_to_user(optval, &prim, sizeof(struct sctp_prim))) 4401 + if (put_user(len, optlen)) 4402 + return -EFAULT; 4403 + if (copy_to_user(optval, &prim, len)) 4425 4404 return -EFAULT; 4426 4405 4427 4406 return 0; ··· 4440 4415 { 4441 4416 struct sctp_setadaptation adaptation; 4442 4417 4443 - if (len != sizeof(struct sctp_setadaptation)) 4418 + if (len < sizeof(struct sctp_setadaptation)) 4444 4419 return -EINVAL; 4445 4420 4421 + len = sizeof(struct sctp_setadaptation); 4422 + 4446 4423 adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind; 4424 + 4425 + if (put_user(len, optlen)) 4426 + return -EFAULT; 4447 4427 if (copy_to_user(optval, &adaptation, len)) 4448 4428 return -EFAULT; 4449 4429 ··· 4482 4452 struct sctp_association *asoc; 4483 4453 struct sctp_sock *sp = sctp_sk(sk); 4484 4454 4485 - if (len != sizeof(struct sctp_sndrcvinfo)) 4455 + if (len < sizeof(struct sctp_sndrcvinfo)) 4486 4456 return -EINVAL; 4487 - if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo))) 4457 + 4458 + len = sizeof(struct sctp_sndrcvinfo); 4459 + 4460 + if (copy_from_user(&info, optval, len)) 4488 4461 return -EFAULT; 4489 4462 4490 4463 asoc = sctp_id2assoc(sk, info.sinfo_assoc_id); ··· 4508 4475 info.sinfo_timetolive = sp->default_timetolive; 4509 4476 } 4510 4477 4511 - if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo))) 4478 + if (put_user(len, optlen)) 4479 + return -EFAULT; 4480 + if (copy_to_user(optval, &info, len)) 4512 4481 return -EFAULT; 4513 4482 4514 4483 return 0; ··· 4561 4526 struct sctp_rtoinfo rtoinfo; 4562 4527 struct sctp_association *asoc; 4563 4528 4564 - if (len != sizeof (struct sctp_rtoinfo)) 4529 + if (len < sizeof (struct sctp_rtoinfo)) 4565 4530 return -EINVAL; 4566 4531 4567 - if (copy_from_user(&rtoinfo, optval, sizeof (struct sctp_rtoinfo))) 4532 + len = sizeof(struct sctp_rtoinfo); 4533 + 4534 + if (copy_from_user(&rtoinfo, optval, len)) 4568 4535 return -EFAULT; 4569 4536 4570 4537 asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id); ··· 4618 4581 struct list_head *pos; 4619 4582 int cnt = 0; 4620 4583 4621 - if (len != sizeof (struct sctp_assocparams)) 4584 + if (len < sizeof (struct sctp_assocparams)) 4622 4585 return -EINVAL; 4623 4586 4624 - if (copy_from_user(&assocparams, optval, 4625 - sizeof (struct sctp_assocparams))) 4587 + len = sizeof(struct sctp_assocparams); 4588 + 4589 + if (copy_from_user(&assocparams, optval, len)) 4626 4590 return -EFAULT; 4627 4591 4628 4592 asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id); ··· 4709 4671 struct sctp_sock *sp; 4710 4672 struct sctp_association *asoc; 4711 4673 4712 - if (len != sizeof(struct sctp_assoc_value)) 4674 + if (len < sizeof(struct sctp_assoc_value)) 4713 4675 return -EINVAL; 4676 + 4677 + len = sizeof(struct sctp_assoc_value); 4714 4678 4715 4679 if (copy_from_user(&params, optval, len)) 4716 4680 return -EFAULT;