Commit 1dacc76d0014a034b8aca14237c127d7c19d7726

Authored by Johannes Berg
Committed by David S. Miller
1 parent 4f45b2cd4e

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>

Showing 8 changed files with 160 additions and 7 deletions Side-by-side Diff

arch/mips/kernel/scall64-n32.S
... ... @@ -164,7 +164,7 @@
164 164 PTR sys_connect
165 165 PTR sys_accept
166 166 PTR sys_sendto
167   - PTR sys_recvfrom
  167 + PTR compat_sys_recvfrom
168 168 PTR compat_sys_sendmsg /* 6045 */
169 169 PTR compat_sys_recvmsg
170 170 PTR sys_shutdown
arch/mips/kernel/scall64-o32.S
... ... @@ -378,8 +378,8 @@
378 378 PTR sys_getsockname
379 379 PTR sys_getsockopt
380 380 PTR sys_listen
381   - PTR sys_recv /* 4175 */
382   - PTR sys_recvfrom
  381 + PTR compat_sys_recv /* 4175 */
  382 + PTR compat_sys_recvfrom
383 383 PTR compat_sys_recvmsg
384 384 PTR sys_send
385 385 PTR compat_sys_sendmsg
arch/sparc/kernel/sys32.S
... ... @@ -121,7 +121,7 @@
121 121 SIGN1(sys32_umask, sys_umask, %o0)
122 122 SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2)
123 123 SIGN1(sys32_sendto, sys_sendto, %o0)
124   -SIGN1(sys32_recvfrom, sys_recvfrom, %o0)
  124 +SIGN1(sys32_recvfrom, compat_sys_recvfrom, %o0)
125 125 SIGN3(sys32_socket, sys_socket, %o0, %o1, %o2)
126 126 SIGN2(sys32_connect, sys_connect, %o0, %o2)
127 127 SIGN2(sys32_bind, sys_bind, %o0, %o2)
include/linux/wireless.h
... ... @@ -1132,6 +1132,14 @@
1132 1132 };
1133 1133 #define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)
1134 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 1143 #define IW_EV_COMPAT_POINT_LEN \
1136 1144 (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \
1137 1145 IW_EV_COMPAT_POINT_OFF)
... ... @@ -23,6 +23,26 @@
23 23  
24 24 if NET
25 25  
  26 +config WANT_COMPAT_NETLINK_MESSAGES
  27 + bool
  28 + help
  29 + This option can be selected by other options that need compat
  30 + netlink messages.
  31 +
  32 +config 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 +
26 46 menu "Networking options"
27 47  
28 48 source "net/packet/Kconfig"
... ... @@ -743,6 +743,18 @@
743 743 return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
744 744 }
745 745  
  746 +asmlinkage 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 +
  751 +asmlinkage 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 +
746 758 asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
747 759 {
748 760 int ret;
749 761  
... ... @@ -788,10 +800,11 @@
788 800 ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]);
789 801 break;
790 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 804 break;
793 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 808 break;
796 809 case SYS_SHUTDOWN:
797 810 ret = sys_shutdown(a0,a1);
net/netlink/af_netlink.c
... ... @@ -1361,7 +1361,7 @@
1361 1361 struct netlink_sock *nlk = nlk_sk(sk);
1362 1362 int noblock = flags&MSG_DONTWAIT;
1363 1363 size_t copied;
1364   - struct sk_buff *skb;
  1364 + struct sk_buff *skb, *frag __maybe_unused = NULL;
1365 1365 int err;
1366 1366  
1367 1367 if (flags&MSG_OOB)
... ... @@ -1373,6 +1373,35 @@
1373 1373 if (skb == NULL)
1374 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 1405 msg->msg_namelen = 0;
1377 1406  
1378 1407 copied = skb->len;
... ... @@ -1403,6 +1432,11 @@
1403 1432 siocb->scm->creds = *NETLINK_CREDS(skb);
1404 1433 if (flags & MSG_TRUNC)
1405 1434 copied = skb->len;
  1435 +
  1436 +#ifdef CONFIG_COMPAT_NETLINK_MESSAGES
  1437 + skb_shinfo(skb)->frag_list = frag;
  1438 +#endif
  1439 +
1406 1440 skb_free_datagram(sk, skb);
1407 1441  
1408 1442 if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2)
... ... @@ -417,6 +417,21 @@
417 417 IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
418 418 };
419 419  
  420 +#ifdef CONFIG_COMPAT
  421 +static 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 436 /************************ COMMON SUBROUTINES ************************/
422 437 /*
423 438  
... ... @@ -1348,7 +1363,23 @@
1348 1363 struct sk_buff *skb;
1349 1364 struct nlmsghdr *nlh;
1350 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
1351 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;
  1382 +
1352 1383 /* Get the description of the Event */
1353 1384 if (cmd <= SIOCIWLAST) {
1354 1385 cmd_index = cmd - SIOCIWFIRST;
1355 1386  
... ... @@ -1446,7 +1477,54 @@
1446 1477 memcpy(((char *) event) + hdr_len, extra, extra_len);
1447 1478  
1448 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 1528 skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
1451 1529 schedule_work(&wireless_nlevent_work);
1452 1530 }