Make red zone size more flexible for stack variables (PR sanitizer/81715).
authorMartin Liska <mliska@suse.cz>
Fri, 30 Nov 2018 14:25:15 +0000 (15:25 +0100)
committerMartin Liska <marxin@gcc.gnu.org>
Fri, 30 Nov 2018 14:25:15 +0000 (14:25 +0000)
2018-11-30  Martin Liska  <mliska@suse.cz>

PR sanitizer/81715
* asan.c (asan_shadow_cst): Remove, partially transform
into flush_redzone_payload.
(RZ_BUFFER_SIZE): New.
(struct asan_redzone_buffer): New.
(asan_redzone_buffer::emit_redzone_byte): Likewise.
(asan_redzone_buffer::flush_redzone_payload): Likewise.
(asan_redzone_buffer::flush_if_full): Likewise.
(asan_emit_stack_protection): Use asan_redzone_buffer class
that is responsible for proper aligned stores and flushing
of shadow memory payload.
* asan.h (ASAN_MIN_RED_ZONE_SIZE): New.
(asan_var_and_redzone_size): Likewise.
* cfgexpand.c (expand_stack_vars): Use smaller alignment
(ASAN_MIN_RED_ZONE_SIZE) in order to make shadow memory
for automatic variables more compact.
2018-11-30  Martin Liska  <mliska@suse.cz>

PR sanitizer/81715
* c-c++-common/asan/asan-stack-small.c: New test.

From-SVN: r266664

gcc/ChangeLog
gcc/asan.c
gcc/asan.h
gcc/cfgexpand.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/asan/asan-stack-small.c [new file with mode: 0644]

index 15cbe00abd6e1c0b48e726944e14ae425cda4ce5..c10bfc3e4d732c0762fcf302c0986e0226ac27cf 100644 (file)
@@ -1,3 +1,22 @@
+2018-11-30  Martin Liska  <mliska@suse.cz>
+
+       PR sanitizer/81715
+       * asan.c (asan_shadow_cst): Remove, partially transform
+       into flush_redzone_payload.
+       (RZ_BUFFER_SIZE): New.
+       (struct asan_redzone_buffer): New.
+       (asan_redzone_buffer::emit_redzone_byte): Likewise.
+       (asan_redzone_buffer::flush_redzone_payload): Likewise.
+       (asan_redzone_buffer::flush_if_full): Likewise.
+       (asan_emit_stack_protection): Use asan_redzone_buffer class
+       that is responsible for proper aligned stores and flushing
+       of shadow memory payload.
+       * asan.h (ASAN_MIN_RED_ZONE_SIZE): New.
+       (asan_var_and_redzone_size): Likewise.
+       * cfgexpand.c (expand_stack_vars): Use smaller alignment
+       (ASAN_MIN_RED_ZONE_SIZE) in order to make shadow memory
+       for automatic variables more compact.
+
 2018-11-30  Alan Modra  <amodra@gmail.com>
 
        * config/rs6000/predicates.md (easy_fp_constant): Avoid long
index 45906bf8fee9ee56499c5b2525315d5a4f3b37f1..5d1d1dec064d5261e9dc96c3e8a62c4732a67dd4 100644 (file)
@@ -1155,20 +1155,6 @@ asan_pp_string (pretty_printer *pp)
   return build1 (ADDR_EXPR, shadow_ptr_types[0], ret);
 }
 
-/* Return a CONST_INT representing 4 subsequent shadow memory bytes.  */
-
-static rtx
-asan_shadow_cst (unsigned char shadow_bytes[4])
-{
-  int i;
-  unsigned HOST_WIDE_INT val = 0;
-  gcc_assert (WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN);
-  for (i = 0; i < 4; i++)
-    val |= (unsigned HOST_WIDE_INT) shadow_bytes[BYTES_BIG_ENDIAN ? 3 - i : i]
-          << (BITS_PER_UNIT * i);
-  return gen_int_mode (val, SImode);
-}
-
 /* Clear shadow memory at SHADOW_MEM, LEN bytes.  Can't call a library call here
    though.  */
 
@@ -1235,6 +1221,136 @@ shadow_mem_size (unsigned HOST_WIDE_INT size)
   return ROUND_UP (size, ASAN_SHADOW_GRANULARITY) / ASAN_SHADOW_GRANULARITY;
 }
 
+/* Always emit 4 bytes at a time.  */
+#define RZ_BUFFER_SIZE 4
+
+/* ASAN redzone buffer container that handles emission of shadow bytes.  */
+struct asan_redzone_buffer
+{
+  /* Constructor.  */
+  asan_redzone_buffer (rtx shadow_mem, HOST_WIDE_INT prev_offset):
+    m_shadow_mem (shadow_mem), m_prev_offset (prev_offset),
+    m_original_offset (prev_offset), m_shadow_bytes (RZ_BUFFER_SIZE)
+  {}
+
+  /* Emit VALUE shadow byte at a given OFFSET.  */
+  void emit_redzone_byte (HOST_WIDE_INT offset, unsigned char value);
+
+  /* Emit RTX emission of the content of the buffer.  */
+  void flush_redzone_payload (void);
+
+private:
+  /* Flush if the content of the buffer is full
+     (equal to RZ_BUFFER_SIZE).  */
+  void flush_if_full (void);
+
+  /* Memory where we last emitted a redzone payload.  */
+  rtx m_shadow_mem;
+
+  /* Relative offset where we last emitted a redzone payload.  */
+  HOST_WIDE_INT m_prev_offset;
+
+  /* Relative original offset.  Used for checking only.  */
+  HOST_WIDE_INT m_original_offset;
+
+public:
+  /* Buffer with redzone payload.  */
+  auto_vec<unsigned char> m_shadow_bytes;
+};
+
+/* Emit VALUE shadow byte at a given OFFSET.  */
+
+void
+asan_redzone_buffer::emit_redzone_byte (HOST_WIDE_INT offset,
+                                       unsigned char value)
+{
+  gcc_assert ((offset & (ASAN_SHADOW_GRANULARITY - 1)) == 0);
+  gcc_assert (offset >= m_prev_offset);
+
+  HOST_WIDE_INT off
+    = m_prev_offset + ASAN_SHADOW_GRANULARITY * m_shadow_bytes.length ();
+  if (off == offset)
+    {
+      /* Consecutive shadow memory byte.  */
+      m_shadow_bytes.safe_push (value);
+      flush_if_full ();
+    }
+  else
+    {
+      if (!m_shadow_bytes.is_empty ())
+       flush_redzone_payload ();
+
+      /* Maybe start earlier in order to use aligned store.  */
+      HOST_WIDE_INT align = (offset - m_prev_offset) % ASAN_RED_ZONE_SIZE;
+      if (align)
+       {
+         offset -= align;
+         for (unsigned i = 0; i < align / BITS_PER_UNIT; i++)
+           m_shadow_bytes.safe_push (0);
+       }
+
+      /* Adjust m_prev_offset and m_shadow_mem.  */
+      HOST_WIDE_INT diff = offset - m_prev_offset;
+      m_shadow_mem = adjust_address (m_shadow_mem, VOIDmode,
+                                    diff >> ASAN_SHADOW_SHIFT);
+      m_prev_offset = offset;
+      m_shadow_bytes.safe_push (value);
+      flush_if_full ();
+    }
+}
+
+/* Emit RTX emission of the content of the buffer.  */
+
+void
+asan_redzone_buffer::flush_redzone_payload (void)
+{
+  gcc_assert (WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN);
+
+  if (m_shadow_bytes.is_empty ())
+    return;
+
+  /* Be sure we always emit to an aligned address.  */
+  gcc_assert (((m_prev_offset - m_original_offset)
+             & (ASAN_RED_ZONE_SIZE - 1)) == 0);
+
+  /* Fill it to RZ_BUFFER_SIZE bytes with zeros if needed.  */
+  unsigned l = m_shadow_bytes.length ();
+  for (unsigned i = 0; i <= RZ_BUFFER_SIZE - l; i++)
+    m_shadow_bytes.safe_push (0);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+            "Flushing rzbuffer at offset %" PRId64 " with: ", m_prev_offset);
+
+  unsigned HOST_WIDE_INT val = 0;
+  for (unsigned i = 0; i < RZ_BUFFER_SIZE; i++)
+    {
+      unsigned char v
+       = m_shadow_bytes[BYTES_BIG_ENDIAN ? RZ_BUFFER_SIZE - i : i];
+      val |= (unsigned HOST_WIDE_INT)v << (BITS_PER_UNIT * i);
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       fprintf (dump_file, "%02x ", v);
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "\n");
+
+  rtx c = gen_int_mode (val, SImode);
+  m_shadow_mem = adjust_address (m_shadow_mem, SImode, 0);
+  emit_move_insn (m_shadow_mem, c);
+  m_shadow_bytes.truncate (0);
+}
+
+/* Flush if the content of the buffer is full
+   (equal to RZ_BUFFER_SIZE).  */
+
+void
+asan_redzone_buffer::flush_if_full (void)
+{
+  if (m_shadow_bytes.length () == RZ_BUFFER_SIZE)
+    flush_redzone_payload ();
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1256,7 +1372,6 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   rtx_code_label *lab;
   rtx_insn *insns;
   char buf[32];
-  unsigned char shadow_bytes[4];
   HOST_WIDE_INT base_offset = offsets[length - 1];
   HOST_WIDE_INT base_align_bias = 0, offset, prev_offset;
   HOST_WIDE_INT asan_frame_size = offsets[0] - base_offset;
@@ -1421,46 +1536,43 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   if (STRICT_ALIGNMENT)
     set_mem_align (shadow_mem, (GET_MODE_ALIGNMENT (SImode)));
   prev_offset = base_offset;
+
+  asan_redzone_buffer rz_buffer (shadow_mem, prev_offset);
   for (l = length; l; l -= 2)
     {
       if (l == 2)
        cur_shadow_byte = ASAN_STACK_MAGIC_RIGHT;
       offset = offsets[l - 1];
-      if ((offset - base_offset) & (ASAN_RED_ZONE_SIZE - 1))
+
+      bool extra_byte = (offset - base_offset) & (ASAN_SHADOW_GRANULARITY - 1);
+      /* If a red-zone is not aligned to ASAN_SHADOW_GRANULARITY then
+        the previous stack variable has size % ASAN_SHADOW_GRANULARITY != 0.
+        In that case we have to emit one extra byte that will describe
+        how many bytes (our of ASAN_SHADOW_GRANULARITY) can be accessed.  */
+      if (extra_byte)
        {
-         int i;
          HOST_WIDE_INT aoff
            = base_offset + ((offset - base_offset)
-                            & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1));
-         shadow_mem = adjust_address (shadow_mem, VOIDmode,
-                                      (aoff - prev_offset)
-                                      >> ASAN_SHADOW_SHIFT);
-         prev_offset = aoff;
-         for (i = 0; i < 4; i++, aoff += ASAN_SHADOW_GRANULARITY)
-           if (aoff < offset)
-             {
-               if (aoff < offset - (HOST_WIDE_INT)ASAN_SHADOW_GRANULARITY + 1)
-                 shadow_bytes[i] = 0;
-               else
-                 shadow_bytes[i] = offset - aoff;
-             }
-           else
-             shadow_bytes[i] = ASAN_STACK_MAGIC_MIDDLE;
-         emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes));
-         offset = aoff;
+                            & ~(ASAN_SHADOW_GRANULARITY - HOST_WIDE_INT_1));
+         rz_buffer.emit_redzone_byte (aoff, offset - aoff);
+         offset = aoff + ASAN_SHADOW_GRANULARITY;
        }
-      while (offset <= offsets[l - 2] - ASAN_RED_ZONE_SIZE)
+
+      /* Calculate size of red zone payload.  */
+      while (offset < offsets[l - 2])
        {
-         shadow_mem = adjust_address (shadow_mem, VOIDmode,
-                                      (offset - prev_offset)
-                                      >> ASAN_SHADOW_SHIFT);
-         prev_offset = offset;
-         memset (shadow_bytes, cur_shadow_byte, 4);
-         emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes));
-         offset += ASAN_RED_ZONE_SIZE;
+         rz_buffer.emit_redzone_byte (offset, cur_shadow_byte);
+         offset += ASAN_SHADOW_GRANULARITY;
        }
+
       cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE;
     }
+
+  /* As the automatic variables are aligned to
+     ASAN_RED_ZONE_SIZE / ASAN_SHADOW_GRANULARITY, the buffer should be
+     flushed here.  */
+  gcc_assert (rz_buffer.m_shadow_bytes.is_empty ());
+
   do_pending_stack_adjust ();
 
   /* Construct epilogue sequence.  */
@@ -1519,7 +1631,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   for (l = length; l; l -= 2)
     {
       offset = base_offset + ((offsets[l - 1] - base_offset)
-                            & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1));
+                            & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1));
       if (last_offset + last_size != offset)
        {
          shadow_mem = adjust_address (shadow_mem, VOIDmode,
@@ -1531,7 +1643,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
          last_size = 0;
        }
       last_size += base_offset + ((offsets[l - 2] - base_offset)
-                                 & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1))
+                                 & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1))
                   - offset;
 
       /* Unpoison shadow memory that corresponds to a variable that is 
@@ -1552,7 +1664,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
                           "%s (%" PRId64 " B)\n", n, size);
                }
 
-               last_size += size & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1);
+               last_size += size & ~(ASAN_MIN_RED_ZONE_SIZE - HOST_WIDE_INT_1);
            }
        }
     }
index 2f431b4f93825601c33fbd682a6fd503de4ce051..e1b9b491e674ed6e8eaf72eb072fab1baef19cd6 100644 (file)
@@ -53,6 +53,11 @@ extern hash_set <tree> *asan_used_labels;
    up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes.  */
 #define ASAN_RED_ZONE_SIZE     32
 
+/* Stack variable use more compact red zones.  The size includes also
+   size of variable itself.  */
+
+#define ASAN_MIN_RED_ZONE_SIZE 16
+
 /* Shadow memory values for stack protection.  Left is below protected vars,
    the first pointer in stack corresponding to that offset contains
    ASAN_STACK_FRAME_MAGIC word, the second pointer to a string describing
@@ -102,6 +107,26 @@ asan_red_zone_size (unsigned int size)
   return c ? 2 * ASAN_RED_ZONE_SIZE - c : ASAN_RED_ZONE_SIZE;
 }
 
+/* Return how much a stack variable occupis on a stack
+   including a space for red zone.  */
+
+static inline unsigned HOST_WIDE_INT
+asan_var_and_redzone_size (unsigned HOST_WIDE_INT size)
+{
+  if (size <= 4)
+    return 16;
+  else if (size <= 16)
+    return 32;
+  else if (size <= 128)
+    return size + 32;
+  else if (size <= 512)
+    return size + 64;
+  else if (size <= 4096)
+    return size + 128;
+  else
+    return size + 256;
+}
+
 extern bool set_asan_shadow_offset (const char *);
 
 extern void set_sanitized_sections (const char *);
index 21bdcdaeaa351972daf6a176da7ad9e5bdafc08c..5e23bc242b98ee281d2284b9c8af68507596975b 100644 (file)
@@ -1125,13 +1125,17 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
              && stack_vars[i].size.is_constant ())
            {
              prev_offset = align_base (prev_offset,
-                                       MAX (alignb, ASAN_RED_ZONE_SIZE),
+                                       MAX (alignb, ASAN_MIN_RED_ZONE_SIZE),
                                        !FRAME_GROWS_DOWNWARD);
              tree repr_decl = NULL_TREE;
-             offset
-               = alloc_stack_frame_space (stack_vars[i].size
-                                          + ASAN_RED_ZONE_SIZE,
-                                          MAX (alignb, ASAN_RED_ZONE_SIZE));
+             unsigned HOST_WIDE_INT size
+               = asan_var_and_redzone_size (stack_vars[i].size.to_constant ());
+             if (data->asan_vec.is_empty ())
+               size = MAX (size, ASAN_RED_ZONE_SIZE);
+
+             unsigned HOST_WIDE_INT alignment = MAX (alignb,
+                                                     ASAN_MIN_RED_ZONE_SIZE);
+             offset = alloc_stack_frame_space (size, alignment);
 
              data->asan_vec.safe_push (prev_offset);
              /* Allocating a constant amount of space from a constant
index b33ed8e6b92c7243a686b54ecd28c6562acd37a5..33b42aa2e863caf77b343faaf406ff2c3a0adb5e 100644 (file)
@@ -1,3 +1,8 @@
+2018-11-30  Martin Liska  <mliska@suse.cz>
+
+       PR sanitizer/81715
+       * c-c++-common/asan/asan-stack-small.c: New test.
+
 2018-11-30  Richard Biener  <rguenther@suse.de>
 
        * gcc.dg/gimplefe-34.c: New testcase.
diff --git a/gcc/testsuite/c-c++-common/asan/asan-stack-small.c b/gcc/testsuite/c-c++-common/asan/asan-stack-small.c
new file mode 100644 (file)
index 0000000..11a56b8
--- /dev/null
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+
+char *pa;
+char *pb;
+char *pc;
+
+void access (volatile char *ptr)
+{
+  *ptr = 'x';
+}
+
+int main (int argc, char **argv)
+{
+  char a;
+  char b;
+  char c;
+
+  pa = &a;
+  pb = &b;
+  pc = &c;
+
+  access (pb);
+  access (pc);
+  // access 'b' here
+  access (pa + 32);
+
+  return 0;
+}