diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-06-19 04:09:57 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-06-19 12:31:20 -0400 |
commit | 3a5a423bb958ad22eeccca66c533e85bf69ba10e (patch) | |
tree | ee184cabce960d6ceaf1d2ac4efd0f406c9b5555 /net | |
parent | 795d855d56c6d172f50a974f603ba923ac93ee76 (diff) |
nl80211: fix attrbuf access race by allocating a separate one
Since my commit 3713b4e364 ("nl80211: allow splitting wiphy
information in dumps"), nl80211_dump_wiphy() uses the global
nl80211_fam.attrbuf for parsing the incoming data. This wouldn't
be a problem if it only did so on the first dump iteration which
is locked against other commands in generic netlink, but due to
space constraints in cb->args (the needed state doesn't fit) I
decided to always parse the original message. That's racy though
since nl80211_fam.attrbuf could be used by some other parsing in
generic netlink concurrently.
For now, fix this by allocating a separate parse buffer (it's a
bit too big for the stack, currently 1448 bytes on 64-bit). For
-next, I'll change the code to parse into the global buffer in
the first round only and then allocate a smaller buffer to keep
the data in cb->args.
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/wireless/nl80211.c | 11 |
1 files changed, 9 insertions, 2 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d5aed3bb3945..b14b7e3cb6e6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -1564,12 +1564,17 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) | |||
1564 | struct cfg80211_registered_device *dev; | 1564 | struct cfg80211_registered_device *dev; |
1565 | s64 filter_wiphy = -1; | 1565 | s64 filter_wiphy = -1; |
1566 | bool split = false; | 1566 | bool split = false; |
1567 | struct nlattr **tb = nl80211_fam.attrbuf; | 1567 | struct nlattr **tb; |
1568 | int res; | 1568 | int res; |
1569 | 1569 | ||
1570 | /* will be zeroed in nlmsg_parse() */ | ||
1571 | tb = kmalloc(sizeof(*tb) * (NL80211_ATTR_MAX + 1), GFP_KERNEL); | ||
1572 | if (!tb) | ||
1573 | return -ENOMEM; | ||
1574 | |||
1570 | mutex_lock(&cfg80211_mutex); | 1575 | mutex_lock(&cfg80211_mutex); |
1571 | res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 1576 | res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, |
1572 | tb, nl80211_fam.maxattr, nl80211_policy); | 1577 | tb, NL80211_ATTR_MAX, nl80211_policy); |
1573 | if (res == 0) { | 1578 | if (res == 0) { |
1574 | split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP]; | 1579 | split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP]; |
1575 | if (tb[NL80211_ATTR_WIPHY]) | 1580 | if (tb[NL80211_ATTR_WIPHY]) |
@@ -1583,6 +1588,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) | |||
1583 | netdev = dev_get_by_index(sock_net(skb->sk), ifidx); | 1588 | netdev = dev_get_by_index(sock_net(skb->sk), ifidx); |
1584 | if (!netdev) { | 1589 | if (!netdev) { |
1585 | mutex_unlock(&cfg80211_mutex); | 1590 | mutex_unlock(&cfg80211_mutex); |
1591 | kfree(tb); | ||
1586 | return -ENODEV; | 1592 | return -ENODEV; |
1587 | } | 1593 | } |
1588 | if (netdev->ieee80211_ptr) { | 1594 | if (netdev->ieee80211_ptr) { |
@@ -1593,6 +1599,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) | |||
1593 | dev_put(netdev); | 1599 | dev_put(netdev); |
1594 | } | 1600 | } |
1595 | } | 1601 | } |
1602 | kfree(tb); | ||
1596 | 1603 | ||
1597 | list_for_each_entry(dev, &cfg80211_rdev_list, list) { | 1604 | list_for_each_entry(dev, &cfg80211_rdev_list, list) { |
1598 | if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) | 1605 | if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) |