Created
October 15, 2017 22:24
-
-
Save arrieta/80fb40e73df311ebdd4a9eced42cd7c7 to your computer and use it in GitHub Desktop.
Provide a memory region that never swaps to disk.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*- | |
/// @file SecureMemoryRegion.cpp | |
/// @brief Provide region of memory that never swaps to disk. | |
/// @author J. Arrieta <[email protected]> | |
/// @date October 15, 2017 | |
/// @copyright (c) 2017 Nabla Zero Labs | |
/// | |
/// $ clang++ -o SecureMemoryRegion SecureMemoryRegion.cpp -std=c++1z \ | |
/// -Wall -Wextra -Ofast -march=native | |
/// | |
/// $ ./SecureMemoryRegion | |
/// SecureMemoryRegion(64) | |
/// Data: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
/// | |
/// $ ./SecureMemoryRegion 32 | |
/// SecureMemoryRegion(32) | |
/// Data: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
/// | |
// C++ Standard Library | |
#include <cerrno> | |
#include <string> | |
#include <algorithm> | |
#include <iostream> | |
#include <iterator> | |
#include <stdexcept> | |
// POSIX Libraries | |
#include <sys/mman.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
std::string | |
ErrnoWithPrefix(const std::string& prefix) | |
{ return prefix + ":" + std::strerror(errno); } | |
[[noreturn]] | |
void | |
Fail(const std::string& msg) | |
{ throw std::runtime_error(msg); } | |
class SecureMemoryRegion { | |
public: | |
SecureMemoryRegion(std::size_t m_size); | |
~SecureMemoryRegion() noexcept(true); | |
const char* | |
data() const | |
{ return m_data; } | |
char* | |
data() | |
{ return m_data; } | |
std::size_t | |
size() const | |
{ return m_size; } | |
private: | |
void | |
zero_out() | |
{ | |
// In production, I would zero out using '\0' instead of 'X' | |
std::fill(m_data, std::next(m_data, m_size), 'X'); | |
} | |
char* m_data { nullptr }; | |
std::size_t m_size { 0 }; | |
}; | |
SecureMemoryRegion::SecureMemoryRegion(std::size_t size) : m_size(size) | |
{ | |
const auto protection = PROT_READ | PROT_WRITE; | |
const auto mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; | |
m_data = reinterpret_cast<char*>(::mmap(nullptr, size, protection, mmap_flags, -1, 0)); | |
if (m_data == MAP_FAILED) { | |
Fail(ErrnoWithPrefix("Failed to mmap")); | |
} | |
if (::mlock(m_data, m_size) != 0) { | |
Fail(ErrnoWithPrefix("Failed to lock memory")); | |
} | |
#if defined(MADV_DONTDUMP) | |
::madvise(m_data, m_size, MADV_DONTDUMP); | |
#endif | |
zero_out(); | |
} | |
SecureMemoryRegion::~SecureMemoryRegion() noexcept(true) | |
{ | |
if (m_data != nullptr) { | |
zero_out(); | |
#if (defined(MADV_DONTDUMP) && defined(MADV_DODUMP)) | |
::madvise(m_data, m_size, MADV_DODUMP); | |
#endif | |
::munlock(m_data, m_size); | |
::munmap(m_data, m_size); | |
m_data = nullptr; | |
m_size = 0; | |
} | |
} | |
std::ostream& | |
operator<<(std::ostream& os, const SecureMemoryRegion& r) | |
{ | |
os << "SecureMemoryRegion(" << r.size() << ")\nData: "; | |
std::copy(r.data(), std::next(r.data(), r.size()), | |
std::ostreambuf_iterator<char>(os)); | |
return os; | |
} | |
int | |
main(int argc, char* argv[]) | |
{ | |
std::size_t size {1 << 6}; | |
if (argc == 2) { | |
size = std::stoi(argv[1]); | |
} | |
SecureMemoryRegion smr(size); | |
std::cout << smr << std::endl; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment