AddressSanitizer has supported dynamic shadow offsets since 2016[1], but
GCC hasn't implemented this yet because targets using dynamic shadow
offsets, such as Fuchsia and iOS, are mostly unsupported in GCC.

However, RISC-V 64 switched to dynamic shadow offsets this year[2] because
virtual memory space support varies across different RISC-V cores, such as
Sv39, Sv48, and Sv57. We realized that the best way to handle this
situation is by using a dynamic shadow offset to obtain the offset at
runtime.

We introduce a new target hook, TARGET_ASAN_DYNAMIC_SHADOW_OFFSET_P, to
determine if the target is using a dynamic shadow offset, so this change
won't affect the static offset path. Additionally, TARGET_ASAN_SHADOW_OFFSET
continues to work even if TARGET_ASAN_DYNAMIC_SHADOW_OFFSET_P is non-zero,
ensuring that KASAN functions as expected.

This patch set has been verified on the Banana Pi F3, currently one of the
most popular RISC-V development boards. All AddressSanitizer-related tests
passed without introducing new regressions.

It was also verified on AArch64 and x86_64 with no regressions in
AddressSanitizer.

[1] 
https://github.com/llvm/llvm-project/commit/130a190bf08a3d955d9db24dac936159dc049e12
[2] 
https://github.com/llvm/llvm-project/commit/da0c8b275564f814a53a5c19497669ae2d99538d
---
 gcc/asan.cc               | 80 ++++++++++++++++++++++++++++++++++++---
 gcc/asan.h                |  3 ++
 gcc/config/riscv/riscv.cc |  3 ++
 gcc/doc/tm.texi           |  6 ++-
 gcc/doc/tm.texi.in        |  2 +
 gcc/sanopt.cc             |  4 ++
 gcc/target.def            |  8 +++-
 gcc/toplev.cc             |  3 +-
 8 files changed, 101 insertions(+), 8 deletions(-)

diff --git a/gcc/asan.cc b/gcc/asan.cc
index 408c25de4de3..1083f1d621fb 100644
--- a/gcc/asan.cc
+++ b/gcc/asan.cc
@@ -458,6 +458,13 @@ asan_shadow_offset ()
   return asan_shadow_offset_value;
 }
 
+static bool
+asan_dynamic_shadow_offset_p ()
+{
+  return (asan_shadow_offset_value == 0)
+        && targetm.asan_dynamic_shadow_offset_p ();
+}
+
 /* Returns Asan shadow offset has been set.  */
 bool
 asan_shadow_offset_set_p ()
@@ -474,6 +481,55 @@ static GTY(()) tree shadow_ptr_types[3];
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
+static GTY (()) tree asan_shadow_memory_dynamic_address;
+
+/* Local copy for the asan_shadow_memory_dynamic_address within the
+   function.  */
+static GTY (()) tree asan_local_shadow_memory_dynamic_address;
+
+static tree
+get_asan_shadow_memory_dynamic_address_decl ()
+{
+  if (asan_shadow_memory_dynamic_address == NULL_TREE)
+    {
+      tree id, decl;
+      id = get_identifier ("__asan_shadow_memory_dynamic_address");
+      decl
+       = build_decl (BUILTINS_LOCATION, VAR_DECL, id, pointer_sized_int_node);
+      SET_DECL_ASSEMBLER_NAME (decl, id);
+      TREE_ADDRESSABLE (decl) = 1;
+      DECL_ARTIFICIAL (decl) = 1;
+      DECL_IGNORED_P (decl) = 1;
+      DECL_EXTERNAL (decl) = 1;
+      TREE_STATIC (decl) = 1;
+      TREE_PUBLIC (decl) = 1;
+      TREE_USED (decl) = 1;
+      asan_shadow_memory_dynamic_address = decl;
+    }
+
+  return asan_shadow_memory_dynamic_address;
+}
+
+void
+asan_maybe_insert_dynamic_shadow_at_function_entry (function *fun)
+{
+  asan_local_shadow_memory_dynamic_address = NULL_TREE;
+  if (!asan_dynamic_shadow_offset_p ())
+    return;
+
+  gimple *g;
+
+  tree lhs = create_tmp_var (pointer_sized_int_node,
+                            "__local_asan_shadow_memory_dynamic_address");
+
+  g = gimple_build_assign (lhs, get_asan_shadow_memory_dynamic_address_decl 
());
+  gimple_set_location (g, fun->function_start_locus);
+  edge e = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun));
+  gsi_insert_on_edge_immediate (e, g);
+
+  asan_local_shadow_memory_dynamic_address = lhs;
+}
+
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -2034,10 +2090,21 @@ asan_emit_stack_protection (rtx base, rtx pbase, 
unsigned int alignb,
   shadow_base = expand_binop (Pmode, lshr_optab, base,
                              gen_int_shift_amount (Pmode, ASAN_SHADOW_SHIFT),
                              NULL_RTX, 1, OPTAB_DIRECT);
-  shadow_base
-    = plus_constant (Pmode, shadow_base,
-                    asan_shadow_offset ()
-                    + (base_align_bias >> ASAN_SHADOW_SHIFT));
+  if (asan_dynamic_shadow_offset_p ())
+    {
+      ret = expand_normal (get_asan_shadow_memory_dynamic_address_decl ());
+      shadow_base
+       = expand_simple_binop (Pmode, PLUS, shadow_base, ret, NULL_RTX,
+                              /* unsignedp = */ 1, OPTAB_WIDEN);
+      shadow_base = plus_constant (Pmode, shadow_base,
+                                  (base_align_bias >> ASAN_SHADOW_SHIFT));
+    }
+  else
+    {
+      shadow_base = plus_constant (Pmode, shadow_base,
+                                  asan_shadow_offset ()
+                                    + (base_align_bias >> ASAN_SHADOW_SHIFT));
+    }
   gcc_assert (asan_shadow_set != -1
              && (ASAN_RED_ZONE_SIZE >> ASAN_SHADOW_SHIFT) == 4);
   shadow_mem = gen_rtx_MEM (SImode, shadow_base);
@@ -2559,7 +2626,10 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, 
location_t location,
   gimple_set_location (g, location);
   gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
-  t = build_int_cst (uintptr_type, asan_shadow_offset ());
+  if (asan_dynamic_shadow_offset_p ())
+    t = asan_local_shadow_memory_dynamic_address;
+  else
+    t = build_int_cst (uintptr_type, asan_shadow_offset ());
   g = gimple_build_assign (make_ssa_name (uintptr_type), PLUS_EXPR,
                           gimple_assign_lhs (g), t);
   gimple_set_location (g, location);
diff --git a/gcc/asan.h b/gcc/asan.h
index d1bf8b1e701b..fd80a62ed240 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -35,6 +35,9 @@ extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, 
bool *,
                                    hash_map<tree, tree> &);
 extern rtx asan_memfn_rtl (tree);
 
+extern void
+asan_maybe_insert_dynamic_shadow_at_function_entry (function *);
+
 extern void hwasan_record_frame_init ();
 extern void hwasan_record_stack_var (rtx, rtx, poly_int64, poly_int64);
 extern void hwasan_emit_prologue ();
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 7694954c4c5c..4ee04776a60e 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -13729,6 +13729,9 @@ riscv_use_by_pieces_infrastructure_p (unsigned 
HOST_WIDE_INT size,
 #undef TARGET_ASAN_SHADOW_OFFSET
 #define TARGET_ASAN_SHADOW_OFFSET riscv_asan_shadow_offset
 
+#undef TARGET_ASAN_DYNAMIC_SHADOW_OFFSET_P
+#define TARGET_ASAN_DYNAMIC_SHADOW_OFFSET_P riscv_asan_dynamic_shadow_offset_p
+
 #ifdef TARGET_BIG_ENDIAN_DEFAULT
 #undef  TARGET_DEFAULT_TARGET_FLAGS
 #define TARGET_DEFAULT_TARGET_FLAGS (MASK_BIG_ENDIAN)
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 109e40384b68..49597bb1dd26 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -12611,7 +12611,11 @@ is zero, which disables this optimization.
 Return the offset bitwise ored into shifted address to get corresponding
 Address Sanitizer shadow memory address.  NULL if Address Sanitizer is not
 supported by the target.  May return 0 if Address Sanitizer is not supported
-by a subtarget.
+or using dynamic shadow offset by a subtarget.
+@end deftypefn
+
+@deftypefn {Target Hook} bool TARGET_ASAN_DYNAMIC_SHADOW_OFFSET_P (void)
+Return true if asan should use dynamic shadow offset.
 @end deftypefn
 
 @deftypefn {Target Hook} {unsigned HOST_WIDE_INT} TARGET_MEMMODEL_CHECK 
(unsigned HOST_WIDE_INT @var{val})
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 93bcd747e374..cd91ca00804e 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8044,6 +8044,8 @@ and the associated definitions of those functions.
 
 @hook TARGET_ASAN_SHADOW_OFFSET
 
+@hook TARGET_ASAN_DYNAMIC_SHADOW_OFFSET_P
+
 @hook TARGET_MEMMODEL_CHECK
 
 @hook TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
diff --git a/gcc/sanopt.cc b/gcc/sanopt.cc
index 0d79a0271f73..86d4b9c49dfb 100644
--- a/gcc/sanopt.cc
+++ b/gcc/sanopt.cc
@@ -1321,6 +1321,10 @@ pass_sanopt::execute (function *fun)
          }
     }
 
+  if (asan_num_accesses || contains_asan_mark || asan_sanitize_stack_p ()
+      || hwasan_sanitize_stack_p ())
+    asan_maybe_insert_dynamic_shadow_at_function_entry (fun);
+
   if (contains_asan_mark)
     {
       sanitize_asan_mark_unpoison ();
diff --git a/gcc/target.def b/gcc/target.def
index 523ae7ec9aaa..aa1b1185eeea 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4700,10 +4700,16 @@ DEFHOOK
  "Return the offset bitwise ored into shifted address to get corresponding\n\
 Address Sanitizer shadow memory address.  NULL if Address Sanitizer is not\n\
 supported by the target.  May return 0 if Address Sanitizer is not supported\n\
-by a subtarget.",
+or using dynamic shadow offset by a subtarget.",
  unsigned HOST_WIDE_INT, (void),
  NULL)
 
+DEFHOOK
+(asan_dynamic_shadow_offset_p,
+ "Return true if asan should use dynamic shadow offset.",
+ bool, (void),
+ hook_bool_void_false)
+
 /* Functions relating to calls - argument passing, returns, etc.  */
 /* Members of struct call have no special macro prefix.  */
 HOOK_VECTOR (TARGET_CALLS, calls)
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index 779049674b4f..ec6eacdf366f 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -1694,7 +1694,8 @@ process_options ()
 
   if ((flag_sanitize & SANITIZE_USER_ADDRESS)
       && ((targetm.asan_shadow_offset == NULL)
-         || (targetm.asan_shadow_offset () == 0)))
+         || ((targetm.asan_shadow_offset () == 0)
+             && !targetm.asan_dynamic_shadow_offset_p ())))
     {
       warning_at (UNKNOWN_LOCATION, 0,
                  "%<-fsanitize=address%> not supported for this target");
-- 
2.34.1

Reply via email to