On Sun, Aug 17, 2025 at 2:04â¯PM penguin p <[email protected]> wrote:
> [...]
> 5 bash 0x100ca36e8 update_line + 9096 (display.c:2133)
> 6 bash 0x100c9b2dc rl_redisplay + 27264 (display.c:1368)
> 7 bash 0x100ca8924 rl_clear_message + 128 (display.c:3194)
> 8 bash 0x100c93ab0 _rl_isearch_fini + 2396 (isearch.c:311)
> 9 bash 0x100c930ec _rl_isearch_cleanup + 68 (isearch.c:893)
> 10 bash 0x100cad22c _rl_state_sigcleanup + 180 (signals.c:599)
> 11 bash 0x100cad388 rl_free_line_state + 92 (signals.c:616)
> 12 bash 0x100cabbd8 _rl_handle_signal + 824 (signals.c:221)
> 13 bash 0x100cab890 _rl_signal_handler + 340 (signals.c:152)
> 14 bash 0x100cad500 _rl_release_sigint + 156 (signals.c:680)
> 15 bash 0x100c9d340 rl_redisplay + 35556 (display.c:1712)
> 16 bash 0x100ca8924 rl_clear_message + 128 (display.c:3194)
> 17 bash 0x100c93ab0 _rl_isearch_fini + 2396 (isearch.c:311)
> 18 bash 0x100c930ec _rl_isearch_cleanup + 68 (isearch.c:893)
> 19 bash 0x100c8a330 rl_search_history + 516 (isearch.c:936)
> [...]
So while _rl_isearch_fini is being executed due to normal search
termination, it gets called again from a signal handler.
This is reproducible by, for example, injecting a SIGINT using strace:
strace -o /dev/null --trace-fds 2 -e write \
--inject=write:signal=SIGINT:when=7 \
-E INPUTRC=/ -E HISTFILE= -E ASAN_OPTIONS=detect_leaks=0 \
./bash --norc -in <<< $'\cRXY'
Which shows the following from UBSAN:
display.c:2901:52: runtime error: null pointer passed as argument 2, which
is declared to never be null
/usr/include/string.h:65:33: note: nonnull attribute specified here
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior display.c:2901:52
And from ASAN:
ERROR: AddressSanitizer: heap-use-after-free on address ...
READ of size 8 at 0xe395f71e0318 thread T0
#0 0xc28098436c84 in _rl_scxt_dispose isearch.c:123:3
#1 0xc2809844be24 in _rl_isearch_cleanup isearch.c:915:3
#2 0xc2809843745c in rl_search_history isearch.c:961:11
#3 0xc280984370a4 in rl_reverse_search_history isearch.c:135:11
...
freed by thread T0 here:
#2 0xc28098437034 in _rl_scxt_dispose isearch.c:127:3
#3 0xc2809844be24 in _rl_isearch_cleanup isearch.c:915:3
#4 0xc2809847fa90 in _rl_state_sigcleanup signals.c:599:5
#5 0xc2809847fbd4 in rl_free_line_state signals.c:616:5
#6 0xc2809847dfa0 in _rl_handle_signal signals.c:221:7
#7 0xc2809847dcb4 in _rl_signal_handler signals.c:152:5
#8 0xc2809847fdd0 in _rl_release_sigint signals.c:680:5
#9 0xc28098463904 in rl_redisplay display.c:1712:3
#10 0xc28098478b60 in rl_clear_message display.c:3194:3
#11 0xc2809844d300 in _rl_isearch_fini isearch.c:311:3
#12 0xc2809844be14 in _rl_isearch_cleanup isearch.c:914:5
#13 0xc2809843745c in rl_search_history isearch.c:961:11
...
The following change should fix this.
---
Ref: https://lists.gnu.org/archive/html/bug-bash/2025-08/msg00080.html
lib/readline/isearch.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lib/readline/isearch.c b/lib/readline/isearch.c
index 241e2ee0..104835d9 100644
--- a/lib/readline/isearch.c
+++ b/lib/readline/isearch.c
@@ -910,13 +910,15 @@ opcode_dispatch:
int
_rl_isearch_cleanup (_rl_search_cxt *cxt, int r)
{
+ /* Unset RL_STATE_ISEARCH now to avoid _rl_state_sigcleanup calling us if a
+ signal is received while _rl_isearch_fini is calling rl_clear_message */
+ RL_UNSETSTATE(RL_STATE_ISEARCH);
+
if (r >= 0)
_rl_isearch_fini (cxt);
_rl_scxt_dispose (cxt, 0);
_rl_iscxt = 0;
- RL_UNSETSTATE(RL_STATE_ISEARCH);
-
return (r != 0);
}
--
2.50.1