* FAILED: patch "[PATCH] net/9p: fix infinite loop in p9_client_rpc on fatal signal" failed to apply to 5.10-stable tree
@ 2026-07-20 14:32 gregkh
2026-07-25 10:55 ` [PATCH 5.10.y 1/2] net/9p: fix infinite loop in p9_client_rpc on fatal signal Sasha Levin
0 siblings, 1 reply; 3+ messages in thread
From: gregkh @ 2026-07-20 14:32 UTC (permalink / raw)
To: kovalev, asmadeus; +Cc: stable
The patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x 6b4f48728faa8bb514368f7eacda05565dea8696
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026072024-extending-list-343c@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 6b4f48728faa8bb514368f7eacda05565dea8696 Mon Sep 17 00:00:00 2001
From: Vasiliy Kovalev <kovalev@altlinux.org>
Date: Wed, 15 Apr 2026 18:52:37 +0300
Subject: [PATCH] net/9p: fix infinite loop in p9_client_rpc on fatal signal
When p9_client_rpc() is called with type P9_TFLUSH and the transport
has no peer (e.g. fd transport backed by pipes with no 9p server),
a fatal signal causes an infinite loop:
again:
err = io_wait_event_killable(req->wq, ...)
/* SIGKILL wakes the task, returns -ERESTARTSYS */
if (err == -ERESTARTSYS && c->status == Connected &&
type == P9_TFLUSH) {
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
goto again;
}
clear_thread_flag() clears TIF_SIGPENDING before jumping back to
io_wait_event_killable(). signal_pending_state() checks TIF_SIGPENDING,
finds it zero, and the task goes to sleep again. The task can only wake
on the next signal delivery that calls signal_wake_up() and sets
TIF_SIGPENDING again. When that happens the loop repeats, clears
TIF_SIGPENDING, and sleeps again indefinitely.
This is triggered in practice by coredump_wait(): when a thread in a
multi-threaded process causes a coredump (e.g. via SIGSYS from Syscall
User Dispatch), coredump_wait() sends SIGKILL to all other threads and
waits for them to call mm_release(). If one of those threads is blocked
in p9_client_rpc() over an fd transport with no peer, it enters the
P9_TFLUSH loop and never calls mm_release(), so coredump_wait() stalls
forever:
INFO: task syz.0.18:676 blocked for more than 143 seconds.
Not tainted 6.12.77+ #1
task:syz.0.18 state:D stack:27600 pid:676 tgid:673 ppid:630 flags:0x00000004
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5344 [inline]
__schedule+0xcb4/0x5d50 kernel/sched/core.c:6724
__schedule_loop kernel/sched/core.c:6801 [inline]
schedule+0xe5/0x350 kernel/sched/core.c:6816
schedule_timeout+0x253/0x290 kernel/time/timer.c:2593
do_wait_for_common kernel/sched/completion.c:95 [inline]
__wait_for_common+0x409/0x600 kernel/sched/completion.c:116
wait_for_common kernel/sched/completion.c:127 [inline]
wait_for_completion_state+0x1d/0x40 kernel/sched/completion.c:264
coredump_wait fs/coredump.c:448 [inline]
do_coredump+0x854/0x4350 fs/coredump.c:629
get_signal+0x1425/0x2730 kernel/signal.c:2903
arch_do_signal_or_restart+0x81/0x880 arch/x86/kernel/signal.c:337
exit_to_user_mode_loop kernel/entry/common.c:111 [inline]
exit_to_user_mode_prepare include/linux/entry-common.h:328 [inline]
__syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline]
syscall_exit_to_user_mode+0xf9/0x160 kernel/entry/common.c:218
do_syscall_64+0x102/0x220 arch/x86/entry/common.c:84
entry_SYSCALL_64_after_hwframe+0x77/0x7f
</TASK>
Fix: check fatal_signal_pending() before clearing TIF_SIGPENDING in the
P9_TFLUSH retry loop. At that point TIF_SIGPENDING is still set, so
fatal_signal_pending() works correctly. If a fatal signal is pending,
jump to recalc_sigpending to restore TIF_SIGPENDING and return
-ERESTARTSYS to the caller.
The same defect is present in stable kernels back to 5.4. On those
kernels the infinite loop is broken earlier by a second SIGKILL from
the parent process (e.g. kill_and_wait() retrying after a timeout),
resulting in a zombie process and a shutdown delay rather than a
permanent D-state hang, but the underlying flaw is the same.
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
Fixes: 91b8534fa8f5 ("9p: make rpc code common and rework flush code")
Closes: https://syzkaller.appspot.com/bug?extid=3ce7863f8fc836a427e7
Cc: stable@vger.kernel.org
Signed-off-by: Vasiliy Kovalev <kovalev@altlinux.org>
Message-ID: <20260415155237.182891-1-kovalev@altlinux.org>
Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
diff --git a/net/9p/client.c b/net/9p/client.c
index b7d947910037..69d3efd340c0 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -600,6 +600,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
if (err == -ERESTARTSYS && c->status == Connected &&
type == P9_TFLUSH) {
+ if (fatal_signal_pending(current))
+ goto recalc_sigpending;
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
goto again;
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH 5.10.y 1/2] net/9p: fix infinite loop in p9_client_rpc on fatal signal 2026-07-20 14:32 FAILED: patch "[PATCH] net/9p: fix infinite loop in p9_client_rpc on fatal signal" failed to apply to 5.10-stable tree gregkh @ 2026-07-25 10:55 ` Sasha Levin 2026-07-25 10:55 ` [PATCH 5.10.y 2/2] [AUTOMATED CONFLICT RESOLUTION] Documentation for 6b4f48728faa8bb514368f7eacda05565dea8696 Sasha Levin 0 siblings, 1 reply; 3+ messages in thread From: Sasha Levin @ 2026-07-25 10:55 UTC (permalink / raw) To: stable; +Cc: Vasiliy Kovalev, Dominique Martinet, Sasha Levin From: Vasiliy Kovalev <kovalev@altlinux.org> [ Upstream commit 6b4f48728faa8bb514368f7eacda05565dea8696 ] When p9_client_rpc() is called with type P9_TFLUSH and the transport has no peer (e.g. fd transport backed by pipes with no 9p server), a fatal signal causes an infinite loop: again: err = io_wait_event_killable(req->wq, ...) /* SIGKILL wakes the task, returns -ERESTARTSYS */ if (err == -ERESTARTSYS && c->status == Connected && type == P9_TFLUSH) { sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; } clear_thread_flag() clears TIF_SIGPENDING before jumping back to io_wait_event_killable(). signal_pending_state() checks TIF_SIGPENDING, finds it zero, and the task goes to sleep again. The task can only wake on the next signal delivery that calls signal_wake_up() and sets TIF_SIGPENDING again. When that happens the loop repeats, clears TIF_SIGPENDING, and sleeps again indefinitely. This is triggered in practice by coredump_wait(): when a thread in a multi-threaded process causes a coredump (e.g. via SIGSYS from Syscall User Dispatch), coredump_wait() sends SIGKILL to all other threads and waits for them to call mm_release(). If one of those threads is blocked in p9_client_rpc() over an fd transport with no peer, it enters the P9_TFLUSH loop and never calls mm_release(), so coredump_wait() stalls forever: INFO: task syz.0.18:676 blocked for more than 143 seconds. Not tainted 6.12.77+ #1 task:syz.0.18 state:D stack:27600 pid:676 tgid:673 ppid:630 flags:0x00000004 Call Trace: <TASK> context_switch kernel/sched/core.c:5344 [inline] __schedule+0xcb4/0x5d50 kernel/sched/core.c:6724 __schedule_loop kernel/sched/core.c:6801 [inline] schedule+0xe5/0x350 kernel/sched/core.c:6816 schedule_timeout+0x253/0x290 kernel/time/timer.c:2593 do_wait_for_common kernel/sched/completion.c:95 [inline] __wait_for_common+0x409/0x600 kernel/sched/completion.c:116 wait_for_common kernel/sched/completion.c:127 [inline] wait_for_completion_state+0x1d/0x40 kernel/sched/completion.c:264 coredump_wait fs/coredump.c:448 [inline] do_coredump+0x854/0x4350 fs/coredump.c:629 get_signal+0x1425/0x2730 kernel/signal.c:2903 arch_do_signal_or_restart+0x81/0x880 arch/x86/kernel/signal.c:337 exit_to_user_mode_loop kernel/entry/common.c:111 [inline] exit_to_user_mode_prepare include/linux/entry-common.h:328 [inline] __syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline] syscall_exit_to_user_mode+0xf9/0x160 kernel/entry/common.c:218 do_syscall_64+0x102/0x220 arch/x86/entry/common.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Fix: check fatal_signal_pending() before clearing TIF_SIGPENDING in the P9_TFLUSH retry loop. At that point TIF_SIGPENDING is still set, so fatal_signal_pending() works correctly. If a fatal signal is pending, jump to recalc_sigpending to restore TIF_SIGPENDING and return -ERESTARTSYS to the caller. The same defect is present in stable kernels back to 5.4. On those kernels the infinite loop is broken earlier by a second SIGKILL from the parent process (e.g. kill_and_wait() retrying after a timeout), resulting in a zombie process and a shutdown delay rather than a permanent D-state hang, but the underlying flaw is the same. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 91b8534fa8f5 ("9p: make rpc code common and rework flush code") Closes: https://syzkaller.appspot.com/bug?extid=3ce7863f8fc836a427e7 Cc: stable@vger.kernel.org Signed-off-by: Vasiliy Kovalev <kovalev@altlinux.org> Message-ID: <20260415155237.182891-1-kovalev@altlinux.org> Signed-off-by: Dominique Martinet <asmadeus@codewreck.org> [ adjusted context to match the older parenthesized `if ((err == -ERESTARTSYS) && ...)` condition style ] Signed-off-by: Sasha Levin <sashal@kernel.org> --- net/9p/client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/9p/client.c b/net/9p/client.c index f49f5fd98ad820..1bcab7176e5f89 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -771,6 +771,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) if ((err == -ERESTARTSYS) && (c->status == Connected) && (type == P9_TFLUSH)) { + if (fatal_signal_pending(current)) + goto recalc_sigpending; sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; -- 2.53.0 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 5.10.y 2/2] [AUTOMATED CONFLICT RESOLUTION] Documentation for 6b4f48728faa8bb514368f7eacda05565dea8696 2026-07-25 10:55 ` [PATCH 5.10.y 1/2] net/9p: fix infinite loop in p9_client_rpc on fatal signal Sasha Levin @ 2026-07-25 10:55 ` Sasha Levin 0 siblings, 0 replies; 3+ messages in thread From: Sasha Levin @ 2026-07-25 10:55 UTC (permalink / raw) To: stable; +Cc: Sasha Levin This commit documents the automated conflict resolution performed by claude for upstream commit 6b4f48728faa8bb514368f7eacda05565dea8696. claude's resolution process: ================================================================================ Build still in progress (no output yet). Waiting for completion — I'll report once it lands. ================================================================================ Backport verification analysis (via claude): ================================================================================ ## Verdict: correct, faithful, minimal — context-only adaptation ### 1. Intent preservation The upstream fix is two lines added as the first statement inside the `type == P9_TFLUSH` retry branch of `p9_client_rpc()`. The backport adds those **exact same two lines, byte-identical, at the exact same logical position** (`net/9p/client.c:773-774`): ```c if ((err == -ERESTARTSYS) && (c->status == Connected) && (type == P9_TFLUSH)) { if (fatal_signal_pending(current)) goto recalc_sigpending; sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; } ``` The `recalc_sigpending:` label exists in 5.10's `p9_client_rpc()` at `client.c:797`, in the same relative position and with byte-identical body to upstream: ```c recalc_sigpending: if (sigpending) { spin_lock_irqsave(¤t->sighand->siglock, flags); recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, flags); } if (err < 0) goto reterr; ``` So both post-conditions the commit message promises hold in 5.10: - `sigpending == 1` (a signal was pending at RPC entry) → `recalc_sigpending()` restores `TIF_SIGPENDING`. - `sigpending == 0` → `TIF_SIGPENDING` was never cleared on this path, so it's still set. Correct either way. - `err == -ERESTARTSYS < 0` → `goto reterr` → `p9_tag_remove()` → `ERR_PTR(safe_errno(-ERESTARTSYS))`. `safe_errno()` (`client.c:94`) passes `-512` through unchanged (`-512 > -MAX_ERRNO`), so the caller genuinely sees `-ERESTARTSYS`, not a mangled `-EPROTO`. ### 2. Differences vs upstream — all pre-existing context, none functional The only diff-level deltas are blob hashes, hunk line numbers (600 → 771), and the `if` condition's parenthesization: | upstream | 5.10 | |---|---| | `if (err == -ERESTARTSYS && c->status == Connected &&`<br>` type == P9_TFLUSH) {` | `if ((err == -ERESTARTSYS) && (c->status == Connected)`<br>` && (type == P9_TFLUSH)) {` | That is purely the v5.17-era 9p checkpatch-style reflow, semantically identical. Other pre-existing divergences in the enclosing function (`wait_event_killable` vs `io_wait_event_killable`, no `READ_ONCE(req->status)`, `p9_req_put(req)` vs `p9_req_put(c, req)`, `p9_tag_remove()` vs `p9_req_put()` at `reterr:`) are all upstream-only refactors from 6.1/6.2 and are **orthogonal to this fix** — none of them is touched, and none changes the signal semantics the fix depends on. Critically, the killable-wait semantics are the same: 5.10's `wait_event_killable` returns `-ERESTARTSYS` only via `signal_pending_state(TASK_KILLABLE, ...)`, which for `TASK_KILLABLE` (`TASK_WAKEKILL|TASK_UNINTERRUPTIBLE`, no `TASK_INTERRUPTIBLE` bit) requires `__fatal_signal_pending()`. So at the new check `TIF_SIGPENDING` is still set and `SIGKILL` is still in `current->pending.signal` — nothing between the wait returning and the check dequeues signals or calls `recalc_sigpending()`. `fatal_signal_pending()` therefore evaluates exactly as upstream intends. `io_wait_event_killable` differs only in io_schedule accounting. ### 3. Concerns checked and cleared - **Build/compile**: `<linux/sched/signal.h>` is already included (`client.c:20`), and `signal_pending()` is already used ~25 lines above in the same function — `fatal_signal_pending()` needs no new include. `goto recalc_sigpending` targets a label already jumped to by the pre-existing `trans_mod->request()` failure path, so no "label used but not defined". Label vs. function name `recalc_sigpending` live in different C namespaces (already the case pre-patch). `git show --check` is clean; diffstat is exactly `1 file changed, 2 insertions(+)` — no stray hunks. - **Applicability**: `Fixes: 91b8534fa8f5` (v3.7) is confirmed an ancestor of HEAD, and the commit message explicitly states the defect reaches back to 5.4. 5.10.261 is in scope. - **No duplicate/competing fix**: `fatal_signal_pending` appears exactly once in all of `net/9p/`, at the new line. No prior stable-only patch to collide with. - **`p9_client_zc_rpc()`**: correctly untouched — 5.10's zc variant has no `again:`/`P9_TFLUSH` retry loop, matching upstream, so the one-site fix is complete. - **Abandoned-TFLUSH lifetime (the one thing worth naming)**: the new early exit means `p9_client_flush()` returns `-ERESTARTSYS` before calling `trans_mod->cancelled(oldreq)`, so `oldreq` stays on the transport's `req_list`. In 5.10 the flush req itself then goes through `reterr: p9_tag_remove()`, which does an *eager* `idr_remove()` + put, whereas upstream ≥6.1 removes from the idr only on the final `p9_req_put()`. I checked whether this makes 5.10 more exposed: it doesn't. `trans_fd` takes its own reference at `trans_fd.c:475` before moving a req to `req_list`, so refcount goes 2→1 and nothing is freed; a late response for a now-unknown tag hits the `!m->rreq` guard in `p9_read_work()` (`trans_fd.c:336`) which logs "Unexpected packet tag" and errors out rather than dereferencing; and `p9_conn_cancel()` completes and puts the stranded req at disconnect. Identical exposure to the pre-existing `err == -ERESTARTSYS && c->status == Connected` path and to `trans_mod->request()` failure. **No UAF, no leak beyond disconnect, and no 5.10-specific regression.** - **Behavioral note (upstream-inherited, not a backport defect)**: as analyzed above, whenever this branch is reached `fatal_signal_pending()` is necessarily true, so the `sigpending = 1; clear_thread_flag(); goto again;` retry becomes effectively unreachable. That is upstream's deliberate semantics ("return `-ERESTARTSYS` to the caller"), and 5.10 reproduces it exactly because its wait is equally killable-only. The backport does not amplify or alter this. ### 4. Overall assessment **High quality — accept.** This is the minimal possible adaptation: the added code is byte-identical to upstream, the target label and its body are identical, all supporting semantics (`safe_errno`, killable-wait signal conditions, `sigpending` bookkeeping) verify out in 5.10, and the surrounding version drift is entirely cosmetic/orthogonal. The only "resolution" required was matching the older parenthesized `if` context. Build verification is the one item still outstanding — `stable build log` (x86_64 + aarch64) is running now; I'll report the result when it lands. Given the change adds no new symbols or includes, a failure would be surprising. ================================================================================ This is an empty commit for documentation purposes only. -- 2.53.0 ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-25 10:55 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-20 14:32 FAILED: patch "[PATCH] net/9p: fix infinite loop in p9_client_rpc on fatal signal" failed to apply to 5.10-stable tree gregkh 2026-07-25 10:55 ` [PATCH 5.10.y 1/2] net/9p: fix infinite loop in p9_client_rpc on fatal signal Sasha Levin 2026-07-25 10:55 ` [PATCH 5.10.y 2/2] [AUTOMATED CONFLICT RESOLUTION] Documentation for 6b4f48728faa8bb514368f7eacda05565dea8696 Sasha Levin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox