summaryrefslogtreecommitdiff
path: root/src/alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/alloc.c')
-rw-r--r--src/alloc.c945
1 files changed, 658 insertions, 287 deletions
diff --git a/src/alloc.c b/src/alloc.c
index b02f13e911f..2ffd2415447 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -34,7 +34,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "dispextern.h"
#include "intervals.h"
#include "puresize.h"
-#include "sheap.h"
#include "sysstdio.h"
#include "systime.h"
#include "character.h"
@@ -50,6 +49,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
#ifdef HAVE_TREE_SITTER
#include "treesit.h"
#endif
@@ -80,6 +83,37 @@ 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 with 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. */
@@ -325,8 +359,16 @@ static struct gcstat
object_ct total_floats, total_free_floats;
object_ct total_intervals, total_free_intervals;
object_ct total_buffers;
+
+ /* Size of the ancillary arrays of live hash-table and obarray objects.
+ The objects themselves are not included (counted as vectors above). */
+ byte_ct total_hash_table_bytes;
} gcstat;
+/* Total size of ancillary arrays of all allocated hash-table and obarray
+ objects, both dead and alive. This number is always kept up-to-date. */
+static ptrdiff_t hash_table_allocated_bytes = 0;
+
/* Points to memory space allocated as "spare", to be freed if we run
out of memory. We keep one large block, four cons-blocks, and
two string blocks. */
@@ -378,31 +420,6 @@ static EMACS_INT gc_threshold;
const char *pending_malloc_warning;
-/* Pointer sanity only on request. FIXME: Code depending on
- SUSPICIOUS_OBJECT_CHECKING is obsolete; remove it entirely. */
-#ifdef ENABLE_CHECKING
-#define SUSPICIOUS_OBJECT_CHECKING 1
-#endif
-
-#ifdef SUSPICIOUS_OBJECT_CHECKING
-struct suspicious_free_record
-{
- void *suspicious_object;
- void *backtrace[128];
-};
-static void *suspicious_objects[32];
-static int suspicious_object_index;
-struct suspicious_free_record suspicious_free_history[64] EXTERNALLY_VISIBLE;
-static int suspicious_free_history_index;
-/* Find the first currently-monitored suspicious pointer in range
- [begin,end) or NULL if no such pointer exists. */
-static void *find_suspicious_object_in_range (void *begin, void *end);
-static void detect_suspicious_free (void *ptr);
-#else
-# define find_suspicious_object_in_range(begin, end) ((void *) NULL)
-# define detect_suspicious_free(ptr) ((void) 0)
-#endif
-
/* Maximum amount of C stack to save when a GC happens. */
#ifndef MAX_SAVE_STACK
@@ -832,7 +849,7 @@ xnmalloc (ptrdiff_t nitems, ptrdiff_t item_size)
{
eassert (0 <= nitems && 0 < item_size);
ptrdiff_t nbytes;
- if (INT_MULTIPLY_WRAPV (nitems, item_size, &nbytes) || SIZE_MAX < nbytes)
+ if (ckd_mul (&nbytes, nitems, item_size) || SIZE_MAX < nbytes)
memory_full (SIZE_MAX);
return xmalloc (nbytes);
}
@@ -846,7 +863,7 @@ xnrealloc (void *pa, ptrdiff_t nitems, ptrdiff_t item_size)
{
eassert (0 <= nitems && 0 < item_size);
ptrdiff_t nbytes;
- if (INT_MULTIPLY_WRAPV (nitems, item_size, &nbytes) || SIZE_MAX < nbytes)
+ if (ckd_mul (&nbytes, nitems, item_size) || SIZE_MAX < nbytes)
memory_full (SIZE_MAX);
return xrealloc (pa, nbytes);
}
@@ -893,13 +910,13 @@ xpalloc (void *pa, ptrdiff_t *nitems, ptrdiff_t nitems_incr_min,
NITEMS_MAX, and what the C language can represent safely. */
ptrdiff_t n, nbytes;
- if (INT_ADD_WRAPV (n0, n0 >> 1, &n))
+ if (ckd_add (&n, n0, n0 >> 1))
n = PTRDIFF_MAX;
if (0 <= nitems_max && nitems_max < n)
n = nitems_max;
ptrdiff_t adjusted_nbytes
- = ((INT_MULTIPLY_WRAPV (n, item_size, &nbytes) || SIZE_MAX < nbytes)
+ = ((ckd_mul (&nbytes, n, item_size) || SIZE_MAX < nbytes)
? min (PTRDIFF_MAX, SIZE_MAX)
: nbytes < DEFAULT_MXFAST ? DEFAULT_MXFAST : 0);
if (adjusted_nbytes)
@@ -911,9 +928,9 @@ xpalloc (void *pa, ptrdiff_t *nitems, ptrdiff_t nitems_incr_min,
if (! pa)
*nitems = 0;
if (n - n0 < nitems_incr_min
- && (INT_ADD_WRAPV (n0, nitems_incr_min, &n)
+ && (ckd_add (&n, n0, nitems_incr_min)
|| (0 <= nitems_max && nitems_max < n)
- || INT_MULTIPLY_WRAPV (n, item_size, &nbytes)))
+ || ckd_mul (&nbytes, n, item_size)))
memory_full (SIZE_MAX);
pa = xrealloc (pa, nbytes);
*nitems = n;
@@ -1052,7 +1069,11 @@ lisp_free (void *block)
BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */
/* Byte alignment of storage blocks. */
-#define BLOCK_ALIGN (1 << 10)
+#ifdef HAVE_UNEXEC
+# define BLOCK_ALIGN (1 << 10)
+#else /* !HAVE_UNEXEC */
+# define BLOCK_ALIGN (1 << 15)
+#endif
verify (POWER_OF_2 (BLOCK_ALIGN));
/* Use aligned_alloc if it or a simple substitute is available.
@@ -1157,6 +1178,16 @@ struct ablocks
(1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void **) (abase))[-1])
#endif
+#if GC_ASAN_POISON_OBJECTS
+# define ASAN_POISON_ABLOCK(b) \
+ __asan_poison_memory_region (&(b)->x, sizeof ((b)->x))
+# define ASAN_UNPOISON_ABLOCK(b) \
+ __asan_unpoison_memory_region (&(b)->x, sizeof ((b)->x))
+#else
+# define ASAN_POISON_ABLOCK(b) ((void) 0)
+# define ASAN_UNPOISON_ABLOCK(b) ((void) 0)
+#endif
+
/* The list of free ablock. */
static struct ablock *free_ablock;
@@ -1235,6 +1266,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 +1279,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 +1311,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 +1324,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 +1458,24 @@ static int interval_block_index = INTERVAL_BLOCK_SIZE;
static INTERVAL interval_free_list;
+#if GC_ASAN_POISON_OBJECTS
+# define ASAN_POISON_INTERVAL_BLOCK(b) \
+ __asan_poison_memory_region ((b)->intervals, \
+ sizeof ((b)->intervals))
+# define ASAN_UNPOISON_INTERVAL_BLOCK(b) \
+ __asan_unpoison_memory_region ((b)->intervals, \
+ sizeof ((b)->intervals))
+# define ASAN_POISON_INTERVAL(i) \
+ __asan_poison_memory_region (i, sizeof *(i))
+# define ASAN_UNPOISON_INTERVAL(i) \
+ __asan_unpoison_memory_region (i, sizeof *(i))
+#else
+# define ASAN_POISON_INTERVAL_BLOCK(b) ((void) 0)
+# define ASAN_UNPOISON_INTERVAL_BLOCK(b) ((void) 0)
+# define ASAN_POISON_INTERVAL(i) ((void) 0)
+# define ASAN_UNPOISON_INTERVAL(i) ((void) 0)
+#endif
+
/* Return a new interval. */
INTERVAL
@@ -1433,6 +1488,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 +1499,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 +1745,41 @@ init_strings (void)
staticpro (&empty_multibyte_string);
}
+#if GC_ASAN_POISON_OBJECTS
+/* 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).
+ */
+# define ASAN_PREPARE_DEAD_SDATA(s, size) \
+ do { \
+ __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)); \
+ } while (false)
+/* Prepare s for storing string data for NBYTES bytes. */
+# define ASAN_PREPARE_LIVE_SDATA(s, nbytes) \
+ __asan_unpoison_memory_region (s, sdata_size (nbytes))
+# define ASAN_POISON_SBLOCK_DATA(b, size) \
+ __asan_poison_memory_region ((b)->data, size)
+# define ASAN_POISON_STRING_BLOCK(b) \
+ __asan_poison_memory_region ((b)->strings, STRING_BLOCK_SIZE)
+# define ASAN_UNPOISON_STRING_BLOCK(b) \
+ __asan_unpoison_memory_region ((b)->strings, STRING_BLOCK_SIZE)
+# define ASAN_POISON_STRING(s) \
+ __asan_poison_memory_region (s, sizeof *(s))
+# define ASAN_UNPOISON_STRING(s) \
+ __asan_unpoison_memory_region (s, sizeof *(s))
+#else
+# define ASAN_PREPARE_DEAD_SDATA(s, size) ((void) 0)
+# define ASAN_PREPARE_LIVE_SDATA(s, nbytes) ((void) 0)
+# define ASAN_POISON_SBLOCK_DATA(b, size) ((void) 0)
+# define ASAN_POISON_STRING_BLOCK(b) ((void) 0)
+# define ASAN_UNPOISON_STRING_BLOCK(b) ((void) 0)
+# define ASAN_POISON_STRING(s) ((void) 0)
+# define ASAN_UNPOISON_STRING(s) ((void) 0)
+#endif
#ifdef GC_CHECK_STRING_BYTES
@@ -1805,12 +1898,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 +1965,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 +1987,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 +2001,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 +2105,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 +2151,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 +2161,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 +2289,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);
}
@@ -2245,7 +2361,7 @@ a multibyte string even if INIT is an ASCII character. */)
ptrdiff_t len = CHAR_STRING (c, str);
EMACS_INT string_len = XFIXNUM (length);
- if (INT_MULTIPLY_WRAPV (len, string_len, &nbytes))
+ if (ckd_mul (&nbytes, len, string_len))
string_overflow ();
val = make_clear_multibyte_string (string_len, nbytes, clearit);
if (!clearit)
@@ -2525,6 +2641,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;
}
@@ -2574,13 +2691,31 @@ struct float_block
};
#define XFLOAT_MARKED_P(fptr) \
- GETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX ((fptr)))
+ GETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX (fptr))
#define XFLOAT_MARK(fptr) \
- SETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX ((fptr)))
+ SETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX (fptr))
#define XFLOAT_UNMARK(fptr) \
- UNSETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX ((fptr)))
+ UNSETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX (fptr))
+
+#if GC_ASAN_POISON_OBJECTS
+# define ASAN_POISON_FLOAT_BLOCK(fblk) \
+ __asan_poison_memory_region ((fblk)->floats, \
+ sizeof ((fblk)->floats))
+# define ASAN_UNPOISON_FLOAT_BLOCK(fblk) \
+ __asan_unpoison_memory_region ((fblk)->floats, \
+ sizeof ((fblk)->floats))
+# define ASAN_POISON_FLOAT(p) \
+ __asan_poison_memory_region (p, sizeof (struct Lisp_Float))
+# define ASAN_UNPOISON_FLOAT(p) \
+ __asan_unpoison_memory_region (p, sizeof (struct Lisp_Float))
+#else
+# define ASAN_POISON_FLOAT_BLOCK(fblk) ((void) 0)
+# define ASAN_UNPOISON_FLOAT_BLOCK(fblk) ((void) 0)
+# define ASAN_POISON_FLOAT(p) ((void) 0)
+# define ASAN_UNPOISON_FLOAT(p) ((void) 0)
+#endif
/* Current float_block. */
@@ -2606,6 +2741,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 +2752,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++;
}
@@ -2665,13 +2803,13 @@ struct cons_block
};
#define XCONS_MARKED_P(fptr) \
- GETMARKBIT (CONS_BLOCK (fptr), CONS_INDEX ((fptr)))
+ GETMARKBIT (CONS_BLOCK (fptr), CONS_INDEX (fptr))
#define XMARK_CONS(fptr) \
- SETMARKBIT (CONS_BLOCK (fptr), CONS_INDEX ((fptr)))
+ SETMARKBIT (CONS_BLOCK (fptr), CONS_INDEX (fptr))
#define XUNMARK_CONS(fptr) \
- UNSETMARKBIT (CONS_BLOCK (fptr), CONS_INDEX ((fptr)))
+ UNSETMARKBIT (CONS_BLOCK (fptr), CONS_INDEX (fptr))
/* Minimum number of bytes of consing since GC before next GC,
when memory is full. */
@@ -2690,6 +2828,19 @@ static int cons_block_index = CONS_BLOCK_SIZE;
static struct Lisp_Cons *cons_free_list;
+#if GC_ASAN_POISON_OBJECTS
+# define ASAN_POISON_CONS_BLOCK(b) \
+ __asan_poison_memory_region ((b)->conses, sizeof ((b)->conses))
+# define ASAN_POISON_CONS(p) \
+ __asan_poison_memory_region (p, sizeof (struct Lisp_Cons))
+# define ASAN_UNPOISON_CONS(p) \
+ __asan_unpoison_memory_region (p, sizeof (struct Lisp_Cons))
+#else
+# define ASAN_POISON_CONS_BLOCK(b) ((void) 0)
+# define ASAN_POISON_CONS(p) ((void) 0)
+# define ASAN_UNPOISON_CONS(p) ((void) 0)
+#endif
+
/* Explicitly free a cons cell by putting it on the free-list. */
void
@@ -2700,6 +2851,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 +2864,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 +2875,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++;
}
@@ -2881,9 +3036,8 @@ enum { VECTOR_BLOCK_SIZE = 4096 };
/* Vector size requests are a multiple of this. */
enum { roundup_size = COMMON_MULTIPLE (LISP_ALIGNMENT, word_size) };
-/* Verify assumptions described above. */
+/* Verify assumption described above. */
verify (VECTOR_BLOCK_SIZE % roundup_size == 0);
-verify (VECTOR_BLOCK_SIZE <= (1 << PSEUDOVECTOR_SIZE_BITS));
/* Round up X to nearest mult-of-ROUNDUP_SIZE --- use at compile time. */
#define vroundup_ct(x) ROUNDUP (x, roundup_size)
@@ -2894,6 +3048,11 @@ verify (VECTOR_BLOCK_SIZE <= (1 << PSEUDOVECTOR_SIZE_BITS));
enum {VECTOR_BLOCK_BYTES = VECTOR_BLOCK_SIZE - vroundup_ct (sizeof (void *))};
+/* The current code expects to be able to represent an unused block by
+ a single PVEC_FREE object, whose size is limited by the header word.
+ (Of course we could use multiple such objects.) */
+verify (VECTOR_BLOCK_BYTES <= (word_size << PSEUDOVECTOR_REST_BITS));
+
/* Size of the minimal vector allocated from block. */
enum { VBLOCK_BYTES_MIN = vroundup_ct (header_size + sizeof (Lisp_Object)) };
@@ -2903,10 +3062,10 @@ enum { VBLOCK_BYTES_MIN = vroundup_ct (header_size + sizeof (Lisp_Object)) };
enum { VBLOCK_BYTES_MAX = vroundup_ct ((VECTOR_BLOCK_BYTES / 2) - word_size) };
/* We maintain one free list for each possible block-allocated
- vector size, and this is the number of free lists we have. */
-
-enum { VECTOR_MAX_FREE_LIST_INDEX =
- (VECTOR_BLOCK_BYTES - VBLOCK_BYTES_MIN) / roundup_size + 1 };
+ vector size, one for blocks one word bigger,
+ and one for all free vectors larger than that. */
+enum { VECTOR_FREE_LIST_ARRAY_SIZE =
+ (VBLOCK_BYTES_MAX - VBLOCK_BYTES_MIN) / roundup_size + 1 + 2 };
/* Common shortcut to advance vector pointer over a block data. */
@@ -2968,9 +3127,20 @@ struct vector_block
static struct vector_block *vector_blocks;
/* Vector free lists, where NTH item points to a chain of free
- vectors of the same NBYTES size, so NTH == VINDEX (NBYTES). */
+ vectors of the same NBYTES size, so NTH == VINDEX (NBYTES),
+ except for the last element which may contain larger vectors.
+
+ I.e., for each vector V in vector_free_lists[I] the following holds:
+ - V has type PVEC_FREE
+ - V's total size in bytes, BS(V) = PVSIZE(V) * word_size + header_size
+ - For I < VECTOR_FREE_LIST_ARRAY_SIZE-1, VINDEX(BS(V)) = I
+ - For I = VECTOR_FREE_LIST_ARRAY_SIZE-1, VINDEX(BS(V)) ≥ I */
+static struct Lisp_Vector *vector_free_lists[VECTOR_FREE_LIST_ARRAY_SIZE];
-static struct Lisp_Vector *vector_free_lists[VECTOR_MAX_FREE_LIST_INDEX];
+/* Index to the bucket in vector_free_lists into which we last inserted
+ or split a free vector. We use this as a heuristic telling us where
+ to start looking for free vectors when the exact-size bucket is empty. */
+static ptrdiff_t last_inserted_vector_free_idx = VECTOR_FREE_LIST_ARRAY_SIZE;
/* Singly-linked list of large vectors. */
@@ -2980,6 +3150,19 @@ static struct large_vector *large_vectors;
Lisp_Object zero_vector;
+#if GC_ASAN_POISON_OBJECTS
+# define ASAN_POISON_VECTOR_CONTENTS(v, bytes) \
+ __asan_poison_memory_region ((v)->contents, bytes)
+# define ASAN_UNPOISON_VECTOR_CONTENTS(v, bytes) \
+ __asan_unpoison_memory_region ((v)->contents, bytes)
+# define ASAN_UNPOISON_VECTOR_BLOCK(b) \
+ __asan_unpoison_memory_region ((b)->data, sizeof (b)->data)
+#else
+# define ASAN_POISON_VECTOR_CONTENTS(v, bytes) ((void) 0)
+# define ASAN_UNPOISON_VECTOR_CONTENTS(v, bytes) ((void) 0)
+# define ASAN_UNPOISON_VECTOR_BLOCK(b) ((void) 0)
+#endif
+
/* Common shortcut to setup vector on a free list. */
static void
@@ -2990,9 +3173,12 @@ setup_on_free_list (struct Lisp_Vector *v, ptrdiff_t nbytes)
XSETPVECTYPESIZE (v, PVEC_FREE, 0, nwords);
eassert (nbytes % roundup_size == 0);
ptrdiff_t vindex = VINDEX (nbytes);
- eassert (vindex < VECTOR_MAX_FREE_LIST_INDEX);
+ /* Anything too large goes into the last slot (overflow bin). */
+ vindex = min(vindex, VECTOR_FREE_LIST_ARRAY_SIZE - 1);
set_next_vector (v, vector_free_lists[vindex]);
+ ASAN_POISON_VECTOR_CONTENTS (v, nbytes - header_size);
vector_free_lists[vindex] = v;
+ last_inserted_vector_free_idx = vindex;
}
/* Get a new vector block. */
@@ -3021,6 +3207,17 @@ init_vectors (void)
staticpro (&zero_vector);
}
+/* Memory footprint in bytes of a pseudovector other than a bool-vector. */
+static ptrdiff_t
+pseudovector_nbytes (const union vectorlike_header *hdr)
+{
+ eassert (!PSEUDOVECTOR_TYPEP (hdr, PVEC_BOOL_VECTOR));
+ ptrdiff_t nwords = ((hdr->size & PSEUDOVECTOR_SIZE_MASK)
+ + ((hdr->size & PSEUDOVECTOR_REST_MASK)
+ >> PSEUDOVECTOR_SIZE_BITS));
+ return vroundup (header_size + word_size * nwords);
+}
+
/* Allocate vector from a vector block. */
static struct Lisp_Vector *
@@ -3039,6 +3236,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;
}
@@ -3046,18 +3244,27 @@ allocate_vector_from_block (ptrdiff_t nbytes)
/* Next, check free lists containing larger vectors. Since
we will split the result, we should have remaining space
large enough to use for one-slot vector at least. */
- for (index = VINDEX (nbytes + VBLOCK_BYTES_MIN);
- index < VECTOR_MAX_FREE_LIST_INDEX; index++)
+ for (index = max (VINDEX (nbytes + VBLOCK_BYTES_MIN),
+ last_inserted_vector_free_idx);
+ index < VECTOR_FREE_LIST_ARRAY_SIZE; index++)
if (vector_free_lists[index])
{
/* This vector is larger than requested. */
vector = vector_free_lists[index];
+ size_t vector_nbytes = pseudovector_nbytes (&vector->header);
+ eassert (vector_nbytes > nbytes);
+ 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;
+ restbytes = vector_nbytes - 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;
}
@@ -3105,9 +3312,7 @@ vectorlike_nbytes (const union vectorlike_header *hdr)
nwords = (boolvec_bytes - header_size + word_size - 1) / word_size;
}
else
- nwords = ((size & PSEUDOVECTOR_SIZE_MASK)
- + ((size & PSEUDOVECTOR_REST_MASK)
- >> PSEUDOVECTOR_SIZE_BITS));
+ return pseudovector_nbytes (hdr);
}
else
nwords = size;
@@ -3129,85 +3334,159 @@ vectorlike_nbytes (const union vectorlike_header *hdr)
static void
cleanup_vector (struct Lisp_Vector *vector)
{
- detect_suspicious_free (vector);
-
- if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_BIGNUM))
- mpz_clear (PSEUDOVEC_STRUCT (vector, Lisp_Bignum)->value);
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_OVERLAY))
- {
- struct Lisp_Overlay *ol = PSEUDOVEC_STRUCT (vector, Lisp_Overlay);
- xfree (ol->interval);
- }
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_FINALIZER))
- unchain_finalizer (PSEUDOVEC_STRUCT (vector, Lisp_Finalizer));
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_FONT))
+ if ((vector->header.size & PSEUDOVECTOR_FLAG) == 0)
+ return; /* nothing more to do for plain vectors */
+ switch (PSEUDOVECTOR_TYPE (vector))
{
- if ((vector->header.size & PSEUDOVECTOR_SIZE_MASK) == FONT_OBJECT_MAX)
- {
- struct font *font = PSEUDOVEC_STRUCT (vector, font);
- struct font_driver const *drv = font->driver;
+ case PVEC_BIGNUM:
+ mpz_clear (PSEUDOVEC_STRUCT (vector, Lisp_Bignum)->value);
+ break;
+ case PVEC_OVERLAY:
+ {
+ struct Lisp_Overlay *ol = PSEUDOVEC_STRUCT (vector, Lisp_Overlay);
+ xfree (ol->interval);
+ }
+ break;
+ case PVEC_FINALIZER:
+ unchain_finalizer (PSEUDOVEC_STRUCT (vector, Lisp_Finalizer));
+ break;
+ case PVEC_FONT:
+ {
+ if ((vector->header.size & PSEUDOVECTOR_SIZE_MASK) == FONT_OBJECT_MAX)
+ {
+ struct font *font = PSEUDOVEC_STRUCT (vector, font);
+ struct font_driver const *drv = font->driver;
- /* The font driver might sometimes be NULL, e.g. if Emacs was
- interrupted before it had time to set it up. */
- if (drv)
- {
- /* Attempt to catch subtle bugs like Bug#16140. */
- eassert (valid_font_driver (drv));
- drv->close_font (font);
- }
- }
- }
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_THREAD))
- finalize_one_thread (PSEUDOVEC_STRUCT (vector, thread_state));
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_MUTEX))
- finalize_one_mutex (PSEUDOVEC_STRUCT (vector, Lisp_Mutex));
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_CONDVAR))
- finalize_one_condvar (PSEUDOVEC_STRUCT (vector, Lisp_CondVar));
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_MARKER))
- {
+ /* The font driver might sometimes be NULL, e.g. if Emacs was
+ interrupted before it had time to set it up. */
+ if (drv)
+ {
+ /* Attempt to catch subtle bugs like Bug#16140. */
+ eassert (valid_font_driver (drv));
+ drv->close_font (font);
+ }
+ }
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* The Android font driver needs the ability to associate extra
+ information with font entities. */
+ if (((vector->header.size & PSEUDOVECTOR_SIZE_MASK)
+ == FONT_ENTITY_MAX)
+ && PSEUDOVEC_STRUCT (vector, font_entity)->is_android)
+ android_finalize_font_entity (PSEUDOVEC_STRUCT (vector, font_entity));
+#endif
+ }
+ break;
+ case PVEC_THREAD:
+ finalize_one_thread (PSEUDOVEC_STRUCT (vector, thread_state));
+ break;
+ case PVEC_MUTEX:
+ finalize_one_mutex (PSEUDOVEC_STRUCT (vector, Lisp_Mutex));
+ break;
+ case PVEC_CONDVAR:
+ finalize_one_condvar (PSEUDOVEC_STRUCT (vector, Lisp_CondVar));
+ break;
+ case PVEC_MARKER:
/* sweep_buffer should already have unchained this from its buffer. */
eassert (! PSEUDOVEC_STRUCT (vector, Lisp_Marker)->buffer);
- }
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_USER_PTR))
- {
- struct Lisp_User_Ptr *uptr = PSEUDOVEC_STRUCT (vector, Lisp_User_Ptr);
- if (uptr->finalizer)
- uptr->finalizer (uptr->p);
- }
+ break;
+ case PVEC_USER_PTR:
+ {
+ struct Lisp_User_Ptr *uptr = PSEUDOVEC_STRUCT (vector, Lisp_User_Ptr);
+ if (uptr->finalizer)
+ uptr->finalizer (uptr->p);
+ }
+ break;
+ case PVEC_TS_PARSER:
+#ifdef HAVE_TREE_SITTER
+ treesit_delete_parser (PSEUDOVEC_STRUCT (vector, Lisp_TS_Parser));
+#endif
+ break;
+ case PVEC_TS_COMPILED_QUERY:
#ifdef HAVE_TREE_SITTER
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_TS_PARSER))
- treesit_delete_parser (PSEUDOVEC_STRUCT (vector, Lisp_TS_Parser));
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_TS_COMPILED_QUERY))
- treesit_delete_query (PSEUDOVEC_STRUCT (vector, Lisp_TS_Query));
+ treesit_delete_query (PSEUDOVEC_STRUCT (vector, Lisp_TS_Query));
#endif
+ break;
+ case PVEC_MODULE_FUNCTION:
#ifdef HAVE_MODULES
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_MODULE_FUNCTION))
- {
- ATTRIBUTE_MAY_ALIAS struct Lisp_Module_Function *function
- = (struct Lisp_Module_Function *) vector;
- module_finalize_function (function);
- }
+ {
+ ATTRIBUTE_MAY_ALIAS struct Lisp_Module_Function *function
+ = (struct Lisp_Module_Function *) vector;
+ module_finalize_function (function);
+ }
#endif
+ break;
+ case PVEC_NATIVE_COMP_UNIT:
#ifdef HAVE_NATIVE_COMP
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_NATIVE_COMP_UNIT))
- {
- struct Lisp_Native_Comp_Unit *cu =
- PSEUDOVEC_STRUCT (vector, Lisp_Native_Comp_Unit);
- unload_comp_unit (cu);
- }
- else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_SUBR))
- {
- struct Lisp_Subr *subr =
- PSEUDOVEC_STRUCT (vector, Lisp_Subr);
- if (!NILP (subr->native_comp_u))
- {
- /* FIXME Alternative and non invasive solution to this
- cast? */
- xfree ((char *)subr->symbol_name);
- xfree (subr->native_c_name);
- }
- }
+ {
+ struct Lisp_Native_Comp_Unit *cu =
+ PSEUDOVEC_STRUCT (vector, Lisp_Native_Comp_Unit);
+ unload_comp_unit (cu);
+ }
+#endif
+ break;
+ case PVEC_SUBR:
+#ifdef HAVE_NATIVE_COMP
+ {
+ struct Lisp_Subr *subr = PSEUDOVEC_STRUCT (vector, Lisp_Subr);
+ if (!NILP (subr->native_comp_u))
+ {
+ /* FIXME Alternative and non invasive solution to this cast? */
+ xfree ((char *)subr->symbol_name);
+ xfree (subr->native_c_name);
+ }
+ }
#endif
+ break;
+ case PVEC_HASH_TABLE:
+ {
+ struct Lisp_Hash_Table *h = PSEUDOVEC_STRUCT (vector, Lisp_Hash_Table);
+ if (h->table_size > 0)
+ {
+ eassert (h->index_bits > 0);
+ xfree (h->index);
+ xfree (h->key_and_value);
+ xfree (h->next);
+ xfree (h->hash);
+ ptrdiff_t bytes = (h->table_size * (2 * sizeof *h->key_and_value
+ + sizeof *h->hash
+ + sizeof *h->next)
+ + hash_table_index_size (h) * sizeof *h->index);
+ hash_table_allocated_bytes -= bytes;
+ }
+ }
+ break;
+ case PVEC_OBARRAY:
+ {
+ struct Lisp_Obarray *o = PSEUDOVEC_STRUCT (vector, Lisp_Obarray);
+ xfree (o->buckets);
+ ptrdiff_t bytes = obarray_size (o) * sizeof *o->buckets;
+ hash_table_allocated_bytes -= bytes;
+ }
+ break;
+ /* Keep the switch exhaustive. */
+ case PVEC_NORMAL_VECTOR:
+ case PVEC_FREE:
+ case PVEC_SYMBOL_WITH_POS:
+ case PVEC_MISC_PTR:
+ case PVEC_PROCESS:
+ case PVEC_FRAME:
+ case PVEC_WINDOW:
+ case PVEC_BOOL_VECTOR:
+ case PVEC_BUFFER:
+ case PVEC_TERMINAL:
+ case PVEC_WINDOW_CONFIGURATION:
+ case PVEC_OTHER:
+ case PVEC_XWIDGET:
+ case PVEC_XWIDGET_VIEW:
+ case PVEC_TS_NODE:
+ case PVEC_SQLITE:
+ case PVEC_COMPILED:
+ case PVEC_CHAR_TABLE:
+ case PVEC_SUB_CHAR_TABLE:
+ case PVEC_RECORD:
+ break;
+ }
}
/* Reclaim space used by unmarked vectors. */
@@ -3223,6 +3502,7 @@ sweep_vectors (void)
gcstat.total_vectors = 0;
gcstat.total_vector_slots = gcstat.total_free_vector_slots = 0;
memset (vector_free_lists, 0, sizeof (vector_free_lists));
+ last_inserted_vector_free_idx = VECTOR_FREE_LIST_ARRAY_SIZE;
/* Looking through vector blocks. */
@@ -3233,6 +3513,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);
@@ -3306,6 +3587,8 @@ sweep_vectors (void)
lisp_free (lv);
}
}
+
+ gcstat.total_hash_table_bytes = hash_table_allocated_bytes;
}
/* Maximum number of elements in a vector. This is a macro so that it
@@ -3355,9 +3638,6 @@ allocate_vectorlike (ptrdiff_t len, bool clearit)
mallopt (M_MMAP_MAX, MMAP_MAX_AREAS);
#endif
- if (find_suspicious_object_in_range (p, (char *) p + nbytes))
- emacs_abort ();
-
tally_consing (nbytes);
vector_cells_consed += len;
@@ -3409,7 +3689,7 @@ allocate_pseudovector (int memlen, int lisplen,
enum { size_max = (1 << PSEUDOVECTOR_SIZE_BITS) - 1 };
enum { rest_max = (1 << PSEUDOVECTOR_REST_BITS) - 1 };
verify (size_max + rest_max <= VECTOR_ELTS_MAX);
- eassert (0 <= tag && tag <= PVEC_FONT);
+ eassert (0 <= tag && tag <= PVEC_TAG_MAX);
eassert (0 <= lisplen && lisplen <= zerolen && zerolen <= memlen);
eassert (lisplen <= size_max);
eassert (memlen <= size_max + rest_max);
@@ -3609,6 +3889,23 @@ struct symbol_block
struct symbol_block *next;
};
+#if GC_ASAN_POISON_OBJECTS
+# define ASAN_POISON_SYMBOL_BLOCK(s) \
+ __asan_poison_memory_region ((s)->symbols, sizeof ((s)->symbols))
+# define ASAN_UNPOISON_SYMBOL_BLOCK(s) \
+ __asan_unpoison_memory_region ((s)->symbols, sizeof ((s)->symbols))
+# define ASAN_POISON_SYMBOL(sym) \
+ __asan_poison_memory_region (sym, sizeof *(sym))
+# define ASAN_UNPOISON_SYMBOL(sym) \
+ __asan_unpoison_memory_region (sym, sizeof *(sym))
+
+#else
+# define ASAN_POISON_SYMBOL_BLOCK(s) ((void) 0)
+# define ASAN_UNPOISON_SYMBOL_BLOCK(s) ((void) 0)
+# define ASAN_POISON_SYMBOL(sym) ((void) 0)
+# define ASAN_UNPOISON_SYMBOL(sym) ((void) 0)
+#endif
+
/* Current symbol block and index of first unused Lisp_Symbol
structure in it. */
@@ -3662,6 +3959,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;
}
@@ -3671,10 +3969,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++;
}
@@ -4562,6 +4863,11 @@ static struct Lisp_String *
live_string_holding (struct mem_node *m, void *p)
{
eassert (m->type == MEM_TYPE_STRING);
+#if GC_ASAN_POISON_OBJECTS
+ if (__asan_address_is_poisoned (p))
+ return NULL;
+#endif
+
struct string_block *b = m->start;
char *cp = p;
ptrdiff_t offset = cp - (char *) &b->strings[0];
@@ -4578,6 +4884,10 @@ live_string_holding (struct mem_node *m, void *p)
|| off == offsetof (struct Lisp_String, u.s.data))
{
struct Lisp_String *s = p = cp -= off;
+#if GC_ASAN_POISON_OBJECTS
+ if (__asan_region_is_poisoned (s, sizeof (*s)))
+ return NULL;
+#endif
if (s->u.s.data)
return s;
}
@@ -4599,6 +4909,11 @@ static struct Lisp_Cons *
live_cons_holding (struct mem_node *m, void *p)
{
eassert (m->type == MEM_TYPE_CONS);
+#if GC_ASAN_POISON_OBJECTS
+ if (__asan_address_is_poisoned (p))
+ return NULL;
+#endif
+
struct cons_block *b = m->start;
char *cp = p;
ptrdiff_t offset = cp - (char *) &b->conses[0];
@@ -4616,6 +4931,10 @@ live_cons_holding (struct mem_node *m, void *p)
|| off == offsetof (struct Lisp_Cons, u.s.u.cdr))
{
struct Lisp_Cons *s = p = cp -= off;
+#if GC_ASAN_POISON_OBJECTS
+ if (__asan_region_is_poisoned (s, sizeof (*s)))
+ return NULL;
+#endif
if (!deadp (s->u.s.car))
return s;
}
@@ -4638,6 +4957,10 @@ static struct Lisp_Symbol *
live_symbol_holding (struct mem_node *m, void *p)
{
eassert (m->type == MEM_TYPE_SYMBOL);
+#if GC_ASAN_POISON_OBJECTS
+ if (__asan_address_is_poisoned (p))
+ return NULL;
+#endif
struct symbol_block *b = m->start;
char *cp = p;
ptrdiff_t offset = cp - (char *) &b->symbols[0];
@@ -4663,6 +4986,10 @@ live_symbol_holding (struct mem_node *m, void *p)
|| off == offsetof (struct Lisp_Symbol, u.s.next))
{
struct Lisp_Symbol *s = p = cp -= off;
+#if GC_ASAN_POISON_OBJECTS
+ if (__asan_region_is_poisoned (s, sizeof (*s)))
+ return NULL;
+#endif
if (!deadp (s->u.s.function))
return s;
}
@@ -4685,6 +5012,11 @@ static struct Lisp_Float *
live_float_holding (struct mem_node *m, void *p)
{
eassert (m->type == MEM_TYPE_FLOAT);
+#if GC_ASAN_POISON_OBJECTS
+ if (__asan_address_is_poisoned (p))
+ return NULL;
+#endif
+
struct float_block *b = m->start;
char *cp = p;
ptrdiff_t offset = cp - (char *) &b->floats[0];
@@ -4699,8 +5031,12 @@ live_float_holding (struct mem_node *m, void *p)
&& (b != float_block
|| offset / sizeof b->floats[0] < float_block_index))
{
- p = cp - off;
- return p;
+ struct Lisp_Float *f = (struct Lisp_Float *) (cp - off);
+#if GC_ASAN_POISON_OBJECTS
+ if (__asan_region_is_poisoned (f, sizeof (*f)))
+ return NULL;
+#endif
+ return f;
}
}
return NULL;
@@ -4979,7 +5315,7 @@ mark_memory (void const *start, void const *end)
a Lisp_Object might be split into registers saved into
non-adjacent words and P might be the low-order word's value. */
intptr_t ip;
- INT_ADD_WRAPV ((intptr_t) p, (intptr_t) lispsym, &ip);
+ ckd_add (&ip, (intptr_t) p, (intptr_t) lispsym);
mark_maybe_pointer ((void *) ip, true);
}
}
@@ -5091,15 +5427,6 @@ typedef union
#endif
} stacktop_sentry;
-/* Yield an address close enough to the top of the stack that the
- garbage collector need not scan above it. Callers should be
- declared NO_INLINE. */
-#ifdef HAVE___BUILTIN_FRAME_ADDRESS
-# define NEAR_STACK_TOP(addr) ((void) (addr), __builtin_frame_address (0))
-#else
-# define NEAR_STACK_TOP(addr) (addr)
-#endif
-
/* Set *P to the address of the top of the stack. This must be a
macro, not a function, so that it is executed in the caller's
environment. It is not inside a do-while so that its storage
@@ -5314,6 +5641,29 @@ valid_lisp_object_p (Lisp_Object obj)
return 0;
}
+/* Like xmalloc, but makes allocation count toward the total consing
+ and hash table or obarray usage.
+ Return NULL for a zero-sized allocation. */
+void *
+hash_table_alloc_bytes (ptrdiff_t nbytes)
+{
+ if (nbytes == 0)
+ return NULL;
+ tally_consing (nbytes);
+ hash_table_allocated_bytes += nbytes;
+ return xmalloc (nbytes);
+}
+
+/* Like xfree, but makes allocation count toward the total consing. */
+void
+hash_table_free_bytes (void *p, ptrdiff_t nbytes)
+{
+ tally_consing (-nbytes);
+ hash_table_allocated_bytes -= nbytes;
+ xfree (p);
+}
+
+
/***********************************************************************
Pure Storage Management
***********************************************************************/
@@ -5407,6 +5757,22 @@ find_string_data_in_pure (const char *data, ptrdiff_t nbytes)
if (pure_bytes_used_non_lisp <= nbytes)
return NULL;
+ /* The Android GCC generates code like:
+
+ 0xa539e755 <+52>: lea 0x430(%esp),%esi
+=> 0xa539e75c <+59>: movdqa %xmm0,0x0(%ebp)
+ 0xa539e761 <+64>: add $0x10,%ebp
+
+ but data is not aligned appropriately, so a GP fault results. */
+
+#if defined __i386__ \
+ && defined HAVE_ANDROID \
+ && !defined ANDROID_STUBIFY \
+ && !defined (__clang__)
+ if ((intptr_t) data & 15)
+ return NULL;
+#endif
+
/* Set up the Boyer-Moore table. */
skip = nbytes + 1;
for (i = 0; i < 256; i++)
@@ -5579,30 +5945,35 @@ make_pure_vector (ptrdiff_t len)
static struct Lisp_Hash_Table *
purecopy_hash_table (struct Lisp_Hash_Table *table)
{
- eassert (NILP (table->weak));
+ eassert (table->weakness == Weak_None);
eassert (table->purecopy);
struct Lisp_Hash_Table *pure = pure_alloc (sizeof *pure, Lisp_Vectorlike);
- struct hash_table_test pure_test = table->test;
-
- /* Purecopy the hash table test. */
- pure_test.name = purecopy (table->test.name);
- pure_test.user_hash_function = purecopy (table->test.user_hash_function);
- pure_test.user_cmp_function = purecopy (table->test.user_cmp_function);
-
- pure->header = table->header;
- pure->weak = purecopy (Qnil);
- pure->hash = purecopy (table->hash);
- pure->next = purecopy (table->next);
- pure->index = purecopy (table->index);
- pure->count = table->count;
- pure->next_free = table->next_free;
- pure->purecopy = table->purecopy;
- eassert (!pure->mutable);
- pure->rehash_threshold = table->rehash_threshold;
- pure->rehash_size = table->rehash_size;
- pure->key_and_value = purecopy (table->key_and_value);
- pure->test = pure_test;
+ *pure = *table;
+ pure->mutable = false;
+
+ if (table->table_size > 0)
+ {
+ ptrdiff_t hash_bytes = table->table_size * sizeof *table->hash;
+ pure->hash = pure_alloc (hash_bytes, -(int)sizeof *table->hash);
+ memcpy (pure->hash, table->hash, hash_bytes);
+
+ ptrdiff_t next_bytes = table->table_size * sizeof *table->next;
+ pure->next = pure_alloc (next_bytes, -(int)sizeof *table->next);
+ memcpy (pure->next, table->next, next_bytes);
+
+ ptrdiff_t nvalues = table->table_size * 2;
+ ptrdiff_t kv_bytes = nvalues * sizeof *table->key_and_value;
+ pure->key_and_value = pure_alloc (kv_bytes,
+ -(int)sizeof *table->key_and_value);
+ for (ptrdiff_t i = 0; i < nvalues; i++)
+ pure->key_and_value[i] = purecopy (table->key_and_value[i]);
+
+ ptrdiff_t index_bytes = hash_table_index_size (table)
+ * sizeof *table->index;
+ pure->index = pure_alloc (index_bytes, -(int)sizeof *table->index);
+ memcpy (pure->index, table->index, index_bytes);
+ }
return pure;
}
@@ -5662,7 +6033,7 @@ purecopy (Lisp_Object obj)
/* Do not purify hash tables which haven't been defined with
:purecopy as non-nil or are weak - they aren't guaranteed to
not change. */
- if (!NILP (table->weak) || !table->purecopy)
+ if (table->weakness != Weak_None || !table->purecopy)
{
/* Instead, add the hash table to the list of pinned objects,
so that it will be marked during GC. */
@@ -5673,8 +6044,7 @@ purecopy (Lisp_Object obj)
return obj; /* Don't hash cons it. */
}
- struct Lisp_Hash_Table *h = purecopy_hash_table (table);
- XSET_HASH_TABLE (obj, h);
+ obj = make_lisp_hash_table (purecopy_hash_table (table));
}
else if (COMPILEDP (obj) || VECTORP (obj) || RECORDP (obj))
{
@@ -5786,6 +6156,7 @@ total_bytes_of_live_objects (void)
tot += object_bytes (gcstat.total_floats, sizeof (struct Lisp_Float));
tot += object_bytes (gcstat.total_intervals, sizeof (struct interval));
tot += object_bytes (gcstat.total_strings, sizeof (struct Lisp_String));
+ tot += gcstat.total_hash_table_bytes;
return tot;
}
@@ -5917,16 +6288,44 @@ mark_pinned_objects (void)
mark_object (pobj->object);
}
+#if defined HAVE_ANDROID && !defined (__clang__)
+
+/* The Android gcc is broken and needs the following version of
+ make_lisp_symbol. Otherwise a mysterious ICE pops up. */
+
+#define make_lisp_symbol android_make_lisp_symbol
+
+static Lisp_Object
+android_make_lisp_symbol (struct Lisp_Symbol *sym)
+{
+ intptr_t symoffset;
+
+ symoffset = (intptr_t) sym;
+ ckd_sub (&symoffset, symoffset, (intptr_t) &lispsym);
+
+ {
+ Lisp_Object a = TAG_PTR_INITIALLY (Lisp_Symbol, symoffset);
+ return a;
+ }
+}
+
+#endif
+
static void
mark_pinned_symbols (void)
{
struct symbol_block *sblk;
- int lim = (symbol_block_pinned == symbol_block
- ? symbol_block_index : SYMBOL_BLOCK_SIZE);
+ int lim;
+ struct Lisp_Symbol *sym, *end;
+
+ if (symbol_block_pinned == symbol_block)
+ lim = symbol_block_index;
+ else
+ lim = SYMBOL_BLOCK_SIZE;
for (sblk = symbol_block_pinned; sblk; sblk = sblk->next)
{
- struct Lisp_Symbol *sym = sblk->symbols, *end = sym + lim;
+ sym = sblk->symbols, end = sym + lim;
for (; sym < end; ++sym)
if (sym->u.s.pinned)
mark_object (make_lisp_symbol (sym));
@@ -6204,6 +6603,9 @@ garbage_collect (void)
mark_terminals ();
mark_kboards ();
mark_threads ();
+ mark_charset ();
+ mark_composite ();
+ mark_profiler ();
#ifdef HAVE_PGTK
mark_pgtkterm ();
#endif
@@ -6222,15 +6624,24 @@ garbage_collect (void)
#ifdef HAVE_X_WINDOWS
mark_xterm ();
+ mark_xselect ();
+#endif
+
+#ifdef HAVE_ANDROID
+ mark_androidterm ();
+#ifndef ANDROID_STUBIFY
+ mark_sfntfont ();
+#endif
#endif
#ifdef HAVE_NS
mark_nsterm ();
#endif
+ mark_fns ();
/* Everything is now marked, except for the data in font caches,
undo lists, and finalizers. The first two are compacted by
- removing an items which aren't reachable otherwise. */
+ removing any items which aren't reachable otherwise. */
compact_font_caches ();
@@ -6291,13 +6702,6 @@ garbage_collect (void)
image_prune_animation_caches (false);
#endif
- if (!NILP (Vpost_gc_hook))
- {
- specpdl_ref gc_count = inhibit_garbage_collection ();
- safe_run_hooks (Qpost_gc_hook);
- unbind_to (gc_count, Qnil);
- }
-
/* Accumulate statistics. */
if (FLOATP (Vgc_elapsed))
{
@@ -6316,6 +6720,13 @@ garbage_collect (void)
if (tot_after < tot_before)
malloc_probe (min (tot_before - tot_after, SIZE_MAX));
}
+
+ if (!NILP (Vpost_gc_hook))
+ {
+ specpdl_ref gc_count = inhibit_garbage_collection ();
+ safe_run_hooks (Qpost_gc_hook);
+ unbind_to (gc_count, Qnil);
+ }
}
DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "",
@@ -6555,7 +6966,7 @@ mark_buffer (struct buffer *buffer)
if (!BUFFER_LIVE_P (buffer))
mark_object (BVAR (buffer, undo_list));
- if (buffer->overlays)
+ if (!itree_empty_p (buffer->overlays))
mark_overlays (buffer->overlays->root);
/* If this is an indirect buffer, mark its base buffer. */
@@ -6587,20 +6998,6 @@ mark_face_cache (struct face_cache *c)
}
}
-NO_INLINE /* To reduce stack depth in mark_object. */
-static void
-mark_localized_symbol (struct Lisp_Symbol *ptr)
-{
- struct Lisp_Buffer_Local_Value *blv = SYMBOL_BLV (ptr);
- Lisp_Object where = blv->where;
- /* If the value is set up for a killed buffer restore its global binding. */
- if ((BUFFERP (where) && !BUFFER_LIVE_P (XBUFFER (where))))
- swap_in_global_binding (ptr);
- mark_object (blv->where);
- mark_object (blv->valcell);
- mark_object (blv->defcell);
-}
-
/* Remove killed buffers or items whose car is a killed buffer from
LIST, and mark other items. Return changed LIST, which is marked. */
@@ -6632,6 +7029,11 @@ static void
mark_frame (struct Lisp_Vector *ptr)
{
struct frame *f = (struct frame *) ptr;
+#ifdef HAVE_TEXT_CONVERSION
+ struct text_conversion_action *tem;
+#endif
+
+
mark_vectorlike (&ptr->header);
mark_face_cache (f->face_cache);
#ifdef HAVE_WINDOW_SYSTEM
@@ -6643,6 +7045,15 @@ mark_frame (struct Lisp_Vector *ptr)
mark_vectorlike (&font->header);
}
#endif
+
+#ifdef HAVE_TEXT_CONVERSION
+ mark_object (f->conversion.compose_region_start);
+ mark_object (f->conversion.compose_region_end);
+ mark_object (f->conversion.compose_region_overlay);
+
+ for (tem = f->conversion.actions; tem; tem = tem->next)
+ mark_object (tem->data);
+#endif
}
static void
@@ -6891,26 +7302,32 @@ process_mark_stack (ptrdiff_t base_sp)
case PVEC_HASH_TABLE:
{
struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *)ptr;
- ptrdiff_t size = ptr->header.size & PSEUDOVECTOR_SIZE_MASK;
set_vector_marked (ptr);
- mark_stack_push_values (ptr->contents, size);
- mark_stack_push_value (h->test.name);
- mark_stack_push_value (h->test.user_hash_function);
- mark_stack_push_value (h->test.user_cmp_function);
- if (NILP (h->weak))
- mark_stack_push_value (h->key_and_value);
+ if (h->weakness == Weak_None)
+ /* The values pushed here may include
+ HASH_UNUSED_ENTRY_KEY, which this function must
+ cope with. */
+ mark_stack_push_values (h->key_and_value,
+ 2 * h->table_size);
else
{
- /* For weak tables, mark only the vector and not its
+ /* For weak tables, don't mark the
contents --- that's what makes it weak. */
eassert (h->next_weak == NULL);
h->next_weak = weak_hash_tables;
weak_hash_tables = h;
- set_vector_marked (XVECTOR (h->key_and_value));
}
break;
}
+ case PVEC_OBARRAY:
+ {
+ struct Lisp_Obarray *o = (struct Lisp_Obarray *)ptr;
+ set_vector_marked (ptr);
+ mark_stack_push_values (o->buckets, obarray_size (o));
+ break;
+ }
+
case PVEC_CHAR_TABLE:
case PVEC_SUB_CHAR_TABLE:
mark_char_table (ptr, (enum pvec_type) pvectype);
@@ -6988,7 +7405,17 @@ process_mark_stack (ptrdiff_t base_sp)
break;
}
case SYMBOL_LOCALIZED:
- mark_localized_symbol (ptr);
+ {
+ struct Lisp_Buffer_Local_Value *blv = SYMBOL_BLV (ptr);
+ Lisp_Object where = blv->where;
+ /* If the value is set up for a killed buffer,
+ restore its global binding. */
+ if (BUFFERP (where) && !BUFFER_LIVE_P (XBUFFER (where)))
+ swap_in_global_binding (ptr);
+ mark_stack_push_value (blv->where);
+ mark_stack_push_value (blv->valcell);
+ mark_stack_push_value (blv->defcell);
+ }
break;
case SYMBOL_FORWARDED:
/* If the value is forwarded to a buffer or keyboard field,
@@ -7032,14 +7459,19 @@ process_mark_stack (ptrdiff_t base_sp)
}
case Lisp_Float:
- CHECK_ALLOCATED_AND_LIVE (live_float_p, MEM_TYPE_FLOAT);
- /* Do not mark floats stored in a dump image: these floats are
- "cold" and do not have mark bits. */
- if (pdumper_object_p (XFLOAT (obj)))
- eassert (pdumper_cold_object_p (XFLOAT (obj)));
- else if (!XFLOAT_MARKED_P (XFLOAT (obj)))
- XFLOAT_MARK (XFLOAT (obj));
- break;
+ {
+ struct Lisp_Float *f = XFLOAT (obj);
+ if (!f)
+ break; /* for HASH_UNUSED_ENTRY_KEY */
+ CHECK_ALLOCATED_AND_LIVE (live_float_p, MEM_TYPE_FLOAT);
+ /* Do not mark floats stored in a dump image: these floats are
+ "cold" and do not have mark bits. */
+ if (pdumper_object_p (f))
+ eassert (pdumper_cold_object_p (f));
+ else if (!XFLOAT_MARKED_P (f))
+ XFLOAT_MARK (f);
+ break;
+ }
case_Lisp_Int:
break;
@@ -7181,11 +7613,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++;
@@ -7203,6 +7637,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);
}
@@ -7229,6 +7664,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];
@@ -7236,6 +7672,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
@@ -7252,7 +7689,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
@@ -7278,13 +7716,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
@@ -7301,6 +7740,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);
}
@@ -7330,6 +7770,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;
@@ -7351,7 +7793,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
{
@@ -7370,6 +7813,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);
}
@@ -7623,78 +8067,6 @@ which_symbols (Lisp_Object obj, EMACS_INT find_max)
return unbind_to (gc_count, found);
}
-#ifdef SUSPICIOUS_OBJECT_CHECKING
-
-static void *
-find_suspicious_object_in_range (void *begin, void *end)
-{
- char *begin_a = begin;
- char *end_a = end;
- int i;
-
- for (i = 0; i < ARRAYELTS (suspicious_objects); ++i)
- {
- char *suspicious_object = suspicious_objects[i];
- if (begin_a <= suspicious_object && suspicious_object < end_a)
- return suspicious_object;
- }
-
- return NULL;
-}
-
-static void
-note_suspicious_free (void *ptr)
-{
- struct suspicious_free_record *rec;
-
- rec = &suspicious_free_history[suspicious_free_history_index++];
- if (suspicious_free_history_index ==
- ARRAYELTS (suspicious_free_history))
- {
- suspicious_free_history_index = 0;
- }
-
- memset (rec, 0, sizeof (*rec));
- rec->suspicious_object = ptr;
- backtrace (&rec->backtrace[0], ARRAYELTS (rec->backtrace));
-}
-
-static void
-detect_suspicious_free (void *ptr)
-{
- int i;
-
- eassert (ptr != NULL);
-
- for (i = 0; i < ARRAYELTS (suspicious_objects); ++i)
- if (suspicious_objects[i] == ptr)
- {
- note_suspicious_free (ptr);
- suspicious_objects[i] = NULL;
- }
-}
-
-#endif /* SUSPICIOUS_OBJECT_CHECKING */
-
-DEFUN ("suspicious-object", Fsuspicious_object, Ssuspicious_object, 1, 1, 0,
- doc: /* Return OBJ, maybe marking it for extra scrutiny.
-If Emacs is compiled with suspicious object checking, capture
-a stack trace when OBJ is freed in order to help track down
-garbage collection bugs. Otherwise, do nothing and return OBJ. */)
- (Lisp_Object obj)
-{
-#ifdef SUSPICIOUS_OBJECT_CHECKING
- /* Right now, we care only about vectors. */
- if (VECTORLIKEP (obj))
- {
- suspicious_objects[suspicious_object_index++] = XVECTOR (obj);
- if (suspicious_object_index == ARRAYELTS (suspicious_objects))
- suspicious_object_index = 0;
- }
-#endif
- return obj;
-}
-
#ifdef ENABLE_CHECKING
bool suppress_checking;
@@ -7926,7 +8298,6 @@ N should be nonnegative. */);
#ifdef HAVE_MALLOC_TRIM
defsubr (&Smalloc_trim);
#endif
- defsubr (&Ssuspicious_object);
Lisp_Object watcher;