GNU bug report logs - #78966
31.0.50; Crash when pressing a key while a new frame is opening in a TTY

Previous Next

Package: emacs;

Reported by: Daniel Clemente <n142857 <at> gmail.com>

Date: Sun, 6 Jul 2025 13:55:02 UTC

Severity: normal

Found in version 31.0.50

To reply to this bug, email your comments to 78966 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#78966; Package emacs. (Sun, 06 Jul 2025 13:55:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Daniel Clemente <n142857 <at> gmail.com>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sun, 06 Jul 2025 13:55:03 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Daniel Clemente <n142857 <at> gmail.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 31.0.50;
 Crash when pressing a key while a new frame is opening in a TTY
Date: Sun, 6 Jul 2025 13:53:44 +0000
Reproduction
============

1. Open emacs --fg-daemon -Q    (I haven't reproduced it without the daemon)
2. In a terminal (I have reproduced it in X terminals: both urxvt and
xterm), prepare to open: emacsclient -nw
3. Press the enter key to run the command, but keep holding it while
emacsclient opens. If you can launch emacsclient in any other way,
holding another key instead, like „space“ or „a“ or an arrow key, will
also reproduce the crash
4. In many cases it crashes. If not, exit (C-x C-c) and repeat step 3
a few times (up to ~20)

To simplify testing, use this loop and, while it runs, hold a key
(„a“) down for a few seconds (usually <10, maximum ~60); it always
triggers the crash:
while :; do emacsclient -nw -e '(delete-frame)'; sleep 0.1 && done

In user terms, this means that if I launch emacsclient (via a key in
my window manager) and start typing very fast, very often it crashes.
The workaround of course is to wait until the frame is fully opened.

I compiled emacs without X support. Apart from that, my setup is
standard (1 keyboard) and this happens under -Q, with no
customization. And only 1 simultaneous frame is needed.


Origin
======

I bisected the commits and found that the crash started happening only
after this change, on 2025-02-10:

4d1ceac9f9332f74ac2ab300eb2a629ea742b1dc
Author:     Martin Rudalics <rudalics <at> gmx.at>
AuthorDate: Mon Feb 10 10:36:38 2025 +0100
Parent:     302274b1862 * lisp/treesit.el: Fix treesit-outline.
Fix handling of visibility on tty frames (Bug#76031)

It doesn't happen in the previous commit, 302274b1862.

Crash in gdb
============

The crash at kbd_buffer_get_event (the line numbers are from a recent
codebase from this week):


Breakpoint 1, terminate_due_to_signal (sig=6,
backtrace_limit=2147483647) at emacs.c:443
443      signal (sig, SIG_DFL);
(gdb) bt
#0  terminate_due_to_signal (sig=6, backtrace_limit=2147483647) at emacs.c:443
#1  0x0000555555748913 in die (msg=0x555555875eec "FRAMEP (frame)",
file=0x555555875d4a "keyboard.c", line=4296) at alloc.c:7355
#2  0x00005555556a7d93 in kbd_buffer_get_event (kbp=0x7fffffffd048,
used_mouse_menu=0x7fffffffd6cf, end_time=0x0) at keyboard.c:4296
#3  0x00005555556a347e in read_event_from_main_queue (end_time=0x0,
local_getcjmp=0x7fffffffd490, used_mouse_menu=0x7fffffffd6cf)
    at keyboard.c:2325
#4  0x00005555556a3788 in read_decoded_event_from_main_queue
(end_time=0x0, local_getcjmp=0x7fffffffd490, prev_event=XIL(0),
    used_mouse_menu=0x7fffffffd6cf) at keyboard.c:2389
#5  0x00005555556a50d9 in read_char (commandflag=1,
map=XIL(0x7ffff089ed43), prev_event=XIL(0),
used_mouse_menu=0x7fffffffd6cf,
    end_time=0x0) at keyboard.c:3020
#6  0x00005555556b6d55 in read_key_sequence (keybuf=0x7fffffffd8e0,
prompt=XIL(0), dont_downcase_last=false,
    can_return_switch_frame=true, fix_current_buffer=true,
prevent_redisplay=false, disable_text_conversion_p=false)
    at keyboard.c:10867
#7  0x00005555556a08b6 in command_loop_1 () at keyboard.c:1424
#8  0x0000555555781bbe in internal_condition_case (bfun=0x5555556a0481
<command_loop_1>, handlers=XIL(0x90),
    hfun=0x55555569f8d8 <cmd_error>) at eval.c:1684
#9  0x00005555556a0029 in command_loop_2 (handlers=XIL(0x90)) at keyboard.c:1163
#10 0x0000555555781041 in internal_catch (tag=XIL(0xfdb0),
func=0x55555569fff9 <command_loop_2>, arg=XIL(0x90)) at eval.c:1364
#11 0x000055555569ffb5 in command_loop () at keyboard.c:1141
#12 0x000055555569f346 in recursive_edit_1 () at keyboard.c:749
#13 0x000055555569f57e in Frecursive_edit () at keyboard.c:832
#14 0x000055555569a962 in main (argc=3, argv=0x7fffffffdef8) at emacs.c:2582
(gdb) frame 2
#2  0x00005555556a7d93 in kbd_buffer_get_event (kbp=0x7fffffffd048,
used_mouse_menu=0x7fffffffd6cf, end_time=0x0) at keyboard.c:4296
4296          focus = FRAME_FOCUS_FRAME (XFRAME (frame));
(gdb) list
4291          if (CONSP (frame))
4292            frame = XCAR (frame);
4293          else if (WINDOWP (frame))
4294            frame = WINDOW_FRAME (XWINDOW (frame));
4295
4296          focus = FRAME_FOCUS_FRAME (XFRAME (frame));
4297          if (! NILP (focus))
4298            frame = focus;
4299
4300          if (!EQ (frame, internal_last_event_frame)
(gdb) p frame
$1 = XIL(0)
(gdb)

(gdb) p event
$5 = (union buffered_input_event *) 0x555555941e60 <kbd_buffer+1152>
(gdb) p *event
$6 = {
  kind = ASCII_KEYSTROKE_EVENT,
  ie = {
    kind = ASCII_KEYSTROKE_EVENT,
    part = scroll_bar_nowhere,
    code = 13,
    modifiers = 0,
    x = XIL(0),
    y = XIL(0),
    timestamp = 0,
    frame_or_window = XIL(0),
    arg = XIL(0),
    device = XIL(0x30)
  }
}
(gdb)


event.frame_or_window is null.



Ideas
=====

What follows now is just brainstorming (but with little knowledge of
Emacs internals).

First I thought that kbd_buffer_get_event could just skip events with
a null frame_or_window.
Or maybe: some other code shouldn't create „normal keystroke“ events
with a null frame_or_window.
But skipping events shouldn't mean losing keystrokes. If I open Emacs
and type abcd very fast while it's opening, I expect abcd to appear
later inside Emacs, not to be lost.

Maybe it's better: if frame_or_window is null, then don't process the
event yet, but process it later (when the frame has been initialized).
read_event_from_main_queue says:
  /* Read from the main queue, and if that gives us something we can't use yet,
     we put it on the appropriate side queue and try again.  */
So maybe it should do this when a frame hasn't been initialized yet.
I see CHECK_LIVE_FRAME (frame_or_window) in other parts of the code,
that could help.

Or it may be a bug somewhere else, or something missing from bug#76031.

Another related bug report is: bug#75056: 31.0.50; tty-child-frames
with server / multiple clients possible hangs


More debug info
===============

In case it might be useful:

(gdb) frame 3
#3  0x00005555556a347e in read_event_from_main_queue (end_time=0x0,
local_getcjmp=0x7fffffffd490, used_mouse_menu=0x7fffffffd6cf)
    at keyboard.c:2325
2325      c = kbd_buffer_get_event (&kb, used_mouse_menu, end_time);
(gdb) p kb
$12 = (KBOARD *) 0x555555ab5af0
(gdb) p *kb
$13 = {
  next_kboard = 0x555555a379c0,
  Voverriding_terminal_local_map_ = XIL(0),
  Vlast_command_ = XIL(0),
  Vreal_last_command_ = XIL(0),
  Vkeyboard_translate_table_ = XIL(0x555555b72a5d),
  Vlast_repeatable_command_ = XIL(0),
  Vprefix_arg_ = XIL(0),
  Vlast_prefix_arg_ = XIL(0),
  kbd_queue_ = XIL(0),
  defining_kbd_macro_ = XIL(0),
  kbd_macro_buffer = 0x0,
  kbd_macro_ptr = 0x707974040200090e,
  kbd_macro_end = 0x6e75020200040d65,
  kbd_macro_bufsize = 0,
  Vlast_kbd_macro_ = XIL(0),
  Vsystem_key_alist_ = XIL(0),
  system_key_syms_ = XIL(0),
  Vwindow_system_ = XIL(0),
  Vlocal_function_key_map_ = XIL(0x7ffff08460f3),
  Vinput_decode_map_ = XIL(0x7ffff08460e3),
  Vdefault_minibuffer_frame_ = XIL(0),
  reference_count = 1,
  echo_string_ = XIL(0),
  kbd_queue_has_data = false,
  immediate_echo = false,
  echo_prompt_ = XIL(0)
}
(gdb) p end_time
$14 = (struct timespec *) 0x0
(gdb)


Emacs data
==========

In GNU Emacs 31.0.50 (build 9, x86_64-pc-linux-gnu) of 2025-07-05 built
 on sonn
Repository revision: 763c3cd081a833940961373a73705640b1808636
Repository branch: master
System Description: Devuan GNU/Linux 5 (daedalus)

Configured using:
 'configure --prefix=/opt/dc/emacs-dev/ --with-tiff=no --without-tiff
 --without-libsystemd --without-dbus --with-mailutils --without-modules
 --with-native-compilation --with-x-toolkit=no --without-imagemagick
 --without-xft --without-harfbuzz --without-freetype --without-libotf
 --without-xwidgets --without-xpm --without-jpeg --without-gif
 --without-png --without-webp --without-rsvg --without-cairo --without-x
 --without-sound --enable-checking=yes,glyphs --enable-profiling
 'CFLAGS=-g3 -O0 ''

Configured features:
GMP GNUTLS LCMS2 LIBSELINUX LIBXML2 NATIVE_COMP NOTIFY INOTIFY PDUMPER
SECCOMP SQLITE3 THREADS XIM ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  value of $XMODIFIERS: @im=SCIM
  locale-coding-system: utf-8-unix

Major mode: Lisp Interaction

Minor modes in effect:
  server-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug lisp-mnt message mailcap yank-media puny
dired dnd dired-loaddefs rfc822 mml mml-sec password-cache epa derived
epg rfc6068 epg-config gnus-util time-date mm-decode mm-bodies mm-encode
mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047
rfc2045 ietf-drums mm-util mail-prsvr mail-utils term/rxvt term/xterm
xterm byte-opt gv compile text-property-search comint subr-x ansi-osc
ansi-color ring tool-bar comp-run bytecomp byte-compile comp-common
regexp-opt rx server warnings icons cl-loaddefs cl-lib rmc iso-transl
tooltip cconv eldoc paren electric uniquify ediff-hook vc-hooks
lisp-float-type elisp-mode tabulated-list replace newcomment text-mode
lisp-mode prog-mode register page tab-bar menu-bar rfn-eshadow isearch
easymenu timer select mouse jit-lock font-lock syntax font-core
term/tty-colors frame minibuffer nadvice seq simple cl-generic
indonesian philippine cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads inotify lcms2 multi-tty
make-network-process tty-child-frames native-compile emacs)

Memory information:
((conses 16 65067 12567) (symbols 48 7221 1) (strings 32 18992 5374)
 (string-bytes 1 568316) (vectors 16 9656)
 (vector-slots 8 92267 12318) (floats 8 31 11521) (intervals 56 265 0)
 (buffers 984 13))




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#78966; Package emacs. (Sun, 06 Jul 2025 14:35:05 GMT) Full text and rfc822 format available.

Message #8 received at 78966 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Clemente <n142857 <at> gmail.com>,
 martin rudalics <rudalics <at> gmx.at>
Cc: 78966 <at> debbugs.gnu.org
Subject: Re: bug#78966: 31.0.50;
 Crash when pressing a key while a new frame is opening in a TTY
Date: Sun, 06 Jul 2025 17:34:25 +0300
> From: Daniel Clemente <n142857 <at> gmail.com>
> Date: Sun, 6 Jul 2025 13:53:44 +0000
> 
> Reproduction
> ============
> 
> 1. Open emacs --fg-daemon -Q    (I haven't reproduced it without the daemon)
> 2. In a terminal (I have reproduced it in X terminals: both urxvt and
> xterm), prepare to open: emacsclient -nw
> 3. Press the enter key to run the command, but keep holding it while
> emacsclient opens. If you can launch emacsclient in any other way,
> holding another key instead, like „space“ or „a“ or an arrow key, will
> also reproduce the crash
> 4. In many cases it crashes. If not, exit (C-x C-c) and repeat step 3
> a few times (up to ~20)
> 
> To simplify testing, use this loop and, while it runs, hold a key
> („a“) down for a few seconds (usually <10, maximum ~60); it always
> triggers the crash:
> while :; do emacsclient -nw -e '(delete-frame)'; sleep 0.1 && done
> 
> In user terms, this means that if I launch emacsclient (via a key in
> my window manager) and start typing very fast, very often it crashes.
> The workaround of course is to wait until the frame is fully opened.
> 
> I compiled emacs without X support. Apart from that, my setup is
> standard (1 keyboard) and this happens under -Q, with no
> customization. And only 1 simultaneous frame is needed.
> 
> 
> Origin
> ======
> 
> I bisected the commits and found that the crash started happening only
> after this change, on 2025-02-10:
> 
> 4d1ceac9f9332f74ac2ab300eb2a629ea742b1dc
> Author:     Martin Rudalics <rudalics <at> gmx.at>
> AuthorDate: Mon Feb 10 10:36:38 2025 +0100
> Parent:     302274b1862 * lisp/treesit.el: Fix treesit-outline.
> Fix handling of visibility on tty frames (Bug#76031)
> 
> It doesn't happen in the previous commit, 302274b1862.
> 
> Crash in gdb
> ============
> 
> The crash at kbd_buffer_get_event (the line numbers are from a recent
> codebase from this week):
> 
> 
> Breakpoint 1, terminate_due_to_signal (sig=6,
> backtrace_limit=2147483647) at emacs.c:443
> 443      signal (sig, SIG_DFL);
> (gdb) bt
> #0  terminate_due_to_signal (sig=6, backtrace_limit=2147483647) at emacs.c:443
> #1  0x0000555555748913 in die (msg=0x555555875eec "FRAMEP (frame)",
> file=0x555555875d4a "keyboard.c", line=4296) at alloc.c:7355
> #2  0x00005555556a7d93 in kbd_buffer_get_event (kbp=0x7fffffffd048,
> used_mouse_menu=0x7fffffffd6cf, end_time=0x0) at keyboard.c:4296

I guess this fragment from tty_read_avail_input is suspect:

      /* Set the frame corresponding to the active tty.  Note that the
         value of selected_frame is not reliable here, redisplay tends
         to temporarily change it.  However, if the selected frame is a
         child frame, don't do that since it will cause switch frame
         events to switch to the root frame instead.  */
      if (FRAME_PARENT_FRAME (XFRAME (selected_frame))
	  && (root_frame (XFRAME (selected_frame))
	      == XFRAME (tty->top_frame)))
	buf.frame_or_window = selected_frame;
      else
	buf.frame_or_window = tty->top_frame;

It somehow assigns nil to the event's frame_or_window field.

P.S. I couldn't reproduce the problem on my system, no matter how many
times I tried.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#78966; Package emacs. (Sun, 06 Jul 2025 16:56:03 GMT) Full text and rfc822 format available.

Message #11 received at 78966 <at> debbugs.gnu.org (full text, mbox):

From: martin rudalics <rudalics <at> gmx.at>
To: Eli Zaretskii <eliz <at> gnu.org>, Daniel Clemente <n142857 <at> gmail.com>
Cc: Gerd Möllmann <gerd.moellmann <at> gmail.com>,
 78966 <at> debbugs.gnu.org
Subject: Re: bug#78966: 31.0.50; Crash when pressing a key while a new frame
 is opening in a TTY
Date: Sun, 6 Jul 2025 18:55:46 +0200
[Message part 1 (text/plain, inline)]
> I guess this fragment from tty_read_avail_input is suspect:
>
>        /* Set the frame corresponding to the active tty.  Note that the
>           value of selected_frame is not reliable here, redisplay tends
>           to temporarily change it.  However, if the selected frame is a
>           child frame, don't do that since it will cause switch frame
>           events to switch to the root frame instead.  */
>        if (FRAME_PARENT_FRAME (XFRAME (selected_frame))
> 	  && (root_frame (XFRAME (selected_frame))
> 	      == XFRAME (tty->top_frame)))
> 	buf.frame_or_window = selected_frame;
>        else
> 	buf.frame_or_window = tty->top_frame;
>
> It somehow assigns nil to the event's frame_or_window field.

Right.  tty->top_frame is Qnil here.  I have not the slightest idea
_when_ we should initialize tty->top_frame whenever we create a frame on
a tty.  Maybe Gerd has.  Meanwhile I can offer the attached patch.

Thanks Daniel for the detailed report, martin
[keyboard.c.diff (text/x-patch, attachment)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#78966; Package emacs. (Sun, 06 Jul 2025 18:34:03 GMT) Full text and rfc822 format available.

Message #14 received at 78966 <at> debbugs.gnu.org (full text, mbox):

From: Gerd Möllmann <gerd.moellmann <at> gmail.com>
To: martin rudalics <rudalics <at> gmx.at>
Cc: Daniel Clemente <n142857 <at> gmail.com>, Eli Zaretskii <eliz <at> gnu.org>,
 78966 <at> debbugs.gnu.org
Subject: Re: bug#78966: 31.0.50; Crash when pressing a key while a new frame
 is opening in a TTY
Date: Sun, 06 Jul 2025 20:33:16 +0200
martin rudalics <rudalics <at> gmx.at> writes:

>> I guess this fragment from tty_read_avail_input is suspect:
>>
>>        /* Set the frame corresponding to the active tty.  Note that the
>>           value of selected_frame is not reliable here, redisplay tends
>>           to temporarily change it.  However, if the selected frame is a
>>           child frame, don't do that since it will cause switch frame
>>           events to switch to the root frame instead.  */
>>        if (FRAME_PARENT_FRAME (XFRAME (selected_frame))
>> 	  && (root_frame (XFRAME (selected_frame))
>> 	      == XFRAME (tty->top_frame)))
>> 	buf.frame_or_window = selected_frame;
>>        else
>> 	buf.frame_or_window = tty->top_frame;
>>
>> It somehow assigns nil to the event's frame_or_window field.
>
> Right.  tty->top_frame is Qnil here.  I have not the slightest idea
> _when_ we should initialize tty->top_frame whenever we create a frame on
> a tty.  Maybe Gerd has.  Meanwhile I can offer the attached patch.

I haven't a good idea either I'm afraid. I was relying on
do_switch_frame being called and setting top_frame. I can only guess
this is torpedoed by input being available right away before
do_switch_frame had a chance to run. That's only a guess though.

Maybe one could set top_frame in make_terminal_frame when a new root
frame is created? That at least doesn't sound completely unreasonable
because in that case, one could argue the new root frame is the to-be
top_frame.

Another idea would be to wait with handling events in command_loop_1
until the frames/the terminal are fully functional. But we don't have
anything resembling that AFAIR. And command_loop_1 and what's called
from there is already complicated enough.

I'd say your solution is a good one, and economic.







This bug report was last modified today.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.