From 574cdcd7a05c6a029fe564699c05506180ea4929 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 12 Sep 2026 23:23:26 +0200 Subject: [PATCH] JIT: Fix block-store address containment boundary Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dc2793e8-3f85-4610-9afb-2b990ed92316 --- src/coreclr/jit/lowerarmarch.cpp | 4 +- src/coreclr/jit/lowerxarch.cpp | 3 +- .../JIT/Regression_ro_2/Runtime_133785.cs | 37 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/tests/JIT/Regression_ro_2/Runtime_133785.cs diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index d3955c7861ae1d..06e1c46417b686 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -761,7 +761,9 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT return; } #else // !TARGET_ARM - if ((ClrSafeInt(offset) + ClrSafeInt(size)).IsOverflow()) + // Keep offset + size strictly below INT32_MAX, as required by unrolled block codegen. + ClrSafeInt endOffset = ClrSafeInt(offset) + ClrSafeInt(size); + if (endOffset.IsOverflow() || (endOffset.Value() == INT32_MAX)) { return; } diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 8695ce0a504cd8..2a6f1e075312e5 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -402,7 +402,8 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT // up to 16 bytes lower than offset + size. But offsets large enough to hit this case are likely // to be extremely rare for this to ever be a CQ issue. // On x86 this shouldn't be needed but then again, offsets large enough to hit this are rare. - if (addrMode->Offset() > (INT32_MAX - static_cast(size))) + // Keep offset + size strictly below INT32_MAX, as required by unrolled block codegen. + if (addrMode->Offset() >= (INT32_MAX - static_cast(size))) { return; } diff --git a/src/tests/JIT/Regression_ro_2/Runtime_133785.cs b/src/tests/JIT/Regression_ro_2/Runtime_133785.cs new file mode 100644 index 00000000000000..22053e82ee81aa --- /dev/null +++ b/src/tests/JIT/Regression_ro_2/Runtime_133785.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_133785 +{ + // On x64, the element offset is 16 + Index * 3 == int.MaxValue - 3. + private const int Index = 715827876; + + private struct S3 + { + public byte A, B, C; + } + + [Fact] + public static void TestEntryPoint() + { + S3[] array = new S3[1]; + S3 value = new S3 { A = 1, B = 2, C = 3 }; + + Assert.Throws(() => StoreInit(array)); + Assert.Throws(() => StoreCopy(array, value)); + Assert.Throws(() => LoadCopy(array, ref value)); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static void StoreInit(S3[] array) => array[Index] = default; + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static void StoreCopy(S3[] array, S3 value) => array[Index] = value; + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static void LoadCopy(S3[] array, ref S3 value) => value = array[Index]; +}