Home | History | Annotate | Line # | Download | only in libgcc
      1 /* Stack scrubbing infrastructure
      2    Copyright (C) 2021-2024 Free Software Foundation, Inc.
      3    Contributed by Alexandre Oliva <oliva (at) adacore.com>
      4 
      5 This file is part of GCC.
      6 
      7 GCC is free software; you can redistribute it and/or modify it under
      8 the terms of the GNU General Public License as published by the Free
      9 Software Foundation; either version 3, or (at your option) any later
     10 version.
     11 
     12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15 for more details.
     16 
     17 Under Section 7 of GPL version 3, you are granted additional
     18 permissions described in the GCC Runtime Library Exception, version
     19 3.1, as published by the Free Software Foundation.
     20 
     21 You should have received a copy of the GNU General Public License and
     22 a copy of the GCC Runtime Library Exception along with this program;
     23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     24 <http://www.gnu.org/licenses/>.  */
     25 
     26 #include "tconfig.h"
     27 #include "tsystem.h"
     28 #include "coretypes.h"
     29 #include "tm.h"
     30 #include "libgcc_tm.h"
     31 #include "libgcc2.h"
     32 
     33 #if ! STACK_GROWS_DOWNWARD
     34 # define TOPS >
     35 #else
     36 # define TOPS <
     37 #endif
     38 
     39 /* Make sure these builtins won't be inlined, even with LTO.  */
     40 #define ATTRIBUTE_NOINLINE \
     41   __attribute__ ((__noinline__, __noclone__, __noipa__))
     42 
     43 #define ATTRIBUTE_STRUB_CALLABLE \
     44   __attribute__ ((__strub__ ("callable"))) ATTRIBUTE_NOINLINE
     45 
     46 /* Enter a stack scrubbing context, initializing the watermark to the caller's
     47    stack address.  */
     48 void ATTRIBUTE_STRUB_CALLABLE
     49 __strub_enter (void **watermark)
     50 {
     51   *watermark = __builtin_frame_address (0);
     52 }
     53 
     54 /* Update the watermark within a stack scrubbing context with the current stack
     55    pointer.  */
     56 void ATTRIBUTE_STRUB_CALLABLE
     57 __strub_update (void **watermark)
     58 {
     59   void *sp = __builtin_frame_address (0);
     60 
     61   if (sp TOPS *watermark)
     62     *watermark = sp;
     63 }
     64 
     65 #if TARGET_STRUB_USE_DYNAMIC_ARRAY && ! defined TARGET_STRUB_MAY_USE_MEMSET
     66 # define TARGET_STRUB_MAY_USE_MEMSET 1
     67 #endif
     68 
     69 #if defined __x86_64__ && __OPTIMIZE__
     70 # define TARGET_STRUB_DISABLE_RED_ZONE \
     71   /* __attribute__ ((__target__ ("no-red-zone"))) // not needed when optimizing */
     72 #elif !defined RED_ZONE_SIZE || defined __i386__
     73 # define TARGET_STRUB_DISABLE_RED_ZONE
     74 #endif
     75 
     76 #ifndef TARGET_STRUB_DISABLE_RED_ZONE
     77 /* Dummy function, called to force the caller to not be a leaf function, so
     78    that it can't use the red zone.  */
     79 static void ATTRIBUTE_STRUB_CALLABLE
     80 __strub_dummy_force_no_leaf (void)
     81 {
     82 }
     83 #endif
     84 
     85 /* Leave a stack scrubbing context, clearing the stack between its top and
     86    *MARK.  */
     87 void ATTRIBUTE_STRUB_CALLABLE
     88 #if ! TARGET_STRUB_MAY_USE_MEMSET
     89 __attribute__ ((__optimize__ ("-fno-tree-loop-distribute-patterns")))
     90 #endif
     91 #ifdef TARGET_STRUB_DISABLE_RED_ZONE
     92 TARGET_STRUB_DISABLE_RED_ZONE
     93 #endif
     94 __strub_leave (void **mark)
     95 {
     96   void *sp = __builtin_stack_address ();
     97 
     98   void **base, **end;
     99 #if ! STACK_GROWS_DOWNWARD
    100   base = sp; /* ??? Do we need an offset here?  */
    101   end = *mark;
    102 #else
    103   base = *mark;
    104   end = sp; /* ??? Does any platform require an offset here?  */
    105 #endif
    106 
    107   if (! (base < end))
    108     return;
    109 
    110 #if TARGET_STRUB_USE_DYNAMIC_ARRAY
    111   /* Compute the length without assuming the pointers are both sufficiently
    112      aligned.  They should be, but pointer differences expected to be exact may
    113      yield unexpected results when the assumption doesn't hold.  Given the
    114      potential security implications, compute the length without that
    115      expectation.  If the pointers are misaligned, we may leave a partial
    116      unscrubbed word behind.  */
    117   ptrdiff_t len = ((char *)end - (char *)base) / sizeof (void *);
    118   /* Allocate a dynamically-sized array covering the desired range, so that we
    119      can safely call memset on it.  */
    120   void *ptr[len];
    121   base = &ptr[0];
    122   end = &ptr[len];
    123 #elifndef TARGET_STRUB_DISABLE_RED_ZONE
    124   /* Prevent the use of the red zone, by making this function non-leaf through
    125      an unreachable call that, because of the asm stmt, the compiler will
    126      consider reachable.  */
    127   asm goto ("" : : : : no_leaf);
    128   if (0)
    129     {
    130     no_leaf:
    131       __strub_dummy_force_no_leaf ();
    132       return;
    133     }
    134 #endif
    135 
    136   /* ldist may turn these loops into a memset (thus the conditional
    137      -fno-tree-loop-distribute-patterns above).  Without the dynamic array
    138      above, that call would likely be unsafe: possibly tail-called, and likely
    139      scribbling over its own stack frame.  */
    140 #if ! STACK_GROWS_DOWNWARD
    141   do
    142     *base++ = 0;
    143   while (base < end);
    144   /* Make sure the stack overwrites are not optimized away.  */
    145   asm ("" : : "m" (end[0]));
    146 #else
    147   do
    148     *--end = 0;
    149   while (base < end);
    150   /* Make sure the stack overwrites are not optimized away.  */
    151   asm ("" : : "m" (base[0]));
    152 #endif
    153 }
    154