From 5c8b6adc2c7d4532eb9f9adbfd53f965fd5553ac Mon Sep 17 00:00:00 2001 From: Peter Ammon Date: Sun, 8 Dec 2024 13:35:44 -0800 Subject: [PATCH] Fix infinite prompt loop if status message is printed in prompt fish will print messages for some jobs when they exit abnormally, such as with SIGABRT. If a job exits abnormally inside the prompt, then (prior to this commit) fish would print the message and re-trigger the prompt, which could result in an infinite loop. This has existed for a very long time. Fix it by reaping jobs after running the prompt, and NOT triggering a redraw based on that reaping. We still print the message but the prompt is not executed. Add a test. Fixes #9796 --- src/reader.rs | 5 +++++ tests/pexpects/prompt_redraw_loop.py | 30 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/pexpects/prompt_redraw_loop.py diff --git a/src/reader.rs b/src/reader.rs index 4f4dc59b2..6cacf8ca3 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -4204,6 +4204,11 @@ impl<'a> Reader<'a> { // hack to work. reader_write_title(L!(""), zelf.parser, false); + // Reap jobs but do NOT trigger a repaint. + // This is to prevent infinite loops in case a job from the prompt triggers a repaint. + // See #9796. + job_reap(zelf.parser, true); + // Some prompt may have requested an exit (#8033). let exit_current_script = zelf.parser.libdata().exit_current_script; zelf.exit_loop_requested |= exit_current_script; diff --git a/tests/pexpects/prompt_redraw_loop.py b/tests/pexpects/prompt_redraw_loop.py new file mode 100644 index 000000000..45aad38ea --- /dev/null +++ b/tests/pexpects/prompt_redraw_loop.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +from pexpect_helper import SpawnedProc +import re + +sp = SpawnedProc() +send, sendline, sleep, expect_prompt, expect_re, expect_str = ( + sp.send, + sp.sendline, + sp.sleep, + sp.expect_prompt, + sp.expect_re, + sp.expect_str, +) +expect_prompt() + +# Regression test for 9796 +# If a process in the prompt exits with a status that would cause a message to be printed, +# don't trigger another prompt execution which would result in an infinite loop. +sendline("sh -c 'kill -ABRT $$'") +expect_prompt(re.escape("fish: Job 1, 'sh -c 'kill -ABRT $$'' terminated by signal SIGABRT (Abort)")) + +# Copy current prompt so we can reuse it. +sendline("functions --copy fish_prompt fish_prompt_orig") +expect_prompt() + +sendline("function fish_prompt; sh -c 'kill -ABRT $$'; fish_prompt_orig; end") +expect_prompt(re.escape("fish: Job 1, 'sh -c 'kill -ABRT $$'' terminated by signal SIGABRT (Abort)")) + +sendline("echo still alive!") +expect_prompt("still alive!")