[PATCH] ipc: fix hung task in copy_ipcs()

Changyu Li <[email protected]>
Newsgroups org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
When the user_ns's UCOUNT_IPC_NAMESPACES limit is reached,
copy_ipcs eventually calls flush_work(&free_ipc_work) to wait for
in-flight work to reduce the number of ipc_namespaces so it can proceed.
Unfortunately flush_work() enters uninterruptible sleep and if there
are enough pending items to be freed by free_ipc_work then this would
trip hung task watchdog.

INFO: task blocked for more than 143 seconds.
      Not tainted syzkaller #0
task:syz-executor845 state:D stack:27496 pid:5856
Call Trace:
 <TASK>
 __flush_work+0x9c2/0xd70 kernel/workqueue.c:4431
 create_ipc_ns ipc/namespace.c:55 [inline]
 copy_ipcs+0x19b/0x6c0 ipc/namespace.c:116
 create_new_namespaces+0x210/0x6b0 kernel/nsproxy.c:112
 unshare_nsproxy_namespaces+0x149/0x190 kernel/nsproxy.c:234
 ksys_unshare+0x5a7/0x950 kernel/fork.c:3291
 </TASK>

Replace the uninterruptible flush_work() with a polling loop that
checks if there's an in-flight free_ipc_work. If there is then wait
interruptibly a short interval for forward progress in free_ipc_work
and retry.

If free_ipc_work is not in-flight nothing will free up quota so bail
with -ENOSPC as before. Otherwise wait interruptibly to not trip hung
task watchdog.

Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=97a62389c5611b0477f3
Signed-off-by: Changyu Li <[email protected]>
---
 ipc/namespace.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/ipc/namespace.c b/ipc/namespace.c
index 1e71353bdb..e8ea422d79 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -26,6 +26,8 @@
 static void free_ipc(struct work_struct *unused);
 static DECLARE_WORK(free_ipc_work, free_ipc);
 
+#define FREE_IPC_WAIT_JIFFIES 5
+
 static struct ucounts *inc_ipc_namespaces(struct user_namespace *ns)
 {
 	return inc_ucount(ns, current_euid(), UCOUNT_IPC_NAMESPACES);
@@ -49,12 +51,19 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 	if (!ucounts) {
 		/*
 		 * IPC namespaces are freed asynchronously, by free_ipc_work.
-		 * If frees were pending, flush_work will wait, and
-		 * return true. Fail the allocation if no frees are pending.
+		 * If there is in flight free_ipc_work, we'll wait for it to
+		 * make progress otherwise fail immediately.
 		 */
-		if (flush_work(&free_ipc_work))
-			goto again;
-		goto fail;
+		if (!work_busy(&free_ipc_work))
+			goto fail;
+
+		schedule_timeout_interruptible(FREE_IPC_WAIT_JIFFIES);
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			goto fail;
+		}
+
+		goto again;
 	}
 
 	err = -ENOMEM;

---
base-commit: 3d08ff75a47a3e7e2ab45a3bcab6723b4d906422
change-id: 20260811-work-53289e81bd3f

Best regards,
--  
Changyu Li <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.