On Sun, Aug 17, 2025 at 2:04 PM penguin p <tgc...@gmail.com> 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


Reply via email to