From 1af072e0f39d25e6f4016dd524b97291ea18eaee Mon Sep 17 00:00:00 2001 From: Michel Hermier Date: Wed, 12 Jan 2022 13:37:54 +0100 Subject: LibC: Make `*alloc` return `NULL` in case of failure (POSIX) --- Tests/LibC/CMakeLists.txt | 1 + Tests/LibC/TestMalloc.cpp | 40 ++++++++++++++++++++++++++++++++++++++ Userland/Libraries/LibC/malloc.cpp | 20 +++++++++++++++++-- 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 Tests/LibC/TestMalloc.cpp diff --git a/Tests/LibC/CMakeLists.txt b/Tests/LibC/CMakeLists.txt index 47980a90e2..02bde80580 100644 --- a/Tests/LibC/CMakeLists.txt +++ b/Tests/LibC/CMakeLists.txt @@ -9,6 +9,7 @@ set(TEST_SOURCES TestLibCSetjmp.cpp TestLibCString.cpp TestLibCTime.cpp + TestMalloc.cpp TestMemmem.cpp TestQsort.cpp TestRaise.cpp diff --git a/Tests/LibC/TestMalloc.cpp b/Tests/LibC/TestMalloc.cpp new file mode 100644 index 0000000000..ad24fd2032 --- /dev/null +++ b/Tests/LibC/TestMalloc.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include +#include + +TEST_CASE(malloc_limits) +{ + EXPECT_NO_CRASH("Allocation of 0 size should succed at allocation and release", [] { + errno = 0; + void* ptr = malloc(0); + EXPECT_EQ(errno, 0); + free(ptr); + return Test::Crash::Failure::DidNotCrash; + }); + + EXPECT_NO_CRASH("Allocation of the maximum `size_t` value should fails with `ENOMEM`", [] { + errno = 0; + void* ptr = malloc(NumericLimits::max()); + EXPECT_EQ(errno, ENOMEM); + EXPECT_EQ(ptr, nullptr); + free(ptr); + return Test::Crash::Failure::DidNotCrash; + }); + + EXPECT_NO_CRASH("Allocation of the maximum `size_t` value that does not overflow should fails with `ENOMEM`", [] { + errno = 0; + void* ptr = malloc(NumericLimits::max() - ChunkedBlock::block_size - sizeof(BigAllocationBlock)); + EXPECT_EQ(errno, ENOMEM); + EXPECT_EQ(ptr, nullptr); + free(ptr); + return Test::Crash::Failure::DidNotCrash; + }); +} diff --git a/Userland/Libraries/LibC/malloc.cpp b/Userland/Libraries/LibC/malloc.cpp index 01202f3914..b9afd147f4 100644 --- a/Userland/Libraries/LibC/malloc.cpp +++ b/Userland/Libraries/LibC/malloc.cpp @@ -183,7 +183,11 @@ static void* os_alloc(size_t size, const char* name) flags |= MAP_RANDOMIZED; #endif auto* ptr = serenity_mmap(nullptr, size, PROT_READ | PROT_WRITE, flags, 0, 0, ChunkedBlock::block_size, name); - VERIFY(ptr != MAP_FAILED); + VERIFY(ptr != nullptr); + if (ptr == MAP_FAILED) { + errno = ENOMEM; + return nullptr; + } return ptr; } @@ -228,6 +232,11 @@ static void* malloc_impl(size_t size, CallerWillInitializeMemory caller_will_ini if (!allocator) { size_t real_size = round_up_to_power_of_two(sizeof(BigAllocationBlock) + size, ChunkedBlock::block_size); + if (real_size < size) { + dbgln_if(MALLOC_DEBUG, "LibC: Detected overflow trying to do big allocation of size {} for {}", real_size, size); + errno = ENOMEM; + return nullptr; + } #ifdef RECYCLE_BIG_ALLOCATIONS if (auto* allocator = big_allocator_for_size(real_size)) { if (!allocator->blocks.is_empty()) { @@ -253,8 +262,12 @@ static void* malloc_impl(size_t size, CallerWillInitializeMemory caller_will_ini } } #endif - g_malloc_stats.number_of_big_allocs++; auto* block = (BigAllocationBlock*)os_alloc(real_size, "malloc: BigAllocationBlock"); + if (block == nullptr) { + dbgln_if(MALLOC_DEBUG, "LibC: Failed to do big allocation of size {} for {}", real_size, size); + return nullptr; + } + g_malloc_stats.number_of_big_allocs++; new (block) BigAllocationBlock(real_size); ue_notify_malloc(&block->m_slot[0], size); return &block->m_slot[0]; @@ -309,6 +322,9 @@ static void* malloc_impl(size_t size, CallerWillInitializeMemory caller_will_ini char buffer[64]; snprintf(buffer, sizeof(buffer), "malloc: ChunkedBlock(%zu)", good_size); block = (ChunkedBlock*)os_alloc(ChunkedBlock::block_size, buffer); + if (block == nullptr) { + return nullptr; + } new (block) ChunkedBlock(good_size); allocator->usable_blocks.append(*block); ++allocator->block_count; -- cgit v1.2.3