Subject: bug#71712: 29.3; Crash on OpenBSD

From: Kirill A. Korinsky <[email-masked]>
Date: Sat, 22 Jun 2024 10:28:43 +0100

On Sat, 22 Jun 2024 08:45:01 +0100,
Eli Zaretskii <[email-masked]> wrote:
> 
> Thanks.  Is this reproducible?  If so, can you show a recipe,
> preferably starting from "emacs -Q"?
>

It crashes some times but I'd like to say that this is the first crash in
this month.

So, I have no idea how to reproduce, frankly speaking I just hasn't found
running emacs and discovered .core

The only clue that I have that I was switched to different virtual screen
and emacs was on not seen one.

Additionally, inside .xsession-errors I do have:

  0xbf72627f9a1 <XSetWMNormalHints+0x2fd71> at emacs
  0xbf72625a8ee <XSetWMNormalHints+0xacbe> at emacs
  0xbf72627f6c7 <XSetWMNormalHints+0x2fa97> at emacs
  Segmentation fault (core dumped) 

> FWIW, I looked at the code, and I cannot understand how this could
> happen.  The cause of the crash is that 'face' is NULL, so face->font
> segfaults.  But 'face' is obtained from 'face_id', which is zero,
> i.e. it's the default face:
> 
> >        glyph = {ch = 36, face_id = 0}
> 
> And init_iterator, which called produce_special_glyphs, makes sure the
> basic faces, including the default face, are recomputed just before
> the call to produce_special_glyphs:
> 
>   if (FRAME_FACE_CACHE (it->f) == NULL)
>     init_frame_faces (it->f);
>   if (FRAME_FACE_CACHE (it->f)->used == 0)
>     recompute_basic_faces (it->f);
> 
> And recompute_basic_faces aborts if it is unsuccessful in recomputing
> the basic faces, one of which is the default face.  Which didn't
> happen here.  So how this could happen is a mystery to me; I'm
> probably missing something.

This is indeed NULL:

  (gdb) up 9
  #9  0x00000bf72613ced7 in produce_special_glyphs (it=0x723f0516cf78, what=<optimized out>) at xdisp.c:31605
  31605   xdisp.c: No such file or directory.
  (gdb) p it
  $1 = (struct it *) 0x723f0516cf78
  (gdb) p it->f
  $2 = (struct frame *) 0xbf99e5ccba8
  (gdb) p it->f->face_cache
  $3 = (struct face_cache *) 0xbf9945600f0
  (gdb) p *it->f->face_cache
  $4 = {buckets = 0xbf9e196d000, f = 0xbf99e5ccba8, faces_by_id = 0xbf93c9b3000, size = 168, used = 0, menu_face_changed_p = false}
  (gdb) p it->f->face_cache->faces_by_id
  $5 = (struct face **) 0xbf93c9b3000
  (gdb) p it->f->face_cache->faces_by_id[0]
  $7 = (struct face *) 0x0
  (gdb) p it->face_id
  $8 = 0
  (gdb)

so, I also dig a bit. I see that faces_by_id is enlarged as:

  /* Maybe enlarge C->faces_by_id.  */
  if (i == c->used)
    {
      if (c->used == c->size)
	c->faces_by_id = xpalloc (c->faces_by_id, &c->size, 1, MAX_FACE_ID,
				  sizeof *c->faces_by_id);
      c->used++;
    }

here, it's trust value from xpalloc, and inside I see that it uses xrealloc
which has this logc:

  if (!val)
    memory_full (size);
  MALLOC_PROBE (size);
  return val;

so, if val is NULL it calls memory_full and if it doesn't crash, it returns
NULL which not always fails as I understand it.

Does it make sense?

-- 
wbr, Kirill