/ src / stack-pool.cpp
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  };