summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Morrow <acm@mongodb.com>2015-10-09 14:46:48 -0400
committerAndrew Morrow <acm@mongodb.com>2015-10-27 08:05:31 -0400
commit17a0b2a3be87fb1a1a86db2ee0bf1871e4b8d6fc (patch)
tree08a3d8696b21b4fae3184220dfebef8e0ec65010 /src
parentd8a428ded9dd3cc47b945f7efdb26dc50b6adf47 (diff)
downloadmongo-17a0b2a3be87fb1a1a86db2ee0bf1871e4b8d6fc.tar.gz
SERVER-20538 Build a general purpose SecureAllocator
Diffstat (limited to 'src')
-rw-r--r--src/mongo/base/SConscript25
-rw-r--r--src/mongo/base/secure_allocator.cpp171
-rw-r--r--src/mongo/base/secure_allocator.h181
-rw-r--r--src/mongo/base/secure_allocator_test.cpp57
-rw-r--r--src/mongo/util/assert_util.cpp7
-rw-r--r--src/mongo/util/log.h2
6 files changed, 439 insertions, 4 deletions
diff --git a/src/mongo/base/SConscript b/src/mongo/base/SConscript
index f5cba35f76d..07d696d48f6 100644
--- a/src/mongo/base/SConscript
+++ b/src/mongo/base/SConscript
@@ -52,3 +52,28 @@ env.CppUnitTest(
'system_error',
],
)
+
+env.Library(
+ target=[
+ 'secure_allocator'
+ ],
+ source=[
+ 'secure_allocator.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/util/secure_zero_memory',
+ ],
+)
+
+env.CppUnitTest(
+ target=[
+ 'secure_allocator_test',
+ ],
+ source=[
+ 'secure_allocator_test.cpp',
+ ],
+ LIBDEPS=[
+ 'secure_allocator',
+ ],
+)
diff --git a/src/mongo/base/secure_allocator.cpp b/src/mongo/base/secure_allocator.cpp
new file mode 100644
index 00000000000..b6ac5d14eaf
--- /dev/null
+++ b/src/mongo/base/secure_allocator.cpp
@@ -0,0 +1,171 @@
+/* Copyright 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/base/secure_allocator.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+#include "mongo/util/assert_util.h"
+#include "mongo/util/log.h"
+#include "mongo/util/secure_zero_memory.h"
+
+namespace mongo {
+
+/**
+ * NOTE(jcarey): Why not new/delete?
+ *
+ * As a general rule, mlock/virtuallock lack any kind of recursive semantics
+ * (they free any locks on the underlying page if called once). While some
+ * platforms do offer those semantics, they're not available globally, so we
+ * have to flow all allocations through page based allocations.
+ */
+namespace secure_allocator_details {
+
+#ifdef _WIN32
+
+void* allocate(std::size_t bytes) {
+ // Flags:
+ //
+ // MEM_COMMIT - allocates the memory charges and zeros the underlying
+ // memory
+ // MEM_RESERVE - Reserves space in the process's virtual address space
+ //
+ // The two flags together give us bytes that are attached to the process
+ // that we can actually write to.
+ //
+ // PAGE_READWRITE - allows read/write access to the page
+ auto ptr = VirtualAlloc(nullptr, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+
+ if (!ptr) {
+ auto str = errnoWithPrefix("Failed to VirtualAlloc");
+ severe() << str;
+ fassertFailed(28835);
+ }
+
+ if (VirtualLock(ptr, bytes) == 0) {
+ auto str = errnoWithPrefix("Failed to VirtualLock");
+ severe() << str;
+ fassertFailed(28828);
+ }
+
+ return ptr;
+}
+
+void deallocate(void* ptr, std::size_t bytes) {
+ secureZeroMemory(ptr, bytes);
+
+ if (VirtualUnlock(ptr, bytes) == 0) {
+ auto str = errnoWithPrefix("Failed to VirtualUnlock");
+ severe() << str;
+ fassertFailed(28829);
+ }
+
+ // VirtualFree needs to take 0 as the size parameter for MEM_RELEASE
+ // (that's how the api works).
+ if (VirtualFree(ptr, 0, MEM_RELEASE) == 0) {
+ auto str = errnoWithPrefix("Failed to VirtualFree");
+ severe() << str;
+ fassertFailed(28830);
+ }
+}
+
+#else
+
+// See https://github.com/libressl-portable/portable/issues/24 for the table
+// that suggests this approach. This assumes that MAP_ANONYMOUS and MAP_ANON are
+// macro definitions, but that seems plausible on all platforms we care about.
+
+#if defined(MAP_ANONYMOUS)
+#define MONGO_MAP_ANONYMOUS MAP_ANONYMOUS
+#else
+#if defined(MAP_ANON)
+#define MONGO_MAP_ANONYMOUS MAP_ANON
+#endif
+#endif
+
+#if !defined(MONGO_MAP_ANONYMOUS)
+#error "Could not determine a way to map anonymous memory, required for secure allocation"
+#endif
+
+void* allocate(std::size_t bytes) {
+ // Flags:
+ //
+ // PROT_READ | PROT_WRITE - allows read write access to the page
+ //
+ // MAP_PRIVATE - Ensure that the mapping is copy-on-write. Otherwise writes
+ // made in this process can be seen in children.
+ //
+ // MAP_ANONYMOUS - The mapping is not backed by a file. fd must be -1 on
+ // some platforms, offset is ignored (so 0).
+ //
+ // skipping flags like MAP_LOCKED and MAP_POPULATE as linux-isms
+ auto ptr =
+ mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MONGO_MAP_ANONYMOUS, -1, 0);
+
+ if (!ptr) {
+ auto str = errnoWithPrefix("Failed to mmap");
+ severe() << str;
+ fassertFailed(28831);
+ }
+
+ if (mlock(ptr, bytes) != 0) {
+ auto str = errnoWithPrefix("Failed to mlock");
+ severe() << str;
+ fassertFailed(28832);
+ }
+
+ return ptr;
+}
+
+void deallocate(void* ptr, std::size_t bytes) {
+ secureZeroMemory(ptr, bytes);
+
+ if (munlock(ptr, bytes) != 0) {
+ severe() << errnoWithPrefix("Failed to munlock");
+ fassertFailed(28833);
+ }
+
+ if (munmap(ptr, bytes) != 0) {
+ severe() << errnoWithPrefix("Failed to munmap");
+ fassertFailed(28834);
+ }
+}
+
+#endif
+
+} // namespace secure_allocator_details
+} // namespace mongo
diff --git a/src/mongo/base/secure_allocator.h b/src/mongo/base/secure_allocator.h
new file mode 100644
index 00000000000..ed10d075968
--- /dev/null
+++ b/src/mongo/base/secure_allocator.h
@@ -0,0 +1,181 @@
+/* Copyright 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/config.h"
+
+#include <cstddef>
+#include <limits>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace mongo {
+
+namespace secure_allocator_details {
+
+void* allocate(std::size_t bytes);
+void deallocate(void* ptr, std::size_t bytes);
+
+} // namespace secure_allocator_details
+
+/**
+ * Provides a secure allocator for trivially copyable types. By secure we mean
+ * memory that will be zeroed on free and locked out of paging while in memory
+ * (to prevent it from being written to disk).
+ *
+ * While this type can be used with any allocator aware container, it should be
+ * considered whether either of the two named specializations below are
+ * sufficient (a string and a vector). The allocations out of this container
+ * are quite expensive, so one should endeavor to use containers which make
+ * few, contiguous allocations where possible.
+ *
+ * Note that this allocator is written without reling on default
+ * semantics injected via allocator_traits, and so defines all
+ * optional allocator members, and does not rely on allocator_traits
+ * to default them in. See http://stackoverflow.com/a/33267132 for a
+ * rationale for GCC 4.8, our current default compiler. There is also
+ * evidence that MSVC 2013's _DEBUG STL does not work correctly with
+ * allocator_traits.
+ *
+ * See also: http://howardhinnant.github.io/allocator_boilerplate.html
+ */
+template <typename T>
+struct SecureAllocator {
+/**
+ * We only support trivially copyable types to avoid situations where the
+ * SecureAllocator is used in containers with complex types that do their
+ * own allocation. I.e. one could otherwise have a:
+ *
+ * std::vector<std::string, SecureAllocator<std::string>>
+ *
+ * where the vectors were stored securely, but the strings spilled to the
+ * heap
+ *
+ */
+#ifdef MONGO_CONFIG_HAVE_STD_IS_TRIVIALLY_COPYABLE
+ static_assert(std::is_trivially_copyable<T>::value,
+ "SecureAllocator can only be used with trivially copyable types");
+#endif
+
+ // NOTE: The standard doesn't seem to require these, but libstdc++
+ // definitly wants them.
+ using reference = T&;
+ using const_reference = const T&;
+
+
+ // NOTE: These members are defined in the same order as specified
+ // in the "Allocator Requirements" section of the standard. Please
+ // retain this ordering.
+
+ using pointer = T*;
+ using const_pointer = const T*;
+ using void_pointer = void*;
+ using const_void_pointer = const void*;
+ using value_type = T;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+
+ template <typename U>
+ struct rebind {
+ using other = SecureAllocator<U>;
+ };
+
+ pointer allocate(size_type n) {
+ return static_cast<pointer>(secure_allocator_details::allocate(sizeof(value_type) * n));
+ }
+
+ pointer allocate(size_type n, const_void_pointer) {
+ return allocate(n);
+ }
+
+ void deallocate(pointer ptr, size_type n) {
+ return secure_allocator_details::deallocate(static_cast<void*>(ptr),
+ sizeof(value_type) * n);
+ }
+
+ size_type max_size() {
+ return std::numeric_limits<size_type>::max();
+ }
+
+ SecureAllocator() = default;
+
+ template <typename U>
+ SecureAllocator(const SecureAllocator<U>& other) {}
+
+ template <typename U, typename... Args>
+ void construct(U* p, Args&&... args) {
+ ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
+ }
+
+ template <typename U>
+ void destroy(U* p) {
+ p->~U();
+ }
+
+ SecureAllocator select_on_container_copy_construction() {
+ // SecureAllocator is stateless, so just return a default
+ // constructed instance.
+ return SecureAllocator();
+ }
+
+ // For background:
+ //
+ // http://stackoverflow.com/questions/27471053/example-usage-of-propagate-on-container-move-assignment
+ //
+ // https://foonathan.github.io/blog/2015/10/05/allocatorawarecontainer-propagation-pitfalls.html
+ //
+ // This allocator is stateless, so we can avoid a runtime check
+ // (even though it would probably be optimized out based on the
+ // constrexpr-esque nature of our equality comparison operator),
+ // so we can set all of these to true.
+ using propagate_on_container_copy_assignment = std::true_type;
+ using propagate_on_container_move_assignment = std::true_type;
+ using propagate_on_container_swap = std::true_type;
+
+ using is_always_equal = std::true_type;
+};
+
+template <typename T, typename U>
+bool operator==(const SecureAllocator<T>& lhs, const SecureAllocator<U>& rhs) {
+ // Note: If you change this, you must re-evaluate the select_ and
+ // propagate_ methods and typedefs above.
+ return true;
+}
+
+template <typename T, typename U>
+bool operator!=(const SecureAllocator<T>& lhs, const SecureAllocator<U>& rhs) {
+ return !(lhs == rhs);
+}
+
+template <typename T>
+using SecureVector = std::vector<T, SecureAllocator<T>>;
+
+using SecureString = std::basic_string<char, std::char_traits<char>, SecureAllocator<char>>;
+
+} // namespace mongo
diff --git a/src/mongo/base/secure_allocator_test.cpp b/src/mongo/base/secure_allocator_test.cpp
new file mode 100644
index 00000000000..f04b286ef2b
--- /dev/null
+++ b/src/mongo/base/secure_allocator_test.cpp
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/base/secure_allocator.h"
+
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+
+TEST(SecureAllocator, SecureVector) {
+ SecureVector<int> vec;
+
+ vec.push_back(1);
+ vec.push_back(2);
+
+ ASSERT_EQUALS(1, vec[0]);
+ ASSERT_EQUALS(2, vec[1]);
+
+ vec.resize(2000, 3);
+ ASSERT_EQUALS(3, vec[2]);
+}
+
+TEST(SecureAllocator, SecureString) {
+ SecureString str;
+
+ str.resize(2000, 'x');
+ ASSERT_EQUALS(0, str.compare(SecureString(2000, 'x')));
+}
+
+} // namespace mongo
diff --git a/src/mongo/util/assert_util.cpp b/src/mongo/util/assert_util.cpp
index 764afacb140..05b5a433956 100644
--- a/src/mongo/util/assert_util.cpp
+++ b/src/mongo/util/assert_util.cpp
@@ -257,11 +257,12 @@ std::string causedBy(const Status& e) {
return causedBy(e.reason());
}
-string errnoWithPrefix(const char* prefix) {
+string errnoWithPrefix(StringData prefix) {
+ const auto suffix = errnoWithDescription();
stringstream ss;
- if (prefix)
+ if (!prefix.empty())
ss << prefix << ": ";
- ss << errnoWithDescription();
+ ss << suffix;
return ss.str();
}
diff --git a/src/mongo/util/log.h b/src/mongo/util/log.h
index 14fd8615469..b0f53a30b87 100644
--- a/src/mongo/util/log.h
+++ b/src/mongo/util/log.h
@@ -211,7 +211,7 @@ bool rotateLogs(bool renameFiles);
/** output the error # and error message with prefix.
handy for use as parm in uassert/massert.
*/
-std::string errnoWithPrefix(const char* prefix);
+std::string errnoWithPrefix(StringData prefix);
extern Tee* const warnings; // Things put here go in serverStatus
extern Tee* const startupWarningsLog; // Things put here get reported in MMS