[PATCH] kobject: avoid blocking allocation while holding uevent_sock_mutex
From: Tao Yu
Date: Wed Aug 26 2026 - 02:46:46 EST
uevent_net_broadcast_untagged() still holds uevent_sock_mutex across
netlink_broadcast(). That serializes all untagged uevent senders behind
one global mutex, and it also keeps the mutex held while the netlink
broadcast path may perform blocking memory allocation.
This becomes visible during USB enumeration, where device_add() sends a
KOBJ_ADD uevent from the usb_hub_wq context. If a listener is slow or
the netlink broadcast path runs into memory pressure, the sender can sit
behind uevent_sock_mutex long enough to trigger hung task reports.
Keep the existing send-side ordering, but move the uevent skb allocation
out of the critical section and use GFP_NOWAIT for the broadcast clones
performed while uevent_sock_mutex is held. This removes the sleeping
allocation point from the locked region without changing uevent delivery
semantics.
Reported-by: syzbot+c22bb42560ec86726aba@xxxxxxxxxxxxxxxxxxxxxxxxx
Signed-off-by: Tao Yu <tao1.yu@xxxxxxxxx>
---
lib/kobject_uevent.c | 28 +++++++++++++++++++---------
1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index ddbc4d7482d24..b8832a5ca583d 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -311,9 +311,26 @@ static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,
{
struct sk_buff *skb = NULL;
struct uevent_sock *ue_sk;
+ bool has_listeners = false;
int retval = 0;
- /* send netlink message */
+ mutex_lock(&uevent_sock_mutex);
+ list_for_each_entry(ue_sk, &uevent_sock_list, list) {
+ if (!netlink_has_listeners(ue_sk->sk, 1))
+ continue;
+
+ has_listeners = true;
+ break;
+ }
+ mutex_unlock(&uevent_sock_mutex);
+
+ if (has_listeners) {
+ skb = alloc_uevent_skb(env, action_string, devpath);
+ if (!skb)
+ return -ENOMEM;
+ }
+
+ /* Keep send-side ordering, but avoid sleeping while holding the mutex. */
mutex_lock(&uevent_sock_mutex);
list_for_each_entry(ue_sk, &uevent_sock_list, list) {
struct sock *uevent_sock = ue_sk->sk;
@@ -321,15 +338,8 @@ static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,
if (!netlink_has_listeners(uevent_sock, 1))
continue;
- if (!skb) {
- retval = -ENOMEM;
- skb = alloc_uevent_skb(env, action_string, devpath);
- if (!skb)
- continue;
- }
-
retval = netlink_broadcast(uevent_sock, skb_get(skb), 0, 1,
- GFP_KERNEL);
+ GFP_NOWAIT);
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS || retval == -ESRCH)
retval = 0;
--
2.34.1