Skip to content

Commit

Permalink
Add Windows Support for Laxy Commit of page map
Browse files Browse the repository at this point in the history
The Pagemap can potentially be accessed where there is no direct knowledge of the location.  On platforms with lazy commit this can
be handled by the OS faulting in a zero page.  Here we modify the
Windows PAL to add an exception handler that will commit pages
in the read only range, i.e. the page map.  We still require explicit
notify_using to be able to modify the pagemap.
  • Loading branch information
mjp41 committed Jan 6, 2025
1 parent f108460 commit bfa2572
Showing 1 changed file with 81 additions and 1 deletion.
82 changes: 81 additions & 1 deletion src/snmalloc/pal/pal_windows.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "../aal/aal.h"
#include "../ds_aal/ds_aal.h"
#include "pal_timer_default.h"

#ifdef _WIN32
Expand Down Expand Up @@ -50,13 +51,76 @@ namespace snmalloc
low_memory_callbacks.notify_all();
}

// A list of reserved ranges, used to handle lazy commit on readonly pages.
// We currently only need one, so haven't implemented a backup if the
// initial 16 is insufficient.
inline static std::array<std::pair<address_t, size_t>, 16> reserved_ranges;

// Lock for the reserved ranges.
inline static FlagWord reserved_ranges_lock{};

// Exception handler for handling lazy commit on readonly pages.
static LONG NTAPI
HandleReadonlyLazyCommit(struct _EXCEPTION_POINTERS* ExceptionInfo)
{
// Check this is an AV
if (
ExceptionInfo->ExceptionRecord->ExceptionCode !=
EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_CONTINUE_SEARCH;

// Check this is a read access
if (ExceptionInfo->ExceptionRecord->ExceptionInformation[0] != 0)
return EXCEPTION_CONTINUE_SEARCH;

// Get faulting address from exception info.
snmalloc::address_t faulting_address =
ExceptionInfo->ExceptionRecord->ExceptionInformation[1];

bool found = false;
{
FlagLock lock(reserved_ranges_lock);
// Check if the address is in a reserved range.
for (auto& r : reserved_ranges)
{
if ((faulting_address - r.first) < r.second)
{
found = true;
break;
}
}
}

if (!found)
return EXCEPTION_CONTINUE_SEARCH;

// Commit the page as readonly
auto pagebase = snmalloc::bits::align_down(faulting_address, page_size);
VirtualAlloc((void*)pagebase, page_size, MEM_COMMIT, PAGE_READONLY);

// Resume execution
return EXCEPTION_CONTINUE_EXECUTION;
}

static void initialise_for_singleton(size_t*) noexcept
{
AddVectoredExceptionHandler(1, HandleReadonlyLazyCommit);
}

// Ensure the exception handler is registered.
static void initialise_readonly_av() noexcept
{
static Singleton<size_t, &initialise_for_singleton> init;
init.get();
}

public:
/**
* Bitmap of PalFeatures flags indicating the optional features that this
* PAL supports. This PAL supports low-memory notifications.
*/
static constexpr uint64_t pal_features = LowMemoryNotification | Entropy |
Time
Time | LazyCommit
# if defined(PLATFORM_HAS_VIRTUALALLOC2) && !defined(USE_SYSTEMATIC_TESTING)
| AlignedAllocation
# endif
Expand Down Expand Up @@ -157,6 +221,22 @@ namespace snmalloc
"out of memory: {} ({}) could not be committed", p, size);
}

static void notify_using_readonly(void* p, size_t size) noexcept
{
initialise_readonly_av();

FlagLock lock(reserved_ranges_lock);
for (auto& r : reserved_ranges)
{
if (r.first == 0)
{
r.first = (address_t)p;
r.second = size;
return;
}
}
}

/// OS specific function for zeroing memory
template<bool page_aligned = false>
static void zero(void* p, size_t size) noexcept
Expand Down

0 comments on commit bfa2572

Please sign in to comment.