]>
Commit | Line | Data |
---|---|---|
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 | |
37 | template <typename T> | |
f8f66c91 OM |
38 | struct 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 |
149 | template <typename T> |
150 | inline bool operator==(lazy_allocator<T> const&, lazy_allocator<T> const&) noexcept | |
151 | { | |
152 | return true; | |
5cf909f3 AN |
153 | } |
154 | ||
f8f66c91 OM |
155 | template <typename T> |
156 | inline bool operator!=(lazy_allocator<T> const&, lazy_allocator<T> const&) noexcept | |
157 | { | |
158 | return false; | |
5cf909f3 | 159 | } |