[PATCH RFC] net: handle device_rename() failures during netns switch
"syzbot" <[email protected]>
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <[email protected]> |
Memory allocation failures during a network namespace switch (e.g., in
device_rename()) trigger WARN_ON(err) in __dev_change_net_namespace(),
cfg802154_switch_netns(), and cfg80211_switch_netns(). WARN_ON must not be
used for conditions that can legitimately happen, such as -ENOMEM, because
it crashes machines with panic_on_warn enabled. A less severe logging
mechanism like dev_warn() or netdev_warn() should be used instead.
Handling these failures is tricky. Reverting the logical namespace change
(dev_net_set(dev, net_old)) when device_rename() fails introduces a KASAN
slab-use-after-free bug. This happens because cfg802154_pernet_exit() and
cfg80211_pernet_exit() attempt to move devices back to init_net when a
namespace is destroyed. If device_rename() fails and the change is
reverted, devices are left in the dying namespace. When the namespace is
subsequently destroyed, default_device_exit_batch() unregisters all
remaining netdevs. Drivers like mac802154 and mac80211 do not properly
handle NETDEV_UNREGISTER for their internal interface lists, leading to a
UAF when the list is later accessed.
On the other hand, simply ignoring the failure and accepting the sysfs
inconsistency leads to another UAF in kernfs_ns_id. device_rename() is
responsible for updating the sysfs directory's namespace tag (kn->ns). If
it fails, the sysfs directory remains tagged with the old network
namespace. When the old namespace is destroyed, the kn->ns pointer becomes
dangling. Later, when the device is unregistered, device_del() tries to
remove sysfs entries and dereferences this freed namespace pointer.
To safely handle device_rename() failures without causing either UAF, we
must not revert the logical namespace change. This ensures the device
successfully moves to the new namespace (e.g., init_net) and avoids being
unregistered by the dying namespace's cleanup routine. Instead, we manually
update the sysfs namespace tag. If device_rename() fails, the kobject still
retains its old name. We can call sysfs_rename_dir_ns() with the old name
and the new namespace. Because the name hasn't changed, kernfs_rename_ns()
will not allocate memory and will successfully update the namespace tag,
preventing the kernfs UAF. Finally, we replace the WARN_ON(err) calls with
netdev_warn() and dev_warn() across net/core/dev.c, net/ieee802154/core.c,
and net/wireless/core.c to log the expected memory allocation failures
without crashing the system.
Fixes: 8b41d1887db7 ("[NET]: Fix running without sysfs")
Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=3344d668bbbc12996d46
Link: https://syzkaller.appspot.com/ai_job?id=85f31f19-953c-4b06-8f37-956317d66ff1
To: "Alexander Aring" <[email protected]>
To: "David S. Miller" <[email protected]>
To: "Eric Dumazet" <[email protected]>
To: "Johannes Berg" <[email protected]>
To: "Jakub Kicinski" <[email protected]>
To: <[email protected]>
To: <[email protected]>
To: "Miquel Raynal" <[email protected]>
To: <[email protected]>
To: "Paolo Abeni" <[email protected]>
To: "Stefan Schmidt" <[email protected]>
Cc: "Simon Horman" <[email protected]>
Cc: <[email protected]>
Cc: "Stanislav Fomichev" <[email protected]>
---
diff --git a/net/core/dev.c b/net/core/dev.c
index 0c6c270d9..25f0d8bf5 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -12652,7 +12652,12 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
dev_set_uevent_suppress(&dev->dev, 1);
err = device_rename(&dev->dev, dev->name);
dev_set_uevent_suppress(&dev->dev, 0);
- WARN_ON(err);
+ if (err) {
+ netdev_warn(dev, "failed to rename device: %d\n", err);
+ sysfs_rename_dir_ns(&dev->dev.kobj,
+ kobject_name(&dev->dev.kobj),
+ kobject_namespace(&dev->dev.kobj));
+ }
/* Send a netdev-add uevent to the new namespace */
kobject_uevent(&dev->dev.kobj, KOBJ_ADD);
@@ -12662,7 +12667,8 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
* namespace is different from the original one.
*/
err = netdev_change_owner(dev, net_old, net);
- WARN_ON(err);
+ if (err)
+ netdev_warn(dev, "failed to change owner: %d\n", err);
netdev_lock(dev);
dev->moving_ns = false;
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index 89b671b12..ddff07ae8 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -245,7 +245,11 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
wpan_dev->netdev->netns_immutable = false;
err = dev_change_net_namespace(wpan_dev->netdev, net,
"wpan%d");
- WARN_ON(err);
+ if (err)
+ dev_warn(
+ &rdev->wpan_phy.dev,
+ "failed to revert netns change for wpan_dev: %d\n",
+ err);
wpan_dev->netdev->netns_immutable = true;
}
@@ -255,7 +259,9 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
wpan_phy_net_set(&rdev->wpan_phy, net);
err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev));
- WARN_ON(err);
+ if (err)
+ dev_warn(&rdev->wpan_phy.dev, "failed to rename device: %d\n",
+ err);
return 0;
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 6783e0672..824009c30 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -184,7 +184,11 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
wdev->netdev->netns_immutable = false;
err = dev_change_net_namespace(wdev->netdev, net,
"wlan%d");
- WARN_ON(err);
+ if (err)
+ dev_warn(
+ &rdev->wiphy.dev,
+ "failed to revert netns change for wdev: %d\n",
+ err);
wdev->netdev->netns_immutable = true;
}
@@ -204,7 +208,9 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
wiphy_net_set(&rdev->wiphy, net);
err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev));
- WARN_ON(err);
+ if (err)
+ dev_warn(&rdev->wiphy.dev, "failed to rename device: %d\n",
+ err);
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
base-commit: e7ae89a0c97ce2b68b0983cd01eda67cf373517d
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].