diff options
| -rw-r--r-- | arch/mips/kernel/scall64-n32.S | 2 | ||||
| -rw-r--r-- | arch/mips/kernel/scall64-o32.S | 4 | ||||
| -rw-r--r-- | arch/sparc/kernel/sys32.S | 2 | ||||
| -rw-r--r-- | include/linux/wireless.h | 8 | ||||
| -rw-r--r-- | net/Kconfig | 20 | ||||
| -rw-r--r-- | net/compat.c | 17 | ||||
| -rw-r--r-- | net/netlink/af_netlink.c | 36 | ||||
| -rw-r--r-- | net/wireless/wext.c | 78 | 
8 files changed, 160 insertions, 7 deletions
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 15874f9812c..7c4a94f4370 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -164,7 +164,7 @@ EXPORT(sysn32_call_table)  	PTR	sys_connect  	PTR	sys_accept  	PTR	sys_sendto -	PTR	sys_recvfrom +	PTR	compat_sys_recvfrom  	PTR	compat_sys_sendmsg		/* 6045 */  	PTR	compat_sys_recvmsg  	PTR	sys_shutdown diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 781e0f1e953..821fc978673 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -378,8 +378,8 @@ sys_call_table:  	PTR	sys_getsockname  	PTR	sys_getsockopt  	PTR	sys_listen -	PTR	sys_recv			/* 4175 */ -	PTR	sys_recvfrom +	PTR	compat_sys_recv			/* 4175 */ +	PTR	compat_sys_recvfrom  	PTR	compat_sys_recvmsg  	PTR	sys_send  	PTR	compat_sys_sendmsg diff --git a/arch/sparc/kernel/sys32.S b/arch/sparc/kernel/sys32.S index f061c4dda9e..3762f6c7894 100644 --- a/arch/sparc/kernel/sys32.S +++ b/arch/sparc/kernel/sys32.S @@ -121,7 +121,7 @@ SIGN2(sys32_syslog, sys_syslog, %o0, %o2)  SIGN1(sys32_umask, sys_umask, %o0)  SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2)  SIGN1(sys32_sendto, sys_sendto, %o0) -SIGN1(sys32_recvfrom, sys_recvfrom, %o0) +SIGN1(sys32_recvfrom, compat_sys_recvfrom, %o0)  SIGN3(sys32_socket, sys_socket, %o0, %o1, %o2)  SIGN2(sys32_connect, sys_connect, %o0, %o2)  SIGN2(sys32_bind, sys_bind, %o0, %o2) diff --git a/include/linux/wireless.h b/include/linux/wireless.h index cb24204851f..5b4c6c772a9 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -1132,6 +1132,14 @@ struct __compat_iw_event {  };  #define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)  #define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length) + +/* Size of the various events for compat */ +#define IW_EV_COMPAT_CHAR_LEN	(IW_EV_COMPAT_LCP_LEN + IFNAMSIZ) +#define IW_EV_COMPAT_UINT_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(__u32)) +#define IW_EV_COMPAT_FREQ_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_COMPAT_PARAM_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_COMPAT_ADDR_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr)) +#define IW_EV_COMPAT_QUAL_LEN	(IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality))  #define IW_EV_COMPAT_POINT_LEN	\  	(IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \  	 IW_EV_COMPAT_POINT_OFF) diff --git a/net/Kconfig b/net/Kconfig index 7051b971067..041c35edb76 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -23,6 +23,26 @@ menuconfig NET  if NET +config WANT_COMPAT_NETLINK_MESSAGES +	bool +	help +	  This option can be selected by other options that need compat +	  netlink messages. + +config COMPAT_NETLINK_MESSAGES +	def_bool y +	depends on COMPAT +	depends on WIRELESS_EXT || WANT_COMPAT_NETLINK_MESSAGES +	help +	  This option makes it possible to send different netlink messages +	  to tasks depending on whether the task is a compat task or not. To +	  achieve this, you need to set skb_shinfo(skb)->frag_list to the +	  compat skb before sending the skb, the netlink code will sort out +	  which message to actually pass to the task. + +	  Newly written code should NEVER need this option but do +	  compat-independent messages instead! +  menu "Networking options"  source "net/packet/Kconfig" diff --git a/net/compat.c b/net/compat.c index 8d739053afe..12728b17a22 100644 --- a/net/compat.c +++ b/net/compat.c @@ -743,6 +743,18 @@ asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, uns  	return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);  } +asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags) +{ +	return sys_recv(fd, buf, len, flags | MSG_CMSG_COMPAT); +} + +asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, size_t len, +				    unsigned flags, struct sockaddr __user *addr, +				    int __user *addrlen) +{ +	return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen); +} +  asmlinkage long compat_sys_socketcall(int call, u32 __user *args)  {  	int ret; @@ -788,10 +800,11 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args)  		ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]);  		break;  	case SYS_RECV: -		ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]); +		ret = compat_sys_recv(a0, compat_ptr(a1), a[2], a[3]);  		break;  	case SYS_RECVFROM: -		ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5])); +		ret = compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], +					  compat_ptr(a[4]), compat_ptr(a[5]));  		break;  	case SYS_SHUTDOWN:  		ret = sys_shutdown(a0,a1); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d46da6cb92e..da3163d15ef 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1361,7 +1361,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,  	struct netlink_sock *nlk = nlk_sk(sk);  	int noblock = flags&MSG_DONTWAIT;  	size_t copied; -	struct sk_buff *skb; +	struct sk_buff *skb, *frag __maybe_unused = NULL;  	int err;  	if (flags&MSG_OOB) @@ -1373,6 +1373,35 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,  	if (skb == NULL)  		goto out; +#ifdef CONFIG_COMPAT_NETLINK_MESSAGES +	if (unlikely(skb_shinfo(skb)->frag_list)) { +		bool need_compat = !!(flags & MSG_CMSG_COMPAT); + +		/* +		 * If this skb has a frag_list, then here that means that +		 * we will have to use the frag_list skb for compat tasks +		 * and the regular skb for non-compat tasks. +		 * +		 * The skb might (and likely will) be cloned, so we can't +		 * just reset frag_list and go on with things -- we need to +		 * keep that. For the compat case that's easy -- simply get +		 * a reference to the compat skb and free the regular one +		 * including the frag. For the non-compat case, we need to +		 * avoid sending the frag to the user -- so assign NULL but +		 * restore it below before freeing the skb. +		 */ +		if (need_compat) { +			struct sk_buff *compskb = skb_shinfo(skb)->frag_list; +			skb_get(compskb); +			kfree_skb(skb); +			skb = compskb; +		} else { +			frag = skb_shinfo(skb)->frag_list; +			skb_shinfo(skb)->frag_list = NULL; +		} +	} +#endif +  	msg->msg_namelen = 0;  	copied = skb->len; @@ -1403,6 +1432,11 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,  	siocb->scm->creds = *NETLINK_CREDS(skb);  	if (flags & MSG_TRUNC)  		copied = skb->len; + +#ifdef CONFIG_COMPAT_NETLINK_MESSAGES +	skb_shinfo(skb)->frag_list = frag; +#endif +  	skb_free_datagram(sk, skb);  	if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) diff --git a/net/wireless/wext.c b/net/wireless/wext.c index ee35e6422f1..3fe3c2c0ce1 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -417,6 +417,21 @@ static const int event_type_size[] = {  	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */  }; +#ifdef CONFIG_COMPAT +static const int compat_event_type_size[] = { +	IW_EV_COMPAT_LCP_LEN,		/* IW_HEADER_TYPE_NULL */ +	0, +	IW_EV_COMPAT_CHAR_LEN,		/* IW_HEADER_TYPE_CHAR */ +	0, +	IW_EV_COMPAT_UINT_LEN,		/* IW_HEADER_TYPE_UINT */ +	IW_EV_COMPAT_FREQ_LEN,		/* IW_HEADER_TYPE_FREQ */ +	IW_EV_COMPAT_ADDR_LEN,		/* IW_HEADER_TYPE_ADDR */ +	0, +	IW_EV_COMPAT_POINT_LEN,		/* Without variable payload */ +	IW_EV_COMPAT_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */ +	IW_EV_COMPAT_QUAL_LEN,		/* IW_HEADER_TYPE_QUAL */ +}; +#endif  /************************ COMMON SUBROUTINES ************************/  /* @@ -1348,6 +1363,22 @@ void wireless_send_event(struct net_device *	dev,  	struct sk_buff *skb;  	struct nlmsghdr *nlh;  	struct nlattr *nla; +#ifdef CONFIG_COMPAT +	struct __compat_iw_event *compat_event; +	struct compat_iw_point compat_wrqu; +	struct sk_buff *compskb; +#endif + +	/* +	 * Nothing in the kernel sends scan events with data, be safe. +	 * This is necessary because we cannot fix up scan event data +	 * for compat, due to being contained in 'extra', but normally +	 * applications are required to retrieve the scan data anyway +	 * and no data is included in the event, this codifies that +	 * practice. +	 */ +	if (WARN_ON(cmd == SIOCGIWSCAN && extra)) +		extra = NULL;  	/* Get the description of the Event */  	if (cmd <= SIOCIWLAST) { @@ -1446,7 +1477,54 @@ void wireless_send_event(struct net_device *	dev,  		memcpy(((char *) event) + hdr_len, extra, extra_len);  	nlmsg_end(skb, nlh); +#ifdef CONFIG_COMPAT +	hdr_len = compat_event_type_size[descr->header_type]; +	event_len = hdr_len + extra_len; +	compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); +	if (!compskb) { +		kfree_skb(skb); +		return; +	} + +	/* Send via the RtNetlink event channel */ +	nlh = rtnetlink_ifinfo_prep(dev, compskb); +	if (WARN_ON(!nlh)) { +		kfree_skb(skb); +		kfree_skb(compskb); +		return; +	} + +	/* Add the wireless events in the netlink packet */ +	nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); +	if (!nla) { +		kfree_skb(skb); +		kfree_skb(compskb); +		return; +	} +	compat_event = nla_data(nla); + +	compat_event->len = event_len; +	compat_event->cmd = cmd; +	if (descr->header_type == IW_HEADER_TYPE_POINT) { +		compat_wrqu.length = wrqu->data.length; +		compat_wrqu.flags = wrqu->data.flags; +		memcpy(&compat_event->pointer, +			((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, +			hdr_len - IW_EV_COMPAT_LCP_LEN); +		if (extra_len) +			memcpy(((char *) compat_event) + hdr_len, +				extra, extra_len); +	} else { +		/* extra_len must be zero, so no if (extra) needed */ +		memcpy(&compat_event->pointer, wrqu, +			hdr_len - IW_EV_COMPAT_LCP_LEN); +	} + +	nlmsg_end(compskb, nlh); + +	skb_shinfo(skb)->frag_list = compskb; +#endif  	skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);  	schedule_work(&wireless_nlevent_work);  }  |