diff options
Diffstat (limited to 'memory/mozalloc')
-rw-r--r-- | memory/mozalloc/cxxalloc.cpp | 26 | ||||
-rw-r--r-- | memory/mozalloc/cxxalloc.h | 82 | ||||
-rw-r--r-- | memory/mozalloc/moz.build | 59 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc.cpp | 159 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc.h | 198 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc_abort.cpp | 96 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc_abort.h | 28 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc_oom.cpp | 52 | ||||
-rw-r--r-- | memory/mozalloc/mozalloc_oom.h | 29 | ||||
-rw-r--r-- | memory/mozalloc/msvc_raise_wrappers.cpp | 17 | ||||
-rw-r--r-- | memory/mozalloc/throw_gcc.h | 152 | ||||
-rw-r--r-- | memory/mozalloc/winheap.cpp | 55 |
12 files changed, 953 insertions, 0 deletions
diff --git a/memory/mozalloc/cxxalloc.cpp b/memory/mozalloc/cxxalloc.cpp new file mode 100644 index 0000000000..41f419fe2d --- /dev/null +++ b/memory/mozalloc/cxxalloc.cpp @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define MOZ_MEMORY_IMPL +#include "mozmemory_wrap.h" +#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC +// See mozmemory_wrap.h for more details. Files that are part of libmozglue, +// need to use _impl suffixes, which is becoming cumbersome. We'll have to use +// something like a malloc.h wrapper and allow the use of the functions without +// a _impl suffix. In the meanwhile, this is enough to get by for C++ code. +#define MALLOC_DECL(name, return_type, ...) \ + MOZ_MEMORY_API return_type name##_impl(__VA_ARGS__); +#include "malloc_decls.h" + +#include "mozilla/Attributes.h" + +extern "C" MFBT_API void* moz_xmalloc(size_t size) MOZ_INFALLIBLE_ALLOCATOR; + +namespace std { +struct nothrow_t; +} + +#define MOZALLOC_EXPORT_NEW MFBT_API + +#include "mozilla/cxxalloc.h" diff --git a/memory/mozalloc/cxxalloc.h b/memory/mozalloc/cxxalloc.h new file mode 100644 index 0000000000..c6fb4bb1dc --- /dev/null +++ b/memory/mozalloc/cxxalloc.h @@ -0,0 +1,82 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_cxxalloc_h +#define mozilla_cxxalloc_h + +/* + * We implement the default operators new/delete as part of + * libmozalloc, replacing their definitions in libstdc++. The + * operator new* definitions in libmozalloc will never return a NULL + * pointer. + * + * Each operator new immediately below returns a pointer to memory + * that can be delete'd by any of + * + * (1) the matching infallible operator delete immediately below + * (2) the matching system |operator delete(void*, std::nothrow)| + * (3) the matching system |operator delete(void*) noexcept(false)| + * + * NB: these are declared |noexcept(false)|, though they will never + * throw that exception. This declaration is consistent with the rule + * that |::operator new() noexcept(false)| will never return NULL. + * + * NB: mozilla::fallible can be used instead of std::nothrow. + */ + +#ifndef MOZALLOC_EXPORT_NEW +# define MOZALLOC_EXPORT_NEW MFBT_API +#endif + +MOZALLOC_EXPORT_NEW void* operator new(size_t size) noexcept(false) { + return moz_xmalloc(size); +} + +MOZALLOC_EXPORT_NEW void* operator new(size_t size, + const std::nothrow_t&) noexcept(true) { + return malloc_impl(size); +} + +MOZALLOC_EXPORT_NEW void* operator new[](size_t size) noexcept(false) { + return moz_xmalloc(size); +} + +MOZALLOC_EXPORT_NEW void* operator new[](size_t size, + const std::nothrow_t&) noexcept(true) { + return malloc_impl(size); +} + +MOZALLOC_EXPORT_NEW void operator delete(void* ptr) noexcept(true) { + return free_impl(ptr); +} + +MOZALLOC_EXPORT_NEW void operator delete(void* ptr, + const std::nothrow_t&) noexcept(true) { + return free_impl(ptr); +} + +MOZALLOC_EXPORT_NEW void operator delete[](void* ptr) noexcept(true) { + return free_impl(ptr); +} + +MOZALLOC_EXPORT_NEW void operator delete[]( + void* ptr, const std::nothrow_t&) noexcept(true) { + return free_impl(ptr); +} + +#if defined(XP_WIN) +// We provide the global sized delete overloads unconditionally because the +// MSVC runtime headers do, despite compiling with /Zc:sizedDealloc- +MOZALLOC_EXPORT_NEW void operator delete(void* ptr, + size_t /*size*/) noexcept(true) { + return free_impl(ptr); +} + +MOZALLOC_EXPORT_NEW void operator delete[](void* ptr, + size_t /*size*/) noexcept(true) { + return free_impl(ptr); +} +#endif + +#endif /* mozilla_cxxalloc_h */ diff --git a/memory/mozalloc/moz.build b/memory/mozalloc/moz.build new file mode 100644 index 0000000000..c5cd784607 --- /dev/null +++ b/memory/mozalloc/moz.build @@ -0,0 +1,59 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +NoVisibilityFlags() + +EXPORTS.mozilla += [ + "cxxalloc.h", + "mozalloc.h", + "mozalloc_abort.h", + "mozalloc_oom.h", +] + +if CONFIG["WRAP_STL_INCLUDES"]: + if CONFIG["CC_TYPE"] in ("clang", "gcc"): + EXPORTS.mozilla += ["throw_gcc.h"] + elif CONFIG["CC_TYPE"] == "clang-cl": + DEFINES["_HAS_EXCEPTIONS"] = 0 + SOURCES += [ + "msvc_raise_wrappers.cpp", + ] + +if CONFIG["OS_TARGET"] == "WINNT": + # Don't build winheap.cpp when mozglue is a static library. + if CONFIG["MOZ_MEMORY"] or not CONFIG["JS_STANDALONE"]: + # Keep this file separate to avoid #include'ing windows.h everywhere. + SOURCES += [ + "winheap.cpp", + ] + +UNIFIED_SOURCES += [ + "mozalloc.cpp", + "mozalloc_abort.cpp", + "mozalloc_oom.cpp", +] + +if CONFIG["MOZ_MEMORY"]: + # In MinGW, we don't want to actually export these functions out of the library + # as the functions in libc++ correctly forward to jemalloc and exporting them + # produces duplicate symbol errors. + if not (CONFIG["CC_TYPE"] == "clang" and CONFIG["OS_TARGET"] == "WINNT"): + SOURCES += [ + "cxxalloc.cpp", + ] + +FINAL_LIBRARY = "mozglue" + +# The strndup declaration in string.h is in an ifdef __USE_GNU section +DEFINES["_GNU_SOURCE"] = True + +DisableStlWrapping() + +LOCAL_INCLUDES += [ + "!/xpcom", + "/memory/build", +] + +DIST_INSTALL = True diff --git a/memory/mozalloc/mozalloc.cpp b/memory/mozalloc/mozalloc.cpp new file mode 100644 index 0000000000..aef8ab943a --- /dev/null +++ b/memory/mozalloc/mozalloc.cpp @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stddef.h> // for size_t + +#if defined(MALLOC_H) +# include MALLOC_H // for memalign, malloc_size, malloc_us +#endif // if defined(MALLOC_H) + +#if !defined(MOZ_MEMORY) +// When jemalloc is disabled, or when building the static runtime variant, +// we need not to use the suffixes. + +# include <stdlib.h> // for malloc, free +# if defined(XP_UNIX) +# include <unistd.h> +# endif // if defined(XP_UNIX) + +# define malloc_impl malloc +# define calloc_impl calloc +# define realloc_impl realloc +# define free_impl free +# define memalign_impl memalign +# define malloc_usable_size_impl malloc_usable_size +# define strdup_impl strdup +# define strndup_impl strndup + +#endif + +#include <errno.h> +#include <new> // for std::bad_alloc +#include <cstring> + +#include <sys/types.h> + +#include "mozilla/Assertions.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/Likely.h" +#include "mozilla/mozalloc.h" +#include "mozilla/mozalloc_oom.h" // for mozalloc_handle_oom + +#if defined(MOZ_MEMORY) +MOZ_MEMORY_API char* strdup_impl(const char*); +MOZ_MEMORY_API char* strndup_impl(const char*, size_t); +#endif + +void* moz_xmalloc(size_t size) { + void* ptr = malloc_impl(size); + if (MOZ_UNLIKELY(!ptr && size)) { + mozalloc_handle_oom(size); + return moz_xmalloc(size); + } + return ptr; +} + +void* moz_xcalloc(size_t nmemb, size_t size) { + void* ptr = calloc_impl(nmemb, size); + if (MOZ_UNLIKELY(!ptr && nmemb && size)) { + mozilla::CheckedInt<size_t> totalSize = + mozilla::CheckedInt<size_t>(nmemb) * size; + mozalloc_handle_oom(totalSize.isValid() ? totalSize.value() : SIZE_MAX); + return moz_xcalloc(nmemb, size); + } + return ptr; +} + +void* moz_xrealloc(void* ptr, size_t size) { + void* newptr = realloc_impl(ptr, size); + if (MOZ_UNLIKELY(!newptr && size)) { + mozalloc_handle_oom(size); + return moz_xrealloc(ptr, size); + } + return newptr; +} + +char* moz_xstrdup(const char* str) { + char* dup = strdup_impl(str); + if (MOZ_UNLIKELY(!dup)) { + mozalloc_handle_oom(0); + return moz_xstrdup(str); + } + return dup; +} + +#if defined(HAVE_STRNDUP) +char* moz_xstrndup(const char* str, size_t strsize) { + char* dup = strndup_impl(str, strsize); + if (MOZ_UNLIKELY(!dup)) { + mozalloc_handle_oom(strsize); + return moz_xstrndup(str, strsize); + } + return dup; +} +#endif // if defined(HAVE_STRNDUP) + +void* moz_xmemdup(const void* ptr, size_t size) { + void* newPtr = moz_xmalloc(size); + memcpy(newPtr, ptr, size); + return newPtr; +} + +#ifndef __wasm__ +# ifndef HAVE_MEMALIGN +// We always have a definition of memalign, but system headers don't +// necessarily come with a declaration. +extern "C" void* memalign(size_t, size_t); +# endif + +void* moz_xmemalign(size_t boundary, size_t size) { + void* ptr = memalign_impl(boundary, size); + if (MOZ_UNLIKELY(!ptr && EINVAL != errno)) { + mozalloc_handle_oom(size); + return moz_xmemalign(boundary, size); + } + // non-NULL ptr or errno == EINVAL + return ptr; +} +#endif + +size_t moz_malloc_usable_size(void* ptr) { + if (!ptr) return 0; + +#if defined(XP_DARWIN) + return malloc_size(ptr); +#elif defined(HAVE_MALLOC_USABLE_SIZE) || defined(MOZ_MEMORY) + return malloc_usable_size_impl(ptr); +#elif defined(XP_WIN) + return _msize(ptr); +#else + return 0; +#endif +} + +size_t moz_malloc_size_of(const void* ptr) { + return moz_malloc_usable_size((void*)ptr); +} + +#if defined(MOZ_MEMORY) +# include "mozjemalloc_types.h" +// mozmemory.h declares jemalloc_ptr_info(), but including that header in this +// file is complicated. So we just redeclare it here instead, and include +// mozjemalloc_types.h for jemalloc_ptr_info_t. +MOZ_JEMALLOC_API void jemalloc_ptr_info(const void* ptr, + jemalloc_ptr_info_t* info); +#endif + +size_t moz_malloc_enclosing_size_of(const void* ptr) { +#if defined(MOZ_MEMORY) + jemalloc_ptr_info_t info; + jemalloc_ptr_info(ptr, &info); + return jemalloc_ptr_is_live(&info) ? info.size : 0; +#else + return 0; +#endif +} diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h new file mode 100644 index 0000000000..1ebbb83237 --- /dev/null +++ b/memory/mozalloc/mozalloc.h @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_mozalloc_h +#define mozilla_mozalloc_h + +/* + * https://bugzilla.mozilla.org/show_bug.cgi?id=427099 + */ + +#if defined(__cplusplus) +# include <new> +// Since libstdc++ 6, including the C headers (e.g. stdlib.h) instead of the +// corresponding C++ header (e.g. cstdlib) can cause confusion in C++ code +// using things defined there. Specifically, with stdlib.h, the use of abs() +// in gfx/graphite2/src/inc/UtfCodec.h somehow ends up picking the wrong abs() +# include <cstdlib> +#else +# include <stdlib.h> +#endif + +#if defined(MOZ_MEMORY) && defined(IMPL_MFBT) +# define MOZ_MEMORY_IMPL +# include "mozmemory_wrap.h" +# define MALLOC_FUNCS MALLOC_FUNCS_MALLOC +// See mozmemory_wrap.h for more details. Files that are part of libmozglue, +// need to use _impl suffixes, which is becoming cumbersome. We'll have to use +// something like a malloc.h wrapper and allow the use of the functions without +// a _impl suffix. In the meanwhile, this is enough to get by for C++ code. +# define NOTHROW_MALLOC_DECL(name, return_type, ...) \ + MOZ_MEMORY_API return_type name##_impl(__VA_ARGS__) noexcept(true); +# define MALLOC_DECL(name, return_type, ...) \ + MOZ_MEMORY_API return_type name##_impl(__VA_ARGS__); +# include "malloc_decls.h" +#endif + +#if defined(__cplusplus) +# include "mozilla/fallible.h" +# include "mozilla/mozalloc_abort.h" +# include "mozilla/TemplateLib.h" +#endif +#include "mozilla/Attributes.h" +#include "mozilla/Types.h" + +MOZ_BEGIN_EXTERN_C + +/* + * We need to use malloc_impl and free_impl in this file when they are + * defined, because of how mozglue.dll is linked on Windows, where using + * malloc/free would end up using the symbols from the MSVCRT instead of + * ours. + */ +#ifndef free_impl +# define free_impl free +# define free_impl_ +#endif +#ifndef malloc_impl +# define malloc_impl malloc +# define malloc_impl_ +#endif + +/* + * Each declaration below is analogous to a "standard" allocation + * function, except that the out-of-memory handling is made explicit. + * The |moz_x| versions will never return a NULL pointer; if memory + * is exhausted, they abort. The |moz_| versions may return NULL + * pointers if memory is exhausted: their return value must be checked. + * + * All these allocation functions are *guaranteed* to return a pointer + * to memory allocated in such a way that that memory can be freed by + * passing that pointer to |free()|. + */ + +MFBT_API void* moz_xmalloc(size_t size) MOZ_INFALLIBLE_ALLOCATOR; + +MFBT_API void* moz_xcalloc(size_t nmemb, size_t size) MOZ_INFALLIBLE_ALLOCATOR; + +MFBT_API void* moz_xrealloc(void* ptr, size_t size) MOZ_INFALLIBLE_ALLOCATOR; + +MFBT_API char* moz_xstrdup(const char* str) MOZ_INFALLIBLE_ALLOCATOR; + +#if defined(HAVE_STRNDUP) +MFBT_API char* moz_xstrndup(const char* str, + size_t strsize) MOZ_INFALLIBLE_ALLOCATOR; +#endif /* if defined(HAVE_STRNDUP) */ + +MFBT_API void* moz_xmemdup(const void* ptr, + size_t size) MOZ_INFALLIBLE_ALLOCATOR; + +MFBT_API void* moz_xmemalign(size_t boundary, + size_t size) MOZ_INFALLIBLE_ALLOCATOR; + +MFBT_API size_t moz_malloc_usable_size(void* ptr); + +MFBT_API size_t moz_malloc_size_of(const void* ptr); + +/* + * Like moz_malloc_size_of(), but works reliably with interior pointers, i.e. + * pointers into the middle of a live allocation. + */ +MFBT_API size_t moz_malloc_enclosing_size_of(const void* ptr); + +MOZ_END_EXTERN_C + +#ifdef __cplusplus + +/* NB: This is defined with MFBT_API just to silence vacuous warnings + * about symbol visibility on OS X/gcc. + * These symbols are force-inline mainly for performance reasons, and + * not exported. While the standard doesn't allow that, we are in a + * controlled environment where the issues the standard tries to + * prevent don't apply, and we can't end up in situations where + * operator new and operator delete are inconsistent. */ +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Winline-new-delete" +# endif + +# if defined(XP_MACOSX) +# define MOZALLOC_EXPORT_NEW MFBT_API MOZ_ALWAYS_INLINE_EVEN_DEBUG +# else +# define MOZALLOC_EXPORT_NEW MOZ_ALWAYS_INLINE_EVEN_DEBUG +# endif + +# include "mozilla/cxxalloc.h" +# ifdef __clang__ +# pragma clang diagnostic pop +# endif + +/* + * This policy is identical to MallocAllocPolicy, except it uses + * moz_xmalloc/moz_xcalloc/moz_xrealloc instead of + * malloc/calloc/realloc. + */ +class InfallibleAllocPolicy { + public: + template <typename T> + T* maybe_pod_malloc(size_t aNumElems) { + return pod_malloc<T>(aNumElems); + } + + template <typename T> + T* maybe_pod_calloc(size_t aNumElems) { + return pod_calloc<T>(aNumElems); + } + + template <typename T> + T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) { + return pod_realloc<T>(aPtr, aOldSize, aNewSize); + } + + template <typename T> + T* pod_malloc(size_t aNumElems) { + if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) { + reportAllocOverflow(); + } + return static_cast<T*>(moz_xmalloc(aNumElems * sizeof(T))); + } + + template <typename T> + T* pod_calloc(size_t aNumElems) { + return static_cast<T*>(moz_xcalloc(aNumElems, sizeof(T))); + } + + template <typename T> + T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) { + if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) { + reportAllocOverflow(); + } + return static_cast<T*>(moz_xrealloc(aPtr, aNewSize * sizeof(T))); + } + + template <typename T> + void free_(T* aPtr, size_t aNumElems = 0) { + free_impl(aPtr); + } + + void reportAllocOverflow() const { mozalloc_abort("alloc overflow"); } + + bool checkSimulatedOOM() const { return true; } +}; + +#endif /* ifdef __cplusplus */ + +#ifdef malloc_impl_ +# undef malloc_impl_ +# undef malloc_impl +#endif +#ifdef free_impl_ +# undef free_impl_ +# undef free_impl +#endif + +#endif /* ifndef mozilla_mozalloc_h */ diff --git a/memory/mozalloc/mozalloc_abort.cpp b/memory/mozalloc/mozalloc_abort.cpp new file mode 100644 index 0000000000..3cfc92533e --- /dev/null +++ b/memory/mozalloc/mozalloc_abort.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/mozalloc_abort.h" + +#ifdef ANDROID +# include <android/log.h> +#endif +#ifdef MOZ_WIDGET_ANDROID +# include "APKOpen.h" +# include "dlfcn.h" +#endif +#include <stdio.h> +#include <string.h> + +#include "mozilla/Assertions.h" +#include "mozilla/Sprintf.h" + +void mozalloc_abort(const char* const msg) { +#ifndef ANDROID + fputs(msg, stderr); + fputs("\n", stderr); +#else + __android_log_print(ANDROID_LOG_ERROR, "Gecko", "mozalloc_abort: %s", msg); +#endif + +#ifdef MOZ_WIDGET_ANDROID + abortThroughJava(msg); +#endif + + MOZ_CRASH_UNSAFE(msg); +} + +#ifdef MOZ_WIDGET_ANDROID +template <size_t N> +void fillAbortMessage(char (&msg)[N], uintptr_t retAddress) { + /* + * On Android, we often don't have reliable backtrace when crashing inside + * abort(). Therefore, we try to find out who is calling abort() and add + * that to the message. + */ + Dl_info info = {}; + dladdr(reinterpret_cast<void*>(retAddress), &info); + + const char* const module = info.dli_fname ? info.dli_fname : ""; + const char* const base_module = strrchr(module, '/'); + const void* const module_offset = + reinterpret_cast<void*>(retAddress - uintptr_t(info.dli_fbase)); + const char* const sym = info.dli_sname ? info.dli_sname : ""; + + SprintfLiteral(msg, "abort() called from %s:%p (%s)", + base_module ? base_module + 1 : module, module_offset, sym); +} +#endif + +#if defined(XP_UNIX) && !defined(MOZ_ASAN) && !defined(MOZ_TSAN) && \ + !defined(LIBFUZZER) +// Define abort() here, so that it is used instead of the system abort(). This +// lets us control the behavior when aborting, in order to get better results +// on *NIX platforms. See mozalloc_abort for details. +// +// For AddressSanitizer, we must not redefine system abort because the ASan +// option "abort_on_error=1" calls abort() and therefore causes the following +// call chain with our redefined abort: +// +// ASan -> abort() -> moz_abort() -> MOZ_CRASH() -> Segmentation fault +// +// That segmentation fault will be interpreted as another bug by ASan and as a +// result, ASan will just exit(1) instead of aborting. +// +// The same applies to ThreadSanitizer when run with "halt_on_error=1" in +// combination with "abort_on_error=1". +// +// When building with libFuzzer, it pulls in the UndefinedBehaviorSanitizer +// runtime which also requires the same workaround as with ASan or TSan. +extern "C" void abort(void) { +# ifdef MOZ_WIDGET_ANDROID + char msg[64] = {}; + fillAbortMessage(msg, uintptr_t(__builtin_return_address(0))); +# else + const char* const msg = "Redirecting call to abort() to mozalloc_abort\n"; +# endif + + mozalloc_abort(msg); + + // We won't reach here because mozalloc_abort() is MOZ_NORETURN. But that + // annotation isn't used on ARM (see mozalloc_abort.h for why) so we add a + // unreachable marker here to avoid a "'noreturn' function does return" + // warning. + MOZ_ASSUME_UNREACHABLE_MARKER(); +} +#endif diff --git a/memory/mozalloc/mozalloc_abort.h b/memory/mozalloc/mozalloc_abort.h new file mode 100644 index 0000000000..b9ff92a18e --- /dev/null +++ b/memory/mozalloc/mozalloc_abort.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_mozalloc_abort_h +#define mozilla_mozalloc_abort_h + +#include "mozilla/Attributes.h" +#include "mozilla/Types.h" + +/** + * Terminate this process in such a way that breakpad is triggered, if + * at all possible. + * + * Note: MOZ_NORETURN seems to break crash stacks on ARM, so we don't + * use that annotation there. + */ +extern "C" MFBT_API +#if !defined(__arm__) + MOZ_NORETURN +#endif + void + mozalloc_abort(const char* const msg); + +#endif /* ifndef mozilla_mozalloc_abort_h */ diff --git a/memory/mozalloc/mozalloc_oom.cpp b/memory/mozalloc/mozalloc_oom.cpp new file mode 100644 index 0000000000..efe5dab4a2 --- /dev/null +++ b/memory/mozalloc/mozalloc_oom.cpp @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/mozalloc_abort.h" +#include "mozilla/mozalloc_oom.h" +#include "mozilla/Assertions.h" + +#define OOM_MSG_LEADER "out of memory: 0x" +#define OOM_MSG_DIGITS "0000000000000000" // large enough for 2^64 +#define OOM_MSG_TRAILER " bytes requested" +#define OOM_MSG_FIRST_DIGIT_OFFSET sizeof(OOM_MSG_LEADER) - 1 +#define OOM_MSG_LAST_DIGIT_OFFSET \ + sizeof(OOM_MSG_LEADER) + sizeof(OOM_MSG_DIGITS) - 3 + +MFBT_DATA size_t gOOMAllocationSize = 0; + +static const char* hex = "0123456789ABCDEF"; + +void mozalloc_handle_oom(size_t size) { + char oomMsg[] = OOM_MSG_LEADER OOM_MSG_DIGITS OOM_MSG_TRAILER; + size_t i; + + // NB: this is handle_oom() stage 1, which simply aborts on OOM. + // we might proceed to a stage 2 in which an attempt is made to + // reclaim memory + // Warning: when stage 2 is done by, for example, notifying + // "memory-pressure" synchronously, please audit all + // nsExpirationTrackers and ensure that the actions they take + // on memory-pressure notifications (via NotifyExpired) are safe. + // Note that Document::SelectorCache::NotifyExpired is _known_ + // to not be safe: it will delete the selector it's caching, + // which might be in use at the time under querySelector or + // querySelectorAll. + + gOOMAllocationSize = size; + + static_assert(OOM_MSG_FIRST_DIGIT_OFFSET > 0, + "Loop below will never terminate (i can't go below 0)"); + + // Insert size into the diagnostic message using only primitive operations + for (i = OOM_MSG_LAST_DIGIT_OFFSET; size && i >= OOM_MSG_FIRST_DIGIT_OFFSET; + i--) { + oomMsg[i] = hex[size % 16]; + size /= 16; + } + + mozalloc_abort(oomMsg); +} diff --git a/memory/mozalloc/mozalloc_oom.h b/memory/mozalloc/mozalloc_oom.h new file mode 100644 index 0000000000..be504bac85 --- /dev/null +++ b/memory/mozalloc/mozalloc_oom.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_mozalloc_oom_h +#define mozilla_mozalloc_oom_h + +#include "mozalloc.h" + +/** + * Called when memory is critically low. Returns iff it was able to + * remedy the critical memory situation; if not, it will abort(). + */ +#ifdef __wasm__ +__attribute__((import_module("env"))) +__attribute__((import_name("mozalloc_handle_oom"))) +#endif +MFBT_API void +mozalloc_handle_oom(size_t requestedSize); + +extern MFBT_DATA size_t gOOMAllocationSize; + +/* TODO: functions to query system memory usage and register + * critical-memory handlers. */ + +#endif /* ifndef mozilla_mozalloc_oom_h */ diff --git a/memory/mozalloc/msvc_raise_wrappers.cpp b/memory/mozalloc/msvc_raise_wrappers.cpp new file mode 100644 index 0000000000..6eb8bdbe31 --- /dev/null +++ b/memory/mozalloc/msvc_raise_wrappers.cpp @@ -0,0 +1,17 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <exception> +#include "mozalloc_abort.h" + +static void __cdecl RaiseHandler(const std::exception& e) { + mozalloc_abort(e.what()); +} + +static struct StaticScopeStruct final { + StaticScopeStruct() { std::exception::_Set_raise_handler(RaiseHandler); } +} StaticScopeInvoke; diff --git a/memory/mozalloc/throw_gcc.h b/memory/mozalloc/throw_gcc.h new file mode 100644 index 0000000000..6a452ca5fc --- /dev/null +++ b/memory/mozalloc/throw_gcc.h @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_throw_gcc_h +#define mozilla_throw_gcc_h + +#if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION < 14000 + +# include "mozilla/Attributes.h" + +# include <stdio.h> // snprintf +# include <string.h> // strerror + +// For gcc, we define these inline to abort so that we're absolutely +// certain that (i) no exceptions are thrown from Gecko; (ii) these +// errors are always terminal and caught by breakpad. + +# include "mozilla/mozalloc_abort.h" + +// libc++ 4.0.0 and higher use C++11 [[noreturn]] attributes for the functions +// below, and since clang does not allow mixing __attribute__((noreturn)) and +// [[noreturn]], we have to explicitly use the latter here. See bug 1329520. +# if defined(__clang__) +# if __has_feature(cxx_attributes) && defined(_LIBCPP_VERSION) && \ + _LIBCPP_VERSION >= 4000 +# define MOZ_THROW_NORETURN [[noreturn]] +# endif +# endif +# ifndef MOZ_THROW_NORETURN +# define MOZ_THROW_NORETURN MOZ_NORETURN +# endif + +// MinGW doesn't appropriately inline these functions in debug builds, +// so we need to do some extra coercion for it to do so. Bug 1332747 +# ifdef __MINGW32__ +# define MOZ_THROW_INLINE MOZ_ALWAYS_INLINE_EVEN_DEBUG +# define MOZ_THROW_EXPORT +# else +# define MOZ_THROW_INLINE MOZ_ALWAYS_INLINE +# define MOZ_THROW_EXPORT MOZ_EXPORT +# endif + +namespace std { + +// NB: user code is not supposed to touch the std:: namespace. We're +// doing this after careful review because we want to define our own +// exception throwing semantics. Don't try this at home! + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_bad_exception( + void) { + mozalloc_abort("fatal: STL threw bad_exception"); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_bad_alloc( + void) { + mozalloc_abort("fatal: STL threw bad_alloc"); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_bad_cast( + void) { + mozalloc_abort("fatal: STL threw bad_cast"); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_bad_typeid( + void) { + mozalloc_abort("fatal: STL threw bad_typeid"); +} + +// used by <functional> +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void +__throw_bad_function_call(void) { + mozalloc_abort("fatal: STL threw bad_function_call"); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_logic_error( + const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_domain_error( + const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void +__throw_invalid_argument(const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_length_error( + const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_out_of_range( + const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_runtime_error( + const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_range_error( + const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void +__throw_overflow_error(const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void +__throw_underflow_error(const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_ios_failure( + const char* msg) { + mozalloc_abort(msg); +} + +MOZ_THROW_NORETURN MOZ_THROW_EXPORT MOZ_THROW_INLINE void __throw_system_error( + int err) { + char error[128]; + snprintf(error, sizeof(error) - 1, "fatal: STL threw system_error: %s (%d)", + strerror(err), err); + mozalloc_abort(error); +} + +MOZ_THROW_NORETURN MOZ_EXPORT MOZ_ALWAYS_INLINE void __throw_regex_error( + int err) { + char error[128]; + snprintf(error, sizeof(error) - 1, "fatal: STL threw regex_error: %s (%d)", + strerror(err), err); + mozalloc_abort(error); +} + +} // namespace std + +# undef MOZ_THROW_NORETURN +# undef MOZ_THROW_INLINE + +#endif + +#endif // mozilla_throw_gcc_h diff --git a/memory/mozalloc/winheap.cpp b/memory/mozalloc/winheap.cpp new file mode 100644 index 0000000000..1d2e1e5599 --- /dev/null +++ b/memory/mozalloc/winheap.cpp @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=4 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/mozalloc.h" +#include <windows.h> + +#if !defined(MOZ_MEMORY) +# include <malloc.h> +# define malloc_impl malloc +# define calloc_impl calloc +# define realloc_impl realloc +# define free_impl free +#endif + +// Warning: C4273: 'HeapAlloc': inconsistent dll linkage +// The Windows headers define HeapAlloc as dllimport, but we define it as +// dllexport, which is a voluntary inconsistency. +#pragma warning(disable : 4273) + +MFBT_API +LPVOID WINAPI HeapAlloc(_In_ HANDLE hHeap, _In_ DWORD dwFlags, + _In_ SIZE_T dwBytes) { + if (dwFlags & HEAP_ZERO_MEMORY) { + return calloc_impl(1, dwBytes); + } + return malloc_impl(dwBytes); +} + +MFBT_API +LPVOID WINAPI HeapReAlloc(_In_ HANDLE hHeap, _In_ DWORD dwFlags, + _In_ LPVOID lpMem, _In_ SIZE_T dwBytes) { + // The HeapReAlloc contract is that failures preserve the existing + // allocation. We can't try to realloc in-place without possibly + // freeing the original allocation, breaking the contract. + // We also can't guarantee we zero all the memory from the end of + // the original allocation to the end of the new one because of the + // difference between the originally requested size and what + // malloc_usable_size would return us. + // So for both cases, just tell the caller we can't do what they + // requested. + if (dwFlags & (HEAP_REALLOC_IN_PLACE_ONLY | HEAP_ZERO_MEMORY)) { + return NULL; + } + return realloc_impl(lpMem, dwBytes); +} + +MFBT_API +BOOL WINAPI HeapFree(_In_ HANDLE hHeap, _In_ DWORD dwFlags, _In_ LPVOID lpMem) { + free_impl(lpMem); + return true; +} |