diff options
Diffstat (limited to 'net/econet/af_econet.c')
| -rw-r--r-- | net/econet/af_econet.c | 91 | 
1 files changed, 42 insertions, 49 deletions
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index f8c1ae4b41f..13992e1d272 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -31,6 +31,7 @@  #include <linux/skbuff.h>  #include <linux/udp.h>  #include <linux/slab.h> +#include <linux/vmalloc.h>  #include <net/sock.h>  #include <net/inet_common.h>  #include <linux/stat.h> @@ -276,12 +277,12 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,  #endif  #ifdef CONFIG_ECONET_AUNUDP  	struct msghdr udpmsg; -	struct iovec iov[msg->msg_iovlen+1]; +	struct iovec iov[2];  	struct aunhdr ah;  	struct sockaddr_in udpdest;  	__kernel_size_t size; -	int i;  	mm_segment_t oldfs; +	char *userbuf;  #endif  	/* @@ -297,23 +298,14 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,  	mutex_lock(&econet_mutex); -	if (saddr == NULL) { -		struct econet_sock *eo = ec_sk(sk); - -		addr.station = eo->station; -		addr.net     = eo->net; -		port	     = eo->port; -		cb	     = eo->cb; -	} else { -		if (msg->msg_namelen < sizeof(struct sockaddr_ec)) { -			mutex_unlock(&econet_mutex); -			return -EINVAL; -		} -		addr.station = saddr->addr.station; -		addr.net = saddr->addr.net; -		port = saddr->port; -		cb = saddr->cb; -	} +        if (saddr == NULL || msg->msg_namelen < sizeof(struct sockaddr_ec)) { +                mutex_unlock(&econet_mutex); +                return -EINVAL; +        } +        addr.station = saddr->addr.station; +        addr.net = saddr->addr.net; +        port = saddr->port; +        cb = saddr->cb;  	/* Look for a device with the right network number. */  	dev = net2dev_map[addr.net]; @@ -328,17 +320,17 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,  		}  	} -	if (len + 15 > dev->mtu) { -		mutex_unlock(&econet_mutex); -		return -EMSGSIZE; -	} -  	if (dev->type == ARPHRD_ECONET) {  		/* Real hardware Econet.  We're not worthy etc. */  #ifdef CONFIG_ECONET_NATIVE  		unsigned short proto = 0;  		int res; +		if (len + 15 > dev->mtu) { +			mutex_unlock(&econet_mutex); +			return -EMSGSIZE; +		} +  		dev_hold(dev);  		skb = sock_alloc_send_skb(sk, len+LL_ALLOCATED_SPACE(dev), @@ -351,7 +343,6 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,  		eb = (struct ec_cb *)&skb->cb; -		/* BUG: saddr may be NULL */  		eb->cookie = saddr->cookie;  		eb->sec = *saddr;  		eb->sent = ec_tx_done; @@ -415,6 +406,11 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,  		return -ENETDOWN;		/* No socket - can't send */  	} +	if (len > 32768) { +		err = -E2BIG; +		goto error; +	} +  	/* Make up a UDP datagram and hand it off to some higher intellect. */  	memset(&udpdest, 0, sizeof(udpdest)); @@ -446,36 +442,26 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,  	/* tack our header on the front of the iovec */  	size = sizeof(struct aunhdr); -	/* -	 * XXX: that is b0rken.  We can't mix userland and kernel pointers -	 * in iovec, since on a lot of platforms copy_from_user() will -	 * *not* work with the kernel and userland ones at the same time, -	 * regardless of what we do with set_fs().  And we are talking about -	 * econet-over-ethernet here, so "it's only ARM anyway" doesn't -	 * apply.  Any suggestions on fixing that code?		-- AV -	 */  	iov[0].iov_base = (void *)&ah;  	iov[0].iov_len = size; -	for (i = 0; i < msg->msg_iovlen; i++) { -		void __user *base = msg->msg_iov[i].iov_base; -		size_t iov_len = msg->msg_iov[i].iov_len; -		/* Check it now since we switch to KERNEL_DS later. */ -		if (!access_ok(VERIFY_READ, base, iov_len)) { -			mutex_unlock(&econet_mutex); -			return -EFAULT; -		} -		iov[i+1].iov_base = base; -		iov[i+1].iov_len = iov_len; -		size += iov_len; + +	userbuf = vmalloc(len); +	if (userbuf == NULL) { +		err = -ENOMEM; +		goto error;  	} +	iov[1].iov_base = userbuf; +	iov[1].iov_len = len; +	err = memcpy_fromiovec(userbuf, msg->msg_iov, len); +	if (err) +		goto error_free_buf; +  	/* Get a skbuff (no data, just holds our cb information) */  	if ((skb = sock_alloc_send_skb(sk, 0,  				       msg->msg_flags & MSG_DONTWAIT, -				       &err)) == NULL) { -		mutex_unlock(&econet_mutex); -		return err; -	} +				       &err)) == NULL) +		goto error_free_buf;  	eb = (struct ec_cb *)&skb->cb; @@ -491,7 +477,7 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,  	udpmsg.msg_name = (void *)&udpdest;  	udpmsg.msg_namelen = sizeof(udpdest);  	udpmsg.msg_iov = &iov[0]; -	udpmsg.msg_iovlen = msg->msg_iovlen + 1; +	udpmsg.msg_iovlen = 2;  	udpmsg.msg_control = NULL;  	udpmsg.msg_controllen = 0;  	udpmsg.msg_flags=0; @@ -499,9 +485,13 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,  	oldfs = get_fs(); set_fs(KERNEL_DS);	/* More privs :-) */  	err = sock_sendmsg(udpsock, &udpmsg, size);  	set_fs(oldfs); + +error_free_buf: +	vfree(userbuf);  #else  	err = -EPROTOTYPE;  #endif +	error:  	mutex_unlock(&econet_mutex);  	return err; @@ -671,6 +661,9 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg)  	err = 0;  	switch (cmd) {  	case SIOCSIFADDR: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +  		edev = dev->ec_ptr;  		if (edev == NULL) {  			/* Magic up a new one. */  |