merge from dev

This commit is contained in:
daan 2020-05-05 11:00:36 -07:00
commit e3744fa3fe
6 changed files with 170 additions and 91 deletions

View File

@ -4,19 +4,22 @@ project(libmimalloc C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
option(MI_OVERRIDE "Override the standard malloc interface" ON)
option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode" OFF)
option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF)
option(MI_USE_CXX "Use the C++ compiler to compile the library" OFF)
option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode (expensive)" OFF)
option(MI_PADDING "Enable padding to detect heap block overflow (used only in DEBUG mode)" ON)
option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" ON)
option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF)
option(MI_SHOW_ERRORS "Show error and warning messages by default (only enabled by default in DEBUG mode)" OFF)
option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF)
option(MI_SEE_ASM "Generate assembly files" OFF)
option(MI_INTERPOSE "Use interpose to override standard malloc on macOS" ON)
option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" OFF) # enables interpose as well
option(MI_LOCAL_DYNAMIC_TLS "Use slightly slower, dlopen-compatible TLS mechanism (Unix)" OFF)
option(MI_BUILD_SHARED "Build shared library" ON)
option(MI_BUILD_STATIC "Build static library" ON)
option(MI_BUILD_OBJECT "Build object library" ON)
option(MI_BUILD_TESTS "Build test executables" ON)
option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode (deprecated, use MI_DEBUG_FULL instead)" OFF)
option(MI_PADDING "Enable padding to detect heap block overflow (only in debug mode)" ON)
option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF)
option(MI_SHOW_ERRORS "Show error and warning messages by default" OFF)
include("cmake/mimalloc-config-version.cmake")
@ -182,10 +185,23 @@ string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC)
if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel)$"))
set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version
endif()
if(MI_BUILD_SHARED)
list(APPEND mi_build_targets "shared")
endif()
if(MI_BUILD_STATIC)
list(APPEND mi_build_targets "static")
endif()
if(MI_BUILD_OBJECT)
list(APPEND mi_build_targets "object")
endif()
if(MI_BUILD_TESTS)
list(APPEND mi_build_targets "tests")
endif()
message(STATUS "")
message(STATUS "Library base name: ${mi_basename}")
message(STATUS "Build type : ${CMAKE_BUILD_TYPE_LC}")
message(STATUS "Install directory: ${mi_install_dir}")
message(STATUS "Build targets : ${mi_build_targets}")
message(STATUS "")
# -----------------------------------------------------------------------------
@ -193,53 +209,59 @@ message(STATUS "")
# -----------------------------------------------------------------------------
# shared library
add_library(mimalloc SHARED ${mi_sources})
set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} OUTPUT_NAME ${mi_basename} )
target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT)
target_compile_options(mimalloc PRIVATE ${mi_cflags})
target_link_libraries(mimalloc PUBLIC ${mi_libraries})
target_include_directories(mimalloc PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include>
)
if(WIN32)
# On windows copy the mimalloc redirection dll too.
target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect.lib)
add_custom_command(TARGET mimalloc POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect.dll" $<TARGET_FILE_DIR:mimalloc>
COMMENT "Copy mimalloc-redirect.dll to output directory")
if(MI_BUILD_SHARED)
add_library(mimalloc SHARED ${mi_sources})
set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} OUTPUT_NAME ${mi_basename} )
target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT)
target_compile_options(mimalloc PRIVATE ${mi_cflags})
target_link_libraries(mimalloc PUBLIC ${mi_libraries})
target_include_directories(mimalloc PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include>
)
if(WIN32)
# On windows copy the mimalloc redirection dll too.
target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect.lib)
add_custom_command(TARGET mimalloc POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect.dll" $<TARGET_FILE_DIR:mimalloc>
COMMENT "Copy mimalloc-redirect.dll to output directory")
endif()
install(TARGETS mimalloc EXPORT mimalloc DESTINATION ${mi_install_dir} LIBRARY)
install(EXPORT mimalloc DESTINATION ${mi_install_dir}/cmake)
endif()
# static library
add_library(mimalloc-static STATIC ${mi_sources})
set_property(TARGET mimalloc-static PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB)
target_compile_options(mimalloc-static PRIVATE ${mi_cflags})
target_link_libraries(mimalloc-static PUBLIC ${mi_libraries})
target_include_directories(mimalloc-static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include>
)
if(WIN32)
# When building both static and shared libraries on Windows, a static library should use a
# different output name to avoid the conflict with the import library of a shared one.
string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename})
set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_output_name})
else()
set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename})
if (MI_BUILD_STATIC)
add_library(mimalloc-static STATIC ${mi_sources})
set_property(TARGET mimalloc-static PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB)
target_compile_options(mimalloc-static PRIVATE ${mi_cflags})
target_link_libraries(mimalloc-static PUBLIC ${mi_libraries})
target_include_directories(mimalloc-static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include>
)
if(WIN32)
# When building both static and shared libraries on Windows, a static library should use a
# different output name to avoid the conflict with the import library of a shared one.
string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename})
set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_output_name})
else()
set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename})
endif()
install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_dir})
endif()
# install static and shared library, and the include files
install(TARGETS mimalloc EXPORT mimalloc DESTINATION ${mi_install_dir} LIBRARY)
install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_dir})
# install include files
install(FILES include/mimalloc.h DESTINATION ${mi_install_dir}/include)
install(FILES include/mimalloc-override.h DESTINATION ${mi_install_dir}/include)
install(FILES include/mimalloc-new-delete.h DESTINATION ${mi_install_dir}/include)
install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_dir}/cmake)
install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_dir}/cmake)
install(EXPORT mimalloc DESTINATION ${mi_install_dir}/cmake)
if(NOT WIN32)
if(NOT WIN32 AND MI_BUILD_SHARED)
# install a symlink in the /usr/local/lib to the versioned library
set(mi_symlink "${CMAKE_SHARED_MODULE_PREFIX}${mi_basename}${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(mi_soname "mimalloc-${mi_version}/${mi_symlink}.${mi_version}")
@ -248,23 +270,25 @@ if(NOT WIN32)
endif()
# single object file for more predictable static overriding
add_library(mimalloc-obj OBJECT src/static.c)
set_property(TARGET mimalloc-obj PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(mimalloc-obj PRIVATE ${mi_defines})
target_compile_options(mimalloc-obj PRIVATE ${mi_cflags})
target_include_directories(mimalloc-obj PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include>
)
if (MI_BUILD_OBJECT)
add_library(mimalloc-obj OBJECT src/static.c)
set_property(TARGET mimalloc-obj PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(mimalloc-obj PRIVATE ${mi_defines})
target_compile_options(mimalloc-obj PRIVATE ${mi_cflags})
target_include_directories(mimalloc-obj PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include>
)
# the following seems to lead to cmake warnings/errors on some systems, disable for now :-(
# install(TARGETS mimalloc-obj EXPORT mimalloc DESTINATION ${mi_install_dir})
# the following seems to lead to cmake warnings/errors on some systems, disable for now :-(
# install(TARGETS mimalloc-obj EXPORT mimalloc DESTINATION ${mi_install_dir})
# the FILES expression can also be: $<TARGET_OBJECTS:mimalloc-obj>
# but that fails cmake versions less than 3.10 so we leave it as is for now
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/mimalloc-obj.dir/src/static.c${CMAKE_C_OUTPUT_EXTENSION}
DESTINATION ${mi_install_dir}
RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} )
# the FILES expression can also be: $<TARGET_OBJECTS:mimalloc-obj>
# but that fails cmake versions less than 3.10 so we leave it as is for now
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/mimalloc-obj.dir/src/static.c${CMAKE_C_OUTPUT_EXTENSION}
DESTINATION ${mi_install_dir}
RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} )
endif()
# -----------------------------------------------------------------------------
# API surface testing
@ -292,10 +316,16 @@ endif()
# Set override properties
# -----------------------------------------------------------------------------
if (MI_OVERRIDE MATCHES "ON")
target_compile_definitions(mimalloc PRIVATE MI_MALLOC_OVERRIDE)
if (MI_BUILD_SHARED)
target_compile_definitions(mimalloc PRIVATE MI_MALLOC_OVERRIDE)
endif()
if(NOT WIN32)
# It is only possible to override malloc on Windows when building as a DLL.
target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE)
target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE)
if (MI_BUILD_STATIC)
target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE)
endif()
if (MI_BUILD_OBJECT)
target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE)
endif()
endif()
endif()

View File

@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file
#ifndef MIMALLOC_H
#define MIMALLOC_H
#define MI_MALLOC_VERSION 163 // major + 2 digits minor
#define MI_MALLOC_VERSION 164 // major + 2 digits minor
// ------------------------------------------------------
// Compiler specific attributes

View File

@ -11,7 +11,7 @@ mimalloc (pronounced "me-malloc")
is a general purpose allocator with excellent [performance](#performance) characteristics.
Initially developed by Daan Leijen for the run-time systems of the
[Koka](https://github.com/koka-lang/koka) and [Lean](https://github.com/leanprover/lean) languages.
Latest release:`v1.6.2` (2020-04-20).
Latest release:`v1.6.3` (2020-05-05).
It is a drop-in replacement for `malloc` and can be used in other programs
without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as:
@ -57,6 +57,8 @@ Enjoy!
### Releases
* 2020-05-05, `v1.6.3`: stable release 1.6: improved behavior in out-of-memory situations, improved malloc zones on macOS,
build PIC static libraries by default, add option to abort on out-of-memory, line buffered statistics.
* 2020-04-20, `v1.6.2`: stable release 1.6: fix compilation on Android, MingW, Raspberry, and Conda,
stability fix for Windows 7, fix multiple mimalloc instances in one executable, fix `strnlen` overload,
fix aligned debug padding.
@ -262,19 +264,19 @@ to make mimalloc more robust against exploits. In particular:
- All internal mimalloc pages are surrounded by guard pages and the heap metadata is behind a guard page as well (so a buffer overflow
exploit cannot reach into the metadata),
- All free list pointers are
[encoded](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396)
- All free list pointers are
[encoded](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396)
with per-page keys which is used both to prevent overwrites with a known pointer, as well as to detect heap corruption,
- Double free's are detected (and ignored),
- The free lists are initialized in a random order and allocation randomly chooses between extension and reuse within a page to
mitigate against attacks that rely on a predicable allocation order. Similarly, the larger heap blocks allocated by mimalloc
- The free lists are initialized in a random order and allocation randomly chooses between extension and reuse within a page to
mitigate against attacks that rely on a predicable allocation order. Similarly, the larger heap blocks allocated by mimalloc
from the OS are also address randomized.
As always, evaluate with care as part of an overall security strategy as all of the above are mitigations but not guarantees.
## Debug Mode
When _mimalloc_ is built using debug mode, various checks are done at runtime to catch development errors.
When _mimalloc_ is built using debug mode, various checks are done at runtime to catch development errors.
- Statistics are maintained in detail for each object size. They can be shown using `MIMALLOC_SHOW_STATS=1` at runtime.
- All objects have padding at the end to detect (byte precise) heap block overflows.

View File

@ -220,7 +220,6 @@ static bool _mi_heap_done(mi_heap_t* heap) {
heap = heap->tld->heap_backing;
if (!mi_heap_is_initialized(heap)) return false;
// delete all non-backing heaps in this thread
mi_heap_t* curr = heap->tld->heaps;
while (curr != NULL) {
@ -238,13 +237,14 @@ static bool _mi_heap_done(mi_heap_t* heap) {
if (heap != &_mi_heap_main) {
_mi_heap_collect_abandon(heap);
}
// merge stats
_mi_stats_done(&heap->tld->stats);
// free if not the main thread
if (heap != &_mi_heap_main) {
mi_assert_internal(heap->tld->segments.count == 0);
mi_assert_internal(heap->tld->segments.count == 0 || heap->thread_id != _mi_thread_id());
_mi_os_free(heap, sizeof(mi_thread_data_t), &_mi_stats_main);
}
#if 0
@ -359,12 +359,16 @@ void mi_thread_done(void) mi_attr_noexcept {
}
static void _mi_thread_done(mi_heap_t* heap) {
// check thread-id as on Windows shutdown with FLS the main (exit) thread may call this on thread-local heaps...
if (heap->thread_id != _mi_thread_id()) return;
// stats
if (!_mi_is_main_thread() && mi_heap_is_initialized(heap)) {
_mi_stat_decrease(&heap->tld->stats.threads, 1);
}
// abandon the thread local heap
if (_mi_heap_done(heap)) return; // returns true if already ran
if (_mi_heap_done(heap)) return; // returns true if already ran
}
void _mi_heap_set_default_direct(mi_heap_t* heap) {

View File

@ -785,8 +785,29 @@ static mi_page_t* mi_huge_page_alloc(mi_heap_t* heap, size_t size) {
}
// Allocate a page
// Note: in debug mode the size includes extra padding size and might have overflowed.
static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size) mi_attr_noexcept {
// huge allocation?
const size_t req_size = size - mi_extra_padding(heap); // correct for padding_size in case of an overflow on `size`
if (mi_unlikely(req_size > (MI_LARGE_OBJ_SIZE_MAX - mi_extra_padding(heap)))) {
if (mi_unlikely(req_size > PTRDIFF_MAX)) { // we don't allocate more than PTRDIFF_MAX (see <https://sourceware.org/ml/libc-announce/2019/msg00001.html>)
_mi_error_message(EOVERFLOW, "allocation request is too large (%zu b requested)\n", req_size);
return NULL;
}
else {
return mi_huge_page_alloc(heap, size);
}
}
else {
// otherwise find a page with free blocks in our size segregated queues
mi_assert_internal(size >= mi_extra_padding(heap));
return mi_find_free_page(heap, size);
}
}
// Generic allocation routine if the fast path (`alloc.c:mi_page_malloc`) does not succeed.
// Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed.
// Note: in debug mode the size includes extra padding size and might have overflowed.
void* _mi_malloc_generic(mi_heap_t* heap, size_t size MI_SOURCE_XPARAM) mi_attr_noexcept
{
mi_assert_internal(heap != NULL);
@ -804,23 +825,13 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size MI_SOURCE_XPARAM) mi_attr
// free delayed frees from other threads
_mi_heap_delayed_free(heap);
// huge allocation?
mi_page_t* page;
const size_t req_size = size - mi_extra_padding(heap); // correct for padding_size in case of an overflow on `size`
if (mi_unlikely(req_size > (MI_LARGE_OBJ_SIZE_MAX - mi_extra_padding(heap)) )) {
if (mi_unlikely(req_size > PTRDIFF_MAX)) { // we don't allocate more than PTRDIFF_MAX (see <https://sourceware.org/ml/libc-announce/2019/msg00001.html>)
_mi_error_message(EOVERFLOW, "allocation request is too large (%zu b requested)\n", req_size);
return NULL;
}
else {
page = mi_huge_page_alloc(heap,size);
}
}
else {
// otherwise find a page with free blocks in our size segregated queues
mi_assert_internal(size >= mi_extra_padding(heap));
page = mi_find_free_page(heap,size);
// find (or allocate) a page of the right size
mi_page_t* page = mi_find_page(heap, size);
if (mi_unlikely(page == NULL)) { // first time out of memory, try to collect and retry the allocation once more
mi_heap_collect(heap, true /* force */);
page = mi_find_page(heap, size);
}
if (mi_unlikely(page == NULL)) { // out of memory
_mi_error_message(ENOMEM, "cannot allocate memory (%zu bytes requested)\n", size);
return NULL;

View File

@ -3,9 +3,13 @@
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <new>
#include <vector>
#include <future>
#include <iostream>
#include <thread>
#include <mimalloc.h>
#include <mimalloc-new-delete.h>
#include <mimalloc-override.h>
@ -23,14 +27,16 @@ void heap_no_delete(); // issue #202
void heap_late_free(); // issue #204
void padding_shrink(); // issue #209
void various_tests();
void test_mt_shutdown();
int main() {
mi_stats_reset(); // ignore earlier allocations
heap_thread_free_large();
heap_no_delete();
heap_late_free();
padding_shrink();
heap_no_delete();
heap_late_free();
padding_shrink();
various_tests();
//test_mt_shutdown();
mi_stats_print(NULL);
return 0;
}
@ -133,7 +139,7 @@ bool test_stl_allocator2() {
// Issue #202
static void heap_no_delete_worker() {
mi_heap_t* heap = mi_heap_new();
void* q = mi_heap_malloc(heap,1024);
void* q = mi_heap_malloc(heap, 1024);
// mi_heap_delete(heap); // uncomment to prevent assertion
}
@ -189,3 +195,29 @@ void heap_thread_free_large() {
t1.join();
}
}
void test_mt_shutdown()
{
const int threads = 5;
std::vector< std::future< std::vector< char* > > > ts;
auto fn = [&]()
{
std::vector< char* > ps;
ps.reserve(1000);
for (int i = 0; i < 1000; i++)
ps.emplace_back(new char[1]);
return ps;
};
for (int i = 0; i < threads; i++)
ts.emplace_back(std::async(std::launch::async, fn));
for (auto& f : ts)
for (auto& p : f.get())
delete[] p;
std::cout << "done" << std::endl;
}