aboutsummaryrefslogtreecommitdiffstats
path: root/net/netlink/af_netlink.c
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 /net/netlink/af_netlink.c
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>
Diffstat (limited to 'net/netlink/af_netlink.c')
-rw-r--r--net/netlink/af_netlink.c36
1 files changed, 35 insertions, 1 deletions
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)