diff options
author | Andrew Morrow <acm@mongodb.com> | 2015-10-26 13:01:53 -0400 |
---|---|---|
committer | Andrew Morrow <acm@mongodb.com> | 2015-10-27 08:06:42 -0400 |
commit | e556e4763ad88c3308347b56b4831c46b014627e (patch) | |
tree | 31eb5f821d77b50a7d7185ef571d3824dfea0d03 /src/mongo/platform | |
parent | 17a0b2a3be87fb1a1a86db2ee0bf1871e4b8d6fc (diff) | |
download | mongo-e556e4763ad88c3308347b56b4831c46b014627e.tar.gz |
SERVER-19614 Implement stack bounds detection
Diffstat (limited to 'src/mongo/platform')
-rw-r--r-- | src/mongo/platform/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator.cpp | 73 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator.h | 103 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_freebsd.cpp | 29 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_linux.cpp | 29 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_openbsd.cpp | 29 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_osx.cpp | 51 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_pthread_getattr_np.cpp | 62 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_solaris.cpp | 51 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_test.cpp | 171 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_unknown.cpp | 39 | ||||
-rw-r--r-- | src/mongo/platform/stack_locator_windows.cpp | 129 |
12 files changed, 769 insertions, 0 deletions
diff --git a/src/mongo/platform/SConscript b/src/mongo/platform/SConscript index 021fd0990dc..59bdff21e9c 100644 --- a/src/mongo/platform/SConscript +++ b/src/mongo/platform/SConscript @@ -10,6 +10,8 @@ source_files = [ 'process_id.cpp', 'shared_library.cpp', 'shared_library_${TARGET_OS_FAMILY}.cpp', + 'stack_locator.cpp', + 'stack_locator_${TARGET_OS}.cpp', 'strcasestr.cpp', ] @@ -24,6 +26,7 @@ env.CppUnitTest('bits_test', 'bits_test.cpp') env.CppUnitTest('endian_test', 'endian_test.cpp') env.CppUnitTest('process_id_test', 'process_id_test.cpp', LIBDEPS=['platform']) env.CppUnitTest('random_test', 'random_test.cpp', LIBDEPS=['$BUILD_DIR/mongo/base']) +env.CppUnitTest('stack_locator_test', 'stack_locator_test.cpp', LIBDEPS=['platform']) if GetOption('experimental-decimal-support') == 'on': env.CppUnitTest('decimal128_test', 'decimal128_test.cpp', LIBDEPS=['$BUILD_DIR/mongo/base']) diff --git a/src/mongo/platform/stack_locator.cpp b/src/mongo/platform/stack_locator.cpp new file mode 100644 index 00000000000..4654d6c1977 --- /dev/null +++ b/src/mongo/platform/stack_locator.cpp @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/platform/stack_locator.h" + +#include "mongo/util/assert_util.h" + +namespace mongo { + +boost::optional<std::size_t> StackLocator::available() const { + if (!begin() || !end()) + return boost::none; + + // Technically, it is undefined behavior to compare or subtract + // two pointers that do not point into the same + // aggregate. However, we know that these are both pointers within + // the same stack, and it seems unlikely that the compiler will + // see that it can elide the comparison here. + + const auto cbegin = reinterpret_cast<const char*>(begin()); + const auto cthis = reinterpret_cast<const char*>(this); + const auto cend = reinterpret_cast<const char*>(end()); + + // TODO: Assumes that stack grows downward + invariant(cthis <= cbegin); + invariant(cthis > cend); + + std::size_t avail = cthis - cend; + + return avail; +} + +boost::optional<size_t> StackLocator::size() const { + if (!begin() || !end()) + return boost::none; + + const auto cbegin = reinterpret_cast<const char*>(begin()); + const auto cend = reinterpret_cast<const char*>(end()); + + // TODO: Assumes that stack grows downward + invariant(cbegin > cend); + + return static_cast<size_t>(cbegin - cend); +} + +} // namespace mongo diff --git a/src/mongo/platform/stack_locator.h b/src/mongo/platform/stack_locator.h new file mode 100644 index 00000000000..9fb387580fd --- /dev/null +++ b/src/mongo/platform/stack_locator.h @@ -0,0 +1,103 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include <boost/optional.hpp> +#include <cstddef> + +namespace mongo { + +/** + * Provides access to the current stack bounds and remaining + * available stack space. + * + * To use one, create it on the stack, like this: + * + * // Construct a new locator + * const StackLocator locator; + * + * // Get the start of the stack + * auto b = locator.begin(); + * + * // Get the end of the stack + * auto e = locator.end(); + * + * // Get the remaining space after 'locator' on the stack. + * auto avail = locator.available(); + */ +class StackLocator { +public: + /** + * Constructs a new StackLocator. The locator must have automatic + * storage duration or the behavior is undefined. + */ + StackLocator(); + + /** + * Returns the address of the beginning of the stack, or nullptr + * if this cannot be done. Beginning here means those addresses + * that represent values of automatic duration found earlier in + * the call chain. Returns nullptr if the beginning of the stack + * could not be found. + */ + void* begin() const { + return _begin; + } + + /** + * Returns the address of the end of the stack, or nullptr if + * this cannot be done. End here means those addresses that + * represent values of automatic duration allocated deeper in the + * call chain. Returns nullptr if the end of the stack could not + * be found. + */ + void* end() const { + return _end; + } + + /** + * Returns the apparent size of the stack. Returns a disengaged + * optional if the size of the stack could not be determined. + */ + boost::optional<size_t> size() const; + + /** + * Returns the remaining stack available after the location of + * this StackLocator. Obviously, the StackLocator must have been + * constructed on the stack. Calling 'available' on a heap + * allocated StackAllocator will have undefined behavior. Returns + * a disengaged optional if the remaining stack cannot be + * determined. + */ + boost::optional<std::size_t> available() const; + +private: + void* _begin = nullptr; + void* _end = nullptr; +}; + +} // namespace mongo diff --git a/src/mongo/platform/stack_locator_freebsd.cpp b/src/mongo/platform/stack_locator_freebsd.cpp new file mode 100644 index 00000000000..af46f763783 --- /dev/null +++ b/src/mongo/platform/stack_locator_freebsd.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "stack_locator_unknown.cpp" diff --git a/src/mongo/platform/stack_locator_linux.cpp b/src/mongo/platform/stack_locator_linux.cpp new file mode 100644 index 00000000000..d3bf42542af --- /dev/null +++ b/src/mongo/platform/stack_locator_linux.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "stack_locator_pthread_getattr_np.cpp" diff --git a/src/mongo/platform/stack_locator_openbsd.cpp b/src/mongo/platform/stack_locator_openbsd.cpp new file mode 100644 index 00000000000..af46f763783 --- /dev/null +++ b/src/mongo/platform/stack_locator_openbsd.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "stack_locator_unknown.cpp" diff --git a/src/mongo/platform/stack_locator_osx.cpp b/src/mongo/platform/stack_locator_osx.cpp new file mode 100644 index 00000000000..2c9b855e9b0 --- /dev/null +++ b/src/mongo/platform/stack_locator_osx.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/platform/stack_locator.h" + +#include <pthread.h> + +#include "mongo/util/assert_util.h" + +namespace mongo { + +StackLocator::StackLocator() { + const auto self = pthread_self(); + _begin = pthread_get_stackaddr_np(self); + invariant(_begin); + + const auto size = pthread_get_stacksize_np(self); + invariant(size); + + // TODO: Assumes stack grows downward on OS X. + _end = static_cast<char*>(_begin) - size; +} + +} // namespace mongo diff --git a/src/mongo/platform/stack_locator_pthread_getattr_np.cpp b/src/mongo/platform/stack_locator_pthread_getattr_np.cpp new file mode 100644 index 00000000000..00961d06286 --- /dev/null +++ b/src/mongo/platform/stack_locator_pthread_getattr_np.cpp @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/platform/stack_locator.h" + +#include <pthread.h> + +#include "mongo/util/assert_util.h" + +namespace mongo { + +StackLocator::StackLocator() { + pthread_t self = pthread_self(); + pthread_attr_t selfAttrs; + pthread_attr_init(&selfAttrs); + pthread_getattr_np(self, &selfAttrs); + + void* base = nullptr; + size_t size = 0; + + auto result = pthread_attr_getstack(&selfAttrs, &base, &size); + pthread_attr_destroy(&selfAttrs); + + invariant(result == 0); + invariant(base != nullptr); + invariant(size != 0); + + // TODO: Assumes a downward growing stack. Note here that + // getstack returns the stack *base*, being the bottom of the + // stack, so we need to add size to it. + _end = base; + _begin = static_cast<char*>(_end) + size; +} + +} // namespace mongo diff --git a/src/mongo/platform/stack_locator_solaris.cpp b/src/mongo/platform/stack_locator_solaris.cpp new file mode 100644 index 00000000000..055f25505ba --- /dev/null +++ b/src/mongo/platform/stack_locator_solaris.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/platform/stack_locator.h" + +#include <thread.h> + +#include "mongo/util/assert_util.h" + +namespace mongo { + +StackLocator::StackLocator() { + stack_t stack; + invariant(thr_stksegment(&stack) == 0); + + invariant(stack.ss_sp != nullptr); + invariant(stack.ss_size != 0); + + // TODO: Assumes stack grows downward on Solaris + _begin = stack.ss_sp; + _end = static_cast<char*>(_begin) - stack.ss_size; +} + +} // namespace mongo diff --git a/src/mongo/platform/stack_locator_test.cpp b/src/mongo/platform/stack_locator_test.cpp new file mode 100644 index 00000000000..356b726135e --- /dev/null +++ b/src/mongo/platform/stack_locator_test.cpp @@ -0,0 +1,171 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include <thread> + +#include "mongo/platform/stack_locator.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +TEST(StackLocator, StacLocatorFindsStackOfTestExecutorThread) { + const StackLocator locator; + + const auto begin = locator.begin(); + ASSERT_TRUE(nullptr != begin); + + const auto end = locator.end(); + ASSERT_TRUE(nullptr != end); + + ASSERT_TRUE(begin != end); + + const auto available = locator.available(); + ASSERT_TRUE(available); + ASSERT_TRUE(available.get() > 0); + + const auto size = locator.size(); + ASSERT_TRUE(size); + ASSERT_TRUE(size.get() > 0); + ASSERT_TRUE(size.get() > available.get()); +} + +TEST(StackLocator, StacksGrowsDown) { + // The current implementation assumes a downward growing stack. Write a test + // that confirms that the current platform is downward growing, so that if the + // system is ever ported to an upward growing stack, we get a test failure. + + const StackLocator locator; + ASSERT_TRUE(nullptr != locator.begin()); + ASSERT_TRUE(nullptr != locator.end()); + + // NOTE: Technically, comparing pointers for ordering is UB if + // they aren't in the same aggregate, but we are already out + // with the dragons at the edge of the map. + ASSERT_TRUE(locator.begin() > locator.end()); +} + +TEST(StackLocator, StackLocatorFindsStackOfStdThread) { + bool foundBounds = false; + + std::thread thr([&] { + const StackLocator locator; + auto avail = locator.available(); + foundBounds = static_cast<bool>(avail); + }); + thr.join(); + ASSERT_TRUE(foundBounds); +} + +struct LocatorThreadHelper { +#ifdef _WIN32 + static DWORD WINAPI run(LPVOID arg) { + static_cast<LocatorThreadHelper*>(arg)->_run(); + return 0; + } +#else + static void* run(void* arg) { + static_cast<LocatorThreadHelper*>(arg)->_run(); + return nullptr; + } +#endif + + void _run() { + const StackLocator locator; + located = static_cast<bool>(locator.available()); + if (located) + size = locator.size().get(); + } + + bool located = false; + size_t size = 0; +}; + +TEST(StackLocator, StackLocatorFindsStackOfNativeThreadWithDefaultStack) { + LocatorThreadHelper helper; + +#ifdef _WIN32 + + HANDLE thread = CreateThread(nullptr, 0, &LocatorThreadHelper::run, &helper, 0, nullptr); + ASSERT_NE(WAIT_FAILED, WaitForSingleObject(thread, INFINITE)); + +#else + + pthread_attr_t attrs; + ASSERT_EQ(0, pthread_attr_init(&attrs)); + pthread_t thread; + ASSERT_EQ(0, pthread_create(&thread, &attrs, &LocatorThreadHelper::run, &helper)); + ASSERT_EQ(0, pthread_join(thread, nullptr)); + +#endif + + ASSERT_TRUE(helper.located); +} + +TEST(StackLocator, StackLocatorFindStackOfNativeThreadWithCustomStack) { + const size_t kThreadStackSize = 64 * 1024 * 1024; + +#ifdef _WIN32 + + LocatorThreadHelper helperNoCommit; + HANDLE thread = CreateThread(nullptr, + kThreadStackSize, + &LocatorThreadHelper::run, + &helperNoCommit, + STACK_SIZE_PARAM_IS_A_RESERVATION, + nullptr); + ASSERT_NE(WAIT_FAILED, WaitForSingleObject(thread, INFINITE)); + ASSERT_TRUE(helperNoCommit.located); + ASSERT_EQ(kThreadStackSize, helperNoCommit.size); + + LocatorThreadHelper helperCommit; + thread = CreateThread( + nullptr, kThreadStackSize, &LocatorThreadHelper::run, &helperCommit, 0, nullptr); + ASSERT_NE(WAIT_FAILED, WaitForSingleObject(thread, INFINITE)); + ASSERT_TRUE(helperCommit.located); + ASSERT_TRUE(kThreadStackSize <= helperCommit.size); + +#else + + LocatorThreadHelper helper; + pthread_attr_t attrs; + ASSERT_EQ(0, pthread_attr_init(&attrs)); + ASSERT_EQ(0, pthread_attr_setstacksize(&attrs, kThreadStackSize)); + pthread_t thread; + ASSERT_EQ(0, pthread_create(&thread, &attrs, &LocatorThreadHelper::run, &helper)); + ASSERT_EQ(0, pthread_join(thread, nullptr)); + ASSERT_TRUE(helper.located); + ASSERT_TRUE(kThreadStackSize <= helper.size); + +#endif +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/platform/stack_locator_unknown.cpp b/src/mongo/platform/stack_locator_unknown.cpp new file mode 100644 index 00000000000..fd3fb62c993 --- /dev/null +++ b/src/mongo/platform/stack_locator_unknown.cpp @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/platform/stack_locator.h" + +#include "mongo/util/assert_util.h" + +namespace mongo { + +StackLocator::StackLocator() = default; + +// namespace mongo diff --git a/src/mongo/platform/stack_locator_windows.cpp b/src/mongo/platform/stack_locator_windows.cpp new file mode 100644 index 00000000000..2ead30c0bf0 --- /dev/null +++ b/src/mongo/platform/stack_locator_windows.cpp @@ -0,0 +1,129 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/platform/stack_locator.h" + +#include "mongo/util/assert_util.h" + +namespace mongo { + +StackLocator::StackLocator() { + // Please see + // + // http://stackoverflow.com/questions/1740888/determining-stack-space-with-visual-studio/1747499#1747499 + // + // for notes on the following arcana. + + // TODO: _WIN32_WINNT >= 0x0602 (windows 8 / 2012 server) may be + // able to use GetCurrentThreadStackLimits + + // Put something on the stack, convieniently the variable we are + // going to write into, and ask the VM system for information + // about the memory region it inhabits, which is the committed + // part of the stack. + MEMORY_BASIC_INFORMATION committedMbi = {}; + invariant(VirtualQuery(&committedMbi, &committedMbi, sizeof(committedMbi)) != 0); + invariant(committedMbi.State == MEM_COMMIT); + + // Now committedMbi.AllocationBase points to the reserved stack + // memory base address (the real bottom of the stack), and + // committedMbi.BaseAddress points to base address of the + // committed region, and committedMbi.RegionSize is the size of + // the commit region. Since the stack grows downward, the top of + // the stack is at the base address for the commit region plus the + // region size. + _begin = static_cast<char*>(committedMbi.BaseAddress) + committedMbi.RegionSize; + + // Now, we skip down to the bottom, where the uncommitted memory + // is, and get its size. So, we ask for the region at the + // allocation base (the real bottom of the stack), after which + // uncommitedMbi will have a BaseAddress and a RegionSize that + // describes the uncommited area. The memory should have the + // RESERVE state set. + MEMORY_BASIC_INFORMATION uncommittedMbi = {}; + invariant(VirtualQuery(committedMbi.AllocationBase, &uncommittedMbi, sizeof(uncommittedMbi)) != + 0); + + invariant(committedMbi.AllocationBase == uncommittedMbi.AllocationBase); + invariant(uncommittedMbi.RegionSize > 0); + + // If the stack was created with CreateThread with dwStackSize + // non-zero and without the STACK_SIZE_PARAM_IS_A_RESERVATION + // flag, then the whole AllocationBase is our stack base, and + // there is no guard page. + if (uncommittedMbi.State == MEM_COMMIT) { + // NOTE: Originally, it seemed to make sense that what you would get back + // here would be the same information as in committedMbi. After all, the whole + // stack is committed. So, querying AllocationBase should give you back + // the same region. + // + // Bizzarely, that doesn't seem to be the case. Even though + // it has the same protections, state, etc., VirtualQuery seems + // to consider the stack that has been used distinct from the part + // that hasn't. + // + // invariant(uncommittedMbi.BaseAddress == committedMbi.BaseAddress); + // invariant(uncommittedMbi.RegionSize == committedMbi.RegionSize); + // + + _end = static_cast<char*>(committedMbi.AllocationBase); + return; + } + invariant(uncommittedMbi.State == MEM_RESERVE); + + if (kDebugBuild) { + // Locate the guard page, which sits bewteen the uncommitted + // region (which we know is not empty!), and the committed + // region. We can count the guard page as usable stack space, + // but it is good to find it so we can validate that we walked + // the stack correctly. + + // The end of the guard page is right after the uncommitted + // area. Form a pointer into the guard page by skipping over the + // committed region. + const auto guard = + static_cast<char*>(uncommittedMbi.BaseAddress) + uncommittedMbi.RegionSize; + + // With that pointer in hand, get the info about the guard + // region. We really only care about its size here (and we + // validate that it has the right bits). + MEMORY_BASIC_INFORMATION guardMbi = {}; + invariant(VirtualQuery(guard, &guardMbi, sizeof(guardMbi)) != 0); + + invariant(committedMbi.AllocationBase == guardMbi.AllocationBase); + invariant((guardMbi.Protect & PAGE_GUARD) != 0); + invariant(guardMbi.RegionSize > 0); + } + + // The end of our stack is the allocation base for the whole stack. + _end = static_cast<char*>(committedMbi.AllocationBase); +} + +} // namespace mongo |