diff options
Diffstat (limited to 'net/socket.c')
| -rw-r--r-- | net/socket.c | 79 | 
1 files changed, 44 insertions, 35 deletions
diff --git a/net/socket.c b/net/socket.c index 02dc82db3d2..ffe92ca32f2 100644 --- a/net/socket.c +++ b/net/socket.c @@ -467,7 +467,7 @@ static struct socket *sock_alloc(void)  	struct inode *inode;  	struct socket *sock; -	inode = new_inode(sock_mnt->mnt_sb); +	inode = new_inode_pseudo(sock_mnt->mnt_sb);  	if (!inode)  		return NULL; @@ -580,7 +580,7 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)  }  EXPORT_SYMBOL(sock_sendmsg); -int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size) +static int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size)  {  	struct kiocb iocb;  	struct sock_iocb siocb; @@ -1871,8 +1871,14 @@ SYSCALL_DEFINE2(shutdown, int, fd, int, how)  #define COMPAT_NAMELEN(msg)	COMPAT_MSG(msg, msg_namelen)  #define COMPAT_FLAGS(msg)	COMPAT_MSG(msg, msg_flags) +struct used_address { +	struct sockaddr_storage name; +	unsigned int name_len; +}; +  static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg, -			 struct msghdr *msg_sys, unsigned flags, int nosec) +			 struct msghdr *msg_sys, unsigned flags, +			 struct used_address *used_address)  {  	struct compat_msghdr __user *msg_compat =  	    (struct compat_msghdr __user *)msg; @@ -1953,8 +1959,30 @@ static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg,  	if (sock->file->f_flags & O_NONBLOCK)  		msg_sys->msg_flags |= MSG_DONTWAIT; -	err = (nosec ? sock_sendmsg_nosec : sock_sendmsg)(sock, msg_sys, -							  total_len); +	/* +	 * If this is sendmmsg() and current destination address is same as +	 * previously succeeded address, omit asking LSM's decision. +	 * used_address->name_len is initialized to UINT_MAX so that the first +	 * destination address never matches. +	 */ +	if (used_address && msg_sys->msg_name && +	    used_address->name_len == msg_sys->msg_namelen && +	    !memcmp(&used_address->name, msg_sys->msg_name, +		    used_address->name_len)) { +		err = sock_sendmsg_nosec(sock, msg_sys, total_len); +		goto out_freectl; +	} +	err = sock_sendmsg(sock, msg_sys, total_len); +	/* +	 * If this is sendmmsg() and sending to current destination address was +	 * successful, remember it. +	 */ +	if (used_address && err >= 0) { +		used_address->name_len = msg_sys->msg_namelen; +		if (msg_sys->msg_name) +			memcpy(&used_address->name, msg_sys->msg_name, +			       used_address->name_len); +	}  out_freectl:  	if (ctl_buf != ctl) @@ -1979,7 +2007,7 @@ SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags)  	if (!sock)  		goto out; -	err = __sys_sendmsg(sock, msg, &msg_sys, flags, 0); +	err = __sys_sendmsg(sock, msg, &msg_sys, flags, NULL);  	fput_light(sock->file, fput_needed);  out: @@ -1998,6 +2026,10 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,  	struct mmsghdr __user *entry;  	struct compat_mmsghdr __user *compat_entry;  	struct msghdr msg_sys; +	struct used_address used_address; + +	if (vlen > UIO_MAXIOV) +		vlen = UIO_MAXIOV;  	datagrams = 0; @@ -2005,27 +2037,22 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,  	if (!sock)  		return err; -	err = sock_error(sock->sk); -	if (err) -		goto out_put; - +	used_address.name_len = UINT_MAX;  	entry = mmsg;  	compat_entry = (struct compat_mmsghdr __user *)mmsg; +	err = 0;  	while (datagrams < vlen) { -		/* -		 * No need to ask LSM for more than the first datagram. -		 */  		if (MSG_CMSG_COMPAT & flags) {  			err = __sys_sendmsg(sock, (struct msghdr __user *)compat_entry, -					    &msg_sys, flags, datagrams); +					    &msg_sys, flags, &used_address);  			if (err < 0)  				break;  			err = __put_user(err, &compat_entry->msg_len);  			++compat_entry;  		} else {  			err = __sys_sendmsg(sock, (struct msghdr __user *)entry, -					    &msg_sys, flags, datagrams); +					    &msg_sys, flags, &used_address);  			if (err < 0)  				break;  			err = put_user(err, &entry->msg_len); @@ -2037,29 +2064,11 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,  		++datagrams;  	} -out_put:  	fput_light(sock->file, fput_needed); -	if (err == 0) -		return datagrams; - -	if (datagrams != 0) { -		/* -		 * We may send less entries than requested (vlen) if the -		 * sock is non blocking... -		 */ -		if (err != -EAGAIN) { -			/* -			 * ... or if sendmsg returns an error after we -			 * send some datagrams, where we record the -			 * error to return on the next call or if the -			 * app asks about it using getsockopt(SO_ERROR). -			 */ -			sock->sk->sk_err = -err; -		} - +	/* We only return an error if no datagrams were able to be sent */ +	if (datagrams != 0)  		return datagrams; -	}  	return err;  }  |