[tor-commits] [tor/master] Code for anonymous mappings via mmap() or CreateFileMapping().

dgoulet at torproject.org dgoulet at torproject.org
Thu Feb 14 16:48:44 UTC 2019


commit 8ca808f81d28bbae9e9059172a7b7479d20e9594
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Feb 5 16:58:56 2019 -0500

    Code for anonymous mappings via mmap() or CreateFileMapping().
    
    Using an anonymous mmap() is a good way to get pages that we can set
    kernel-level flags on, like minherit() or madvise() or mlock().
    We're going to use that so that we can make uninheritable locked
    pages to store PRNG data.
---
 configure.ac              |   3 +
 src/lib/malloc/include.am |   6 +-
 src/lib/malloc/map_anon.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++
 src/lib/malloc/map_anon.h |  37 ++++++++
 src/test/test_util.c      | 107 +++++++++++++++++++++++
 5 files changed, 364 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 75bb0c7bd..7de3c3a46 100644
--- a/configure.ac
+++ b/configure.ac
@@ -605,8 +605,10 @@ AC_CHECK_FUNCS(
 	llround \
 	localtime_r \
 	lround \
+	madvise \
 	memmem \
 	memset_s \
+	minherit \
 	mmap \
 	pipe \
 	pipe2 \
@@ -1450,6 +1452,7 @@ AC_CHECK_HEADERS([errno.h \
 		  inttypes.h \
 		  limits.h \
 		  linux/types.h \
+		  mach/vm_inherit.h \
 		  machine/limits.h \
 		  malloc.h \
 		  malloc/malloc.h \
diff --git a/src/lib/malloc/include.am b/src/lib/malloc/include.am
index 502cc1c6b..95d96168e 100644
--- a/src/lib/malloc/include.am
+++ b/src/lib/malloc/include.am
@@ -6,7 +6,8 @@ noinst_LIBRARIES += src/lib/libtor-malloc-testing.a
 endif
 
 src_lib_libtor_malloc_a_SOURCES =			\
-	src/lib/malloc/malloc.c
+	src/lib/malloc/malloc.c				\
+	src/lib/malloc/map_anon.c
 
 if USE_OPENBSD_MALLOC
 src_lib_libtor_malloc_a_SOURCES += src/ext/OpenBSD_malloc_Linux.c
@@ -18,4 +19,5 @@ src_lib_libtor_malloc_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
 src_lib_libtor_malloc_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
 
 noinst_HEADERS +=					\
-	src/lib/malloc/malloc.h
+	src/lib/malloc/malloc.h				\
+	src/lib/malloc/map_anon.h
diff --git a/src/lib/malloc/map_anon.c b/src/lib/malloc/map_anon.c
new file mode 100644
index 000000000..2fc6e89ea
--- /dev/null
+++ b/src/lib/malloc/map_anon.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file map_anon.c
+ * \brief Manage anonymous mappings.
+ **/
+
+#include "orconfig.h"
+#include "lib/malloc/map_anon.h"
+#include "lib/malloc/malloc.h"
+#include "lib/err/torerr.h"
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MACH_VM_INHERIT_H
+#include <mach/vm_inherit.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+/**
+ * Macro to get the high bytes of a size_t, if there are high bytes.
+ * Windows needs this; other operating systems define a size_t that does
+ * what it should.
+ */
+#if SIZEOF_SIZE_T > 4
+#define HIGH_SIZE_T_BYTES(sz) ((sz) >> 32)
+#else
+#define HIGH_SIZE_T_BYTES(sz) (0)
+#endif
+
+/* Here we define a MINHERIT macro that is minherit() or madvise(), depending
+ * on what we actually want.
+ *
+ * If there's a flag that sets pages to zero after fork, we define FLAG_ZERO
+ * to be that flag.  If there's a flag unmaps pages after fork, we define
+ * FLAG_NOINHERIT to be that flag.
+ */
+#if defined(HAVE_MINHERIT)
+#define MINHERIT minherit
+
+#ifdef INHERIT_ZERO
+#define FLAG_ZERO INHERIT_ZERO
+#endif
+#ifdef INHERIT_NONE
+#define FLAG_NOINHERIT INHERIT_NONE
+#elif defined(VM_INHERIT_NONE)
+#define FLAG_NOINHERIT VM_INHERIT_NONE
+#endif
+
+#elif defined(HAVE_MADVISE)
+
+#define MINHERIT madvise
+
+#ifdef MADV_WIPEONFORK
+#define FLAG_ZERO MADV_WIPEONFORK
+#endif
+#ifdef MADV_DONTFORK
+#define FLAG_NOINHERIT MADV_DONTFORK
+#endif
+
+#endif
+
+/**
+ * Helper: try to prevent the <b>sz</b> bytes at <b>mem</b> from being swapped
+ * to disk.  Return 0 on success or if the facility is not available on this
+ * OS; return -1 on failure.
+ */
+static int
+lock_mem(void *mem, size_t sz)
+{
+#ifdef _WIN32
+  return VirtualLock(mem, sz) ? 0 : -1;
+#elif defined(HAVE_MLOCK)
+  return mlock(mem, sz);
+#else
+  (void) mem;
+  (void) sz;
+
+  return 0;
+#endif
+}
+
+/**
+ * Helper: try to prevent the <b>sz</b> bytes at <b>mem</b> from appearing in
+ * a core dump.  Return 0 on success or if the facility is not available on
+ * this OS; return -1 on failure.
+ */
+static int
+nodump_mem(void *mem, size_t sz)
+{
+#if defined(MADV_DONTDUMP)
+  return madvise(mem, sz, MADV_DONTDUMP);
+#else
+  (void) mem;
+  (void) sz;
+  return 0;
+#endif
+}
+
+/**
+ * Helper: try to prevent the <b>sz</b> bytes at <b>mem</b> from being
+ * accessible in child processes -- ideally by having them set to 0 after a
+ * fork, and if that doesn't work, by having them unmapped after a fork.
+ * Return 0 on success or if the facility is not available on this OS; return
+ * -1 on failure.
+ */
+static int
+noinherit_mem(void *mem, size_t sz)
+{
+#ifdef FLAG_ZERO
+  int r = MINHERIT(mem, sz, FLAG_ZERO);
+  if (r == 0)
+    return 0;
+#endif
+#ifdef FLAG_NOINHERIT
+  return MINHERIT(mem, sz, FLAG_NOINHERIT);
+#else
+  (void)mem;
+  (void)sz;
+  return 0;
+#endif
+}
+
+/**
+ * Return a new anonymous memory mapping that holds <b>sz</b> bytes.
+ *
+ * Memory mappings are unlike the results from malloc() in that they are
+ * handled separately by the operating system, and as such can have different
+ * kernel-level flags set on them.
+ *
+ * The "flags" argument may be zero or more of ANONMAP_PRIVATE and
+ * ANONMAP_NOINHERIT.
+ *
+ * Memory returned from this function must be released with
+ * tor_munmap_anonymous().
+ *
+ * [Note: OS people use the word "anonymous" here to mean that the memory
+ * isn't associated with any file. This has *nothing* to do with the kind of
+ * anonymity that Tor is trying to provide.]
+ */
+void *
+tor_mmap_anonymous(size_t sz, unsigned flags)
+{
+  void *ptr;
+#if defined(_WIN32)
+  HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE,
+                                     NULL, /*attributes*/
+                                     PAGE_READWRITE,
+                                     HIGH_SIZE_T_BYTES(sz),
+                                     sz & 0xffffffff,
+                                     NULL /* name */);
+  raw_assert(mapping != NULL);
+  ptr = MapViewOfFile(mapping, FILE_MAP_WRITE,
+                      0, 0, /* Offset */
+                      0 /* Extend to end of mapping */);
+  raw_assert(ptr);
+  CloseHandle(mapping); /* mapped view holds a reference */
+#elif defined(HAVE_SYS_MMAN_H)
+  ptr = mmap(NULL, sz,
+             PROT_READ|PROT_WRITE,
+             MAP_ANON|MAP_PRIVATE,
+             -1, 0);
+  raw_assert(ptr != MAP_FAILED);
+  raw_assert(ptr != NULL);
+#else
+  ptr = tor_malloc_zero(sz);
+#endif
+
+  if (flags & ANONMAP_PRIVATE) {
+    int lock_result = lock_mem(ptr, sz);
+    raw_assert(lock_result == 0);
+    int nodump_result = nodump_mem(ptr, sz);
+    raw_assert(nodump_result == 0);
+  }
+
+  if (flags & ANONMAP_NOINHERIT) {
+    int noinherit_result = noinherit_mem(ptr, sz);
+    raw_assert(noinherit_result == 0);
+  }
+
+  return ptr;
+}
+
+/**
+ * Release <b>sz</b> bytes of memory that were previously mapped at
+ * <b>mapping</b> by tor_mmap_anonymous().
+ **/
+void
+tor_munmap_anonymous(void *mapping, size_t sz)
+{
+  if (!mapping)
+    return;
+
+#if defined(_WIN32)
+  (void)sz;
+  UnmapViewOfFile(mapping);
+#elif defined(HAVE_SYS_MMAN_H)
+  munmap(mapping, sz);
+#else
+  (void)sz;
+  tor_free(mapping);
+#endif
+}
diff --git a/src/lib/malloc/map_anon.h b/src/lib/malloc/map_anon.h
new file mode 100644
index 000000000..cc5797e4e
--- /dev/null
+++ b/src/lib/malloc/map_anon.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file map_anon.h
+ * \brief Headers for map_anon.c
+ **/
+
+#ifndef TOR_MAP_ANON_H
+#define TOR_MAP_ANON_H
+
+#include "lib/malloc/malloc.h"
+#include <stddef.h>
+
+/**
+ * When this flag is specified, try to prevent the mapping from being
+ * swapped or dumped.
+ *
+ * In some operating systems, this flag is not implemented.
+ */
+#define ANONMAP_PRIVATE   (1u<<0)
+/**
+ * When this flag is specified, try to prevent the mapping from being
+ * inherited after a fork().  In some operating systems, trying to access it
+ * afterwards will cause its contents to be zero.  In others, trying to access
+ * it afterwards will cause a crash.
+ *
+ * In some operating systems, this flag is not implemented at all.
+ */
+#define ANONMAP_NOINHERIT (1u<<1)
+
+void *tor_mmap_anonymous(size_t sz, unsigned flags);
+void tor_munmap_anonymous(void *mapping, size_t sz);
+
+#endif /* !defined(TOR_MAP_ANON_H) */
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 4e95303f2..ba78b5593 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -40,6 +40,7 @@
 #include "lib/time/tvdiff.h"
 #include "lib/encoding/confline.h"
 #include "lib/net/socketpair.h"
+#include "lib/malloc/map_anon.h"
 
 #ifdef HAVE_PWD_H
 #include <pwd.h>
@@ -59,6 +60,12 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
 
 #ifdef _WIN32
 #include <tchar.h>
@@ -6093,6 +6100,104 @@ test_util_log_mallinfo(void *arg)
   tor_free(mem);
 }
 
+static void
+test_util_map_anon(void *arg)
+{
+  (void)arg;
+  char *ptr = NULL;
+  size_t sz = 16384;
+
+  /* Basic checks. */
+  ptr = tor_mmap_anonymous(sz, 0);
+  tt_ptr_op(ptr, OP_NE, 0);
+  ptr[sz-1] = 3;
+  tt_int_op(ptr[0], OP_EQ, 0);
+  tt_int_op(ptr[sz-2], OP_EQ, 0);
+  tt_int_op(ptr[sz-1], OP_EQ, 3);
+
+  /* Try again, with a private (non-swappable) mapping. */
+  tor_munmap_anonymous(ptr, sz);
+  ptr = tor_mmap_anonymous(sz, ANONMAP_PRIVATE);
+  tt_ptr_op(ptr, OP_NE, 0);
+  ptr[sz-1] = 10;
+  tt_int_op(ptr[0], OP_EQ, 0);
+  tt_int_op(ptr[sz/2], OP_EQ, 0);
+  tt_int_op(ptr[sz-1], OP_EQ, 10);
+
+  /* Now let's test a drop-on-fork mapping. */
+  tor_munmap_anonymous(ptr, sz);
+  ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT);
+  tt_ptr_op(ptr, OP_NE, 0);
+  ptr[sz-1] = 10;
+  tt_int_op(ptr[0], OP_EQ, 0);
+  tt_int_op(ptr[sz/2], OP_EQ, 0);
+  tt_int_op(ptr[sz-1], OP_EQ, 10);
+
+ done:
+  tor_munmap_anonymous(ptr, sz);
+}
+
+static void
+test_util_map_anon_nofork(void *arg)
+{
+  (void)arg;
+#if !defined(HAVE_MADVISE) && !defined(HAVE_MINHERIT)
+  /* The operating system doesn't support this. */
+  tt_skip();
+ done:
+  ;
+#else
+  /* We have the right OS support.  We're going to try marking the buffer as
+   * either zero-on-fork or as drop-on-fork, whichever is supported.  Then we
+   * will fork and send a byte back to the parent process.  This will either
+   * crash, or send zero. */
+
+  char *ptr = NULL;
+  size_t sz = 16384;
+  int pipefd[2] = {-1, -1};
+
+  tor_munmap_anonymous(ptr, sz);
+  ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT);
+  tt_ptr_op(ptr, OP_NE, 0);
+  memset(ptr, 0xd0, sz);
+
+  tt_int_op(0, OP_EQ, pipe(pipefd));
+  pid_t child = fork();
+  if (child == 0) {
+    /* We're in the child. */
+    close(pipefd[0]);
+    ssize_t r = write(pipefd[1], &ptr[sz-1], 1); /* This may crash. */
+    close(pipefd[1]);
+    if (r < 0)
+      exit(1);
+    exit(0);
+  }
+  tt_int_op(child, OP_GT, 0);
+  /* In the parent. */
+  close(pipefd[1]);
+  pipefd[1] = -1;
+  char buf[1];
+  ssize_t r = read(pipefd[0], buf, 1);
+#if defined(INHERIT_ZERO) || defined(MADV_WIPEONFORK)
+  tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
+  tt_int_op(buf[0], OP_EQ, 0);
+#else
+  tt_int_op(r, OP_LE, 0); // child said nothing; it should have crashed.
+#endif
+  int ws;
+  waitpid(child, &ws, 0);
+
+ done:
+  tor_munmap_anonymous(ptr, sz);
+  if (pipefd[0] >= 0) {
+    close(pipefd[0]);
+  }
+  if (pipefd[1] >= 0) {
+    close(pipefd[1]);
+  }
+#endif
+}
+
 #define UTIL_LEGACY(name)                                               \
   { #name, test_util_ ## name , 0, NULL, NULL }
 
@@ -6230,5 +6335,7 @@ struct testcase_t util_tests[] = {
   UTIL_TEST(htonll, 0),
   UTIL_TEST(get_unquoted_path, 0),
   UTIL_TEST(log_mallinfo, 0),
+  UTIL_TEST(map_anon, 0),
+  UTIL_TEST(map_anon_nofork, 0),
   END_OF_TESTCASES
 };





More information about the tor-commits mailing list