From 4314167cec9e274d360c51d56bafcd2ae56c9663 Mon Sep 17 00:00:00 2001 From: Tom de Vries Date: Fri, 20 Feb 2026 12:21:05 +0100 Subject: [PATCH] [gdb] Handle pagination prompt during posted event PR python/24796 reports the following problem. We have a python file test.py: ... import gdb def Event(): for x in range(0, 10): print("Line") gdb.post_event(Event) ... Without pagination, we simple have: ... (gdb) source test.py (gdb) Line Line ... Line Line print 1 $1 = 1 (gdb) ... But with pagination (and height set to 8), we get instead: ... (gdb) source test.py (gdb) Line Line ... Line --Type for more, q to quit, c to continue without paging--c Line Line Line print 1 readline: readline_callback_read_char() called with no handler! Fatal signal: Aborted ----- Backtrace ----- 0x651099 gdb_internal_backtrace_1 /data/vries/gdb/src/gdb/bt-utils.c:122 0x651137 _Z22gdb_internal_backtracev /data/vries/gdb/src/gdb/bt-utils.c:175 0x9462aa handle_fatal_signal /data/vries/gdb/src/gdb/event-top.c:1008 0x7f4efaf5f08f ??? /usr/src/debug/glibc-2.40/signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 0x7f4efafb245c __pthread_kill_implementation /usr/src/debug/glibc-2.40/nptl/pthread_kill.c:44 0x7f4efaf5efc5 __GI_raise ../sysdeps/posix/raise.c:26 0x7f4efaf46916 __GI_abort /usr/src/debug/glibc-2.40/stdlib/abort.c:79 0x117502b rl_callback_read_char /data/vries/gdb/src/readline/readline/callback.c:139 0x944bc2 gdb_rl_callback_read_char_wrapper_sjlj /data/vries/gdb/src/gdb/event-top.c:197 0x944cd3 gdb_rl_callback_read_char_wrapper_noexcept /data/vries/gdb/src/gdb/event-top.c:240 0x944d51 gdb_rl_callback_read_char_wrapper /data/vries/gdb/src/gdb/event-top.c:252 0x10623e3 stdin_event_handler /data/vries/gdb/src/gdb/ui.c:154 0x1a06e9c handle_file_event /data/vries/gdb/src/gdbsupport/event-loop.cc:551 0x1a074df gdb_wait_for_event /data/vries/gdb/src/gdbsupport/event-loop.cc:672 0x1a063bc _Z16gdb_do_one_eventi /data/vries/gdb/src/gdbsupport/event-loop.cc:263 0x6d547f _ZN6interp12do_one_eventEi /data/vries/gdb/src/gdb/interps.h:93 0xb77fb6 start_event_loop /data/vries/gdb/src/gdb/main.c:403 0xb781a4 captured_command_loop /data/vries/gdb/src/gdb/main.c:468 0xb7a10d captured_main /data/vries/gdb/src/gdb/main.c:1381 0xb7a209 _Z8gdb_mainP18captured_main_args /data/vries/gdb/src/gdb/main.c:1400 0x419704 main /data/vries/gdb/src/gdb/gdb.c:38 ... What leads to the crash, is that this code in gdb_readline_wrapper_line: ... /* Prevent parts of the prompt from being redisplayed if annotations are enabled, and readline's state getting out of sync. We'll reinstall the callback handler, which puts the terminal in raw mode (or in readline lingo, in prepped state), when we're next ready to process user input, either in display_gdb_prompt, or if we're handling an asynchronous target event and running in the background, just before returning to the event loop to process further input (or more target events). */ if (current_ui->command_editing) gdb_rl_callback_handler_remove (); ... when activated by the pagination prompt removes the callback handler (which sets rl_linefunc to NULL). The comment mentions re-installing the callback handler, but that doesn't happen, so when calling rl_callback_read_char we run into: ... if (rl_linefunc == NULL) { _rl_errmsg ("readline_callback_read_char() called with no handler!"); abort (); } ... An example of re-installation is found here in infrun.c: ... /* Cleanup that reinstalls the readline callback handler, if the target is running in the background. If while handling the target event something triggered a secondary prompt, like e.g., a pagination prompt, we'll have removed the callback handler (see gdb_readline_wrapper_line). Need to do this as we go back to the event loop, ready to process further input. Note this has no effect if the handler hasn't actually been removed, because calling rl_callback_handler_install resets the line buffer, thus losing input. */ static void void reinstall_readline_callback_handler_cleanup () ... Fix the crash by adding the same cleanup in run_events. Tested on x86_64-linux. Approved-By: Andrew Burgess Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=24796 --- gdb/infrun.c | 12 +------- gdb/infrun.h | 9 ++++++ gdb/run-on-main-thread.c | 6 ++++ gdb/testsuite/gdb.python/py-post-event.exp | 36 ++++++++++++++++++++++ gdb/testsuite/gdb.python/py-post-event.py | 24 +++++++++++++++ 5 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 gdb/testsuite/gdb.python/py-post-event.exp create mode 100644 gdb/testsuite/gdb.python/py-post-event.py diff --git a/gdb/infrun.c b/gdb/infrun.c index 428359c5580..6550ee40e1c 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -4429,17 +4429,7 @@ wait_for_inferior (inferior *inf) finish_state.release (); } -/* Cleanup that reinstalls the readline callback handler, if the - target is running in the background. If while handling the target - event something triggered a secondary prompt, like e.g., a - pagination prompt, we'll have removed the callback handler (see - gdb_readline_wrapper_line). Need to do this as we go back to the - event loop, ready to process further input. Note this has no - effect if the handler hasn't actually been removed, because calling - rl_callback_handler_install resets the line buffer, thus losing - input. */ - -static void +void reinstall_readline_callback_handler_cleanup () { struct ui *ui = current_ui; diff --git a/gdb/infrun.h b/gdb/infrun.h index 1227472ebc4..0a7cdadf1fa 100644 --- a/gdb/infrun.h +++ b/gdb/infrun.h @@ -418,5 +418,14 @@ private: bool m_prev_enable_commit_resumed; }; +/* Cleanup that reinstalls the readline callback handler, if the + target is running in the background. If something triggered a secondary + prompt, like e.g., a pagination prompt, we'll have removed the callback + handler (see gdb_readline_wrapper_line). Need to do this when going back + to the event loop, ready to process further input. Note this has no + effect if the handler hasn't actually been removed, because calling + rl_callback_handler_install resets the line buffer, thus losing + input. */ +extern void reinstall_readline_callback_handler_cleanup (); #endif /* GDB_INFRUN_H */ diff --git a/gdb/run-on-main-thread.c b/gdb/run-on-main-thread.c index 6410eb53036..131317bcbb6 100644 --- a/gdb/run-on-main-thread.c +++ b/gdb/run-on-main-thread.c @@ -21,6 +21,8 @@ #include "gdbsupport/cleanups.h" #include "gdbsupport/cxx-thread.h" #include "gdbsupport/event-loop.h" +#include "infrun.h" +#include "gdbsupport/scope-exit.h" /* The serial event used when posting runnables. */ @@ -60,6 +62,10 @@ run_events (int error, gdb_client_data client_data) local = std::move (runnables); } + /* Schedule cleanup in case secondary prompts (for instance, the pagination + prompt) happened while running events. */ + SCOPE_EXIT { reinstall_readline_callback_handler_cleanup (); }; + for (auto &item : local) { try diff --git a/gdb/testsuite/gdb.python/py-post-event.exp b/gdb/testsuite/gdb.python/py-post-event.exp new file mode 100644 index 00000000000..e37a7cf2fc6 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-post-event.exp @@ -0,0 +1,36 @@ +# Copyright 2026 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Regression test for PR24796. Check that a pagination prompt while handling +# a posted event doesn't crash GDB. + +foreach_with_prefix pagination { off on } { + clean_restart + + gdb_test_no_output "set pagination $pagination" + gdb_test_no_output "set height 8" + + gdb_test -no-prompt-anchor "source py-post-event.py" + if { $pagination == "on" } { + gdb_test_multiple "" "" { + -re $pagination_prompt { + send_gdb "c\n" + } + } + } + + # Check that prompt is reactive. + gdb_test "print 1" " = 1" +} diff --git a/gdb/testsuite/gdb.python/py-post-event.py b/gdb/testsuite/gdb.python/py-post-event.py new file mode 100644 index 00000000000..efd8f3396f9 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-post-event.py @@ -0,0 +1,24 @@ +# Copyright 2026 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import gdb + + +def Event(): + for x in range(0, 10): + print("Line") + + +gdb.post_event(Event)