Subject: Re: bug#63752: 28.2; GCC 13.1 breaks Emacs subprocess handling when built with -D_FORTIFY_SOURCE

From: Eli Zaretskii <[email-masked]>
Date: Thu, 06 Jul 2023 08:28:33 +0300

> From: Cyril Arnould <[email-masked]>
> CC: "[email-masked]" <[email-masked]>, "[email-masked]"
> 	<[email-masked]>
> Date: Wed, 5 Jul 2023 20:23:33 +0000
> 
> Thanks for the pointers! Following your advice, the first sysdep1.c with
> code up until serial_open was working quite quickly. On a hunch, I've
> singled out the emacs_intr_read function and got lucky: If I only put
> the emacs_intr_read, emacs_read and emacs_read_quit functions into
> sysdep1.c, the problems go away if sysdep1.o is compiled without
> -D_FORTIFY_SOURCE=2.

Thanks, I hope Andrea will have ideas after looking at the
differences.

There are several symbols in the diffs which are used only in the
-D_FORTIFY_SOURCE=2 code, whose precise meanings are unclear to me.
Those are the declaration qualifier __mingw_bos_extern_ovr and the
functions __mingw_bos_ptr_chk_warn and __mingw_call__read.

Under __MINGW_FORTIFY_LEVEL > 0 __mingw_bos_extern_ovr is defined like
this:

  #  define __mingw_bos_extern_ovr extern __inline__ __cdecl \
       __attribute__((__always_inline__, __gnu_inline__)) \
       __mingw_attribute_artificial

Otherwise, it's defined to nothing.  The above just controls the
inlining, but that the 'artificial' attribute is described like this
in the GCC manual:

  'artificial'
       This attribute is useful for small inline wrappers that if possible
       should appear during debugging as a unit.  Depending on the debug
       info format it either means marking the function as artificial or
       using the caller location for all instructions within the inlined
       body.

I'm not sure I understand what this means in the context of the issue
we are discussing here.

The other 2 symbols seem to be about checking a pointer and calling
'_read' while checking the pointer to the buffer passed to it.
__mingw_bos_ptr_chk_warn is defined like this under
__MINGW_FORTIFY_LEVEL > 0:

  #  define __mingw_bos_ptr_chk_warn(p, n, maxtype) \
       ((__mingw_bos_known(p) \
       && __builtin_constant_p(__mingw_bos(p, maxtype) < (size_t)(n)) \
       && __mingw_bos(p, maxtype) < (size_t)(n)) \
       ? __mingw_chk_fail_warn() : __mingw_bos_ptr_chk(p, n, maxtype))

And here's __mingw_call__read:

  #define __MINGW_ASM_CRT_CALL(func) __asm__(__STRINGIFY(func))
  _CRTIMP int __cdecl __mingw_call__read(int, void *, unsigned int) __MINGW_ASM_CRT_CALL(_read);

AFAICT, __mingw_chk_fail_warn eventually does this:

  void __cdecl __attribute__((__noreturn__)) __chk_fail(void) {
    static const char msg[] = "*** buffer overflow detected ***: terminated\n";
    write(STDERR_FILENO, msg, strlen(msg));
    if (IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE)) {
      __fastfail(FAST_FAIL_RANGE_CHECK_FAILURE);
    } else {
      TerminateProcess(GetCurrentProcess(), STATUS_STACK_BUFFER_OVERRUN);
      __builtin_unreachable();
    }
  }

Again, not sure what this means for the issue at hand.  In particular,
__fastfail emits "int 0x29", see

  https://learn.microsoft.com/en-us/cpp/intrinsics/fastfail?view=msvc-170

> Aside from those functions, sysdep1.c also contains all of the includes
> of the original file; I figured it doesn't make much sense to narrow
> those down further. I also had to add the MAX_RW_COUNT
> definition. There's a patch attached; it can be tested as follows:

Thanks, I hope Andrea will have some ideas.

It's too bad the problem goes away under GDB, because we don't even
know whether the crash is due to some fatal error in the Emacs code,
or due to those extra-checking routines inserted into the code by the
FORTIFY option.  IOW, it could be that the problem happens because the
FORTIFY routines don't understand something Emacs does and consider it
a bug worthy of aborting the program.

Another interesting question is why the problems happen only in Emacs
built with native compilation.  Do we use emacs_read or its callers in
some special ways when they are called from comp.c or comp.el?

Maybe we should ask the MinGW64 folks who implemented the FORTIFY
support for MinGW64 GCC to join this discussion and help us figure out
which part(s) of this puzzle are relevant and why?  Cyril, can you
reach out to them and ask them help us out here?  There are too many
unknowns here, and having someone on board who understands what the
various FORTIFY additions mean and do, and perhaps also how to
selectively disable them (to find the one that really matters) will
definitely help.

Thanks.

P.S. Please keep ANdrea CC'd on this discussion.