stack-pool.cpp
1 /** 2 * This file is part of Darling. 3 * 4 * Copyright (C) 2022 Darling developers 5 * 6 * Darling is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * Darling is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <darlingserver/stack-pool.hpp> 21 22 #include <unistd.h> 23 #include <sys/mman.h> 24 #include <assert.h> 25 #include <system_error> 26 27 #if DSERVER_ASAN 28 #include <sanitizer/asan_interface.h> 29 #endif 30 31 bool DarlingServer::StackPool::Stack::isValid() const { 32 return base != nullptr && size != 0; 33 }; 34 35 DarlingServer::StackPool::Stack::operator bool() const { 36 return isValid(); 37 }; 38 39 DarlingServer::StackPool::StackPool(size_t idleStackCount, size_t stackSize, bool useGuardPages): 40 _idleStackCount(idleStackCount), 41 _stackSize(stackSize), 42 _useGuardPages(useGuardPages) 43 { 44 for (size_t i = 0; i < _idleStackCount; ++i) { 45 _stacks.push_back(_allocate(_stackSize, _useGuardPages)); 46 } 47 }; 48 49 void* DarlingServer::StackPool::_allocate(size_t stackSize, bool useGuardPages) { 50 void* stack = NULL; 51 size_t pageSize = sysconf(_SC_PAGESIZE); 52 53 if (useGuardPages) { 54 stack = mmap(NULL, stackSize + pageSize * 2, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 55 } else { 56 stack = mmap(NULL, stackSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 57 } 58 59 if (stack == MAP_FAILED) { 60 throw std::system_error(errno, std::generic_category()); 61 } 62 63 if (useGuardPages) { 64 mprotect(stack, pageSize, PROT_NONE); 65 stack = (char*)stack + pageSize; 66 mprotect((char*)stack + stackSize, pageSize, PROT_NONE); 67 } 68 69 return stack; 70 }; 71 72 void DarlingServer::StackPool::_free(void* stack, size_t stackSize, bool useGuardPages) { 73 size_t pageSize = sysconf(_SC_PAGESIZE); 74 75 if (useGuardPages) { 76 if (munmap((char*)stack - pageSize, stackSize + pageSize * 2) < 0) { 77 throw std::system_error(errno, std::generic_category()); 78 } 79 } else { 80 if (munmap(stack, stackSize) < 0) { 81 throw std::system_error(errno, std::generic_category()); 82 } 83 } 84 }; 85 86 void DarlingServer::StackPool::allocate(Stack& stack) { 87 std::scoped_lock lock(_mutex); 88 89 if (_stacks.size() > 0) { 90 // great, we can use one from the pool 91 92 stack.base = _stacks.back(); 93 stack.size = _stackSize; 94 stack.usesGuardPages = _useGuardPages; 95 96 _stacks.pop_back(); 97 } else { 98 // we don't have any available, so we have to allocate one now 99 stack.base = _allocate(_stackSize, _useGuardPages); 100 stack.size = _stackSize; 101 stack.usesGuardPages = _useGuardPages; 102 } 103 }; 104 105 void DarlingServer::StackPool::free(Stack& stack) { 106 std::scoped_lock lock(_mutex); 107 108 // for now, we only support a single standard stack size and guard page usage 109 assert(stack.size == _stackSize); 110 assert(stack.usesGuardPages == _useGuardPages); 111 112 if (_stacks.size() > _idleStackCount) { 113 // we have more stacks than we want; 114 // just free this one 115 _free(stack.base, stack.size, stack.usesGuardPages); 116 } else { 117 // let's keep this one around 118 _stacks.push_back(stack.base); 119 120 #if DSERVER_ASAN 121 // make sure to unpoison this memory region, since it might be re-used later 122 __asan_unpoison_memory_region(stack.base, stack.size); 123 #endif 124 } 125 126 stack = Stack(); 127 };