Patch "nl80211: fix dumpit error path RTNL deadlocks" has been added to the 4.4-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
nl80211: fix dumpit error path RTNL deadlocks
to the 4.4-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
nl80211-fix-dumpit-error-path-rtnl-deadlocks.patch
and it can be found in the queue-4.4 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
ea90e0dc8cecba6359b481e24d9c37160f6f524fMon Sep 17 00:00:00 2001
From: Johannes Berg <[email protected]>
Date: Wed, 15 Mar 2017 14:26:04 +0100
Subject: nl80211: fix dumpit error path RTNL deadlocks
From: Johannes Berg <[email protected]>
commit ea90e0dc8cecba6359b481e24d9c37160f6f524f upstream.
Sowmini pointed out Dmitry's RTNL deadlock report to me, and it turns out to be perfectly accurate - there are various error paths that miss unlock of the RTNL.
To fix those, change the locking a bit to not be conditional in all those nl80211_prepare_*_dump() functions, but make those require the RTNL to start with, and fix the buggy error paths. This also let me use sparse (by appropriately overriding the rtnl_lock/rtnl_unlock functions) to validate the changes.
--- net/wireless/nl80211.c | 121 +++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 68 deletions(-)
net/wireless/nl80211.c +53 -68
@@ -492,21 +492,17 @@ static int nl80211_prepare_wdev_dump(str {int err; -rtnl_lock (); -if (!cb->args[0 ]) { err =nlmsg_parse (cb->nlh,GENL_HDRLEN + nl80211_fam.hdrsize, nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy);if (err) - goto out_unlock; +return err; *wdev =__cfg80211_wdev_from_attrs (sock_net (skb->sk), nl80211_fam.attrbuf); -if (IS_ERR (*wdev)) { - err =PTR_ERR (*wdev); - goto out_unlock; - } +if (IS_ERR (*wdev)) +return PTR_ERR (*wdev); *rdev =wiphy_to_rdev ((*wdev)->wiphy);/* 0 is the first index - add 1 to parse only once */ cb->args[0 ] = (*rdev)->wiphy_idx +1 ;
@@ -516,10 +512,8 @@ static int nl80211_prepare_wdev_dump(strstruct wiphy *wiphy =wiphy_idx_to_wiphy (cb->args[0 ] -1 );struct wireless_dev *tmp; -if (!wiphy) { - err = -ENODEV ; - goto out_unlock; - } +if (!wiphy) +return -ENODEV ; *rdev =wiphy_to_rdev (wiphy); *wdev =NULL ;
@@ -530,21 +524,11 @@ static int nl80211_prepare_wdev_dump(str } } -if (!*wdev) { - err = -ENODEV ; - goto out_unlock; - } +if (!*wdev) +return -ENODEV ; }return 0 ; - out_unlock: -rtnl_unlock (); -return err; -} - -static void nl80211_finish_wdev_dump (struct cfg80211_registered_device *rdev) -{ -rtnl_unlock (); }/* IE validation */
@@ -3884,9 +3868,10 @@ static int nl80211_dump_station(struct sint sta_idx = cb->args[2 ];int err; +rtnl_lock (); err =nl80211_prepare_wdev_dump (skb, cb, &rdev, &wdev);if (err) -return err; + goto out_err;if (!wdev->netdev) { err = -EINVAL ;
@@ -3922,7 +3907,7 @@ static int nl80211_dump_station(struct s cb->args[2 ] = sta_idx; err = skb->len; out_err: -nl80211_finish_wdev_dump (rdev); +rtnl_unlock ();return err; }
@@ -4639,9 +4624,10 @@ static int nl80211_dump_mpath(struct sk_int path_idx = cb->args[2 ];int err; +rtnl_lock (); err =nl80211_prepare_wdev_dump (skb, cb, &rdev, &wdev);if (err) -return err; + goto out_err;if (!rdev->ops->dump_mpath) { err = -EOPNOTSUPP ;
@@ -4675,7 +4661,7 @@ static int nl80211_dump_mpath(struct sk_ cb->args[2 ] = path_idx; err = skb->len; out_err: -nl80211_finish_wdev_dump (rdev); +rtnl_unlock ();return err; }
@@ -4835,9 +4821,10 @@ static int nl80211_dump_mpp(struct sk_buint path_idx = cb->args[2 ];int err; +rtnl_lock (); err =nl80211_prepare_wdev_dump (skb, cb, &rdev, &wdev);if (err) -return err; + goto out_err;if (!rdev->ops->dump_mpp) { err = -EOPNOTSUPP ;
@@ -4870,7 +4857,7 @@ static int nl80211_dump_mpp(struct sk_bu cb->args[2 ] = path_idx; err = skb->len; out_err: -nl80211_finish_wdev_dump (rdev); +rtnl_unlock ();return err; }
@@ -6806,9 +6793,12 @@ static int nl80211_dump_scan(struct sk_bint start = cb->args[2 ], idx =0 ;int err; +rtnl_lock (); err =nl80211_prepare_wdev_dump (skb, cb, &rdev, &wdev); -if (err) +if (err) { +rtnl_unlock ();return err; + }wdev_lock (wdev);spin_lock_bh (&rdev->bss_lock);
@@ -6831,7 +6821,7 @@ static int nl80211_dump_scan(struct sk_bwdev_unlock (wdev); cb->args[2 ] = idx; -nl80211_finish_wdev_dump (rdev); +rtnl_unlock ();return skb->len; }
@@ -6915,9 +6905,10 @@ static int nl80211_dump_survey(struct skint res;bool radio_stats; +rtnl_lock (); res =nl80211_prepare_wdev_dump (skb, cb, &rdev, &wdev);if (res) -return res; + goto out_err;/* prepare_wdev_dump parsed the attributes */ radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS ];
@@ -6958,7 +6949,7 @@ static int nl80211_dump_survey(struct sk cb->args[2 ] = survey_idx; res = skb->len; out_err: -nl80211_finish_wdev_dump (rdev); +rtnl_unlock ();return res; }
@@ -10158,17 +10149,13 @@ static int nl80211_prepare_vendor_dump(svoid *data =NULL ;unsigned int data_len =0 ; -rtnl_lock (); -if (cb->args[0 ]) {/* subtract the 1 again here */ struct wiphy *wiphy =wiphy_idx_to_wiphy (cb->args[0 ] -1 );struct wireless_dev *tmp; -if (!wiphy) { - err = -ENODEV ; - goto out_unlock; - } +if (!wiphy) +return -ENODEV ; *rdev =wiphy_to_rdev (wiphy); *wdev =NULL ;
@@ -10189,13 +10176,11 @@ static int nl80211_prepare_vendor_dump(s nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy);if (err) - goto out_unlock; +return err;if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID ] || - !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD ]) { - err = -EINVAL ; - goto out_unlock; - } + !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD ]) +return -EINVAL ; *wdev =__cfg80211_wdev_from_attrs (sock_net (skb->sk), nl80211_fam.attrbuf);
@@ -10204,10 +10189,8 @@ static int nl80211_prepare_vendor_dump(s *rdev =__cfg80211_rdev_from_attrs (sock_net (skb->sk), nl80211_fam.attrbuf); -if (IS_ERR (*rdev)) { - err =PTR_ERR (*rdev); - goto out_unlock; - } +if (IS_ERR (*rdev)) +return PTR_ERR (*rdev); vid =nla_get_u32 (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID ]); subcmd =nla_get_u32 (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD ]);
@@ -10220,19 +10203,15 @@ static int nl80211_prepare_vendor_dump(sif (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)continue ; -if (!vcmd->dumpit) { - err = -EOPNOTSUPP ; - goto out_unlock; - } +if (!vcmd->dumpit) +return -EOPNOTSUPP ; vcmd_idx = i;break ; } -if (vcmd_idx <0 ) { - err = -EOPNOTSUPP ; - goto out_unlock; - } +if (vcmd_idx <0 ) +return -EOPNOTSUPP ;if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA ]) { data =nla_data (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA ]);
@@ -10249,9 +10228,6 @@ static int nl80211_prepare_vendor_dump(s/* keep rtnl locked in successful case */ return 0 ; - out_unlock: -rtnl_unlock (); -return err; } static int nl80211_vendor_cmd_dump(struct sk_buff *skb,
@@ -10266,9 +10242,10 @@ static int nl80211_vendor_cmd_dump(strucint err;struct nlattr *vendor_data; +rtnl_lock (); err =nl80211_prepare_vendor_dump (skb, cb, &rdev, &wdev);if (err) -return err; + goto out; vcmd_idx = cb->args[2 ]; data = (void *)cb->args[3 ];
@@ -10277,18 +10254,26 @@ static int nl80211_vendor_cmd_dump(strucif (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |WIPHY_VENDOR_CMD_NEED_NETDEV )) { -if (!wdev) -return -EINVAL ; +if (!wdev) { + err = -EINVAL ; + goto out; + }if (vcmd->flags &WIPHY_VENDOR_CMD_NEED_NETDEV && - !wdev->netdev) -return -EINVAL ; + !wdev->netdev) { + err = -EINVAL ; + goto out; + }if (vcmd->flags &WIPHY_VENDOR_CMD_NEED_RUNNING ) {if (wdev->netdev && - !netif_running (wdev->netdev)) -return -ENETDOWN ; -if (!wdev->netdev && !wdev->p2p_started) -return -ENETDOWN ; + !netif_running (wdev->netdev)) { + err = -ENETDOWN ; + goto out; + } +if (!wdev->netdev && !wdev->p2p_started) { + err = -ENETDOWN ; + goto out; + } } }
Patches currently in stable-queue which might be from [email protected] are
queue-4.4/nl80211-fix-dumpit-error-path-rtnl-deadlocks.patch