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 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) | |||
121 | SIGN1(sys32_umask, sys_umask, %o0) | 121 | SIGN1(sys32_umask, sys_umask, %o0) |
122 | SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2) | 122 | SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2) |
123 | SIGN1(sys32_sendto, sys_sendto, %o0) | 123 | SIGN1(sys32_sendto, sys_sendto, %o0) |
124 | SIGN1(sys32_recvfrom, sys_recvfrom, %o0) | 124 | SIGN1(sys32_recvfrom, compat_sys_recvfrom, %o0) |
125 | SIGN3(sys32_socket, sys_socket, %o0, %o1, %o2) | 125 | SIGN3(sys32_socket, sys_socket, %o0, %o1, %o2) |
126 | SIGN2(sys32_connect, sys_connect, %o0, %o2) | 126 | SIGN2(sys32_connect, sys_connect, %o0, %o2) |
127 | SIGN2(sys32_bind, sys_bind, %o0, %o2) | 127 | SIGN2(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 | ||
24 | if NET | 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 | menu "Networking options" | 46 | menu "Networking options" |
27 | 47 | ||
28 | source "net/packet/Kconfig" | 48 | source "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 | ||
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 | asmlinkage long compat_sys_socketcall(int call, u32 __user *args) | 758 | asmlinkage 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 | ||
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 | /************************ 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 | } |