]> git.eshelyaron.com Git - emacs.git/commitdiff
Add support for additional memory checks using AddressSanitizer.
authorVibhav Pant <vibhavp@gmail.com>
Tue, 29 Nov 2022 20:30:09 +0000 (02:00 +0530)
committerVibhav Pant <vibhavp@gmail.com>
Tue, 29 Nov 2022 21:33:26 +0000 (03:03 +0530)
When Emacs is compiled with AddressSanitizer support, enable
poisoning/unpoisoning freed/unused Lisp objects and other internal
memory management structures. If enabled, this will mark freed bytes
that have been put on free lists for future use, and initially
allocated memory blocks/chunks as "poisoned", triggering an ASan error
if they are accessed improperly. Structures are unpoisoned when they
have been taken off their respective free lists.

* configure.ac: Check for the existence of the ASan API header.

* src/alloc.c (ASAN_POISON_ABLOCK, ASAN_UNPOISON_ABLOCK)
(ASAN_POISON_INTERVAL_BLOCK, ASAN_UNPOISON_INTERVAL_BLOCK)
(ASAN_POISON_INTERVAL, ASAN_UNPOISON_INTERVAL)
(ASAN_PREPARE_DEAD_SDATA, ASAN_PREPARE_LIVE_SDATA)
(ASAN_POISON_SBLOCK_DATA, ASAN_POISON_STRING_BLOCK)
(ASAN_UNPOISON_STRING_BLOCK, ASAN_POISON_STRING)
(ASAN_UNPOISON_STRING, ASAN_POISON_FLOAT_BLOCK)
(ASAN_UNPOISON_FLOAT_BLOCK, ASAN_POISON_FLOAT)
(ASAN_UNPOISON_FLOAT, ASAN_POISON_CONS_BLOCK)
(ASAN_POISON_CONS, ASAN_UNPOISON_CONS)
(ASAN_POISON_VECTOR_CONTENTS, ASAN_UNPOISON_VECTOR_CONTENTS)
(ASAN_UNPOISON_VECTOR_BLOCK, ASAN_POISON_SYMBOL_BLOCK)
(ASAN_UNPOISON_SYMBOL_BLOCK, ASAN_POISON_SYMBOL)
(ASAN_UNPOISON_SYMBOL) [ADDRESS_SANITIZER]: New functions. When
address sanitization is enabled, define them to poison/unpoison
objects.

(lisp_align_malloc): Poison newly allocated blocks on `free_ablock',
unpoison ablocks taken from it respectively.
(lisp_align_free): Poison individual ablocks when they are put on the
free list, unpoison them when an entire `ablocks' chunk is being
freed.

(make_interval): Poison interval blocks on initial allocation,
unpoison individual intervals on allocation and removal from
`interval_free_list'.
(sweep_intervals): Unpoison interval blocks before sweeping, poison
dead/unmarked intervals.

(allocate_string): Poison string blocks on initial allocation,
unpoison Lisp_Strings on removal from the free list.
(allocate_string_data): Poison `sblock' data on initial allocation,
unpoison individual `sdata' contents on allocation or removal from the
free list. Call `ASAN_PREPARE_LIVE_SDATA' on the new `sdata' struct.
(sweep_strings): Unpoison string blocks before sweeping them,
poisoning dead strings and their sdata afterwards.
(compact_small_strings): Call `ASAN_PREPARE_LIVE_DATA' on the `sdata'
to where compacted strings to moved to.
(pin_string): Call `ASAN_PREPARE_DEAD_SDATA' on `old_sdata'.

(make_float): Poison float blocks on allocation, unpoisoning
individual Lisp_Floats on allocation or removal from
`float_free_list'.
(sweep_floats): Unpoison float blocks before sweeping, poison
dead/unmarked floats.

(free_cons): Poison `ptr'.
(Fcons): Poison cons blocks on allocation, unpoisoning individual
Lisp_Cons on allocation or removal from `cons_free_list'.
(sweep_conses): Poison dead/unmarked conses.

(setup_free_list): Poison vectors put on `vector_free_lists'.
(allocate_vector_from_block): Unpoison vectors taken from the free
list, poison excess vector bytes when vectors allocated from the free
list are larger than requested.
(sweep_vectors): Unpoison vector blocks before sweeping them.

(Fmake_symbol): Poison symbol blocks on initial allocation,
unpoisoning individual Lisp_Symbols on allocation or removal from
`symbol_free_list'.
(sweep_symbols): Unpoison symbol blocks before sweeping, poisoning
dead/unmarked symbols.

configure.ac
etc/DEBUG
src/alloc.c

index cc4e59ee5acb2dac4b0e188514886abcbc47c8a5..622004cf42a1a0b6c77b8cd552dc5952bf39113b 100644 (file)
@@ -1841,7 +1841,8 @@ AC_CHECK_HEADERS_ONCE(
   coff.h pty.h
   sys/resource.h
   sys/utsname.h pwd.h utmp.h util.h
-  sanitizer/lsan_interface.h])
+  sanitizer/lsan_interface.h
+  sanitizer/asan_interface.h])
 
 AC_CACHE_CHECK([for ADDR_NO_RANDOMIZE],
   [emacs_cv_personality_addr_no_randomize],
index ef9160a209035cdba7060e0ae74d8c33cf0a2e1b..c565374118d1ced64f6226d8c96dd1624779dbc8 100644 (file)
--- a/etc/DEBUG
+++ b/etc/DEBUG
@@ -1002,6 +1002,14 @@ Address sanitization is incompatible with undefined-behavior
 sanitization, unfortunately.  Address sanitization is also
 incompatible with the --with-dumping=unexec option of 'configure'.
 
+When compiled with address sanitization, Emacs will also try to mark
+dead/free lisp objects as poisoned, forbidding them from being
+accessed without being unpoisoned first.  This adds an extra layer
+of checking with objects in internal free lists, which may otherwise
+evade traditional use-after-free checks. To disable this, add
+'allow_user_poisoning=0' to ASAN_OPTIONS, or build Emacs with
+'-DGC_ASAN_POISON_OBJECTS=0' in CFLAGS.
+
 ** Running Emacs under Valgrind
 
 Valgrind <https://valgrind.org/> is free software that can be useful
index 0653f2e0ccc4f53fbafbf4e7dcfc885c24c5a0b7..687982f9d5d6b4fef17affe65c9e441c36acf45b 100644 (file)
@@ -80,6 +80,38 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include <valgrind/memcheck.h>
 #endif
 
+/* AddressSanitizer exposes additional functions for manually marking
+   memory as poisoned/unpoisoned.  When ASan is enabled and the needed
+   header is available, memory is poisoned when:
+
+   * An ablock is freed (lisp_align_free), or ablocks are initially
+   allocated (lisp_align_malloc).
+   * An interval_block is initially allocated (make_interval).
+   * A dead INTERVAL is put on the interval free list
+   (sweep_intervals).
+   * A sdata is marked as dead (sweep_strings, pin_string).
+   * An sblock is initially allocated (allocate_string_data).
+   * A string_block is initially allocated (allocate_string).
+   * A dead string is put on string_free_list (sweep_strings).
+   * A float_block is initially allocated (make_float).
+   * A dead float is put on float_free_list.
+   * A cons_block is initially allocated (Fcons).
+   * A dead cons is put on cons_free_list (sweep_cons).
+   * A dead vector is put on vector_free_list (setup_on_free_list),
+   or a new vector block is allocated (allocate_vector_from_block).
+   Accordingly, objects reused from the free list are unpoisoned.
+
+   This feature can be disabled wtih the run-time flag
+   `allow_user_poisoning' set to zero.
+*/
+#if ADDRESS_SANITIZER && defined HAVE_SANITIZER_ASAN_INTERFACE_H \
+  && !defined GC_ASAN_POISON_OBJECTS
+# define GC_ASAN_POISON_OBJECTS 1
+# include <sanitizer/asan_interface.h>
+#else
+# define GC_ASAN_POISON_OBJECTS 0
+#endif
+
 /* GC_CHECK_MARKED_OBJECTS means do sanity checks on allocated objects.
    We turn that on by default when ENABLE_CHECKING is defined;
    define GC_CHECK_MARKED_OBJECTS to zero to disable.  */
@@ -1157,6 +1189,26 @@ struct ablocks
   (1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void **) (abase))[-1])
 #endif
 
+static void
+ASAN_POISON_ABLOCK (const volatile struct ablock *b)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (&b->x, sizeof (b->x));
+#else
+  (void) (b);
+#endif
+}
+
+static void
+ASAN_UNPOISON_ABLOCK (const volatile struct ablock *b)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (&b->x, sizeof (b->x));
+#else
+  (void) (b);
+#endif
+}
+
 /* The list of free ablock.   */
 static struct ablock *free_ablock;
 
@@ -1235,6 +1287,7 @@ lisp_align_malloc (size_t nbytes, enum mem_type type)
        {
          abase->blocks[i].abase = abase;
          abase->blocks[i].x.next_free = free_ablock;
+         ASAN_POISON_ABLOCK (&abase->blocks[i]);
          free_ablock = &abase->blocks[i];
        }
       intptr_t ialigned = aligned;
@@ -1247,6 +1300,7 @@ lisp_align_malloc (size_t nbytes, enum mem_type type)
       eassert ((intptr_t) ABLOCKS_BUSY (abase) == aligned);
     }
 
+  ASAN_UNPOISON_ABLOCK (free_ablock);
   abase = ABLOCK_ABASE (free_ablock);
   ABLOCKS_BUSY (abase)
     = (struct ablocks *) (2 + (intptr_t) ABLOCKS_BUSY (abase));
@@ -1278,6 +1332,7 @@ lisp_align_free (void *block)
 #endif
   /* Put on free list.  */
   ablock->x.next_free = free_ablock;
+  ASAN_POISON_ABLOCK (ablock);
   free_ablock = ablock;
   /* Update busy count.  */
   intptr_t busy = (intptr_t) ABLOCKS_BUSY (abase) - 2;
@@ -1290,9 +1345,12 @@ lisp_align_free (void *block)
       bool aligned = busy;
       struct ablock **tem = &free_ablock;
       struct ablock *atop = &abase->blocks[aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1];
-
       while (*tem)
        {
+#if GC_ASAN_POISON_OBJECTS
+         __asan_unpoison_memory_region (&(*tem)->x,
+                                        sizeof ((*tem)->x));
+#endif
          if (*tem >= (struct ablock *) abase && *tem < atop)
            {
              i++;
@@ -1421,6 +1479,47 @@ static int interval_block_index = INTERVAL_BLOCK_SIZE;
 
 static INTERVAL interval_free_list;
 
+static void
+ASAN_POISON_INTERVAL_BLOCK (const volatile struct interval_block *b)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (b->intervals, sizeof (b->intervals));
+#else
+  (void) (b);
+#endif
+}
+
+static void
+ASAN_UNPOISON_INTERVAL_BLOCK (const volatile struct interval_block *b)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (b->intervals, sizeof (b->intervals));
+#else
+  (void) (b);
+#endif
+}
+
+
+static void
+ASAN_POISON_INTERVAL (const volatile INTERVAL i)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (i, sizeof (*i));
+#else
+  (void) (i);
+#endif
+}
+
+static void
+ASAN_UNPOISON_INTERVAL (const volatile INTERVAL i)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (i, sizeof (*i));
+#else
+  (void) (i);
+#endif
+}
+
 /* Return a new interval.  */
 
 INTERVAL
@@ -1433,6 +1532,7 @@ make_interval (void)
   if (interval_free_list)
     {
       val = interval_free_list;
+      ASAN_UNPOISON_INTERVAL (val);
       interval_free_list = INTERVAL_PARENT (interval_free_list);
     }
   else
@@ -1443,10 +1543,12 @@ make_interval (void)
            = lisp_malloc (sizeof *newi, false, MEM_TYPE_NON_LISP);
 
          newi->next = interval_block;
+         ASAN_POISON_INTERVAL_BLOCK (newi);
          interval_block = newi;
          interval_block_index = 0;
        }
       val = &interval_block->intervals[interval_block_index++];
+      ASAN_UNPOISON_INTERVAL (val);
     }
 
   MALLOC_UNBLOCK_INPUT;
@@ -1687,6 +1789,91 @@ init_strings (void)
   staticpro (&empty_multibyte_string);
 }
 
+/* Prepare s for denoting a free sdata struct, i.e, poison all bytes
+ * in the flexible array member, except the first SDATA_OFFSET bytes.
+ * This is only effective for strings of size n where n > sdata_size
+ * (n). */
+static void
+ASAN_PREPARE_DEAD_SDATA (const volatile sdata *s, ptrdiff_t size)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (s, sdata_size (size));
+  __asan_unpoison_memory_region (&s->string,
+                                sizeof (struct Lisp_String *));
+  __asan_unpoison_memory_region (&SDATA_NBYTES (s),
+                                sizeof (SDATA_NBYTES (s)));
+#else
+  (void) size;
+  (void) s;
+#endif
+}
+
+/* Prepare s for storing string data for NBYTES bytes.  */
+static void
+ASAN_PREPARE_LIVE_SDATA (const volatile sdata *s,
+                        ptrdiff_t nbytes)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (s, sdata_size (nbytes));
+#else
+  (void) (s);
+  (void) (nbytes);
+#endif
+}
+
+static void
+ASAN_POISON_SBLOCK_DATA (const volatile struct sblock *b,
+                       size_t size)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (b->data, size);
+#else
+  (void) (b);
+  (void) (offset);
+  (void) (size);
+#endif
+}
+
+static void
+ASAN_POISON_STRING_BLOCK (const volatile struct string_block *b)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (b->strings, STRING_BLOCK_SIZE);
+#else
+  (void) (b);
+#endif
+}
+
+static void
+ASAN_UNPOISON_STRING_BLOCK (const volatile struct string_block *b)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (b->strings, STRING_BLOCK_SIZE);
+#else
+  (void) (b);
+#endif
+}
+
+static void
+ASAN_POISON_STRING (const volatile struct Lisp_String *s)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (s, sizeof (*s));
+#else
+  (void) (s);
+#endif
+}
+
+static void
+ASAN_UNPOISON_STRING (const volatile struct Lisp_String *s)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (s, sizeof (*s));
+#else
+  (void) (s);
+#endif
+}
+
 
 #ifdef GC_CHECK_STRING_BYTES
 
@@ -1805,12 +1992,14 @@ allocate_string (void)
          NEXT_FREE_LISP_STRING (s) = string_free_list;
          string_free_list = s;
        }
+      ASAN_POISON_STRING_BLOCK (b);
     }
 
   check_string_free_list ();
 
   /* Pop a Lisp_String off the free-list.  */
   s = string_free_list;
+  ASAN_UNPOISON_STRING (s);
   string_free_list = NEXT_FREE_LISP_STRING (s);
 
   MALLOC_UNBLOCK_INPUT;
@@ -1870,6 +2059,7 @@ allocate_string_data (struct Lisp_String *s,
 #endif
 
       b = lisp_malloc (size + GC_STRING_EXTRA, clearit, MEM_TYPE_NON_LISP);
+      ASAN_POISON_SBLOCK_DATA (b, size);
 
 #ifdef DOUG_LEA_MALLOC
       if (!mmap_lisp_allowed_p ())
@@ -1891,6 +2081,8 @@ allocate_string_data (struct Lisp_String *s,
        {
          /* Not enough room in the current sblock.  */
          b = lisp_malloc (SBLOCK_SIZE, false, MEM_TYPE_NON_LISP);
+         ASAN_POISON_SBLOCK_DATA (b, SBLOCK_SIZE);
+
          data = b->data;
          b->next = NULL;
          b->next_free = data;
@@ -1903,10 +2095,19 @@ allocate_string_data (struct Lisp_String *s,
        }
 
       data = b->next_free;
+
       if (clearit)
-       memset (SDATA_DATA (data), 0, nbytes);
+       {
+#if GC_ASAN_POISON_OBJECTS
+         /* We are accessing SDATA_DATA (data) before it gets
+          * normally unpoisoned, so do it manually.  */
+         __asan_unpoison_memory_region (SDATA_DATA (data), nbytes);
+#endif
+         memset (SDATA_DATA (data), 0, nbytes);
+       }
     }
 
+  ASAN_PREPARE_LIVE_SDATA (data, nbytes);
   data->string = s;
   b->next_free = (sdata *) ((char *) data + needed + GC_STRING_EXTRA);
   eassert ((uintptr_t) b->next_free % alignof (sdata) == 0);
@@ -1998,12 +2199,16 @@ sweep_strings (void)
       int i, nfree = 0;
       struct Lisp_String *free_list_before = string_free_list;
 
+      ASAN_UNPOISON_STRING_BLOCK (b);
+
       next = b->next;
 
       for (i = 0; i < STRING_BLOCK_SIZE; ++i)
        {
          struct Lisp_String *s = b->strings + i;
 
+         ASAN_UNPOISON_STRING (s);
+
          if (s->u.s.data)
            {
              /* String was not on free-list before.  */
@@ -2040,6 +2245,8 @@ sweep_strings (void)
 
                  /* Put the string on the free-list.  */
                  NEXT_FREE_LISP_STRING (s) = string_free_list;
+                 ASAN_POISON_STRING (s);
+                 ASAN_PREPARE_DEAD_SDATA (data, SDATA_NBYTES (data));
                  string_free_list = s;
                  ++nfree;
                }
@@ -2048,6 +2255,8 @@ sweep_strings (void)
            {
              /* S was on the free-list before.  Put it there again.  */
              NEXT_FREE_LISP_STRING (s) = string_free_list;
+             ASAN_POISON_STRING (s);
+
              string_free_list = s;
              ++nfree;
            }
@@ -2174,6 +2383,7 @@ compact_small_strings (void)
                  if (from != to)
                    {
                      eassert (tb != b || to < from);
+                     ASAN_PREPARE_LIVE_SDATA (to, nbytes);
                      memmove (to, from, size + GC_STRING_EXTRA);
                      to->string->u.s.data = SDATA_DATA (to);
                    }
@@ -2525,6 +2735,7 @@ pin_string (Lisp_Object string)
       memcpy (s->u.s.data, data, size);
       old_sdata->string = NULL;
       SDATA_NBYTES (old_sdata) = size;
+      ASAN_PREPARE_DEAD_SDATA (old_sdata, size);
     }
   s->u.s.size_byte = -3;
 }
@@ -2582,6 +2793,48 @@ struct float_block
 #define XFLOAT_UNMARK(fptr) \
   UNSETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX ((fptr)))
 
+static void
+ASAN_POISON_FLOAT_BLOCK (const volatile struct float_block *fblk)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (fblk->floats,
+                                sizeof (fblk->floats));
+#else
+  (void) (fblk);
+#endif
+}
+
+static void
+ASAN_UNPOISON_FLOAT_BLOCK (const volatile struct float_block *fblk)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (fblk->floats,
+                                sizeof (fblk->floats));
+#else
+  (void) (fblk);
+#endif
+}
+
+static void
+ASAN_POISON_FLOAT (const volatile struct Lisp_Float *p)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (p, sizeof (struct Lisp_Float));
+#else
+  (void) (p);
+#endif
+}
+
+static void
+ASAN_UNPOISON_FLOAT (const volatile struct Lisp_Float *p)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (p, sizeof (struct Lisp_Float));
+#else
+  (void) (p);
+#endif
+}
+
 /* Current float_block.  */
 
 static struct float_block *float_block;
@@ -2606,6 +2859,7 @@ make_float (double float_value)
   if (float_free_list)
     {
       XSETFLOAT (val, float_free_list);
+      ASAN_UNPOISON_FLOAT (float_free_list);
       float_free_list = float_free_list->u.chain;
     }
   else
@@ -2616,9 +2870,11 @@ make_float (double float_value)
            = lisp_align_malloc (sizeof *new, MEM_TYPE_FLOAT);
          new->next = float_block;
          memset (new->gcmarkbits, 0, sizeof new->gcmarkbits);
+         ASAN_POISON_FLOAT_BLOCK (new);
          float_block = new;
          float_block_index = 0;
        }
+      ASAN_UNPOISON_FLOAT (&float_block->floats[float_block_index]);
       XSETFLOAT (val, &float_block->floats[float_block_index]);
       float_block_index++;
     }
@@ -2690,6 +2946,36 @@ static int cons_block_index = CONS_BLOCK_SIZE;
 
 static struct Lisp_Cons *cons_free_list;
 
+static void
+ASAN_POISON_CONS_BLOCK (const volatile struct cons_block *b)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (b->conses, sizeof (b->conses));
+#else
+    (void) (b);
+#endif
+}
+
+static void
+ASAN_POISON_CONS (const volatile struct Lisp_Cons *p)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (p, sizeof (struct Lisp_Cons));
+#else
+  (void) (p);
+#endif
+}
+
+static void
+ASAN_UNPOISON_CONS (const volatile struct Lisp_Cons *p)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (p, sizeof (struct Lisp_Cons));
+#else
+  (void) (p);
+#endif
+}
+
 /* Explicitly free a cons cell by putting it on the free-list.  */
 
 void
@@ -2700,6 +2986,7 @@ free_cons (struct Lisp_Cons *ptr)
   cons_free_list = ptr;
   ptrdiff_t nbytes = sizeof *ptr;
   tally_consing (-nbytes);
+  ASAN_POISON_CONS (ptr);
 }
 
 DEFUN ("cons", Fcons, Scons, 2, 2, 0,
@@ -2712,6 +2999,7 @@ DEFUN ("cons", Fcons, Scons, 2, 2, 0,
 
   if (cons_free_list)
     {
+      ASAN_UNPOISON_CONS (cons_free_list);
       XSETCONS (val, cons_free_list);
       cons_free_list = cons_free_list->u.s.u.chain;
     }
@@ -2722,10 +3010,12 @@ DEFUN ("cons", Fcons, Scons, 2, 2, 0,
          struct cons_block *new
            = lisp_align_malloc (sizeof *new, MEM_TYPE_CONS);
          memset (new->gcmarkbits, 0, sizeof new->gcmarkbits);
+         ASAN_POISON_CONS_BLOCK (new);
          new->next = cons_block;
          cons_block = new;
          cons_block_index = 0;
        }
+      ASAN_UNPOISON_CONS (&cons_block->conses[cons_block_index]);
       XSETCONS (val, &cons_block->conses[cons_block_index]);
       cons_block_index++;
     }
@@ -2980,6 +3270,38 @@ static struct large_vector *large_vectors;
 
 Lisp_Object zero_vector;
 
+static void
+ASAN_POISON_VECTOR_CONTENTS (const volatile struct Lisp_Vector *v,
+                            ptrdiff_t bytes)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (v->contents, bytes);
+#else
+  (void) (v);
+#endif
+}
+
+static void
+ASAN_UNPOISON_VECTOR_CONTENTS (const volatile struct Lisp_Vector *v,
+                              ptrdiff_t bytes)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (v->contents, bytes);
+#else
+  (void) (v);
+#endif
+}
+
+static void
+ASAN_UNPOISON_VECTOR_BLOCK (const volatile struct vector_block *b)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (b->data, sizeof (b->data));
+#else
+  (void) (v);
+#endif
+}
+
 /* Common shortcut to setup vector on a free list.  */
 
 static void
@@ -2992,6 +3314,7 @@ setup_on_free_list (struct Lisp_Vector *v, ptrdiff_t nbytes)
   ptrdiff_t vindex = VINDEX (nbytes);
   eassert (vindex < VECTOR_MAX_FREE_LIST_INDEX);
   set_next_vector (v, vector_free_lists[vindex]);
+  ASAN_POISON_VECTOR_CONTENTS (v, nbytes - header_size);
   vector_free_lists[vindex] = v;
 }
 
@@ -3039,6 +3362,7 @@ allocate_vector_from_block (ptrdiff_t nbytes)
   if (vector_free_lists[index])
     {
       vector = vector_free_lists[index];
+      ASAN_UNPOISON_VECTOR_CONTENTS (vector, nbytes - header_size);
       vector_free_lists[index] = next_vector (vector);
       return vector;
     }
@@ -3052,12 +3376,19 @@ allocate_vector_from_block (ptrdiff_t nbytes)
       {
        /* This vector is larger than requested.  */
        vector = vector_free_lists[index];
+       ASAN_UNPOISON_VECTOR_CONTENTS (vector, nbytes - header_size);
        vector_free_lists[index] = next_vector (vector);
 
        /* Excess bytes are used for the smaller vector,
           which should be set on an appropriate free list.  */
        restbytes = index * roundup_size + VBLOCK_BYTES_MIN - nbytes;
        eassert (restbytes % roundup_size == 0);
+#if GC_ASAN_POISON_OBJECTS
+       /* Ensure that accessing excess bytes does not trigger ASan.
+        */
+       __asan_unpoison_memory_region (ADVANCE (vector, nbytes),
+                                      restbytes);
+#endif
        setup_on_free_list (ADVANCE (vector, nbytes), restbytes);
        return vector;
       }
@@ -3233,6 +3564,7 @@ sweep_vectors (void)
       for (vector = (struct Lisp_Vector *) block->data;
           VECTOR_IN_BLOCK (vector, block); vector = next)
        {
+         ASAN_UNPOISON_VECTOR_BLOCK (block);
          if (XVECTOR_MARKED_P (vector))
            {
              XUNMARK_VECTOR (vector);
@@ -3608,6 +3940,47 @@ struct symbol_block
   struct symbol_block *next;
 };
 
+static void
+ASAN_POISON_SYMBOL_BLOCK (const volatile struct symbol_block *s)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (s->symbols, sizeof (s->symbols));
+#else
+  (void) (s);
+#endif
+}
+
+static void
+ASAN_UNPOISON_SYMBOL_BLOCK (const volatile struct symbol_block *s)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (s->symbols, sizeof (s->symbols));
+#else
+  (void) (s);
+#endif
+}
+
+static void
+ASAN_POISON_SYMBOL (const volatile struct Lisp_Symbol *sym)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_poison_memory_region (sym, sizeof (*sym));
+#else
+  (void) (sym);
+#endif
+}
+
+static void
+ASAN_UNPOISON_SYMBOL (const volatile struct Lisp_Symbol *sym)
+{
+#if GC_ASAN_POISON_OBJECTS
+  __asan_unpoison_memory_region (sym, sizeof (*sym));
+#else
+  (void) (sym);
+#endif
+
+}
+
 /* Current symbol block and index of first unused Lisp_Symbol
    structure in it.  */
 
@@ -3661,6 +4034,7 @@ Its value is void, and its function definition and property list are nil.  */)
 
   if (symbol_free_list)
     {
+      ASAN_UNPOISON_SYMBOL (symbol_free_list);
       XSETSYMBOL (val, symbol_free_list);
       symbol_free_list = symbol_free_list->u.s.next;
     }
@@ -3670,10 +4044,13 @@ Its value is void, and its function definition and property list are nil.  */)
        {
          struct symbol_block *new
            = lisp_malloc (sizeof *new, false, MEM_TYPE_SYMBOL);
+         ASAN_POISON_SYMBOL_BLOCK (new);
          new->next = symbol_block;
          symbol_block = new;
          symbol_block_index = 0;
        }
+
+      ASAN_UNPOISON_SYMBOL (&symbol_block->symbols[symbol_block_index]);
       XSETSYMBOL (val, &symbol_block->symbols[symbol_block_index]);
       symbol_block_index++;
     }
@@ -7179,11 +7556,13 @@ sweep_conses (void)
                  struct Lisp_Cons *acons = &cblk->conses[pos];
                  if (!XCONS_MARKED_P (acons))
                     {
+                     ASAN_UNPOISON_CONS (&cblk->conses[pos]);
                       this_free++;
                       cblk->conses[pos].u.s.u.chain = cons_free_list;
                       cons_free_list = &cblk->conses[pos];
                       cons_free_list->u.s.car = dead_object ();
-                    }
+                     ASAN_POISON_CONS (&cblk->conses[pos]);
+                   }
                   else
                     {
                       num_used++;
@@ -7201,6 +7580,7 @@ sweep_conses (void)
         {
           *cprev = cblk->next;
           /* Unhook from the free list.  */
+         ASAN_UNPOISON_CONS (&cblk->conses[0]);
           cons_free_list = cblk->conses[0].u.s.u.chain;
           lisp_align_free (cblk);
         }
@@ -7227,6 +7607,7 @@ sweep_floats (void)
   for (struct float_block *fblk; (fblk = *fprev); )
     {
       int this_free = 0;
+      ASAN_UNPOISON_FLOAT_BLOCK (fblk);
       for (int i = 0; i < lim; i++)
        {
          struct Lisp_Float *afloat = &fblk->floats[i];
@@ -7234,6 +7615,7 @@ sweep_floats (void)
            {
              this_free++;
              fblk->floats[i].u.chain = float_free_list;
+             ASAN_POISON_FLOAT (&fblk->floats[i]);
              float_free_list = &fblk->floats[i];
            }
          else
@@ -7250,7 +7632,8 @@ sweep_floats (void)
         {
           *fprev = fblk->next;
           /* Unhook from the free list.  */
-          float_free_list = fblk->floats[0].u.chain;
+         ASAN_UNPOISON_FLOAT (&fblk->floats[0]);
+         float_free_list = fblk->floats[0].u.chain;
           lisp_align_free (fblk);
         }
       else
@@ -7276,13 +7659,14 @@ sweep_intervals (void)
   for (struct interval_block *iblk; (iblk = *iprev); )
     {
       int this_free = 0;
-
+      ASAN_UNPOISON_INTERVAL_BLOCK (iblk);
       for (int i = 0; i < lim; i++)
         {
           if (!iblk->intervals[i].gcmarkbit)
             {
               set_interval_parent (&iblk->intervals[i], interval_free_list);
               interval_free_list = &iblk->intervals[i];
+             ASAN_POISON_INTERVAL (&iblk->intervals[i]);
               this_free++;
             }
           else
@@ -7299,6 +7683,7 @@ sweep_intervals (void)
         {
           *iprev = iblk->next;
           /* Unhook from the free list.  */
+         ASAN_UNPOISON_INTERVAL (&iblk->intervals[0]);
           interval_free_list = INTERVAL_PARENT (&iblk->intervals[0]);
           lisp_free (iblk);
         }
@@ -7328,6 +7713,8 @@ sweep_symbols (void)
 
   for (sblk = symbol_block; sblk; sblk = *sprev)
     {
+      ASAN_UNPOISON_SYMBOL_BLOCK (sblk);
+
       int this_free = 0;
       struct Lisp_Symbol *sym = sblk->symbols;
       struct Lisp_Symbol *end = sym + lim;
@@ -7349,7 +7736,8 @@ sweep_symbols (void)
               sym->u.s.next = symbol_free_list;
               symbol_free_list = sym;
               symbol_free_list->u.s.function = dead_object ();
-              ++this_free;
+             ASAN_POISON_SYMBOL (sym);
+             ++this_free;
             }
           else
             {
@@ -7368,6 +7756,7 @@ sweep_symbols (void)
         {
           *sprev = sblk->next;
           /* Unhook from the free list.  */
+         ASAN_UNPOISON_SYMBOL (&sblk->symbols[0]);
           symbol_free_list = sblk->symbols[0].u.s.next;
           lisp_free (sblk);
         }