diff options
author | Helmut Schaa <helmut.schaa@googlemail.com> | 2010-12-02 12:44:09 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-12-08 15:23:48 -0500 |
commit | 7e2447075690860e2cea96b119fc9cadbaa7e83c (patch) | |
tree | 1bede27944240867d96e0440fbadb254b04368cd /net/mac80211/tx.c | |
parent | f933ebed7888a9a7d73ebeeb6bcbb3f710c423b4 (diff) |
mac80211: Fix BUG in pskb_expand_head when transmitting shared skbs
mac80211 doesn't handle shared skbs correctly at the moment. As a result
a possible resize can trigger a BUG in pskb_expand_head.
[ 676.030000] Kernel bug detected[#1]:
[ 676.030000] Cpu 0
[ 676.030000] $ 0 : 00000000 00000000 819662ff 00000002
[ 676.030000] $ 4 : 81966200 00000020 00000000 00000020
[ 676.030000] $ 8 : 819662e0 800043c0 00000002 00020000
[ 676.030000] $12 : 3b9aca00 00000000 00000000 00470000
[ 676.030000] $16 : 80ea2000 00000000 00000000 00000000
[ 676.030000] $20 : 818aa200 80ea2018 80ea2000 00000008
[ 676.030000] $24 : 00000002 800ace5c
[ 676.030000] $28 : 8199a000 8199bd20 81938f88 80f180d4
[ 676.030000] Hi : 0000026e
[ 676.030000] Lo : 0000757e
[ 676.030000] epc : 801245e4 pskb_expand_head+0x44/0x1d8
[ 676.030000] Not tainted
[ 676.030000] ra : 80f180d4 ieee80211_skb_resize+0xb0/0x114 [mac80211]
[ 676.030000] Status: 1000a403 KERNEL EXL IE
[ 676.030000] Cause : 10800024
[ 676.030000] PrId : 0001964c (MIPS 24Kc)
[ 676.030000] Modules linked in: mac80211_hwsim rt2800lib rt2x00soc rt2x00pci rt2x00lib mac80211 crc_itu_t crc_ccitt cfg80211 compat arc4 aes_generic deflate ecb cbc [last unloaded: rt2800pci]
[ 676.030000] Process kpktgend_0 (pid: 97, threadinfo=8199a000, task=81879f48, tls=00000000)
[ 676.030000] Stack : ffffffff 00000000 00000000 00000014 00000004 80ea2000 00000000 00000000
[ 676.030000] 818aa200 80f180d4 ffffffff 0000000a 81879f78 81879f48 81879f48 00000018
[ 676.030000] 81966246 80ea2000 818432e0 80f1a420 80203050 81814d98 00000001 81879f48
[ 676.030000] 81879f48 00000018 81966246 818432e0 0000001a 8199bdd4 0000001c 80f1b72c
[ 676.030000] 80203020 8001292c 80ef4aa2 7f10b55d 801ab5b8 81879f48 00000188 80005c90
[ 676.030000] ...
[ 676.030000] Call Trace:
[ 676.030000] [<801245e4>] pskb_expand_head+0x44/0x1d8
[ 676.030000] [<80f180d4>] ieee80211_skb_resize+0xb0/0x114 [mac80211]
[ 676.030000] [<80f1a420>] ieee80211_xmit+0x150/0x22c [mac80211]
[ 676.030000] [<80f1b72c>] ieee80211_subif_start_xmit+0x6f4/0x73c [mac80211]
[ 676.030000] [<8014361c>] pktgen_thread_worker+0xfac/0x16f8
[ 676.030000] [<8002ebe8>] kthread+0x7c/0x88
[ 676.030000] [<80008e0c>] kernel_thread_helper+0x10/0x18
[ 676.030000]
[ 676.030000]
[ 676.030000] Code: 24020001 10620005 2502001f <0200000d> 0804917a 00000000 2502001f 00441023 00531021
Fix this by making a local copy of shared skbs prior to mangeling them.
To avoid copying the skb unnecessarily move the skb_copy call below the
checks that don't need write access to the skb.
Also, move the assignment of nh_pos and h_pos below the skb_copy to point
to the correct skb.
It would be possible to avoid another resize of the copied skb by using
skb_copy_expand instead of skb_copy but that would make the patch more
complex. Also, shared skbs are a corner case right now, so the resize
shouldn't matter much.
Cc: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>
Cc: stable@kernel.org
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/tx.c')
-rw-r--r-- | net/mac80211/tx.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index df6aac523532..7a637b80a62e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1737,15 +1737,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1737 | int nh_pos, h_pos; | 1737 | int nh_pos, h_pos; |
1738 | struct sta_info *sta = NULL; | 1738 | struct sta_info *sta = NULL; |
1739 | u32 sta_flags = 0; | 1739 | u32 sta_flags = 0; |
1740 | struct sk_buff *tmp_skb; | ||
1740 | 1741 | ||
1741 | if (unlikely(skb->len < ETH_HLEN)) { | 1742 | if (unlikely(skb->len < ETH_HLEN)) { |
1742 | ret = NETDEV_TX_OK; | 1743 | ret = NETDEV_TX_OK; |
1743 | goto fail; | 1744 | goto fail; |
1744 | } | 1745 | } |
1745 | 1746 | ||
1746 | nh_pos = skb_network_header(skb) - skb->data; | ||
1747 | h_pos = skb_transport_header(skb) - skb->data; | ||
1748 | |||
1749 | /* convert Ethernet header to proper 802.11 header (based on | 1747 | /* convert Ethernet header to proper 802.11 header (based on |
1750 | * operation mode) */ | 1748 | * operation mode) */ |
1751 | ethertype = (skb->data[12] << 8) | skb->data[13]; | 1749 | ethertype = (skb->data[12] << 8) | skb->data[13]; |
@@ -1918,6 +1916,20 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1918 | goto fail; | 1916 | goto fail; |
1919 | } | 1917 | } |
1920 | 1918 | ||
1919 | /* | ||
1920 | * If the skb is shared we need to obtain our own copy. | ||
1921 | */ | ||
1922 | if (skb_shared(skb)) { | ||
1923 | tmp_skb = skb; | ||
1924 | skb = skb_copy(skb, GFP_ATOMIC); | ||
1925 | kfree_skb(tmp_skb); | ||
1926 | |||
1927 | if (!skb) { | ||
1928 | ret = NETDEV_TX_OK; | ||
1929 | goto fail; | ||
1930 | } | ||
1931 | } | ||
1932 | |||
1921 | hdr.frame_control = fc; | 1933 | hdr.frame_control = fc; |
1922 | hdr.duration_id = 0; | 1934 | hdr.duration_id = 0; |
1923 | hdr.seq_ctrl = 0; | 1935 | hdr.seq_ctrl = 0; |
@@ -1936,6 +1948,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1936 | encaps_len = 0; | 1948 | encaps_len = 0; |
1937 | } | 1949 | } |
1938 | 1950 | ||
1951 | nh_pos = skb_network_header(skb) - skb->data; | ||
1952 | h_pos = skb_transport_header(skb) - skb->data; | ||
1953 | |||
1939 | skb_pull(skb, skip_header_bytes); | 1954 | skb_pull(skb, skip_header_bytes); |
1940 | nh_pos -= skip_header_bytes; | 1955 | nh_pos -= skip_header_bytes; |
1941 | h_pos -= skip_header_bytes; | 1956 | h_pos -= skip_header_bytes; |