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