diff options
Diffstat (limited to 'net/sctp/socket.c')
| -rw-r--r-- | net/sctp/socket.c | 85 | 
1 files changed, 51 insertions, 34 deletions
| diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 3204a9b29407..039fcb618c34 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -85,7 +85,7 @@  static int sctp_writeable(struct sock *sk);  static void sctp_wfree(struct sk_buff *skb);  static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, -				size_t msg_len, struct sock **orig_sk); +				size_t msg_len);  static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);  static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);  static int sctp_wait_for_accept(struct sock *sk, long timeo); @@ -188,13 +188,13 @@ static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,  		list_for_each_entry(chunk, &t->transmitted, transmitted_list)  			cb(chunk); -	list_for_each_entry(chunk, &q->retransmit, list) +	list_for_each_entry(chunk, &q->retransmit, transmitted_list)  		cb(chunk); -	list_for_each_entry(chunk, &q->sacked, list) +	list_for_each_entry(chunk, &q->sacked, transmitted_list)  		cb(chunk); -	list_for_each_entry(chunk, &q->abandoned, list) +	list_for_each_entry(chunk, &q->abandoned, transmitted_list)  		cb(chunk);  	list_for_each_entry(chunk, &q->out_chunk_list, list) @@ -335,16 +335,14 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,  	if (len < sizeof (struct sockaddr))  		return NULL; +	if (!opt->pf->af_supported(addr->sa.sa_family, opt)) +		return NULL; +  	/* V4 mapped address are really of AF_INET family */  	if (addr->sa.sa_family == AF_INET6 && -	    ipv6_addr_v4mapped(&addr->v6.sin6_addr)) { -		if (!opt->pf->af_supported(AF_INET, opt)) -			return NULL; -	} else { -		/* Does this PF support this AF? */ -		if (!opt->pf->af_supported(addr->sa.sa_family, opt)) -			return NULL; -	} +	    ipv6_addr_v4mapped(&addr->v6.sin6_addr) && +	    !opt->pf->af_supported(AF_INET, opt)) +		return NULL;  	/* If we get this far, af is valid. */  	af = sctp_get_af_specific(addr->sa.sa_family); @@ -1883,8 +1881,14 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)  		 */  		if (sinit) {  			if (sinit->sinit_num_ostreams) { -				asoc->c.sinit_num_ostreams = -					sinit->sinit_num_ostreams; +				__u16 outcnt = sinit->sinit_num_ostreams; + +				asoc->c.sinit_num_ostreams = outcnt; +				/* outcnt has been changed, so re-init stream */ +				err = sctp_stream_init(&asoc->stream, outcnt, 0, +						       GFP_KERNEL); +				if (err) +					goto out_free;  			}  			if (sinit->sinit_max_instreams) {  				asoc->c.sinit_max_instreams = @@ -1971,7 +1975,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)  	timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);  	if (!sctp_wspace(asoc)) {  		/* sk can be changed by peel off when waiting for buf. */ -		err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len, &sk); +		err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);  		if (err) {  			if (err == -ESRCH) {  				/* asoc is already dead. */ @@ -2277,7 +2281,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; @@ -3498,6 +3502,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 +3542,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)) @@ -3891,13 +3902,20 @@ static int sctp_setsockopt_reset_streams(struct sock *sk,  	struct sctp_association *asoc;  	int retval = -EINVAL; -	if (optlen < sizeof(struct sctp_reset_streams)) +	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))  		return PTR_ERR(params); +	if (params->srs_number_streams * sizeof(__u16) > +	    optlen - sizeof(*params)) +		goto out; +  	asoc = sctp_id2assoc(sk, params->srs_assoc_id);  	if (!asoc)  		goto out; @@ -4494,7 +4512,7 @@ static int sctp_init_sock(struct sock *sk)  	SCTP_DBG_OBJCNT_INC(sock);  	local_bh_disable(); -	percpu_counter_inc(&sctp_sockets_allocated); +	sk_sockets_allocated_inc(sk);  	sock_prot_inuse_add(net, sk->sk_prot, 1);  	/* Nothing can fail after this block, otherwise @@ -4538,7 +4556,7 @@ static void sctp_destroy_sock(struct sock *sk)  	}  	sctp_endpoint_free(sp->ep);  	local_bh_disable(); -	percpu_counter_dec(&sctp_sockets_allocated); +	sk_sockets_allocated_dec(sk);  	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);  	local_bh_enable();  } @@ -5011,7 +5029,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;  } @@ -5080,7 +5098,6 @@ static int sctp_getsockopt_peeloff_common(struct sock *sk, sctp_peeloff_arg_t *p  	*newfile = sock_alloc_file(newsock, 0, NULL);  	if (IS_ERR(*newfile)) {  		put_unused_fd(retval); -		sock_release(newsock);  		retval = PTR_ERR(*newfile);  		*newfile = NULL;  		return retval; @@ -5642,6 +5659,9 @@ copy_getaddrs:  		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: @@ -6078,7 +6098,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(¶ms, optval, sizeof(params))) +		if (copy_from_user(¶ms, optval, len))  			return -EFAULT;  	} else  		return -EINVAL; @@ -6248,7 +6268,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); @@ -6260,7 +6282,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)) @@ -6286,7 +6307,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; @@ -6331,7 +6352,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; @@ -7999,12 +8020,12 @@ void sctp_sock_rfree(struct sk_buff *skb)  /* Helper function to wait for space in the sndbuf.  */  static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, -				size_t msg_len, struct sock **orig_sk) +				size_t msg_len)  {  	struct sock *sk = asoc->base.sk; -	int err = 0;  	long current_timeo = *timeo_p;  	DEFINE_WAIT(wait); +	int err = 0;  	pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc,  		 *timeo_p, msg_len); @@ -8033,17 +8054,13 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,  		release_sock(sk);  		current_timeo = schedule_timeout(current_timeo);  		lock_sock(sk); -		if (sk != asoc->base.sk) { -			release_sock(sk); -			sk = asoc->base.sk; -			lock_sock(sk); -		} +		if (sk != asoc->base.sk) +			goto do_error;  		*timeo_p = current_timeo;  	}  out: -	*orig_sk = sk;  	finish_wait(&asoc->wait, &wait);  	/* Release the association's refcnt.  */ |