Subject: bug#72692: Emacs 31.05 (40eecd594ac) get SIGSEGV on Linux (Linux 6.6.45 Kde Wayland)

From: Pip Cet <[email-masked]>
Date: Mon, 19 Aug 2024 14:44:12 +0000

"Eli Zaretskii" <[email-masked]> writes:

>> Date: Mon, 19 Aug 2024 06:07:35 +0000
>> From: Pip Cet <[email-masked]>
>> Cc: [email-masked], [email-masked]
>>
>> "Eli Zaretskii" <[email-masked]> writes:
>>
>> > Where do you see this copying in realize_non_ascii_face?
>>
>> static struct face *
>> realize_non_ascii_face (struct frame *f, Lisp_Object font_object,
>> 			struct face *base_face)
>> {
>>   struct face_cache *cache = FRAME_FACE_CACHE (f);
>>   struct face *face;
>>
>>   face = xmalloc (sizeof *face);
>>   *face = *base_face;
>>         ^____ here
>>
>> (The entire structure is copied, and fontset isn't explicitly changed
>> afterwards).
>
> I need new glasses.

:-)

> In any case, if you are right, and we free an ASCII face but leave its
> non-ASCII variants unchanged, we have a much deeper problem: a
> non-ASCII face is meaningless if its ASCII face was freed and
> re-realized, because the non-ASCII face needs to be re-realized as
> well.  So if this happens, we need to understand how and plug that
> much worse problem.

Well, that points to an easy fix: keep a doubly-linked list of linked
non-ASCII faces in the ASCII face, and free all of them in one go.

> In the cases I was able to reproduce on my system, the call to
> realize_basic_faces was followed by setting the frame's 'face_change'
> flag, which causes the next redisplay free all the frame's faces and
> re-realize all of them.

If we free all of them, there's no problem.

> (But the alpha-background parameter is not
> supported here, so I needed to change other attributes.)  As long as
> this happens before we try to display anything, the fact that we
> uncached the fontset will not cause any harm.  IOW, the fact that the
> fontset was freed and uncached in itself is not a catastrophe, as long
> as the freed fontset is not accessed before it is regenerated.

At the very least, that needs a very explicit comment explaining that
->ascii_face may point into freed (or, worse, freed-then-allocated)
memory, and under which specific circumstances.

> So can you show a backtrace from the call to free_realized_faces

I think you mean 'free_realized_face'. No 's'. (Not (just) being pedantic
here, but the problem is that 'realize_basic_faces' does not call
'free_realized_faces' but only 'free_realized_face').

> (supposedly called by realize_basic_faces?) which causes these
> printfs:

>> fontset 103 used for face 0x117cca0
>> fontset 103 used for face 0x2a7a3c0
>> fontset 103 used for face 0x117cca0
>> fontset 103 used for face 0x2a62860

(That printf is currently in face_for_char, but I think I understand
what you mean).

> Also, could you add to the print-outs the pointer to the face's
> ascii_face, so that we see whether there's only one ASCII face
> involved here or more than one?

Good idea.  I was going to respond that of course they must be the same
ASCII face, because they share a fontset, but then that's precisely the
bug we're trying to hunt down...

GDB log:

$ gdb --ar ./emacs -Q --load ../hibiscus.el
GNU gdb (Gentoo 15.1 vanilla) 15.1
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./emacs...
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) [answered Y; input not from terminal]
DISPLAY = :0.0
TERM = dumb
Breakpoint 1 at 0x5da023: file emacs.c, line 432.
Breakpoint 2 at 0x593ccd: file xterm.c, line 27102.
(gdb) b xfaces.c:6290 if face->fontset == 83
Breakpoint 3 at 0x55d337: file xfaces.c, line 6290.
(gdb) r
Starting program: /home/pip/emacs/src/emacs -Q --load ../hibiscus.el
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
allocated 0xfc4ac0
allocated 0xfc5010
allocated 0xfc5150
allocated 0xfc5290
allocated 0xfc53d0
allocated 0xfc5510
allocated 0xfc58c0
allocated 0xfc5a00
allocated 0xfc6b90
allocated 0xfc6cd0
allocated 0xfc6e10
allocated 0xfc6f50
allocated 0xfc7090
allocated 0xfc71d0
allocated 0xfc7310
allocated 0xfc7450
allocated 0xfc7590
allocated 0xfc76d0
allocated 0xfc7810
[New Thread 0x7fffe3ffe6c0 (LWP 1467)]
[New Thread 0x7fffdbfff6c0 (LWP 1468)]
[New Thread 0x7fffe366e6c0 (LWP 1469)]
[Detaching after fork from child process 1470]
allocated 0x105f020
fontset 3 allocated for face 0x105f020
allocated 0x10209a0
allocated 0xfdb390
allocated 0xfdb4d0
allocated 0x11c4ad0
allocated 0x11c4c10
allocated 0xfdad30
allocated 0xfdae70
allocated 0xfda8d0
allocated 0xfdaa10
allocated 0x115ac20
allocated 0x115ad60
allocated 0x115aea0
allocated 0x1160180
allocated 0x11602c0
allocated 0x1160400
allocated 0x1160540
allocated 0x1269a30
allocated 0x1269b70
[New Thread 0x7fffe1fff6c0 (LWP 1472)]
freeing 0x105f020
allocated 0x105f020
fontset 22 allocated for face 0x105f020
freeing 0x10209a0
allocated 0x10209a0
freeing 0xfdb390
allocated 0xfdb390
freeing 0xfdb4d0
allocated 0xfdb4d0
freeing 0x11c4ad0
allocated 0x11c4ad0
freeing 0x11c4c10
allocated 0x11c4c10
freeing 0xfdad30
allocated 0xfdad30
freeing 0xfdae70
allocated 0xfdae70
freeing 0xfda8d0
allocated 0xfda8d0
freeing 0xfdaa10
allocated 0xfdaa10
freeing 0x115ac20
allocated 0x115ac20
freeing 0x115ad60
allocated 0x115ad60
freeing 0x115aea0
allocated 0x115aea0
freeing 0x1160180
allocated 0x1160180
freeing 0x11602c0
allocated 0x11602c0
freeing 0x1160400
allocated 0x1160400
freeing 0x1160540
allocated 0x1160540
freeing 0x1269a30
allocated 0x1269a30
freeing 0x1269b70
allocated 0x1269b70
[New Thread 0x7fffe15ef6c0 (LWP 1473)]
freeing 0xfc4ac0
freeing 0xfc5010
freeing 0xfc5150
freeing 0xfc5290
freeing 0xfc53d0
freeing 0xfc5510
freeing 0xfc58c0
freeing 0xfc5a00
freeing 0xfc6b90
freeing 0xfc6cd0
freeing 0xfc6e10
freeing 0xfc6f50
freeing 0xfc7090
freeing 0xfc71d0
freeing 0xfc7310
freeing 0xfc7450
freeing 0xfc7590
freeing 0xfc76d0
freeing 0xfc7810
allocated 0xfc53d0
allocated 0xfc5290
allocated 0xfc5150
allocated 0xfc5010
allocated 0xfc4ac0
allocated 0x13cd5b0
allocated 0x13d3af0
allocated 0xfc5510
allocated 0xfc58c0
allocated 0xfc5a00
allocated 0xfc6b90
allocated 0xfc6cd0
allocated 0xfc6e10
allocated 0xfc6f50
allocated 0xfc7090
allocated 0xfc71d0
allocated 0xfc7310
allocated 0xfc7450
allocated 0xfc7590
freeing 0x105f020
freeing 0x10209a0
freeing 0xfdb390
freeing 0xfdb4d0
freeing 0x11c4ad0
freeing 0x11c4c10
freeing 0xfdad30
freeing 0xfdae70
freeing 0xfda8d0
freeing 0xfdaa10
freeing 0x115ac20
freeing 0x115ad60
freeing 0x115aea0
freeing 0x1160180
freeing 0x11602c0
freeing 0x1160400
freeing 0x1160540
freeing 0x1269a30
freeing 0x1269b70
allocated 0xfdad30
fontset 41 allocated for face 0xfdad30
allocated 0x11c4c10
allocated 0x11c4ad0
allocated 0xfdb4d0
allocated 0xfdb390
allocated 0x10209a0
allocated 0x105f020
allocated 0xfdae70
allocated 0xfc76d0
allocated 0xfc7810
allocated 0xfda8d0
allocated 0xfdaa10
allocated 0x1269a30
allocated 0x1269b70
allocated 0x115ac20
allocated 0x115ad60
allocated 0x115aea0
allocated 0x1160180
allocated 0x11602c0
allocated 0x1160400
fontset 60 allocated for face 0x1160400
freeing 0xfdad30
allocated 0xfdad30
fontset 61 allocated for face 0xfdad30
freeing 0xfdad30
freeing 0x11c4c10
freeing 0x11c4ad0
freeing 0xfdb4d0
freeing 0xfdb390
freeing 0x10209a0
freeing 0x105f020
freeing 0xfdae70
freeing 0xfc76d0
freeing 0xfc7810
freeing 0xfda8d0
freeing 0xfdaa10
freeing 0x1269a30
freeing 0x1269b70
freeing 0x115ac20
freeing 0x115ad60
freeing 0x115aea0
freeing 0x1160180
freeing 0x11602c0
freeing 0x1160400
allocated 0x10209a0
fontset 62 allocated for face 0x10209a0
allocated 0xfdb390
allocated 0xfdb4d0
fontset 64 allocated for face 0xfdb4d0
allocated 0x11c4ad0
allocated 0x11c4c10
allocated 0xfdad30
allocated 0x1733a10
allocated 0xfdae70
allocated 0x1706010
allocated 0x105f020
allocated 0x1706bb0
allocated 0x1725720
allocated 0x17310c0
allocated 0x1269a30
allocated 0xfda8d0
allocated 0xfdaa10
allocated 0xfc76d0
allocated 0xfc7810
fontset 79 allocated for face 0xfc7810
allocated 0x1730940
fontset 80 allocated for face 0x1730940
freeing 0xfc53d0
freeing 0xfc5290
freeing 0xfc5150
freeing 0xfc5010
freeing 0xfc4ac0
freeing 0x13cd5b0
freeing 0x13d3af0
freeing 0xfc5510
freeing 0xfc58c0
freeing 0xfc5a00
freeing 0xfc6b90
freeing 0xfc6cd0
freeing 0xfc6e10
freeing 0xfc6f50
freeing 0xfc7090
freeing 0xfc71d0
freeing 0xfc7310
freeing 0xfc7450
freeing 0xfc7590
allocated 0x13d3af0
fontset 81 allocated for face 0x13d3af0
allocated 0x173cfe0
fontset 82 allocated for face 0x173cfe0
freeing 0x10209a0
allocated 0x10209a0

Thread 1 "emacs" hit Breakpoint 3, realize_gui_face (cache=0x12243a0, attrs=0x7fffffffb600) at xfaces.c:6290
6290		  fprintf (stderr, "fontset %d allocated for face %p\n", face->fontset, face);
(gdb) bt
#0  realize_gui_face (cache=0x12243a0, attrs=0x7fffffffb600) at xfaces.c:6290
#1  0x000000000055cc06 in realize_face (cache=0x12243a0, attrs=0x7fffffffb600, former_face_id=0) at xfaces.c:6121
#2  0x000000000055c863 in realize_default_face (f=0x11c38f8) at xfaces.c:6029
#3  0x000000000055bd9b in realize_basic_faces (f=0x11c38f8) at xfaces.c:5881
#4  0x000000000054e61e in recompute_basic_faces (f=0x11c38f8) at xfaces.c:737
#5  0x00000000004447ff in gui_set_alpha_background (f=0x11c38f8, arg=XIL(0x7ffff33a80b7), oldval=XIL(0)) at frame.c:5237
#6  0x00000000005a1a8b in x_set_alpha_background (f=0x11c38f8, arg=XIL(0x7ffff33a80b7), oldval=XIL(0)) at xfns.c:742
#7  0x0000000000441833 in gui_set_frame_parameters_1 (f=0x11c38f8, alist=XIL(0), default_parameter=false) at frame.c:4400
#8  0x000000000044240e in gui_set_frame_parameters (f=0x11c38f8, alist=XIL(0x7ffff33a79c3)) at frame.c:4560
#9  0x000000000043faca in Fmodify_frame_parameters (frame=XIL(0), alist=XIL(0x7ffff33a79c3)) at frame.c:3549
#10 0x00000000006bd69e in funcall_subr (subr=0xe898c0 <Smodify_frame_parameters>, numargs=2, args=0x7fffe3fff2f8) at eval.c:3140
#11 0x00000000007188f3 in exec_byte_code (fun=XIL(0x7ffff235902d), args_template=771, nargs=3, args=0x7fffffffc048) at bytecode.c:813
#12 0x00000000006bdcce in funcall_lambda (fun=XIL(0x7ffff235902d), nargs=3, arg_vector=0x7fffffffc030) at eval.c:3229
#13 0x00000000006bdb65 in apply_lambda (fun=XIL(0x7ffff235902d), args=XIL(0x7ffff33a7ea3), count=...) at eval.c:3192
#14 0x00000000006bc096 in eval_sub (form=XIL(0x7ffff33a7eb3)) at eval.c:2622
#15 0x00000000006b6b55 in Fprogn (body=XIL(0x7ffff33a7e23)) at eval.c:430
#16 0x00000000006b6b85 in prog_ignore (body=XIL(0x7ffff33a7ec3)) at eval.c:441
#17 0x00000000006b8750 in Fwhile (args=XIL(0x7ffff33b07f3)) at eval.c:1121
#18 0x00000000006bbb1d in eval_sub (form=XIL(0x7ffff33b0803)) at eval.c:2526
#19 0x00000000006fd345 in readevalloop_eager_expand_eval (val=XIL(0x7ffff33b0803), macroexpand=XIL(0xb370)) at lread.c:2355
#20 0x00000000006fdbaf in readevalloop
    (readcharfun=XIL(0x175107d), infile0=0x0, sourcename=XIL(0x1368874), printflag=false, unibyte=XIL(0), readfun=XIL(0), start=XIL(0), end=XIL(0))
    at lread.c:2537
#21 0x00000000006fdefe in Feval_buffer (buffer=XIL(0x175107d), printflag=XIL(0), filename=XIL(0x1368874), unibyte=XIL(0), do_allow_print=XIL(0x30))
    at lread.c:2612
#22 0x00000000006bd755 in funcall_subr (subr=0xe9ae40 <Seval_buffer>, numargs=5, args=0x7fffe3fff290) at eval.c:3146
#23 0x00000000007188f3 in exec_byte_code (fun=XIL(0x7ffff23a51fd), args_template=257, nargs=1, args=0x7fffe3fff298) at bytecode.c:813
#24 0x00000000006bdcce in funcall_lambda (fun=XIL(0x7ffff26cf0cd), nargs=4, arg_vector=0x7fffffffcdc8) at eval.c:3229
#25 0x00000000006bd0c8 in funcall_general (fun=XIL(0x7ffff26cf0cd), numargs=4, args=0x7fffffffcdc8) at eval.c:3021
#26 0x00000000006bd377 in Ffuncall (nargs=5, args=0x7fffffffcdc0) at eval.c:3070
#27 0x00000000006fb836 in Fload (file=XIL(0x1368924), noerror=XIL(0), nomessage=XIL(0x30), nosuffix=XIL(0), must_suffix=XIL(0)) at lread.c:1615
#28 0x00000000006bd755 in funcall_subr (subr=0xe9adc0 <Sload>, numargs=3, args=0x7fffe3fff1b0) at eval.c:3146
#29 0x00000000007188f3 in exec_byte_code (fun=XIL(0x7ffff28a0895), args_template=769, nargs=3, args=0x7fffe3fff3f0) at bytecode.c:813
#30 0x00000000006bdcce in funcall_lambda (fun=XIL(0x7ffff28f15ed), nargs=0, arg_vector=0x7fffffffd5a0) at eval.c:3229
#31 0x00000000006bdb65 in apply_lambda (fun=XIL(0x7ffff28f15ed), args=XIL(0), count=...) at eval.c:3192
#32 0x00000000006bc096 in eval_sub (form=XIL(0x7ffff2ab1e2b)) at eval.c:2622
#33 0x00000000006bb5c9 in Feval (form=XIL(0x7ffff2ab1e2b), lexical=XIL(0x30)) at eval.c:2439
#34 0x00000000005e204c in top_level_2 () at keyboard.c:1179
#35 0x00000000006b9638 in internal_condition_case (bfun=0x5e1fe5 <top_level_2>, handlers=XIL(0x90), hfun=0x5e18bd <cmd_error>) at eval.c:1598
#36 0x00000000005e20a7 in top_level_1 (ignore=XIL(0)) at keyboard.c:1191
#37 0x00000000006b8afc in internal_catch (tag=XIL(0x12360), func=0x5e2065 <top_level_1>, arg=XIL(0)) at eval.c:1277
#38 0x00000000005e1f2f in command_loop () at keyboard.c:1140
#39 0x00000000005e1396 in recursive_edit_1 () at keyboard.c:749
#40 0x00000000005e15a9 in Frecursive_edit () at keyboard.c:832
#41 0x00000000005dd1fe in main (argc=4, argv=0x7fffffffdba8) at emacs.c:2624

Lisp Backtrace:
"modify-frame-parameters" (0xe3fff2f8)
"set-frame-parameter" (0xffffc030)
"while" (0xffffc310)
"eval-buffer" (0xe3fff290)
"load-with-code-conversion" (0xffffcdc8)
"load" (0xe3fff1b0)
"command-line-1" (0xe3fff0b8)
"command-line" (0xe3fff040)
"normal-top-level" (0xffffd5a0)
(gdb) c
Continuing.
fontset 83 allocated for face 0x10209a0
freeing 0xfdb390
allocated 0xfdb390
freeing 0xfdb4d0
allocated 0xfdb4d0
fontset 85 allocated for face 0xfdb4d0
freeing 0x11c4ad0
allocated 0x11c4ad0
freeing 0x11c4c10
allocated 0x11c4c10
freeing 0xfdad30
allocated 0xfdad30
freeing 0x1733a10
allocated 0x1733a10
freeing 0xfdae70
allocated 0xfdae70
freeing 0x1706010
allocated 0x1706010
freeing 0x105f020
allocated 0x105f020
freeing 0x1706bb0
allocated 0x1706bb0
freeing 0x1725720
allocated 0x1725720
freeing 0x17310c0
allocated 0x17310c0
freeing 0x1269a30
allocated 0x1269a30
freeing 0xfda8d0
allocated 0xfda8d0
freeing 0xfdaa10
allocated 0xfdaa10
freeing 0xfc76d0
allocated 0xfc76d0
freeing 0xfc7810
allocated 0xfc7810
fontset 100 allocated for face 0xfc7810
freeing 0x1730940
allocated 0xfc5290
fontset 101 allocated for face 0xfc5290
fontset 83 used for face 0x10209a0
[Thread 0x7fffe1fff6c0 (LWP 1472) exited]
allocated 0x124a140
fontset 103 allocated for face 0x124a140
fontset 83 used for face 0x10209a0
fontset 83 used for face 0x10209a0
fontset 83 used for face 0x10209a0
fontset 83 used for face 0x2a2b6c0
fontset 83 used for face 0x10209a0
fontset 83 used for face 0x10209a0
fontset 83 used for face 0x2a2b6c0
fontset 83 used for face 0x10209a0
fontset 83 used for face 0x10209a0
fontset 83 used for face 0x10209a0
fontset 83 used for face 0x2a2b6c0
fontset 83 used for face 0x10209a0
Freeing fontset 83 that's still in use by 0x2a2b6c0 (ASCII face 0x10209a0==0x10209a0)!
freeing 0x10209a0
allocated 0x10209a0
fontset 104 allocated for face 0x10209a0
freeing 0xfdb390
allocated 0xfdb390
freeing 0xfdb4d0
allocated 0xfdb4d0
fontset 106 allocated for face 0xfdb4d0
freeing 0x11c4ad0
allocated 0x11c4ad0
freeing 0x11c4c10
allocated 0x11c4c10
freeing 0xfdad30
allocated 0xfdad30
freeing 0x1733a10
allocated 0x1733a10
freeing 0xfdae70
allocated 0xfdae70
freeing 0x1706010
allocated 0x1706010
freeing 0x105f020
allocated 0x105f020
freeing 0x1706bb0
allocated 0x1706bb0
freeing 0x1725720
allocated 0x1725720
freeing 0x17310c0
allocated 0x17310c0
freeing 0x1269a30
allocated 0x1269a30
freeing 0xfda8d0
allocated 0xfda8d0
freeing 0xfdaa10
allocated 0xfdaa10
freeing 0xfc76d0
allocated 0xfc76d0
freeing 0xfc7810
allocated 0xfc7810
fontset 121 allocated for face 0xfc7810
freeing 0xfc5290
allocated 0xfc5290
fontset 122 allocated for face 0xfc5290
fontset 104 used for face 0x10209a0
fontset 83 used for face 0x2a2b6c0

lisp.h:2126: Emacs fatal error: assertion failed: CHAR_TABLE_P (a)

Thread 1 "emacs" hit Breakpoint 1, terminate_due_to_signal (sig=6, backtrace_limit=2147483647) at emacs.c:432
432	  signal (sig, SIG_DFL);
(gdb) up
#1  0x0000000000684cdc in die (msg=0x81ee60 "CHAR_TABLE_P (a)", file=0x81ed30 "lisp.h", line=2126) at alloc.c:8058
8058	  terminate_due_to_signal (SIGABRT, INT_MAX);
(gdb) 
#2  0x000000000078d6b8 in XCHAR_TABLE (a=XIL(0)) at /home/pip/emacs/src/lisp.h:2126
2126	  eassert (CHAR_TABLE_P (a));
(gdb) 
#3  0x00000000007902a8 in face_for_char (f=0x11c38f8, face=0x2a2b6c0, c=19233, pos=2, object=XIL(0)) at fontset.c:1005
1005	  eassert (!BASE_FONTSET_P (fontset));
(gdb) 
#4  0x000000000044b7bd in FACE_FOR_CHAR (f=0x11c38f8, face=0x2a2b6c0, character=19233, pos=2, object=XIL(0)) at /home/pip/emacs/src/dispextern.h:1962
1962	  return face_for_char (f, face, character, pos, object);
(gdb) p face
$1 = (struct face *) 0x2a2b6c0
(gdb) p face->fontset
$2 = 83
(gdb) p face->ascii_face
$3 = (struct face *) 0x10209a0
(gdb) show args
Argument list to give program being debugged when it is started is "-Q --load ../hibiscus.el".

patch:

diff --git a/src/fontset.c b/src/fontset.c
index 16d14669c89..0a1e3717eab 100644
--- a/src/fontset.c
+++ b/src/fontset.c
@@ -900,7 +900,7 @@ fontset_ascii (int id)
 {
   Lisp_Object fontset, elt;
 
-  fontset= FONTSET_FROM_ID (id);
+  fontset = FONTSET_FROM_ID (id);
   elt = FONTSET_ASCII (fontset);
   if (CONSP (elt))
     elt = XCAR (elt);
@@ -921,8 +921,6 @@ free_face_fontset (struct frame *f, struct face *face)
   eassert (! BASE_FONTSET_P (fontset));
   eassert (f == XFRAME (FONTSET_FRAME (fontset)));
   ASET (Vfontset_table, face->fontset, Qnil);
-  if (face->fontset < next_fontset_id)
-    next_fontset_id = face->fontset;
   if (! NILP (FONTSET_DEFAULT (fontset)))
     {
       int id = XFIXNUM (FONTSET_ID (FONTSET_DEFAULT (fontset)));
@@ -931,8 +929,6 @@ free_face_fontset (struct frame *f, struct face *face)
       eassert (!NILP (fontset) && ! BASE_FONTSET_P (fontset));
       eassert (f == XFRAME (FONTSET_FRAME (fontset)));
       ASET (Vfontset_table, id, Qnil);
-      if (id < next_fontset_id)
-	next_fontset_id = face->fontset;
     }
   face->fontset = -1;
 }
@@ -1000,6 +996,7 @@ face_for_char (struct frame *f, struct face *face, int c,
      and display it as "glyphless".  That is certainly better than
      violating the assertion below or crashing when assertions are not
      compiled in.  */
+  fprintf (stderr, "fontset %d used for face %p\n", face->fontset, face);
   if (face->fontset < 0 && !face->font)
     return face->id;
 
diff --git a/src/xfaces.c b/src/xfaces.c
index 684b6ccfac7..088ad46111f 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -4576,6 +4576,7 @@ make_realized_face (Lisp_Object *attr)
 {
   enum { off = offsetof (struct face, id) };
   struct face *face = xmalloc (sizeof *face);
+  printf ("allocated %p\n", face);
 
   memcpy (face->lface, attr, sizeof face->lface);
   memset (&face->id, 0, sizeof *face - off);
@@ -4598,7 +4599,24 @@ free_realized_face (struct frame *f, struct face *face)
 	{
 	  /* Free fontset of FACE if it is ASCII face.  */
 	  if (face->fontset >= 0 && face == face->ascii_face)
-	    free_face_fontset (f, face);
+	    {
+	      struct face_cache *cache = FRAME_FACE_CACHE (f);
+	      if (cache)
+		{
+		  for (int i = 0; i < cache->used; i++)
+		    {
+		      struct face *face2 = cache->faces_by_id[i];
+		      if (face2 != 0 && face2 != face && face2->fontset == face->fontset)
+			{
+			  fprintf (stderr, "Freeing fontset %d that's still in use by %p (ASCII face %p==%p)!\n", face->fontset,
+				   face2, face2->ascii_face, face);
+			}
+		    }
+		}
+	      free_face_fontset (f, face);
+	    }
+	  else
+	    fprintf (stderr, "fontset %d not freed, used by %p\n", face->fontset, face);
 
 #ifdef HAVE_X_WINDOWS
 	  /* This function might be called with the frame's display
@@ -4627,6 +4645,7 @@ free_realized_face (struct frame *f, struct face *face)
 #ifdef HAVE_X_WINDOWS
     free_face:
 #endif /* HAVE_X_WINDOWS */
+      printf ("freeing %p\n", face);
       xfree (face);
     }
 }
@@ -6268,6 +6287,7 @@ realize_gui_face (struct face_cache *cache, Lisp_Object attrs[LFACE_VECTOR_SIZE]
 	{
 	  face->font = XFONT_OBJECT (attrs[LFACE_FONT_INDEX]);
 	  face->fontset = make_fontset_for_ascii_face (f, fontset, face);
+	  fprintf (stderr, "fontset %d allocated for face %p\n", face->fontset, face);
 	}
       else
 	{

hibiscus.el:

(while t
  (insert (concat (make-string 1 (floor (random 132000)))))
  (set-frame-parameter nil 'alpha-background 1.0)
  (sit-for 1.0))

Pip