Commit 881abfc7 authored by Paul Eggert's avatar Paul Eggert

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.
parent 244346c7
......@@ -31,6 +31,13 @@ functions 'json-serialize', 'json-insert', 'json-parse-string', and
'json-parse-buffer' are typically much faster than their Lisp
counterparts from json.el.
** 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
......
......@@ -33,6 +33,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#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
......
......@@ -40,6 +40,8 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
# 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);
......
......@@ -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;
}
......
/* 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 <https://www.gnu.org/licenses/>. */
#ifndef PTR_BOUNDS_H
#define PTR_BOUNDS_H
#include <stddef.h>
/* 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 */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment