bug#81407: 31.0.90; Crash in display_line: stale glyph_row after window-config change from menu-bar :enable eval during redisplay
Eli Zaretskii <[email protected]> Sun, 02 Aug 2026 11:20:01 +0300
| Newsgroups | gmane.emacs.bugs |
|---|---|
| Message-ID | <[email protected]> |
> From: Aaron Iba <[email protected]> > Date: Mon, 27 Jul 2026 09:14:54 -0400 > Cc: [email protected] > > > Try the patch below, it seems to avoid the trouble here. > > Thanks! I applied it to the emacs-31 branch (builds as 31.0.91 now) > and tested on macOS/NS, arm64, --without-native-compilation. Results: > the patch helps but the guard is unreliable here -- I still get the > crash in a fair fraction of runs, and I found out why. > > Test results with the -Q recipe (5 plain runs + 3 instrumented runs): > > - guard fires, evil Lisp aborted, no crash: ~1/4 of runs > ("Error during redisplay: (my-evil-fontify 1157) signaled (error > \"fontification-functions cause glyph matrix reallocation; > disabled\")" appears in *Messages*, Emacs is fine afterwards) > - guard does NOT fire, no crash (freed block still readable): ~1/4 > - guard does NOT fire, SIGSEGV as before: ~1/2 > > Why the guard misses: on this code path the window's matrices are not > adjusted in place -- they are freed and new matrix structs are > allocated. I put a logging breakpoint on adjust_glyph_matrix in the > patched build, printing MATRIX, its rows/rows_allocated on entry, and > the current value of window_desired_matrix. After the evil > delete-other-windows, every adjust_glyph_matrix call comes in with a > fresh struct (rows == NULL, rows_allocated == 0): > > run where the guard MISSED and Emacs crashed: > [adjust] matrix=0x80e1fc4d0 rows=0x0 alloc=0 > window_desired_matrix=0x80efa2e60 MATCH=False > [adjust] matrix=0x80e1fc540 rows=0x0 alloc=0 > window_desired_matrix=0x80efa2e60 MATCH=False > [adjust] matrix=0x80e1fc7e0 rows=0x0 alloc=0 > window_desired_matrix=0x80efa2e60 MATCH=False > [adjust] matrix=0x80e1fc850 rows=0x0 alloc=0 > window_desired_matrix=0x80efa2e60 MATCH=False > ... none of the new structs equals the old desired matrix; the old > struct (0x80efa2e60) was freed; display_line crashes afterwards at > xdisp.c:26855 (it->current_y += row->height) reading the freed row. > > run where the guard FIRED: > [adjust] matrix=0xbd8faf870 rows=0x0 alloc=0 > window_desired_matrix=0xbd8faf870 MATCH=True > ... note rows == NULL and rows_allocated == 0: this is also a > brand-new struct -- malloc just happened to reuse the freed old > struct's address, so the pointer comparison matched by accident. Sorry, I don't understand: where do you see code that frees the matrix and then allocates it, without changing the value of window_desired_matrix? The patch adds the detection of reallocation in every case where adjust_glyph_matrix is about to call any memory-allocation function to modify matrix->rows or row->glyphs of some matrix rows. Which parts of adjust_glyph_matrix that reallocate the glyph rows are not covered? > So `matrix == window_desired_matrix' compares against a pointer that > is dangling by the time the comparison runs; whether it matches is > heap-layout luck. (I suspect the same was true when it "avoided the > trouble" on your machine.) Please explain how come window_desired_matrix becomes a dangling pointer, because I'm afraid I don't understand that. Specifically, please describe the flow of control which causes window_desired_matrix no longer point to the window's desired matrix manipulated by adjust_glyph_matrix. > This also means the crashing scenario is slightly different from what > the patch assumes: the desired matrix is not enlarged behind > redisplay's back -- it is destroyed and replaced. Where in the code does this happen, please? > Perhaps the check > belongs where matrices are freed or detached from the window rather > than (only) in adjust_glyph_matrix: e.g. remember the window (or > compare w->desired_matrix against the matrix captured at > handle_fontified_prop time after the fontification call returns), or > error in free_glyph_matrix when it is called on window_desired_matrix. free_glyph_matrix is only called when a frame is deleted. Do you have a recipe where it is called for a live frame? > Two smaller observations, in case they matter: > > - disable_fontification_functions_p is never reset, so one offense > permanently disables fontification in that buffer (until it is > killed). Maybe intended as punishment, but a buffer whose hook > misbehaved once (e.g. transiently, via some state in another > library) stays unfontified with no way back that I can see short > of killing it. That's intentional: such buffer's fontification-functions cannot be trusted, so they are effectively disabled. Why should we trust the hook to misbehave just once? > One more scope finding: fontification-functions is not the only > Lisp-during-window-redisplay entry point that can do this. The same > evil call fired from a mode-line :eval form also crashes the patched > build (SIGSEGV inside display_mode_line <- display_mode_lines), and > window_desired_matrix is not set around mode-line evaluation, so the > new guard cannot see it at all: > > ;;; repro-modeline.el --- emacs -Q -l repro-modeline.el This doesn't crash for me. Emacs just exits when it reaches kill-emacs. And if I comment out kill-emacs, I get a working Emacs session. Anyway, there's a limit to which I'm prepared to safeguard Emacs against crazy Lisp hooks. If they want deliberately to crash Emacs, let them crash.