Skip to content

[3.7] bpo-35214: Initial clang MemorySanitizer support (GH-10479) #10492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Include/Python.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@
#include "pyport.h"
#include "pymacro.h"

/* A convenient way for code to know if clang's memory sanitizer is enabled. */
#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
# if !defined(MEMORY_SANITIZER)
# define MEMORY_SANITIZER
# endif
# endif
#endif

#include "pyatomic.h"

/* Debug-mode build with pymalloc implies PYMALLOC_DEBUG.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The interpreter and extension modules have had annotations added so that
they work properly under clang's Memory Sanitizer. A new configure flag
--with-memory-sanitizer has been added to make test builds of this nature
easier to perform.
11 changes: 11 additions & 0 deletions Modules/_ctypes/callproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@
#include <alloca.h>
#endif

#ifdef MEMORY_SANITIZER
#include <sanitizer/msan_interface.h>
#endif

#if defined(_DEBUG) || defined(__MINGW32__)
/* Don't use structured exception handling on Windows if this is defined.
MingW, AFAIK, doesn't support it.
Expand Down Expand Up @@ -1125,6 +1129,13 @@ PyObject *_ctypes_callproc(PPROC pProc,
rtype = _ctypes_get_ffi_type(restype);
resbuf = alloca(max(rtype->size, sizeof(ffi_arg)));

#ifdef MEMORY_SANITIZER
/* ffi_call actually initializes resbuf, but from asm, which
* MemorySanitizer can't detect. Avoid false positives from MSan. */
if (resbuf != NULL) {
__msan_unpoison(resbuf, max(rtype->size, sizeof(ffi_arg)));
}
#endif
avalues = (void **)alloca(sizeof(void *) * argcount);
atypes = (ffi_type **)alloca(sizeof(ffi_type *) * argcount);
if (!resbuf || !avalues || !atypes) {
Expand Down
7 changes: 7 additions & 0 deletions Modules/_posixsubprocess.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#include <dirent.h>
#endif

#ifdef MEMORY_SANITIZER
# include <sanitizer/msan_interface.h>
#endif

#if defined(__ANDROID__) && __ANDROID_API__ < 21 && !defined(SYS_getdents64)
# include <sys/linux-syscalls.h>
# define SYS_getdents64 __NR_getdents64
Expand Down Expand Up @@ -287,6 +291,9 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
sizeof(buffer))) > 0) {
struct linux_dirent64 *entry;
int offset;
#ifdef MEMORY_SANITIZER
__msan_unpoison(buffer, bytes);
#endif
for (offset = 0; offset < bytes; offset += entry->d_reclen) {
int fd;
entry = (struct linux_dirent64 *)(buffer + offset);
Expand Down
2 changes: 1 addition & 1 deletion Modules/faulthandler.c
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,7 @@ void _PyFaulthandler_Fini(void)
#ifdef HAVE_SIGALTSTACK
if (stack.ss_sp != NULL) {
/* Fetch the current alt stack */
stack_t current_stack;
stack_t current_stack = {};
if (sigaltstack(NULL, &current_stack) == 0) {
if (current_stack.ss_sp == stack.ss_sp) {
/* The current alt stack is the one that we installed.
Expand Down
9 changes: 9 additions & 0 deletions Python/bootstrap_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
# endif
#endif

#ifdef MEMORY_SANITIZER
# include <sanitizer/msan_interface.h>
#endif

#ifdef Py_DEBUG
int _Py_HashSecret_Initialized = 0;
#else
Expand Down Expand Up @@ -143,6 +147,11 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
else {
n = syscall(SYS_getrandom, dest, n, flags);
}
# ifdef MEMORY_SANITIZER
if (n > 0) {
__msan_unpoison(dest, n);
}
# endif
#endif

if (n < 0) {
Expand Down
4 changes: 3 additions & 1 deletion Python/pymath.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ double _Py_force_double(double x)

/* inline assembly for getting and setting the 387 FPU control word on
gcc/x86 */

#ifdef MEMORY_SANITIZER
__attribute__((no_sanitize_memory))
#endif
unsigned short _Py_get_387controlword(void) {
unsigned short cw;
__asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
Expand Down
45 changes: 44 additions & 1 deletion configure
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,8 @@ enable_optimizations
with_lto
with_hash_algorithm
with_address_sanitizer
with_memory_sanitizer
with_undefined_behavior_sanitizer
with_libs
with_system_expat
with_system_ffi
Expand Down Expand Up @@ -1508,7 +1510,10 @@ Optional Packages:
--with-hash-algorithm=[fnv|siphash24]
select hash algorithm
--with-address-sanitizer
enable AddressSanitizer
enable AddressSanitizer (asan)
--with-memory-sanitizer enable MemorySanitizer (msan)
--with-undefined-behavior-sanitizer
enable UndefinedBehaviorSanitizer (ubsan)
--with-libs='lib1 ...' link against additional libs
--with-system-expat build pyexpat module using an installed expat
library
Expand Down Expand Up @@ -10087,6 +10092,44 @@ if test "${with_address_sanitizer+set}" = set; then :
$as_echo "$withval" >&6; }
BASECFLAGS="-fsanitize=address -fno-omit-frame-pointer $BASECFLAGS"
LDFLAGS="-fsanitize=address $LDFLAGS"
# ASan works by controlling memory allocation, our own malloc interferes.
with_pymalloc="no"

else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi


{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-memory-sanitizer" >&5
$as_echo_n "checking for --with-memory-sanitizer... " >&6; }

# Check whether --with-memory_sanitizer was given.
if test "${with_memory_sanitizer+set}" = set; then :
withval=$with_memory_sanitizer;
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $withval" >&5
$as_echo "$withval" >&6; }
BASECFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer $BASECFLAGS"
LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 $LDFLAGS"
# MSan works by controlling memory allocation, our own malloc interferes.
with_pymalloc="no"

else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi


{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-undefined-behavior-sanitizer" >&5
$as_echo_n "checking for --with-undefined-behavior-sanitizer... " >&6; }

# Check whether --with-undefined_behavior_sanitizer was given.
if test "${with_undefined_behavior_sanitizer+set}" = set; then :
withval=$with_undefined_behavior_sanitizer;
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $withval" >&5
$as_echo "$withval" >&6; }
BASECFLAGS="-fsanitize=undefined $BASECFLAGS"
LDFLAGS="-fsanitize=undefined $LDFLAGS"

else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
Expand Down
28 changes: 27 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2910,11 +2910,37 @@ esac
AC_MSG_CHECKING(for --with-address-sanitizer)
AC_ARG_WITH(address_sanitizer,
AS_HELP_STRING([--with-address-sanitizer],
[enable AddressSanitizer]),
[enable AddressSanitizer (asan)]),
[
AC_MSG_RESULT($withval)
BASECFLAGS="-fsanitize=address -fno-omit-frame-pointer $BASECFLAGS"
LDFLAGS="-fsanitize=address $LDFLAGS"
# ASan works by controlling memory allocation, our own malloc interferes.
with_pymalloc="no"
],
[AC_MSG_RESULT(no)])

AC_MSG_CHECKING(for --with-memory-sanitizer)
AC_ARG_WITH(memory_sanitizer,
AS_HELP_STRING([--with-memory-sanitizer],
[enable MemorySanitizer (msan)]),
[
AC_MSG_RESULT($withval)
BASECFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer $BASECFLAGS"
LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 $LDFLAGS"
# MSan works by controlling memory allocation, our own malloc interferes.
with_pymalloc="no"
],
[AC_MSG_RESULT(no)])

AC_MSG_CHECKING(for --with-undefined-behavior-sanitizer)
AC_ARG_WITH(undefined_behavior_sanitizer,
AS_HELP_STRING([--with-undefined-behavior-sanitizer],
[enable UndefinedBehaviorSanitizer (ubsan)]),
[
AC_MSG_RESULT($withval)
BASECFLAGS="-fsanitize=undefined $BASECFLAGS"
LDFLAGS="-fsanitize=undefined $LDFLAGS"
],
[AC_MSG_RESULT(no)])

Expand Down