forked from Github/emacs
Compare commits
11 commits
master
...
feature/as
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8043ba186d | ||
|
|
b9495c3713 | ||
|
|
7f8aa9db63 | ||
|
|
aa9ca2c149 | ||
|
|
772650fb9f | ||
|
|
f254029f65 | ||
|
|
5b12778507 | ||
|
|
5fa567d79c | ||
|
|
af296b19ef | ||
|
|
1b4a112c46 | ||
|
|
5c653d3ec9 |
5 changed files with 315 additions and 11 deletions
|
|
@ -1841,7 +1841,9 @@ 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
|
||||
sanitizer/common_interface_defs.h])
|
||||
|
||||
AC_CACHE_CHECK([for ADDR_NO_RANDOMIZE],
|
||||
[emacs_cv_personality_addr_no_randomize],
|
||||
|
|
|
|||
22
etc/DEBUG
22
etc/DEBUG
|
|
@ -1002,6 +1002,28 @@ Address sanitization is incompatible with undefined-behavior
|
|||
sanitization, unfortunately. Address sanitization is also
|
||||
incompatible with the --with-dumping=unexec option of 'configure'.
|
||||
|
||||
*** Address poisoning/unpoisoning
|
||||
|
||||
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.
|
||||
|
||||
While using GDB, memory addresses can be inspected by using helper
|
||||
functions additionally provided by the ASan library:
|
||||
|
||||
(gdb) call __asan_describe_address(ptr)
|
||||
|
||||
To check whether an address range is poisoned or not, use:
|
||||
|
||||
(gdb) call __asan_region_is_poisoned(ptr, 8)
|
||||
|
||||
Additional functions can be found in the header
|
||||
'sanitizer/asan_interface.h' in your compiler's headers directory.
|
||||
|
||||
** Running Emacs under Valgrind
|
||||
|
||||
Valgrind <https://valgrind.org/> is free software that can be useful
|
||||
|
|
|
|||
271
src/alloc.c
271
src/alloc.c
|
|
@ -80,6 +80,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 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 +1188,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 +1276,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 +1289,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 +1321,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 +1334,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 +1468,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 +1498,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 +1509,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 +1755,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 +1908,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 +1975,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 +1997,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 +2011,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 +2115,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 +2161,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 +2171,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 +2299,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 +2651,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 +2709,24 @@ struct float_block
|
|||
#define XFLOAT_UNMARK(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. */
|
||||
|
||||
static struct float_block *float_block;
|
||||
|
|
@ -2606,6 +2751,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 +2762,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 +2838,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 +2861,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 +2874,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 +2885,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 +3145,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
|
||||
|
|
@ -2992,6 +3170,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 +3218,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 +3232,18 @@ 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 +3419,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 +3795,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. */
|
||||
|
||||
|
|
@ -3661,6 +3865,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 +3875,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++;
|
||||
}
|
||||
|
|
@ -4561,6 +4769,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];
|
||||
|
|
@ -4577,6 +4790,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;
|
||||
}
|
||||
|
|
@ -4598,6 +4815,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];
|
||||
|
|
@ -4615,6 +4837,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;
|
||||
}
|
||||
|
|
@ -4637,6 +4863,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];
|
||||
|
|
@ -4662,6 +4892,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;
|
||||
}
|
||||
|
|
@ -4684,6 +4918,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];
|
||||
|
|
@ -4698,8 +4937,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;
|
||||
|
|
@ -7180,11 +7423,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++;
|
||||
|
|
@ -7202,6 +7447,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);
|
||||
}
|
||||
|
|
@ -7228,6 +7474,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];
|
||||
|
|
@ -7235,6 +7482,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
|
||||
|
|
@ -7251,7 +7499,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
|
||||
|
|
@ -7277,13 +7526,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
|
||||
|
|
@ -7300,6 +7550,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);
|
||||
}
|
||||
|
|
@ -7329,6 +7580,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;
|
||||
|
|
@ -7350,7 +7603,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
|
||||
{
|
||||
|
|
@ -7369,6 +7623,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -497,8 +497,13 @@ Symbols are also allowed; their print names are used instead. */)
|
|||
int ws = sizeof (word_t);
|
||||
const word_t *w1 = (const word_t *) SDATA (string1);
|
||||
const word_t *w2 = (const word_t *) SDATA (string2);
|
||||
while (b < nb - ws + 1 && w1[b / ws] == w2[b / ws])
|
||||
b += ws;
|
||||
while (b < nb - ws + 1)
|
||||
{
|
||||
if (UNALIGNED_LOAD_SIZE (w1, b / ws)
|
||||
!= UNALIGNED_LOAD_SIZE (w2, b / ws))
|
||||
break;
|
||||
b += ws;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan forward to the differing byte. */
|
||||
|
|
|
|||
20
src/lisp.h
20
src/lisp.h
|
|
@ -5296,6 +5296,26 @@ __lsan_ignore_object (void const *p)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* If built with USE_SANITIZER_UNALIGNED_LOAD defined, use compiler
|
||||
provided ASan functions to perform unaligned loads, allowing ASan
|
||||
to catch bugs which it might otherwise miss. */
|
||||
#if defined HAVE_SANITIZER_COMMON_INTERFACE_DEFS_H \
|
||||
&& defined ADDRESS_SANITIZER \
|
||||
&& defined USE_SANITIZER_UNALIGNED_LOAD
|
||||
# include <sanitizer/common_interface_defs.h>
|
||||
# if (SIZE_MAX == UINT64_MAX)
|
||||
# define UNALIGNED_LOAD_SIZE(a, i) \
|
||||
(size_t) __sanitizer_unaligned_load64 ((void *) ((a) + (i)))
|
||||
# elif (SIZE_MAX == UINT32_MAX)
|
||||
# define UNALIGNED_LOAD_SIZE(a, i) \
|
||||
(size_t) __sanitizer_unaligned_load32 ((void *) ((a) + (i)))
|
||||
# else
|
||||
# define UNALIGNED_LOAD_SIZE(a, i) *((a) + (i))
|
||||
# endif
|
||||
#else
|
||||
# define UNALIGNED_LOAD_SIZE(a, i) *((a) + (i))
|
||||
#endif
|
||||
|
||||
extern void xputenv (const char *);
|
||||
|
||||
extern char *egetenv_internal (const char *, ptrdiff_t);
|
||||
|
|
|
|||
Loading…
Reference in a new issue