Skip to content

[Interpreter] Fix localloc to guarantee 16-byte stack alignment#125720

Open
Copilot wants to merge 3 commits intomainfrom
copilot/fix-localloc-stack-alignment
Open

[Interpreter] Fix localloc to guarantee 16-byte stack alignment#125720
Copilot wants to merge 3 commits intomainfrom
copilot/fix-localloc-stack-alignment

Conversation

Copy link
Contributor

Copilot AI commented Mar 18, 2026

FrameDataAllocator::Alloc() was aligning localloc buffers to sizeof(void*) (4–8 bytes) instead of INTERP_STACK_ALIGNMENT (16 bytes), causing the ShowLocallocAlignment regression test to fail on any platform using the CoreCLR interpreter (e.g., WASM).

Description

  • Fragment backing memory: Replace malloc with VMToOSInterface::AlignedAllocate(INTERP_STACK_ALIGNMENT, size) so every fragment's base address is 16-byte aligned. Pair freeVMToOSInterface::AlignedFree accordingly.
  • Bump allocation alignment: Change ALIGN_UP(size, sizeof(void*))ALIGN_UP(size, INTERP_STACK_ALIGNMENT) so pFramePos advances by a multiple of 16, keeping it aligned across all subsequent allocations and after PopInfo restores.

VMToOSInterface is already available in all cee_wks_core VM files via common.h, which is force-included as a precompiled header (see src/coreclr/vm/wks/CMakeLists.txt). No additional include is needed.

Customer Impact

Programs using stackalloc (localloc) under the CoreCLR interpreter receive misaligned pointers. On WASM and other platforms requiring 16-byte alignment (e.g., for SIMD), this causes incorrect behavior or crashes.

Regression

Not a regression in a released build; this is a correctness gap in the interpreter that has existed since its introduction.

Testing

Existing JIT/Regression/Dev11/External/dev11_239804/ShowLocallocAlignment test covers this scenario. Full clr+libs build verified clean on Linux x64.

Risk

Low. The change is isolated to interpframeallocator.cpp — a single file only compiled under FEATURE_INTERPRETER. Both allocation paths (AlignedAllocate/AlignedFree) are already used elsewhere in the interpreter for the main stack (interpexec.cpp). No behavioral change outside the interpreter.

Original prompt

This section details on the original issue you should resolve

<issue_title>[Interpreter] localloc does not guarantee platform stack alignment</issue_title>
<issue_description>FrameDataAllocator::Alloc() in src/coreclr/vm/interpframeallocator.cpp aligns localloc buffer sizes to sizeof(void*) (4 bytes on WASM, 8 bytes on 64-bit platforms):

size = ALIGN_UP(size, sizeof(void*));

The JIT guarantees localloc returns pointers aligned to STACK_ALIGN (16 bytes on most platforms including WASM — see src/coreclr/jit/targetwasm.h), but the interpreter does not honor this. The fragment backing memory comes from raw malloc() with no explicit alignment, and subsequent bump allocations within a fragment are only pointer-size aligned.

This causes the ShowLocallocAlignment regression test to fail on WASM (and would fail on any platform using the interpreter) because stackalloc returns 8-byte aligned addresses instead of the expected 16-byte alignment.

Repro

Run JIT/Regression/Dev11/External/dev11_239804/ShowLocallocAlignment on a platform using the CoreCLR interpreter (e.g. WASM).

Expected

Localloc returns a 16-byte aligned pointer (matching STACK_ALIGN).

Actual

Localloc returns a pointer aligned only to sizeof(void*).

Suggested fix

Change the alignment in FrameDataAllocator::Alloc() to use INTERP_STACK_ALIGNMENT (16) instead of sizeof(void*), and ensure the returned pointer (not just the size) is aligned.</issue_description>

Comments on the Issue (you are @copilot in this section)

@radekdoulik @janvorli @BrzVlad should we align FrameDataAllocator::Alloc() to INTERP_STACK_ALIGNMENT? @BrzVlad Sounds good. We could use AlignedAllocate here as well.

📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @BrzVlad, @janvorli, @kg
See info in area-owners.md if you want to be subscribed.

…NMENT

Co-authored-by: radekdoulik <4147484+radekdoulik@users.noreply.github.com>
Copilot AI changed the title [WIP] [Interpreter] Fix localloc alignment issue for stack [Interpreter] Fix localloc to guarantee 16-byte stack alignment Mar 18, 2026
Copilot AI requested a review from radekdoulik March 18, 2026 10:26

#ifdef FEATURE_INTERPRETER

#include <minipal.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot This seems to break build

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 648462e. minipal.h has no include guards, so adding it explicitly caused a duplicate definition of VMToOSInterface — it's already available via common.h, which is force-included as a precompiled header for all cee_wks_core VM files (same reason interpexec.cpp can use VMToOSInterface::AlignedAllocate without an explicit include).

Co-authored-by: BrzVlad <4720621+BrzVlad@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes CoreCLR interpreter localloc alignment to guarantee 16-byte alignment (matching INTERP_STACK_ALIGNMENT), addressing failures in ShowLocallocAlignment and preventing misaligned stackalloc buffers on interpreter platforms (e.g., WASM).

Changes:

  • Allocate frame data fragments with VMToOSInterface::AlignedAllocate(INTERP_STACK_ALIGNMENT, ...) and free with AlignedFree.
  • Align bump allocations in FrameDataAllocator::Alloc() to INTERP_STACK_ALIGNMENT instead of sizeof(void*).
Comments suppressed due to low confidence (1)

src/coreclr/vm/interpframeallocator.cpp:94

  • If the first fragment allocation fails (or a fragment’s backing allocation fails), pFirst can be left non-null while pCurrent is still null, and the partially-constructed fragment isn’t deleted. This can corrupt the allocator state: after the nullptr return triggers OutOfMemoryException, subsequent Alloc calls will skip the pFirst == nullptr path and then dereference pCurrent.

Consider allocating into a temporary FrameDataFragment* and only assigning to pFirst/pCurrent after verifying pFrameStart != nullptr; otherwise delete the fragment before returning failure (same pattern should be applied for later fragment allocations too).

    if (pFirst == nullptr)
    {
        pFirst = new (nothrow) FrameDataFragment(size);
        if (pFirst == nullptr || pFirst->pFrameStart == nullptr)
        {
            return nullptr;
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Interpreter] localloc does not guarantee platform stack alignment

4 participants