]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
x86: Check corrupted return address when unwinding stack
authorH.J. Lu <hjl.tools@gmail.com>
Thu, 11 Aug 2022 23:21:23 +0000 (16:21 -0700)
committerH.J. Lu <hjl.tools@gmail.com>
Mon, 17 Oct 2022 21:21:47 +0000 (14:21 -0700)
If shadow stack is enabled, when unwinding stack, we count how many stack
frames we pop to reach the landing pad and adjust shadow stack by the same
amount.  When counting the stack frame, we compare the return address on
normal stack against the return address on shadow stack.  If they don't
match, return _URC_FATAL_PHASE2_ERROR for the corrupted return address on
normal stack.  Don't check the return address for

1. Non-catchable exception where exception_class == 0.  Process will be
terminated.
2. Zero return address which marks the outermost stack frame.
3. Signal stack frame since kernel puts a restore token on shadow stack.

* unwind-generic.h (_Unwind_Frames_Increment): Add the EXC
argument.
* unwind.inc (_Unwind_RaiseException_Phase2): Pass EXC to
_Unwind_Frames_Increment.
(_Unwind_ForcedUnwind_Phase2): Likewise.
* config/i386/shadow-stack-unwind.h (_Unwind_Frames_Increment):
Take the EXC argument.  Return _URC_FATAL_PHASE2_ERROR if the
return address on normal stack doesn't match the return address
on shadow stack.

libgcc/config/i386/shadow-stack-unwind.h
libgcc/unwind-generic.h
libgcc/unwind.inc

index 2b02682bdaebde8c1bc08df938ea6eb272f7bb8c..89d44165000ec9a5022797aa8a708f192f9e8121 100644 (file)
@@ -54,10 +54,39 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
    aligned.  If the original shadow stack is 8 byte aligned, we just
    need to pop 2 slots, one restore token, from shadow stack.  Otherwise,
    we need to pop 3 slots, one restore token + 4 byte padding, from
-   shadow stack.  */
-#ifndef __x86_64__
+   shadow stack.
+
+   When popping a stack frame, we compare the return address on normal
+   stack against the return address on shadow stack.  If they don't match,
+   return _URC_FATAL_PHASE2_ERROR for the corrupted return address on
+   normal stack.  Don't check the return address for
+   1. Non-catchable exception where exception_class == 0.  Process will
+      be terminated.
+   2. Zero return address which marks the outermost stack frame.
+   3. Signal stack frame since kernel puts a restore token on shadow
+      stack.
+ */
 #undef _Unwind_Frames_Increment
-#define _Unwind_Frames_Increment(context, frames)      \
+#ifdef __x86_64__
+#define _Unwind_Frames_Increment(exc, context, frames) \
+    {                                                  \
+      frames++;                                                \
+      if (exc->exception_class != 0                    \
+         && _Unwind_GetIP (context) != 0               \
+         && !_Unwind_IsSignalFrame (context))          \
+       {                                               \
+         _Unwind_Word ssp = _get_ssp ();               \
+         if (ssp != 0)                                 \
+           {                                           \
+             ssp += 8 * frames;                        \
+             _Unwind_Word ra = *(_Unwind_Word *) ssp;  \
+             if (ra != _Unwind_GetIP (context))        \
+               return _URC_FATAL_PHASE2_ERROR;         \
+           }                                           \
+       }                                               \
+    }
+#else
+#define _Unwind_Frames_Increment(exc, context, frames) \
   if (_Unwind_IsSignalFrame (context))                 \
     do                                                 \
       {                                                        \
@@ -83,5 +112,19 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
       }                                                        \
     while (0);                                         \
   else                                                 \
-    frames++;
+    {                                                  \
+      frames++;                                                \
+      if (exc->exception_class != 0                    \
+         && _Unwind_GetIP (context) != 0)              \
+       {                                               \
+         _Unwind_Word ssp = _get_ssp ();               \
+         if (ssp != 0)                                 \
+           {                                           \
+             ssp += 4 * frames;                        \
+             _Unwind_Word ra = *(_Unwind_Word *) ssp;  \
+             if (ra != _Unwind_GetIP (context))        \
+               return _URC_FATAL_PHASE2_ERROR;         \
+           }                                           \
+       }                                               \
+    }
 #endif
index a87c9b3ccf69b940542ff06830e680cc43d3e680..bf721282d03849ba62198a1562f4709d4c008938 100644 (file)
@@ -292,6 +292,6 @@ EXCEPTION_DISPOSITION _GCC_specific_handler (PEXCEPTION_RECORD, void *,
 #define _Unwind_Frames_Extra(frames)
 
 /* Increment frame count.  */
-#define _Unwind_Frames_Increment(context, frames) frames++
+#define _Unwind_Frames_Increment(exc, context, frames) frames++
 
 #endif /* unwind.h */
index 5efd8af1b157bd616a2783783136d717f6c58e11..a7111a7b3a8f7200c59b1a81ac6b7d11fc767a2d 100644 (file)
@@ -73,7 +73,7 @@ _Unwind_RaiseException_Phase2(struct _Unwind_Exception *exc,
       gcc_assert (!match_handler);
 
       uw_update_context (context, &fs);
-      _Unwind_Frames_Increment (context, frames);
+      _Unwind_Frames_Increment (exc, context, frames);
     }
 
   *frames_p = frames;
@@ -191,7 +191,7 @@ _Unwind_ForcedUnwind_Phase2 (struct _Unwind_Exception *exc,
       /* Update cur_context to describe the same frame as fs, and discard
         the previous context if necessary.  */
       uw_advance_context (context, &fs);
-      _Unwind_Frames_Increment (context, frames);
+      _Unwind_Frames_Increment (exc, context, frames);
     }
 
   *frames_p = frames;