https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91989

            Bug ID: 91989
           Summary: libssp/spp.c: __[stack_]chk_fail() may run arbitrary
                    code if __builtin_trap() returns
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: other
          Assignee: unassigned at gcc dot gnu.org
          Reporter: franke at computer dot org
  Target Milestone: ---

Issue found in MinGW-w64 GCC 7.4.0 from current Cygwin package, but still
applies to current SVN trunk (r276567):

The abort code used by __[stack_]chk_fail() assumes that something may prevent
__builtin_trap() (and segfault) from working:

libssp/ssp.c:
static void
fail (......)
{
  ...
  /* Try very hard to exit.  Note that signals may be blocked preventing
     the first two options from working.  The use of volatile is here to
     prevent optimizers from "knowing" that __builtin_trap is called first,
     and that it doesn't return, and so "obviously" the rest of the code
     is dead.  */
  {
    volatile int state;
    for (state = 0; ; state++)
      switch (state)
        {
        case 0:
          __builtin_trap ();
          break;
        case 1:
          *(volatile int *)-1L = 0;
          break;
        case 2:
          _exit (127);
          break;
        }
  }
}

The volatile state variable only prevents that the loop is optimized away, but
does not prevent that the compiler assumes an __attribute__((noreturn)) for
__builtin_trap():

gcc/builtins.c:
void
expand_builtin_trap (void)
{
  ...
  emit_barrier ();
}

Depending on how the optimizer moves code around, this may result in arbitrary
code following __builtin_trap(). In libssp.a from Mingw-w64 GCC 7.0.3, the
generated code is equivalent to:

    volatile int state;
    for (state = 0; ; state++)
      switch (state)
        {
        case 0:
          __builtin_trap ();
          // fall through ...
        case 2:
          _exit (127);
          break;
        case 1:
          *(volatile int *)-1L = 0;
          break;
        }

In this case, we were lucky. But if gcc would decide to move 'case 0:' code to
the end of the function, arbitrary code from start of next function would be
executed.

Conclusion: If there are runtime settings that let __buildin_trap() return, the
compiler should not assume an __attribute__((noreturn)) for this builtin. If
there are no such settings, the volatile loop in ssp.c is not needed.

Reply via email to