Subject: bug#75754: styled_format stack usage/GC protection

From: Eli Zaretskii <[email-masked]>
Date: Wed, 22 Jan 2025 17:56:09 +0200

> Date: Wed, 22 Jan 2025 10:18:32 +0000
> From:  Pip Cet via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <[email-masked]>
> 
> While the alloca_size value is small, sa_avail is negative when we enter
> SAFE_ALLOCA, so SAFE_ALLOCA uses xmalloc to allocate the memory on the
> heap.
> 
> The structure contains a Lisp_Object.  This Lisp_Object must be
> protected from GC by being present on the C stack if GC can ever happen
> in this function.  SAFE_ALLOCA doesn't protect it.
> 
> I'm not entirely sure this is a problem, but
> 
> (let ((print-unreadable-function (lambda (&rest args) (garbage-collect))))
>     (format "%S" (symbol-function '+)))
> 
> produces this backtrace:
> 
> #0  garbage_collect () at alloc.c:6450
> #1  0x00005555557f7700 in Fgarbage_collect () at alloc.c:6697
> #2  0x000055555582e1ee in eval_sub (form=XIL(0x7ffff4dab073)) at eval.c:2584
> #3  0x0000555555828d9f in Fprogn (body=XIL(0)) at eval.c:439
> #4  0x0000555555830604 in funcall_lambda (fun=XIL(0x555556045c9d), nargs=2, arg_vector=0x7fffffff6b78) at eval.c:3339
> #5  0x000055555582f62d in funcall_general (fun=XIL(0x555556045c9d), numargs=2, args=0x7fffffff6b78) at eval.c:3033
> #6  0x000055555582f89a in Ffuncall (nargs=3, args=0x7fffffff6b70) at eval.c:3082
> #7  0x0000555555863e8a in print_vectorlike_unreadable
>     (obj=XIL(0x555555ee7765), printcharfun=XIL(0), escapeflag=true, buf=0x7fffffff6ce0 "\220m\377\377\377\177") at print.c:1683
> #8  0x0000555555866e5f in print_object (obj=XIL(0x555555ee7765), printcharfun=XIL(0), escapeflag=true) at print.c:2647
> #9  0x0000555555862ec2 in print (obj=XIL(0x555555ee7765), printcharfun=XIL(0), escapeflag=true) at print.c:1296
> #10 0x0000555555861cef in Fprin1_to_string (object=XIL(0x555555ee7765), noescape=XIL(0), overrides=XIL(0)) at print.c:814
> #11 0x000055555581fd14 in styled_format (nargs=2, args=0x7fffffffca20, message=false) at editfns.c:3633
> #12 0x000055555581f444 in Fformat (nargs=2, args=0x7fffffffca20) at editfns.c:3370
> 
> indicating that GC can happen.
> 
> The code attempts to protect the current argument by keeping it in a
> redundant automatic variable:
> 
> 	  Lisp_Object arg = spec->argument;
>           ...
> 	  spec->argument = arg = Fprin1_to_string (arg, noescape, Qnil);

I think indeed the protection here is by having the Lisp object in an
automatic variable.

> In any case, this protects only the current argument; if we detect a
> multibyte situation too late, we may restart the loop and, I think,
> reuse info->argument values which were unprotected during GC.

Can you elaborate how this could happen by walking through the
relevant code?

> On the feature/igc branch, where protection is definitely required, I'm
> thinking about a fix.  A quick fix would be to replace all elements of
> struct info by Lisp_Object values and use SAFE_ALLOCA_LISP.

Are automatic variables not protected on the igc branch as they are
with the old GC?

Can we use AUTO_STRING to solve the problem?