Subject: bug#79131: 31.0.50; igc: nested signal, SIGSEGV

From: Eli Zaretskii <[email-masked]>
Date: Fri, 08 Aug 2025 16:34:04 +0300

> Date: Fri, 08 Aug 2025 13:08:12 +0000
> From: Pip Cet <[email-masked]>
> Cc: Gerd Möllmann <[email-masked]>, [email-masked], [email-masked], [email-masked]
> 
> "Eli Zaretskii" <[email-masked]> writes:
> 
> >> Then Ipropose to go with whatever Pip finds out to be the best: either
> >> specbind or *_no_quit.
> >
> > Before we do, I'd like to see the actual places where we currently
> > call functions which can quit, while intervals are in inconsistent
> > state.
> 
> The explicit calls to Fmemq in intervals.c would be two such places:
> 
> static INTERVAL
> adjust_intervals_for_insertion (INTERVAL tree,
> 				ptrdiff_t position, ptrdiff_t length)
> {
>     ....
>       /* Does any actual property pose an actual problem?  We break
>          the loop if we find a nonsticky property.  */
>       for (; CONSP (tail); tail = Fcdr (XCDR (tail)))
> 	{
> 	  Lisp_Object prop, tmp;
> 	  prop = XCAR (tail);
> 
> 	  /* Is this particular property front-sticky?  */
> 	  if (CONSP (front) && ! NILP (Fmemq (prop, front))) <--- quit happens here
> 	    continue;
> 
> 	  /* Is this particular property rear-nonsticky?  */
> 	  if (CONSP (rear) && ! NILP (Fmemq (prop, rear)))
> 	    break;
>     ....
>   }
> 
>   /* If we are positioned between intervals, check the stickiness of
>      both of them.  We have to do this too, if we are at BEG or Z.  */
>   if (position == i->position || eobp)
>     {
>       ...
>       for (temp = prev ? prev : i; temp; temp = INTERVAL_PARENT_OR_NULL (temp))
> 	{
> 	  temp->total_length += length; <--- adjustment happens here
>           ...
>     }
>   else
>     {
>       for (temp = i; temp; temp = INTERVAL_PARENT_OR_NULL (temp))
> 	{
> 	  temp->total_length += length;
>           ...
> 	}
>     }
> 
>   return tree;
> 
> 
> Here's the case for insertion:
> 
> Intervals are in an inconsistent state from the point where Z is
> increased in insert_from_gap_1 until the total length of the root
> interval is increased to match it in adjust_intervals_for_insertion
> (both of these functions are called from insert_from_gap).
> 
> So adjust_intervals_for_insertion must not call any function that can
> quit (if it does so before the total length is updated, intervals will
> be in an inconsistent state. If it does so afterwards, we'll fail to
> adjust point after the insertion).
> 
> But it calls Fmemq and Fassq, both directly and via TMEM and textget, in
> certain circumstances. Those calls happen before total_length is
> adjusted. My gdb session indicates that Vinhibit_quit is nil when this
> function is reached for a normal character insertion.
> 
> Fmemq and Fassq sometimes quit, though they will also sometimes ignore
> the quit flag and return normally, because of the rather complicated
> logic in FOR_EACH_TAIL_INTERNAL; my guess from looking at the code is
> that we need to walk to at least the third element of a list before we
> first check the quit flag.
> 
> We might be able to produce an inconsistent state by setting a
> breakpoint in gdb and setting Vquit_flag = Qt at the wrong moment; would
> that help in any way?

I think it's simpler to bind inhibit-quit non-nil inside
offset_intervals.  memq_no_quit differs from Fmemq in other aspects,
and I don't see why we should risk unintended consequences to fix this
rare (if at all) problem.