aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-07-01 07:26:02 -0400
committerDavid S. Miller <davem@davemloft.net>2009-07-15 11:53:39 -0400
commit1dacc76d0014a034b8aca14237c127d7c19d7726 (patch)
treed3ba044578fab9076ef4a73694fa7d23d4a50969
parent4f45b2cd4e78b5e49d7d41548345b879d3fdfeae (diff)
net/compat/wext: send different messages to compat tasks
Wireless extensions have the unfortunate problem that events are multicast netlink messages, and are not independent of pointer size. Thus, currently 32-bit tasks on 64-bit platforms cannot properly receive events and fail with all kinds of strange problems, for instance wpa_supplicant never notices disassociations, due to the way the 64-bit event looks (to a 32-bit process), the fact that the address is all zeroes is lost, it thinks instead it is 00:00:00:00:01:00. The same problem existed with the ioctls, until David Miller fixed those some time ago in an heroic effort. A different problem caused by this is that we cannot send the ASSOCREQIE/ASSOCRESPIE events because sending them causes a 32-bit wpa_supplicant on a 64-bit system to overwrite its internal information, which is worse than it not getting the information at all -- so we currently resort to sending a custom string event that it then parses. This, however, has a severe size limitation we are frequently hitting with modern access points; this limitation would can be lifted after this patch by sending the correct binary, not custom, event. A similar problem apparently happens for some other netlink users on x86_64 with 32-bit tasks due to the alignment for 64-bit quantities. In order to fix these problems, I have implemented a way to send compat messages to tasks. When sending an event, we send the non-compat event data together with a compat event data in skb_shinfo(main_skb)->frag_list. Then, when the event is read from the socket, the netlink code makes sure to pass out only the skb that is compatible with the task. This approach was suggested by David Miller, my original approach required always sending two skbs but that had various small problems. To determine whether compat is needed or not, I have used the MSG_CMSG_COMPAT flag, and adjusted the call path for recv and recvfrom to include it, even if those calls do not have a cmsg parameter. I have not solved one small part of the problem, and I don't think it is necessary to: if a 32-bit application uses read() rather than any form of recvmsg() it will still get the wrong (64-bit) event. However, neither do applications actually do this, nor would it be a regression. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--arch/mips/kernel/scall64-n32.S2
-rw-r--r--arch/mips/kernel/scall64-o32.S4
-rw-r--r--arch/sparc/kernel/sys32.S2
-rw-r--r--include/linux/wireless.h8
-rw-r--r--net/Kconfig20
-rw-r--r--net/compat.c17
-rw-r--r--net/netlink/af_netlink.c36
-rw-r--r--net/wireless/wext.c78
8 files changed, 160 insertions, 7 deletions
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index 15874f9812cc..7c4a94f43706 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -164,7 +164,7 @@ EXPORT(sysn32_call_table)
164 PTR sys_connect 164 PTR sys_connect
165 PTR sys_accept 165 PTR sys_accept
166 PTR sys_sendto 166 PTR sys_sendto
167 PTR sys_recvfrom 167 PTR compat_sys_recvfrom
168 PTR compat_sys_sendmsg /* 6045 */ 168 PTR compat_sys_sendmsg /* 6045 */
169 PTR compat_sys_recvmsg 169 PTR compat_sys_recvmsg
170 PTR sys_shutdown 170 PTR sys_shutdown
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 781e0f1e9533..821fc978673d 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -378,8 +378,8 @@ sys_call_table:
378 PTR sys_getsockname 378 PTR sys_getsockname
379 PTR sys_getsockopt 379 PTR sys_getsockopt
380 PTR sys_listen 380 PTR sys_listen
381 PTR sys_recv /* 4175 */ 381 PTR compat_sys_recv /* 4175 */
382 PTR sys_recvfrom 382 PTR compat_sys_recvfrom
383 PTR compat_sys_recvmsg 383 PTR compat_sys_recvmsg
384 PTR sys_send 384 PTR sys_send
385 PTR compat_sys_sendmsg 385 PTR compat_sys_sendmsg
diff --git a/arch/sparc/kernel/sys32.S b/arch/sparc/kernel/sys32.S
index f061c4dda9ef..3762f6c78944 100644
--- a/arch/sparc/kernel/sys32.S
+++ b/arch/sparc/kernel/sys32.S
@@ -121,7 +121,7 @@ SIGN2(sys32_syslog, sys_syslog, %o0, %o2)
121SIGN1(sys32_umask, sys_umask, %o0) 121SIGN1(sys32_umask, sys_umask, %o0)
122SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2) 122SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2)
123SIGN1(sys32_sendto, sys_sendto, %o0) 123SIGN1(sys32_sendto, sys_sendto, %o0)
124SIGN1(sys32_recvfrom, sys_recvfrom, %o0) 124SIGN1(sys32_recvfrom, compat_sys_recvfrom, %o0)
125SIGN3(sys32_socket, sys_socket, %o0, %o1, %o2) 125SIGN3(sys32_socket, sys_socket, %o0, %o1, %o2)
126SIGN2(sys32_connect, sys_connect, %o0, %o2) 126SIGN2(sys32_connect, sys_connect, %o0, %o2)
127SIGN2(sys32_bind, sys_bind, %o0, %o2) 127SIGN2(sys32_bind, sys_bind, %o0, %o2)
diff --git a/include/linux/wireless.h b/include/linux/wireless.h
index cb24204851f7..5b4c6c772a9b 100644
--- a/include/linux/wireless.h
+++ b/include/linux/wireless.h
@@ -1132,6 +1132,14 @@ struct __compat_iw_event {
1132}; 1132};
1133#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer) 1133#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)
1134#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length) 1134#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length)
1135
1136/* Size of the various events for compat */
1137#define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ)
1138#define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32))
1139#define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq))
1140#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param))
1141#define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr))
1142#define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality))
1135#define IW_EV_COMPAT_POINT_LEN \ 1143#define IW_EV_COMPAT_POINT_LEN \
1136 (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \ 1144 (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \
1137 IW_EV_COMPAT_POINT_OFF) 1145 IW_EV_COMPAT_POINT_OFF)
diff --git a/net/Kconfig b/net/Kconfig
index 7051b9710675..041c35edb763 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -23,6 +23,26 @@ menuconfig NET
23 23
24if NET 24if NET
25 25
26config WANT_COMPAT_NETLINK_MESSAGES
27 bool
28 help
29 This option can be selected by other options that need compat
30 netlink messages.
31
32config COMPAT_NETLINK_MESSAGES
33 def_bool y
34 depends on COMPAT
35 depends on WIRELESS_EXT || WANT_COMPAT_NETLINK_MESSAGES
36 help
37 This option makes it possible to send different netlink messages
38 to tasks depending on whether the task is a compat task or not. To
39 achieve this, you need to set skb_shinfo(skb)->frag_list to the
40 compat skb before sending the skb, the netlink code will sort out
41 which message to actually pass to the task.
42
43 Newly written code should NEVER need this option but do
44 compat-independent messages instead!
45
26menu "Networking options" 46menu "Networking options"
27 47
28source "net/packet/Kconfig" 48source "net/packet/Kconfig"
diff --git a/net/compat.c b/net/compat.c
index 8d739053afe4..12728b17a226 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
743 return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); 743 return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
744} 744}
745 745
746asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags)
747{
748 return sys_recv(fd, buf, len, flags | MSG_CMSG_COMPAT);
749}
750
751asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, size_t len,
752 unsigned flags, struct sockaddr __user *addr,
753 int __user *addrlen)
754{
755 return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen);
756}
757
746asmlinkage long compat_sys_socketcall(int call, u32 __user *args) 758asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
747{ 759{
748 int ret; 760 int ret;
@@ -788,10 +800,11 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
788 ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]); 800 ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]);
789 break; 801 break;
790 case SYS_RECV: 802 case SYS_RECV:
791 ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]); 803 ret = compat_sys_recv(a0, compat_ptr(a1), a[2], a[3]);
792 break; 804 break;
793 case SYS_RECVFROM: 805 case SYS_RECVFROM:
794 ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5])); 806 ret = compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3],
807 compat_ptr(a[4]), compat_ptr(a[5]));
795 break; 808 break;
796 case SYS_SHUTDOWN: 809 case SYS_SHUTDOWN:
797 ret = sys_shutdown(a0,a1); 810 ret = sys_shutdown(a0,a1);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index d46da6cb92e4..da3163d15ef0 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,
1361 struct netlink_sock *nlk = nlk_sk(sk); 1361 struct netlink_sock *nlk = nlk_sk(sk);
1362 int noblock = flags&MSG_DONTWAIT; 1362 int noblock = flags&MSG_DONTWAIT;
1363 size_t copied; 1363 size_t copied;
1364 struct sk_buff *skb; 1364 struct sk_buff *skb, *frag __maybe_unused = NULL;
1365 int err; 1365 int err;
1366 1366
1367 if (flags&MSG_OOB) 1367 if (flags&MSG_OOB)
@@ -1373,6 +1373,35 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
1373 if (skb == NULL) 1373 if (skb == NULL)
1374 goto out; 1374 goto out;
1375 1375
1376#ifdef CONFIG_COMPAT_NETLINK_MESSAGES
1377 if (unlikely(skb_shinfo(skb)->frag_list)) {
1378 bool need_compat = !!(flags & MSG_CMSG_COMPAT);
1379
1380 /*
1381 * If this skb has a frag_list, then here that means that
1382 * we will have to use the frag_list skb for compat tasks
1383 * and the regular skb for non-compat tasks.
1384 *
1385 * The skb might (and likely will) be cloned, so we can't
1386 * just reset frag_list and go on with things -- we need to
1387 * keep that. For the compat case that's easy -- simply get
1388 * a reference to the compat skb and free the regular one
1389 * including the frag. For the non-compat case, we need to
1390 * avoid sending the frag to the user -- so assign NULL but
1391 * restore it below before freeing the skb.
1392 */
1393 if (need_compat) {
1394 struct sk_buff *compskb = skb_shinfo(skb)->frag_list;
1395 skb_get(compskb);
1396 kfree_skb(skb);
1397 skb = compskb;
1398 } else {
1399 frag = skb_shinfo(skb)->frag_list;
1400 skb_shinfo(skb)->frag_list = NULL;
1401 }
1402 }
1403#endif
1404
1376 msg->msg_namelen = 0; 1405 msg->msg_namelen = 0;
1377 1406
1378 copied = skb->len; 1407 copied = skb->len;
@@ -1403,6 +1432,11 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
1403 siocb->scm->creds = *NETLINK_CREDS(skb); 1432 siocb->scm->creds = *NETLINK_CREDS(skb);
1404 if (flags & MSG_TRUNC) 1433 if (flags & MSG_TRUNC)
1405 copied = skb->len; 1434 copied = skb->len;
1435
1436#ifdef CONFIG_COMPAT_NETLINK_MESSAGES
1437 skb_shinfo(skb)->frag_list = frag;
1438#endif
1439
1406 skb_free_datagram(sk, skb); 1440 skb_free_datagram(sk, skb);
1407 1441
1408 if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) 1442 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 ee35e6422f1f..3fe3c2c0ce11 100644
--- a/net/wireless/wext.c
+++ b/net/wireless/wext.c
@@ -417,6 +417,21 @@ static const int event_type_size[] = {
417 IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ 417 IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
418}; 418};
419 419
420#ifdef CONFIG_COMPAT
421static const int compat_event_type_size[] = {
422 IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */
423 0,
424 IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */
425 0,
426 IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */
427 IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */
428 IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */
429 0,
430 IW_EV_COMPAT_POINT_LEN, /* Without variable payload */
431 IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */
432 IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
433};
434#endif
420 435
421/************************ COMMON SUBROUTINES ************************/ 436/************************ COMMON SUBROUTINES ************************/
422/* 437/*
@@ -1348,6 +1363,22 @@ void wireless_send_event(struct net_device * dev,
1348 struct sk_buff *skb; 1363 struct sk_buff *skb;
1349 struct nlmsghdr *nlh; 1364 struct nlmsghdr *nlh;
1350 struct nlattr *nla; 1365 struct nlattr *nla;
1366#ifdef CONFIG_COMPAT
1367 struct __compat_iw_event *compat_event;
1368 struct compat_iw_point compat_wrqu;
1369 struct sk_buff *compskb;
1370#endif
1371
1372 /*
1373 * Nothing in the kernel sends scan events with data, be safe.
1374 * This is necessary because we cannot fix up scan event data
1375 * for compat, due to being contained in 'extra', but normally
1376 * applications are required to retrieve the scan data anyway
1377 * and no data is included in the event, this codifies that
1378 * practice.
1379 */
1380 if (WARN_ON(cmd == SIOCGIWSCAN && extra))
1381 extra = NULL;
1351 1382
1352 /* Get the description of the Event */ 1383 /* Get the description of the Event */
1353 if (cmd <= SIOCIWLAST) { 1384 if (cmd <= SIOCIWLAST) {
@@ -1446,7 +1477,54 @@ void wireless_send_event(struct net_device * dev,
1446 memcpy(((char *) event) + hdr_len, extra, extra_len); 1477 memcpy(((char *) event) + hdr_len, extra, extra_len);
1447 1478
1448 nlmsg_end(skb, nlh); 1479 nlmsg_end(skb, nlh);
1480#ifdef CONFIG_COMPAT
1481 hdr_len = compat_event_type_size[descr->header_type];
1482 event_len = hdr_len + extra_len;
1449 1483
1484 compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
1485 if (!compskb) {
1486 kfree_skb(skb);
1487 return;
1488 }
1489
1490 /* Send via the RtNetlink event channel */
1491 nlh = rtnetlink_ifinfo_prep(dev, compskb);
1492 if (WARN_ON(!nlh)) {
1493 kfree_skb(skb);
1494 kfree_skb(compskb);
1495 return;
1496 }
1497
1498 /* Add the wireless events in the netlink packet */
1499 nla = nla_reserve(compskb, IFLA_WIRELESS, event_len);
1500 if (!nla) {
1501 kfree_skb(skb);
1502 kfree_skb(compskb);
1503 return;
1504 }
1505 compat_event = nla_data(nla);
1506
1507 compat_event->len = event_len;
1508 compat_event->cmd = cmd;
1509 if (descr->header_type == IW_HEADER_TYPE_POINT) {
1510 compat_wrqu.length = wrqu->data.length;
1511 compat_wrqu.flags = wrqu->data.flags;
1512 memcpy(&compat_event->pointer,
1513 ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF,
1514 hdr_len - IW_EV_COMPAT_LCP_LEN);
1515 if (extra_len)
1516 memcpy(((char *) compat_event) + hdr_len,
1517 extra, extra_len);
1518 } else {
1519 /* extra_len must be zero, so no if (extra) needed */
1520 memcpy(&compat_event->pointer, wrqu,
1521 hdr_len - IW_EV_COMPAT_LCP_LEN);
1522 }
1523
1524 nlmsg_end(compskb, nlh);
1525
1526 skb_shinfo(skb)->frag_list = compskb;
1527#endif
1450 skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); 1528 skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
1451 schedule_work(&wireless_nlevent_work); 1529 schedule_work(&wireless_nlevent_work);
1452} 1530}