Skip to content
Open
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
8 changes: 6 additions & 2 deletions src/coreclr/vm/stubgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2003,9 +2003,13 @@ void StubSigBuilder::EnsureEnoughQuickBytes(size_t cbToAppend)
STANDARD_VM_CONTRACT;

SIZE_T cbBuffer = m_qbSigBuffer.Size();
if ((m_cbSig + cbToAppend) >= cbBuffer)
if ((cbBuffer - m_cbSig) < cbToAppend)
{
m_qbSigBuffer.ReSizeThrows(2 * cbBuffer);
SIZE_T cbNew = max(m_cbSig + cbToAppend, 2 * cbBuffer);
// Detect integer overflow
if ((cbNew - m_cbSig) < cbToAppend)
COMPlusThrowHR(COR_E_OVERFLOW);
Comment on lines +2008 to +2011
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

The overflow check here can be defeated because the potentially overflowing operations happen before the check (m_cbSig + cbToAppend and 2 * cbBuffer can wrap in SIZE_T). Consider doing the size calculations with S_SIZE_T/ClrSafeInt (including the doubling) and checking IsOverflow() before resizing, instead of relying on post-hoc subtraction with unsigned values.

Suggested change
SIZE_T cbNew = max(m_cbSig + cbToAppend, 2 * cbBuffer);
// Detect integer overflow
if ((cbNew - m_cbSig) < cbToAppend)
COMPlusThrowHR(COR_E_OVERFLOW);
S_SIZE_T cbRequired = S_SIZE_T(m_cbSig) + S_SIZE_T(cbToAppend);
if (cbRequired.IsOverflow())
COMPlusThrowHR(COR_E_OVERFLOW);
S_SIZE_T cbDoubled = S_SIZE_T(cbBuffer) * S_SIZE_T(2);
SIZE_T cbNew = cbRequired.Value();
if (!cbDoubled.IsOverflow())
{
cbNew = max(cbNew, cbDoubled.Value());
}

Copilot uses AI. Check for mistakes.
m_qbSigBuffer.ReSizeThrows(cbNew);
m_pbSigCursor = ((BYTE*)m_qbSigBuffer.Ptr()) + m_cbSig;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,45 @@ extern "C" DLL_EXPORT int __cdecl NativeEntryPoint()
return NativeFunction();
}

struct IntWrapper
{
int Value;
};

#pragma managed
int64_t ManagedSum18ByRef(const int& a0,
const IntWrapper& a1,
const IntWrapper& a2,
const IntWrapper& a3,
const IntWrapper& a4,
const IntWrapper& a5,
const IntWrapper& a6,
const IntWrapper& a7,
const IntWrapper& a8,
const IntWrapper& a9,
const IntWrapper& a10,
const IntWrapper& a11,
const IntWrapper& a12,
const IntWrapper& a13,
const IntWrapper& a14,
const IntWrapper& a15,
const IntWrapper& a16,
const int& a17);

#pragma unmanaged
int64_t NativeSum18ByRef()
{
int a0 = 0;
IntWrapper w[16];
for (int i = 0; i < 16; ++i)
{
w[i].Value = i + 1;
}
int a17 = 17;
return ManagedSum18ByRef(a0, w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7],
w[8], w[9], w[10], w[11], w[12], w[13], w[14], w[15], a17);
}

#pragma managed

// Needed to provide a regression case for https://github.com/dotnet/runtime/issues/110365
Expand All @@ -39,6 +78,11 @@ public ref class TestClass
return NativeFunction();
}

int64_t ManagedEntryPointSum18ByRef()
{
return NativeSum18ByRef();
}

static void ChangeReturnedValue(int i)
{
s_valueToReturnStorage.valueToReturn = i;
Expand All @@ -58,3 +102,26 @@ int ManagedCallee()
{
return TestClass::GetReturnValue();
}

int64_t ManagedSum18ByRef(const int& a0,
const IntWrapper& a1,
const IntWrapper& a2,
const IntWrapper& a3,
const IntWrapper& a4,
const IntWrapper& a5,
const IntWrapper& a6,
const IntWrapper& a7,
const IntWrapper& a8,
const IntWrapper& a9,
const IntWrapper& a10,
const IntWrapper& a11,
const IntWrapper& a12,
const IntWrapper& a13,
const IntWrapper& a14,
const IntWrapper& a15,
const IntWrapper& a16,
const int& a17)
{
return (int64_t)a0 + a1.Value + a2.Value + a3.Value + a4.Value + a5.Value + a6.Value + a7.Value + a8.Value + a9.Value
+ a10.Value + a11.Value + a12.Value + a13.Value + a14.Value + a15.Value + a16.Value + a17;
}
16 changes: 16 additions & 0 deletions src/tests/Interop/IJW/NativeCallingManaged/NativeCallingManaged.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ public static int TestEntryPoint()
}
TestFramework.EndTestCase();

// Regression test for https://github.com/dotnet/runtime/issues/127166:
// Native code calling a managed function with 17+ by-ref parameters
// hit an OverflowException because StubSigBuilder::EnsureEnoughQuickBytes
// only doubled the buffer size once, which was insufficient when the
// internal signature (with preserved custom modifiers) exceeded 512 bytes.
TestFramework.BeginTestCase("Call managed method with 18 by-ref parameters from native");
MethodInfo sum18Method = testType.GetMethod("ManagedEntryPointSum18ByRef");
long sum = (long)sum18Method.Invoke(testInstance, null);
const long expectedSum = 153; // 0 + (1+2+...+16) + 17
if (sum != expectedSum)
{
TestFramework.LogError("IJW", "Incorrect sum returned: " + sum + " (expected " + expectedSum + ")");
success = false;
}
TestFramework.EndTestCase();

return success ? 100 : 99;
}
}
Expand Down
Loading