Hi,

ASAN was enabled for the SPARC architecture during GCC 9 development but it 
doesn't really work on SPARC64/Linux because of the specific layout of the 
virtual memory address space.  Fortunately this is (easily) fixable and the 
fix has been accepted upstream, along with other fixes for SPARC (I have 
attached the asan/asan_mapping_sparc64.h file accepted upstream).

But, since GCC also hardcodes the scaling done by ASAN, this also requires a 
small adjustment to the compiler proper by means of a hook, tentatively called 
TARGET_ASAN_SHADOW_LEFT_SHIFT, which is defined to NULL except for SPARC.  It
yields a 100% clean ASAN testsuite on SPARC64/Linux (32-bit and 64-bit).

Tested on SPARC64/Linux, SPARC/Solaris and x86-64/Linux, OK for the mainline?


2019-03-11  Eric Botcazou  <ebotca...@adacore.com>

        PR sanitizer/80953
        * target.def (asan_shadow_left_shift): New hook.
        (asan_shadow_offset): Minor tweak.
        * doc/tm.texi.in: Add TARGET_ASAN_SHADOW_LEFT_SHIFT.
        * doc/tm.texi: Regenerate.
        * asan.c (asan_emit_stack_protection): Do a preliminary left shift if
        TARGET_ASAN_SHADOW_LEFT_SHIFT is positive.
        (build_shadow_mem_access): Likewise.
        * config/sparc/sparc.c (TARGET_ASAN_SHADOW_LEFT_SHIFT): Define to...
        (sparc_asan_shadow_left_shift): ...this.  New function.

-- 
Eric Botcazou
Index: asan.c
===================================================================
--- asan.c	(revision 269546)
+++ asan.c	(working copy)
@@ -1380,6 +1380,7 @@ asan_emit_stack_protection (rtx base, rt
   unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT;
   tree str_cst, decl, id;
   int use_after_return_class = -1;
+  unsigned int shift;
 
   if (shadow_ptr_types[0] == NULL_TREE)
     asan_init_shadow_ptr_types ();
@@ -1524,8 +1525,19 @@ asan_emit_stack_protection (rtx base, rt
   TREE_ASM_WRITTEN (decl) = 1;
   TREE_ASM_WRITTEN (id) = 1;
   emit_move_insn (mem, expand_normal (build_fold_addr_expr (decl)));
-  shadow_base = expand_binop (Pmode, lshr_optab, base,
-			      gen_int_shift_amount (Pmode, ASAN_SHADOW_SHIFT),
+  shadow_base = base;
+  if (targetm.asan_shadow_left_shift
+      && (shift = targetm.asan_shadow_left_shift ()) > 0)
+    {
+      shadow_base = expand_binop (Pmode, ashl_optab, shadow_base,
+				  gen_int_shift_amount (Pmode, shift),
+				  NULL_RTX, 1, OPTAB_DIRECT);
+      shift += ASAN_SHADOW_SHIFT;
+    }
+  else
+    shift = ASAN_SHADOW_SHIFT;
+  shadow_base = expand_binop (Pmode, lshr_optab, shadow_base,
+			      gen_int_shift_amount (Pmode, shift),
 			      NULL_RTX, 1, OPTAB_DIRECT);
   shadow_base
     = plus_constant (Pmode, shadow_base,
@@ -2023,9 +2035,24 @@ build_shadow_mem_access (gimple_stmt_ite
 {
   tree t, uintptr_type = TREE_TYPE (base_addr);
   tree shadow_type = TREE_TYPE (shadow_ptr_type);
+  unsigned int shift;
   gimple *g;
 
-  t = build_int_cst (uintptr_type, ASAN_SHADOW_SHIFT);
+  if (targetm.asan_shadow_left_shift
+      && (shift = targetm.asan_shadow_left_shift ()) > 0)
+    {
+      t = build_int_cst (uintptr_type, shift);
+      g = gimple_build_assign (make_ssa_name (uintptr_type), LSHIFT_EXPR,
+			       base_addr, t);
+      gimple_set_location (g, location);
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+      base_addr = gimple_assign_lhs (g);
+      shift += ASAN_SHADOW_SHIFT;
+    }
+  else
+    shift = ASAN_SHADOW_SHIFT;
+
+  t = build_int_cst (uintptr_type, shift);
   g = gimple_build_assign (make_ssa_name (uintptr_type), RSHIFT_EXPR,
 			   base_addr, t);
   gimple_set_location (g, location);
Index: config/sparc/sparc.c
===================================================================
--- config/sparc/sparc.c	(revision 269546)
+++ config/sparc/sparc.c	(working copy)
@@ -674,6 +674,7 @@ static rtx sparc_struct_value_rtx (tree,
 static rtx sparc_function_value (const_tree, const_tree, bool);
 static rtx sparc_libcall_value (machine_mode, const_rtx);
 static bool sparc_function_value_regno_p (const unsigned int);
+static unsigned int sparc_asan_shadow_left_shift (void);
 static unsigned HOST_WIDE_INT sparc_asan_shadow_offset (void);
 static void sparc_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
 static void sparc_file_end (void);
@@ -835,6 +836,9 @@ char sparc_hard_reg_printed[8];
 #undef TARGET_EXPAND_BUILTIN_SAVEREGS
 #define TARGET_EXPAND_BUILTIN_SAVEREGS sparc_builtin_saveregs
 
+#undef TARGET_ASAN_SHADOW_LEFT_SHIFT
+#define TARGET_ASAN_SHADOW_LEFT_SHIFT sparc_asan_shadow_left_shift
+
 #undef TARGET_ASAN_SHADOW_OFFSET
 #define TARGET_ASAN_SHADOW_OFFSET sparc_asan_shadow_offset
 
@@ -12493,7 +12497,16 @@ sparc_init_machine_status (void)
 {
   return ggc_cleared_alloc<machine_function> ();
 }
-
+
+/* Implement the TARGET_ASAN_SHADOW_LEFT_SHIFT hook.  */
+
+static unsigned int
+sparc_asan_shadow_left_shift (void)
+{
+  /* This is tailored to the 52-bit VM layout on SPARC-T4 and later.  */
+  return TARGET_ARCH64 ? 12 : 0;
+}
+
 /* Implement the TARGET_ASAN_SHADOW_OFFSET hook.  */
 
 static unsigned HOST_WIDE_INT
Index: doc/tm.texi
===================================================================
--- doc/tm.texi	(revision 269546)
+++ doc/tm.texi	(working copy)
@@ -11975,10 +11975,17 @@ MIPS, where add-immediate takes a 16-bit
 is zero, which disables this optimization.
 @end deftypevr
 
+@deftypefn {Target Hook} {unsigned int} TARGET_ASAN_SHADOW_LEFT_SHIFT (void)
+Return the amount by which an address must first be shifted to the left
+and then back to the right, before being normally shifted to the right,
+to get the corresponding Address Sanitizer shadow address.  NULL means that
+such a left shift is not needed.
+@end deftypefn
+
 @deftypefn {Target Hook} {unsigned HOST_WIDE_INT} TARGET_ASAN_SHADOW_OFFSET (void)
-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.
+Return the offset added to a shifted address to get the corresponding
+Address Sanitizer shadow memory address.  NULL means that the Address
+Sanitizer is not supported by the target.
 @end deftypefn
 
 @deftypefn {Target Hook} {unsigned HOST_WIDE_INT} TARGET_MEMMODEL_CHECK (unsigned HOST_WIDE_INT @var{val})
Index: doc/tm.texi.in
===================================================================
--- doc/tm.texi.in	(revision 269546)
+++ doc/tm.texi.in	(working copy)
@@ -8110,6 +8110,8 @@ and the associated definitions of those
 
 @hook TARGET_CONST_ANCHOR
 
+@hook TARGET_ASAN_SHADOW_LEFT_SHIFT
+
 @hook TARGET_ASAN_SHADOW_OFFSET
 
 @hook TARGET_MEMMODEL_CHECK
Index: target.def
===================================================================
--- target.def	(revision 269546)
+++ target.def	(working copy)
@@ -4307,14 +4307,24 @@ DEFHOOK
 memory model bits are allowed.",
  unsigned HOST_WIDE_INT, (unsigned HOST_WIDE_INT val), NULL)
 
-/* Defines an offset bitwise ored into shifted address to get corresponding
-   Address Sanitizer shadow address, or -1 if Address Sanitizer is not
-   supported by the target.  */
+/* Defines the amount by which an address must first be shifted to the left
+   to get the corresponding Address Sanitizer shadow address.  */
+DEFHOOK
+(asan_shadow_left_shift,
+ "Return the amount by which an address must first be shifted to the left\n\
+and then back to the right, before being normally shifted to the right,\n\
+to get the corresponding Address Sanitizer shadow address.  NULL means that\n\
+such a left shift is not needed.",
+ unsigned int, (void),
+ NULL)
+ 
+/* Defines the offset added to a shifted address to get the corresponding
+   Address Sanitizer shadow address.  */
 DEFHOOK
 (asan_shadow_offset,
- "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.",
+ "Return the offset added to a shifted address to get the corresponding\n\
+Address Sanitizer shadow memory address.  NULL means that the Address\n\
+Sanitizer is not supported by the target.",
  unsigned HOST_WIDE_INT, (void),
  NULL)
 
//===-- asan_mapping_sparc64.h ----------------------------------*- C++ -*-===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// SPARC64-specific definitions for ASan memory mapping.
//===----------------------------------------------------------------------===//
#ifndef ASAN_MAPPING_SPARC64_H
#define ASAN_MAPPING_SPARC64_H

// This is tailored to the 52-bit VM layout on SPARC-T4 and later.
// The VM space is split into two 51-bit halves at both ends: the low part
// has all the bits above the 51st cleared, while the high part has them set.
//   0xfff8000000000000 - 0xffffffffffffffff
//   0x0000000000000000 - 0x0007ffffffffffff

#define VMA_BITS 52
#define HIGH_BITS (64 - VMA_BITS)

// The idea is to chop the high bits before doing the scaling, so the two
// parts become contiguous again and the usual scheme can be applied.

#define MEM_TO_SHADOW(mem) \
  ((((mem) << HIGH_BITS) >> (HIGH_BITS + (SHADOW_SCALE))) + (SHADOW_OFFSET))

#define kLowMemBeg 0
#define kLowMemEnd (SHADOW_OFFSET - 1)

#define kLowShadowBeg SHADOW_OFFSET
#define kLowShadowEnd MEM_TO_SHADOW(kLowMemEnd)

// But of course there is the huge hole between the high shadow memory,
// which is in the low part, and the beginning of the high part.

#define kHighMemBeg (-(1ULL << (VMA_BITS - 1)))

#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg)
#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd)

#define kMidShadowBeg 0
#define kMidShadowEnd 0

// With the zero shadow base we can not actually map pages starting from 0.
// This constant is somewhat arbitrary.
#define kZeroBaseShadowStart 0
#define kZeroBaseMaxShadowStart (1 << 18)

#define kShadowGapBeg (kLowShadowEnd + 1)
#define kShadowGapEnd (kHighShadowBeg - 1)

#define kShadowGap2Beg 0
#define kShadowGap2End 0

#define kShadowGap3Beg 0
#define kShadowGap3End 0

namespace __asan {

static inline bool AddrIsInLowMem(uptr a) {
  PROFILE_ASAN_MAPPING();
  return a <= kLowMemEnd;
}

static inline bool AddrIsInLowShadow(uptr a) {
  PROFILE_ASAN_MAPPING();
  return a >= kLowShadowBeg && a <= kLowShadowEnd;
}

static inline bool AddrIsInMidMem(uptr a) {
  PROFILE_ASAN_MAPPING();
  return false;
}

static inline bool AddrIsInMidShadow(uptr a) {
  PROFILE_ASAN_MAPPING();
  return false;
}

static inline bool AddrIsInHighMem(uptr a) {
  PROFILE_ASAN_MAPPING();
  return kHighMemBeg && a >= kHighMemBeg && a <= kHighMemEnd;
}

static inline bool AddrIsInHighShadow(uptr a) {
  PROFILE_ASAN_MAPPING();
  return kHighMemBeg && a >= kHighShadowBeg && a <= kHighShadowEnd;
}

static inline bool AddrIsInShadowGap(uptr a) {
  PROFILE_ASAN_MAPPING();
  return a >= kShadowGapBeg && a <= kShadowGapEnd;
}

}  // namespace __asan

#endif  // ASAN_MAPPING_SPARC64_H

Reply via email to