]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
asan: Support dynamic shadow offset
authorKito Cheng <kito.cheng@sifive.com>
Fri, 15 Nov 2024 04:14:54 +0000 (12:14 +0800)
committerKito Cheng <kito.cheng@sifive.com>
Mon, 25 Nov 2024 06:34:33 +0000 (06:34 +0000)
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/ChangeLog:

* asan.cc (asan_dynamic_shadow_offset_p): New.
(asan_shadow_memory_dynamic_address): New.
(asan_local_shadow_memory_dynamic_address): New.
(get_asan_shadow_memory_dynamic_address_decl): New.
(asan_maybe_insert_dynamic_shadow_at_function_entry): New.
(asan_emit_stack_protection): Support dynamic shadow offset.
(build_shadow_mem_access): Ditto.
* asan.h (asan_maybe_insert_dynamic_shadow_at_function_entry): New.
* doc/tm.texi (TARGET_ASAN_DYNAMIC_SHADOW_OFFSET_P): New.
* doc/tm.texi.in (TARGET_ASAN_DYNAMIC_SHADOW_OFFSET_P): Ditto.
* sanopt.cc (pass_sanopt::execute): Handle dynamic shadow offset.
* target.def (asan_dynamic_shadow_offset_p): New.
* toplev.cc (process_options): Handle dynamic shadow offset.

gcc/asan.cc
gcc/asan.h
gcc/doc/tm.texi
gcc/doc/tm.texi.in
gcc/sanopt.cc
gcc/target.def
gcc/toplev.cc

index 087595bd9c7087bbe3a377369902a15fec09cafe..a54ecf2b1ed5418cbbba8243d9bdd1a8e9545b1d 100644 (file)
@@ -457,6 +457,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 ()
@@ -473,6 +480,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.  */
 
@@ -2033,10 +2089,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);
@@ -2558,7 +2625,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);
index d1bf8b1e701b15525c6a900d324f2aebfb778cba..fd80a62ed24076688bc8dd3c052d35a884a0b687 100644 (file)
@@ -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 ();
index 7ff45373d590232507a39fdcab3663f2374b9e92..31b8c29dd4460a0f25054dcb95e3b473cf5f8a32 100644 (file)
@@ -12619,7 +12619,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})
index 73643c239ca6f5d3084edc880de947ecc1c40c79..88139757191eb0e769a712df45d031be07b67dea 100644 (file)
@@ -8046,6 +8046,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
index 604db6b3912a6a21bb9c29dd531799cf057d666a..581bb5af709e998e47e50f7bb6719a701cb01fcd 100644 (file)
@@ -1320,6 +1320,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 ();
index 000dd11679896ef08dd853a6de7d73eacf099e4e..269787023e45f48984d20f6052e14740dee5adee 100644 (file)
@@ -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)
index 1bb94db3916fe7bc0e2f53899f48c6ae56d03317..7eb7733c2761433db94239e7e03e68b7c9e72b92 100644 (file)
@@ -1693,7 +1693,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");