Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,15 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
}
}

#if FEATURE_FIXED_OUT_ARGS
#if defined(TARGET_WASM)

// Wasm passes the shadow stack pointer as an implicit first argument, and GT_LCLHEAP
// updates it. Arguments are pushed onto the Wasm operand stack in order, so any
// GT_LCLHEAP in an argument must be evaluated before the stack pointer is pushed.
//
const bool hasStackArgsWeCareAbout = comp->compLocallocUsed;

#elif FEATURE_FIXED_OUT_ARGS

// For Arm/x64 we only care because we can't reorder a register
// argument that uses GT_LCLHEAP. This is an optimization to
Expand All @@ -986,7 +994,7 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)

const bool hasStackArgsWeCareAbout = m_hasStackArgs;

#endif // FEATURE_FIXED_OUT_ARGS
#endif // defined(TARGET_WASM)

// If we have any stack args we have to force the evaluation
// of any arguments passed in registers that might throw an exception
Expand Down
63 changes: 63 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_131557/Runtime_131557.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// When a localloc result flows straight into a call argument, the localloc must be evaluated
// before any argument is set up. On wasm the shadow stack pointer is an implicit first argument
// and is updated by the localloc, so pushing it first handed the callee a stack pointer inside
// the freshly allocated region and the callee's frame overwrote the buffer.

using System.Runtime.CompilerServices;
using Xunit;

public unsafe class Runtime_131557
{
private static int s_sink;

[Fact]
public static void TestEntryPoint()
{
Assert.Equal(0, Test());
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static int Test()
{
byte* buffer = stackalloc byte[256];
return Use(buffer, 256);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static int Use(byte* p, int n)
{
for (int i = 0; i < n; i++)
{
p[i] = (byte)(i ^ 0x5a);
Comment thread
adamperlin marked this conversation as resolved.
}

// Give this frame some address-exposed locals of its own to clobber the buffer with.
int a = n, b = n + 1, c = n + 2, d = n + 3;
Mix(ref a, ref b, ref c, ref d);
s_sink += a + b + c + d;

int corrupted = 0;
for (int i = 0; i < n; i++)
{
if (p[i] != (byte)(i ^ 0x5a))
{
corrupted++;
}
}

return corrupted;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void Mix(ref int a, ref int b, ref int c, ref int d)
{
a = ~a;
b = ~b;
c = ~c;
d = ~d;
s_sink += a + b + c + d;
}
}
Loading