From d37f26b20efc89024c65161ee74b82ad90589a40 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 6 Dec 2017 22:29:33 -0800 Subject: [PATCH 2/3] Port to gcc -fcheck-pointer-bounds This is a minimal port, just to get Emacs running; it does not attempt to make the pointer bounds at all tight. * src/ptr-bounds.h: New file. * src/alloc.c, src/gmalloc.c: Include it. * src/alloc.c (live_string_holding, live_cons_holding) (live_symbol_holding, live_misc_holding, garbage_collect_1) (sweep_conses, sweep_floats): * src/gmalloc.c (malloc_initialize_1, _free_internal_nolock) (_realloc_internal_nolock): Widen pointer bounds as necessary. We're in a memory allocator so this is OK. * src/lisp.h (lisp_h_XSYMBOL, make_lisp_symbol) [__CHKP__]: Do not convert from pointer to integer and back again, so that GCC does not lose track of pointer bounds. (XSYMBOL) [__CHKP__ && !USE_LSB_TAG]: Now a compile-time error. Although it's possible to support both -fcheck-pointer-bounds and --with-wide-int, it's more work; keep things simple for now. (DEFINE_LISP_SYMBOL) [__CHKP__]: Now a no-op, to avoid trouble with unbounded pointers. --- etc/NEWS | 7 +++++++ src/alloc.c | 37 ++++++++++++++++++++++++------------- src/gmalloc.c | 6 +++++- src/lisp.h | 34 +++++++++++++++++++++++++++------- src/ptr-bounds.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 src/ptr-bounds.h diff --git a/etc/NEWS b/etc/NEWS index cbd50f0227..e032055312 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -24,6 +24,13 @@ When you add a new item, use the appropriate mark if you are sure it applies, * Installation Changes in Emacs 27.1 +** Emacs has been ported to the -fcheck-pointer-bounds option of GCC. +This causes Emacs to check bounds of some arrays addressed by its +internal pointers, which can be helpful when debugging the Emacs +interpreter or modules that it uses. If your platform supports it you +can enable it when configuring, e.g., './configure CFLAGS="-g3 -O2 +-mmpx -fcheck-pointer-bounds"' on Intel MPX platforms. + * Startup Changes in Emacs 27.1 diff --git a/src/alloc.c b/src/alloc.c index 38daee065a..96b9aaa0d2 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -33,6 +33,7 @@ along with GNU Emacs. If not, see . */ #include "lisp.h" #include "dispextern.h" #include "intervals.h" +#include "ptr-bounds.h" #include "puresize.h" #include "sheap.h" #include "systime.h" @@ -4564,6 +4565,7 @@ live_string_holding (struct mem_node *m, void *p) must not be on the free-list. */ if (0 <= offset && offset < STRING_BLOCK_SIZE * sizeof b->strings[0]) { + cp = ptr_bounds_copy (cp, b); struct Lisp_String *s = p = cp -= offset % sizeof b->strings[0]; if (s->u.s.data) return make_lisp_ptr (s, Lisp_String); @@ -4598,6 +4600,7 @@ live_cons_holding (struct mem_node *m, void *p) && (b != cons_block || offset / sizeof b->conses[0] < cons_block_index)) { + cp = ptr_bounds_copy (cp, b); struct Lisp_Cons *s = p = cp -= offset % sizeof b->conses[0]; if (!EQ (s->u.s.car, Vdead)) return make_lisp_ptr (s, Lisp_Cons); @@ -4633,6 +4636,7 @@ live_symbol_holding (struct mem_node *m, void *p) && (b != symbol_block || offset / sizeof b->symbols[0] < symbol_block_index)) { + cp = ptr_bounds_copy (cp, b); struct Lisp_Symbol *s = p = cp -= offset % sizeof b->symbols[0]; if (!EQ (s->u.s.function, Vdead)) return make_lisp_symbol (s); @@ -4692,6 +4696,7 @@ live_misc_holding (struct mem_node *m, void *p) && (b != marker_block || offset / sizeof b->markers[0] < marker_block_index)) { + cp = ptr_bounds_copy (cp, b); union Lisp_Misc *s = p = cp -= offset % sizeof b->markers[0]; if (s->u_any.type != Lisp_Misc_Free) return make_lisp_ptr (s, Lisp_Misc); @@ -5955,6 +5960,7 @@ garbage_collect_1 (void *end) stack_copy = xrealloc (stack_copy, stack_size); stack_copy_size = stack_size; } + stack = ptr_bounds_set (stack, stack_size); no_sanitize_memcpy (stack_copy, stack, stack_size); } } @@ -6848,7 +6854,9 @@ sweep_conses (void) for (pos = start; pos < stop; pos++) { - if (!CONS_MARKED_P (&cblk->conses[pos])) + struct Lisp_Cons *acons + = ptr_bounds_copy (&cblk->conses[pos], cblk); + if (!CONS_MARKED_P (acons)) { this_free++; cblk->conses[pos].u.s.u.chain = cons_free_list; @@ -6858,7 +6866,7 @@ sweep_conses (void) else { num_used++; - CONS_UNMARK (&cblk->conses[pos]); + CONS_UNMARK (acons); } } } @@ -6901,17 +6909,20 @@ sweep_floats (void) register int i; int this_free = 0; for (i = 0; i < lim; i++) - if (!FLOAT_MARKED_P (&fblk->floats[i])) - { - this_free++; - fblk->floats[i].u.chain = float_free_list; - float_free_list = &fblk->floats[i]; - } - else - { - num_used++; - FLOAT_UNMARK (&fblk->floats[i]); - } + { + struct Lisp_Float *afloat = ptr_bounds_copy (&fblk->floats[i], fblk); + if (!FLOAT_MARKED_P (afloat)) + { + this_free++; + fblk->floats[i].u.chain = float_free_list; + float_free_list = &fblk->floats[i]; + } + else + { + num_used++; + FLOAT_UNMARK (afloat); + } + } lim = FLOAT_BLOCK_SIZE; /* If this block contains only free floats and we have already seen more than two blocks worth of free floats then deallocate diff --git a/src/gmalloc.c b/src/gmalloc.c index a17d39c1ee..97ab76561f 100644 --- a/src/gmalloc.c +++ b/src/gmalloc.c @@ -40,6 +40,8 @@ License along with this library. If not, see . # include "lisp.h" #endif +#include "ptr-bounds.h" + #ifdef HAVE_MALLOC_H # if GNUC_PREREQ (4, 2, 0) # pragma GCC diagnostic ignored "-Wdeprecated-declarations" @@ -558,7 +560,7 @@ malloc_initialize_1 (void) _heapinfo[0].free.size = 0; _heapinfo[0].free.next = _heapinfo[0].free.prev = 0; _heapindex = 0; - _heapbase = (char *) _heapinfo; + _heapbase = (char *) ptr_bounds_init (_heapinfo); _heaplimit = BLOCK (_heapbase + heapsize * sizeof (malloc_info)); register_heapinfo (); @@ -997,6 +999,7 @@ _free_internal_nolock (void *ptr) if (ptr == NULL) return; + ptr = ptr_bounds_init (ptr); PROTECT_MALLOC_STATE (0); @@ -1308,6 +1311,7 @@ _realloc_internal_nolock (void *ptr, size_t size) else if (ptr == NULL) return _malloc_internal_nolock (size); + ptr = ptr_bounds_init (ptr); block = BLOCK (ptr); PROTECT_MALLOC_STATE (0); diff --git a/src/lisp.h b/src/lisp.h index d2050ec0e7..38a098da82 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -379,10 +379,18 @@ typedef EMACS_INT Lisp_Word; XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0)) # define lisp_h_XFASTINT(a) XINT (a) # define lisp_h_XINT(a) (XLI (a) >> INTTYPEBITS) -# define lisp_h_XSYMBOL(a) \ +# ifdef __CHKP__ +# define lisp_h_XSYMBOL(a) \ + (eassert (SYMBOLP (a)), \ + (struct Lisp_Symbol *) ((char *) XUNTAG (a, Lisp_Symbol) \ + + (intptr_t) lispsym)) +# else + /* If !__CHKP__ this is equivalent, and is a bit faster as of GCC 7. */ +# define lisp_h_XSYMBOL(a) \ (eassert (SYMBOLP (a)), \ (struct Lisp_Symbol *) ((intptr_t) XLI (a) - Lisp_Symbol \ + (char *) lispsym)) +# endif # define lisp_h_XTYPE(a) ((enum Lisp_Type) (XLI (a) & ~VALMASK)) # define lisp_h_XUNTAG(a, type) \ __builtin_assume_aligned ((char *) XLP (a) - (type), GCALIGNMENT) @@ -826,10 +834,15 @@ typedef EMACS_UINT Lisp_Word_tag; /* Declare extern constants for Lisp symbols. These can be helpful when using a debugger like GDB, on older platforms where the debug - format does not represent C macros. */ -#define DEFINE_LISP_SYMBOL(name) \ - DEFINE_GDB_SYMBOL_BEGIN (Lisp_Object, name) \ - DEFINE_GDB_SYMBOL_END (LISPSYM_INITIALLY (name)) + format does not represent C macros. However, they are unbounded + and would just be asking for trouble if checking pointer bounds. */ +#ifdef __CHKP__ +# define DEFINE_LISP_SYMBOL(name) +#else +# define DEFINE_LISP_SYMBOL(name) \ + DEFINE_GDB_SYMBOL_BEGIN (Lisp_Object, name) \ + DEFINE_GDB_SYMBOL_END (LISPSYM_INITIALLY (name)) +#endif /* The index of the C-defined Lisp symbol SYM. This can be used in a static initializer. */ @@ -889,6 +902,8 @@ INLINE struct Lisp_Symbol * { #if USE_LSB_TAG return lisp_h_XSYMBOL (a); +#elif defined __CHKP__ +# error "pointer-checking not supported with wide integers" #else eassert (SYMBOLP (a)); intptr_t i = (intptr_t) XUNTAG (a, Lisp_Symbol); @@ -900,8 +915,13 @@ INLINE struct Lisp_Symbol * INLINE Lisp_Object make_lisp_symbol (struct Lisp_Symbol *sym) { - intptr_t symoffset = (char *) sym - (char *) lispsym; - Lisp_Object a = TAG_PTR (Lisp_Symbol, (char *) symoffset); +#ifdef __CHKP__ + char *symoffset = (char *) sym - (intptr_t) lispsym; +#else + /* If !__CHKP__ this is equivalent, and is a bit faster as of GCC 7. */ + char *symoffset = (char *) ((char *) sym - (char *) lispsym); +#endif + Lisp_Object a = TAG_PTR (Lisp_Symbol, symoffset); eassert (XSYMBOL (a) == sym); return a; } diff --git a/src/ptr-bounds.h b/src/ptr-bounds.h new file mode 100644 index 0000000000..54979824c0 --- /dev/null +++ b/src/ptr-bounds.h @@ -0,0 +1,52 @@ +/* Pointer bounds checking for GNU Emacs + +Copyright 2017 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +#ifndef PTR_BOUNDS_H +#define PTR_BOUNDS_H + +#include + +/* When not checking pointer bounds, the following macros simply + return their first argument. These macros return either void *, or + the same type as their first argument. */ + +/* Return a copy of P, but with the bounds of Q. */ +#ifdef __CHKP__ +# define ptr_bounds_copy(p, q) __builtin___bnd_copy_ptr_bounds (p, q) +#else +# define ptr_bounds_copy(p, q) ((void) (void const *) {q}, p) +#endif + +/* Return a copy of P, but with infinite bounds. + This is a loophole in pointer bounds checking. */ +#ifdef __CHKP__ +# define ptr_bounds_init(p) __builtin___bnd_init_ptr_bounds (p) +#else +# define ptr_bounds_init(p) (p) +#endif + +/* Return a copy of P, but with bounds [P, P + N). + This is a loophole in pointer bounds checking. */ +#ifdef __CHKP__ +# define ptr_bounds_set(p, n) __builtin___bnd_set_ptr_bounds (p, n) +#else +# define ptr_bounds_set(p, n) ((void) (size_t) {n}, p) +#endif + +#endif /* PTR_BOUNDS_H */ -- 2.14.1