]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursordist/lazy_allocator.hh
Merge pull request #13387 from omoerbeek/rec-b-root-servers
[thirdparty/pdns.git] / pdns / recursordist / lazy_allocator.hh
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
e8c59f2d 22#pragma once
5cf909f3
AN
23#include <cstddef>
24#include <utility>
25#include <type_traits>
15880e10
OM
26#include <new>
27#include <sys/mman.h>
f41541ab 28#include <unistd.h>
15880e10 29
d6ff1755
TIH
30// On OpenBSD and NetBSD mem used as stack should be marked MAP_STACK
31#if defined(__OpenBSD__) || defined(__NetBSD__)
8997ce47
OM
32#define PDNS_MAP_STACK MAP_STACK
33#else
34#define PDNS_MAP_STACK 0
15880e10 35#endif
5cf909f3
AN
36
37template <typename T>
f8f66c91
OM
38struct lazy_allocator
39{
40 using value_type = T;
41 using pointer = T*;
42 using size_type = std::size_t;
43 static_assert(std::is_trivial<T>::value,
44 "lazy_allocator must only be used with trivial types");
5cf909f3 45
f41541ab
RG
46#ifndef LAZY_ALLOCATOR_USES_NEW
47 /* Pad the requested size to be a multiple of page size, so that we can
48 properly restrict read and write access to our guard pages only */
49 static size_type getAlignmentPadding(size_type requestedSize, size_type pageSize)
50 {
51 size_type remaining = requestedSize % pageSize;
52 if (remaining == 0) {
53 return 0;
54 }
55 return pageSize - remaining;
56 }
57#endif /* LAZY_ALLOCATOR_USES_NEW */
58
f8f66c91
OM
59 pointer
60 allocate(size_type const n)
61 {
f41541ab 62#ifdef LAZY_ALLOCATOR_USES_NEW
f8f66c91 63 return static_cast<pointer>(::operator new(n * sizeof(value_type)));
f41541ab
RG
64#else /* LAZY_ALLOCATOR_USES_NEW */
65 /* This implements a very basic protection against stack overflow
66 by placing two guard pages around the requested memory: one
67 page right before the new stack and one right after.
68 The guard pages cannot be read or written to, any attempt to
69 do so will trigger an immediate access violation, terminating
70 the program.
71 This is much better than the default behaviour for two reasons:
72 1/ the program is stopped right before corrupting memory, which
73 prevents random corruption
74 2/ it's easy to find the point where the stack overflow occurred
75 The memory overhead is two pages (usually 4k on Linux) per stack,
76 and the runtime CPU overhead is one call to mprotect() for every
77 stack allocation.
78 */
f53088f2 79 static const size_type pageSize = sysconf(_SC_PAGESIZE);
f41541ab
RG
80
81 const size_type requestedSize = n * sizeof(value_type);
82 const auto padding = getAlignmentPadding(requestedSize, pageSize);
83 const size_type allocatedSize = requestedSize + padding + (pageSize * 2);
84
d6ff1755
TIH
85#if defined(__OpenBSD__) || defined(__NetBSD__)
86 // OpenBSD and NetBSD don't like mmap MAP_STACK regions that have
f53088f2 87 // PROT_NONE, so allocate r/w and mprotect the guard pages
680bf548 88 // explicitly.
f53088f2
OM
89 const int protection = PROT_READ | PROT_WRITE;
90#else
91 const int protection = PROT_NONE;
92#endif
8997ce47 93 void* p = mmap(nullptr, allocatedSize, protection, MAP_PRIVATE | MAP_ANON | PDNS_MAP_STACK, -1, 0);
f41541ab
RG
94 if (p == MAP_FAILED) {
95 throw std::bad_alloc();
96 }
97 char* basePointer = static_cast<char*>(p);
98 void* usablePointer = basePointer + pageSize;
d6ff1755 99#if defined(__OpenBSD__) || defined(__NetBSD__)
f53088f2
OM
100 int res = mprotect(basePointer, pageSize, PROT_NONE);
101 if (res != 0) {
102 munmap(p, allocatedSize);
103 throw std::bad_alloc();
104 }
105 res = mprotect(basePointer + allocatedSize - pageSize, pageSize, PROT_NONE);
106#else
107 int res = mprotect(usablePointer, allocatedSize - (pageSize * 2), PROT_READ | PROT_WRITE);
108#endif
f41541ab 109 if (res != 0) {
f53088f2 110 munmap(p, allocatedSize);
f41541ab
RG
111 throw std::bad_alloc();
112 }
113 return static_cast<pointer>(usablePointer);
584ab041 114#endif
f8f66c91 115 }
5cf909f3 116
f8f66c91
OM
117 void
118 deallocate(pointer const ptr, size_type const n) noexcept
119 {
f41541ab 120#ifdef LAZY_ALLOCATOR_USES_NEW
f8f66c91
OM
121#if defined(__cpp_sized_deallocation) && (__cpp_sized_deallocation >= 201309)
122 ::operator delete(ptr, n * sizeof(value_type));
584ab041 123#else
f8f66c91
OM
124 (void)n;
125 ::operator delete(ptr);
584ab041 126#endif
f41541ab 127#else /* LAZY_ALLOCATOR_USES_NEW */
f53088f2 128 static const size_type pageSize = sysconf(_SC_PAGESIZE);
f41541ab
RG
129
130 const size_type requestedSize = n * sizeof(value_type);
131 const auto padding = getAlignmentPadding(requestedSize, pageSize);
132 const size_type allocatedSize = requestedSize + padding + (pageSize * 2);
133
134 void* basePointer = static_cast<char*>(ptr) - pageSize;
135 munmap(basePointer, allocatedSize);
136#endif /* LAZY_ALLOCATOR_PROTECT */
f8f66c91 137 }
5cf909f3 138
f8f66c91 139 void construct(T*) const noexcept {}
5cf909f3 140
f8f66c91
OM
141 template <typename X, typename... Args>
142 void
143 construct(X* place, Args&&... args) const noexcept
144 {
145 new (static_cast<void*>(place)) X(std::forward<Args>(args)...);
146 }
5cf909f3
AN
147};
148
f8f66c91
OM
149template <typename T>
150inline bool operator==(lazy_allocator<T> const&, lazy_allocator<T> const&) noexcept
151{
152 return true;
5cf909f3
AN
153}
154
f8f66c91
OM
155template <typename T>
156inline bool operator!=(lazy_allocator<T> const&, lazy_allocator<T> const&) noexcept
157{
158 return false;
5cf909f3 159}