Commit 8ca808f8 authored by Nick Mathewson's avatar Nick Mathewson 🐻
Browse files

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.
parent bfd1d702
......@@ -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 \
......
......@@ -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
/* 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
}
/* 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) */
......@@ -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
};
Supports Markdown
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