From e76119d7542b19eb03f8d725480cbf98f7fa03d9 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 26 Dec 2013 00:57:28 -0800 Subject: [PATCH] Fix core dumps with gcc -fsanitize=address and GNU/Linux. On my Fedora 19 platform the core dumps were so big that my desktop became nearly catatonic. * configure.ac: Check whether addresses are sanitized. (CANNOT_DUMP): Warn if addresses are sanitized and not CANNOT_DUMP. (DOUG_LEA_MALLOC): Do not define if addresses are sanitized. (SYSTEM_MALLOC): Define if addresses are sanitized. * src/alloc.c (no_sanitize_memcpy) [MAX_SAVE_STACK > 0]: New function. (Fgarbage_collect) [MAX_SAVE_STACK > 0]: Use it. (USE_ALIGNED_MALLOC): Do not define if addresses are sanitized. (mark_memory): Use ATTRIBUTE_NO_SANITIZE_ADDRESS rather than a clang-only syntax. * src/conf_post.h (__has_feature): New macro, if not already defined. (ADDRESS_SANITIZER, ADDRESS_SANITIZER_WORKAROUND) (ATTRIBUTE_NO_SANITIZE_ADDRESS): New macros. --- ChangeLog | 8 ++++++++ configure.ac | 45 +++++++++++++++++++++++++++++------------- src/ChangeLog | 14 +++++++++++++ src/alloc.c | 52 +++++++++++++++++++++++++++++++++---------------- src/conf_post.h | 39 ++++++++++++++++++++++++++++++++++++- 5 files changed, 127 insertions(+), 31 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3630d570b28..ddf7d92ac14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2013-12-26 Paul Eggert + + Fix core dumps with gcc -fsanitize=address and GNU/Linux. + * configure.ac: Check whether addresses are sanitized. + (CANNOT_DUMP): Warn if addresses are sanitized and not CANNOT_DUMP. + (DOUG_LEA_MALLOC): Do not define if addresses are sanitized. + (SYSTEM_MALLOC): Define if addresses are sanitized. + 2013-12-24 Paul Eggert Automate the procedure for updating copyright year. diff --git a/configure.ac b/configure.ac index ea76baf8f8f..8aaf2c6a8ac 100644 --- a/configure.ac +++ b/configure.ac @@ -1048,6 +1048,21 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], LDFLAGS="$late_LDFLAGS" +AC_CACHE_CHECK([whether addresses are sanitized], + [emacs_cv_sanitize_address], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#ifndef __has_feature + #define __has_feature(f) 0 + #endif + #if defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer) + #else + #error "Addresses are not sanitized." + #endif + ]])], + [emacs_cv_sanitize_address=yes], + [emacs_cv_sanitize_address=no])]) + dnl The function dump-emacs will not be defined and temacs will do dnl (load "loadup") automatically unless told otherwise. test "x$CANNOT_DUMP" = "x" && CANNOT_DUMP=no @@ -1055,8 +1070,11 @@ case "$opsys" in your-opsys-here) CANNOT_DUMP=yes ;; esac -test "$CANNOT_DUMP" = "yes" && \ +if test "$CANNOT_DUMP" = "yes"; then AC_DEFINE(CANNOT_DUMP, 1, [Define if Emacs cannot be dumped on your system.]) +elif test "$emacs_cv_sanitize_address" = yes; then + AC_MSG_WARN([[Addresses are sanitized; suggest CANNOT_DUMP=yes]]) +fi AC_SUBST(CANNOT_DUMP) @@ -1888,20 +1906,21 @@ GNU_MALLOC=yes AC_CACHE_CHECK( [whether malloc is Doug Lea style], [emacs_cv_var_doug_lea_malloc], - [AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - static void hook (void) {}]], - [[malloc_set_state (malloc_get_state ()); - __after_morecore_hook = hook; - __malloc_initialize_hook = hook;]])], - [emacs_cv_var_doug_lea_malloc=yes], - [emacs_cv_var_doug_lea_malloc=no])]) + [emacs_cv_var_doug_lea_malloc=no + dnl Hooks do not work with address sanitization. + if test "$emacs_cv_sanitize_address" != yes; then + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include + static void hook (void) {}]], + [[malloc_set_state (malloc_get_state ()); + __after_morecore_hook = hook; + __malloc_initialize_hook = hook;]])], + [emacs_cv_var_doug_lea_malloc=yes])]) + fi doug_lea_malloc=$emacs_cv_var_doug_lea_malloc - -dnl See comments in aix4-2.h about maybe using system malloc there. -system_malloc=no +system_malloc=$emacs_cv_sanitize_address case "$opsys" in ## darwin ld insists on the use of malloc routines in the System framework. darwin|sol2-10) system_malloc=yes ;; diff --git a/src/ChangeLog b/src/ChangeLog index 1e8684c4ddb..70df9cd3641 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,17 @@ +2013-12-26 Paul Eggert + + Fix core dumps with gcc -fsanitize=address and GNU/Linux. + On my Fedora 19 platform the core dumps were so big that + my desktop became nearly catatonic. + * alloc.c (no_sanitize_memcpy) [MAX_SAVE_STACK > 0]: New function. + (Fgarbage_collect) [MAX_SAVE_STACK > 0]: Use it. + (USE_ALIGNED_MALLOC): Do not define if addresses are sanitized. + (mark_memory): Use ATTRIBUTE_NO_SANITIZE_ADDRESS rather than + a clang-only syntax. + * conf_post.h (__has_feature): New macro, if not already defined. + (ADDRESS_SANITIZER, ADDRESS_SANITIZER_WORKAROUND) + (ATTRIBUTE_NO_SANITIZE_ADDRESS): New macros. + 2013-12-25 Eli Zaretskii * w32fns.c (Fw32_shell_execute): Make DOCUMENT absolute only if it diff --git a/src/alloc.c b/src/alloc.c index 447b465a076..14c322a3a64 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -203,7 +203,27 @@ const char *pending_malloc_warning; #if MAX_SAVE_STACK > 0 static char *stack_copy; static ptrdiff_t stack_copy_size; -#endif + +/* Copy to DEST a block of memory from SRC of size SIZE bytes, + avoiding any address sanitization. */ + +static void * ATTRIBUTE_NO_SANITIZE_ADDRESS +no_sanitize_memcpy (void *dest, void const *src, size_t size) +{ + if (! ADDRESS_SANITIZER) + return memcpy (dest, src, size); + else + { + size_t i; + char *d = dest; + char const *s = src; + for (i = 0; i < size; i++) + d[i] = s[i]; + return dest; + } +} + +#endif /* MAX_SAVE_STACK > 0 */ static Lisp_Object Qconses; static Lisp_Object Qsymbols; @@ -920,20 +940,26 @@ lisp_free (void *block) /* The entry point is lisp_align_malloc which returns blocks of at most BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */ -#if !defined SYSTEM_MALLOC && !defined DOUG_LEA_MALLOC -# define USE_ALIGNED_ALLOC 1 +/* Use aligned_alloc if it or a simple substitute is available. + Address sanitization breaks aligned allocation, as of gcc 4.8.2 and + clang 3.3 anyway. */ + +#if ! ADDRESS_SANITIZER +# if !defined SYSTEM_MALLOC && !defined DOUG_LEA_MALLOC +# define USE_ALIGNED_ALLOC 1 /* Defined in gmalloc.c. */ void *aligned_alloc (size_t, size_t); -#elif defined HAVE_ALIGNED_ALLOC -# define USE_ALIGNED_ALLOC 1 -#elif defined HAVE_POSIX_MEMALIGN -# define USE_ALIGNED_ALLOC 1 +# elif defined HAVE_ALIGNED_ALLOC +# define USE_ALIGNED_ALLOC 1 +# elif defined HAVE_POSIX_MEMALIGN +# define USE_ALIGNED_ALLOC 1 static void * aligned_alloc (size_t alignment, size_t size) { void *p; return posix_memalign (&p, alignment, size) == 0 ? p : 0; } +# endif #endif /* BLOCK_ALIGN has to be a power of 2. */ @@ -4553,16 +4579,8 @@ mark_maybe_pointer (void *p) /* Mark Lisp objects referenced from the address range START+OFFSET..END or END+OFFSET..START. */ -static void +static void ATTRIBUTE_NO_SANITIZE_ADDRESS mark_memory (void *start, void *end) -#if defined (__clang__) && defined (__has_feature) -#if __has_feature(address_sanitizer) - /* Do not allow -faddress-sanitizer to check this function, since it - crosses the function stack boundary, and thus would yield many - false positives. */ - __attribute__((no_address_safety_analysis)) -#endif -#endif { void **pp; int i; @@ -5477,7 +5495,7 @@ See Info node `(elisp)Garbage Collection'. */) stack_copy = xrealloc (stack_copy, stack_size); stack_copy_size = stack_size; } - memcpy (stack_copy, stack, stack_size); + no_sanitize_memcpy (stack_copy, stack, stack_size); } } #endif /* MAX_SAVE_STACK > 0 */ diff --git a/src/conf_post.h b/src/conf_post.h index 66dd9a36f00..04c4f4f5f15 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -50,8 +50,19 @@ typedef bool bool_bf; #endif #endif +/* When not using Clang, assume its attributes and features are absent. */ #ifndef __has_attribute -# define __has_attribute(a) false /* non-clang */ +# define __has_attribute(a) false +#endif +#ifndef __has_feature +# define __has_feature(a) false +#endif + +/* True if addresses are being sanitized. */ +#if defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer) +# define ADDRESS_SANITIZER true +#else +# define ADDRESS_SANITIZER false #endif #ifdef DARWIN_OS @@ -204,6 +215,32 @@ extern void _DebPrint (const char *fmt, ...); #define ATTRIBUTE_CONST _GL_ATTRIBUTE_CONST +/* Work around GCC bug 59600: when a function is inlined, the inlined + code may have its addresses sanitized even if the function has the + no_sanitize_address attribute. This bug is present in GCC 4.8.2 + and clang 3.3, the latest releases as of December 2013, and the + only platforms known to support address sanitization. When the bug + is fixed the #if can be updated accordingly. */ +#if ADDRESS_SANITIZER +# define ADDRESS_SANITIZER_WORKAROUND NO_INLINE +#else +# define ADDRESS_SANITIZER_WORKAROUND +#endif + +/* Attribute of functions whose code should not have addresses + sanitized. */ + +#if (__has_attribute (no_sanitize_address) \ + || 4 < __GNUC__ + (8 <= __GNUC_MINOR__)) +# define ATTRIBUTE_NO_SANITIZE_ADDRESS \ + __attribute__ ((no_sanitize_address)) ADDRESS_SANITIZER_WORKAROUND +#elif __has_attribute (no_address_safety_analysis) +# define ATTRIBUTE_NO_SANITIZE_ADDRESS \ + __attribute__ ((no_address_safety_analysis)) ADDRESS_SANITIZER_WORKAROUND +#else +# define ATTRIBUTE_NO_SANITIZE_ADDRESS +#endif + /* Some versions of GNU/Linux define noinline in their headers. */ #ifdef noinline #undef noinline -- 2.39.2