You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On architectures that require natural alignment for acquire/release operations, reflection access to under-aligned fields in packed structs can fault. FieldDesc::GetInstanceField and SetInstanceField use volatile scalar operations for primitive fields, which can raise SIGBUS on arm64 when the field address is misaligned.
This also affects reflection consumers such as the field-walking fallback in ValueType.Equals.
This change applies a consistent scalar-access policy across CoreCLR FieldInfo.GetValue, SetValue, GetValueDirect, and SetValueDirect, including first and cached calls, instance fields, and static fields:
Naturally aligned primitive, enum, native-int, pointer, and function-pointer fields use the existing volatile scalar operations.
Atomicity remains platform-dependent; this change does not add a 64-bit atomicity guarantee on 32-bit systems.
Misaligned non-GC scalar fields use memcpyNoGCRefs and intentionally do not provide atomicity or volatile ordering.
Managed cached access is used only when the required alignment can be guaranteed. Eight-byte fields remain on the native path on 32-bit.
Reflection getters load scalar values into aligned storage before boxing them.
GC-reference access and arbitrary value-type copying retain their existing behavior.
Testing
Confirmed the original regression fails against the unmodified runtime with SIGBUS on macOS arm64.
Built checked CoreCLR, libraries, and host successfully.
Built and ran the targeted checked CoreCLR regression successfully on macOS arm64.
Built and ran the regression successfully with NativeAOT.
Added coverage for aligned and misaligned fields across normal and direct reflection APIs, first and cached calls, instance and static fields, and affected scalar types.
Added an ARM32-specific case for the runtime-aligned 8-byte native path. ARM32 execution remains for CI.
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.
This helper is used by the unconditional TestEntryPoint, but it always searches for a 16-byte granule crossing even on 32-bit targets. CoreCLR only guarantees DATA_ALIGNMENT of 4 bytes there (src/coreclr/vm/arm/cgencpu.h:16, src/coreclr/vm/i386/cgencpu.h:25), and these packed boxes can have a fixed residue because their allocation size is repeatedly rounded to 16 bytes; after AllocationLimit attempts the test can reach line 296 and fail without exercising the runtime fix. Keep the granule-crossing predicate for arm64, and use a normal field-misalignment predicate (or another deterministic placement) on other architectures.
NativeAOT audit result: no NativeAOT product change is needed for this bug class.
Normal and TypedReference primitive/enum reads and writes flow through RuntimeAugments.LoadValueTypeField / StoreValueTypeField and their value-type-target variants, then RhBox / RhUnbox. These are block copies, not volatile/acquire-release scalar operations. Non-GC values use Unsafe.CopyBlock; GC-containing values use RhBulkMoveWithWriteBarrier. On ARM32, the block-copy codegen uses integer ldr/str chunks rather than alignment-sensitive ldrd/vldr long/double operations.
Packed unmanaged-pointer reads use Unsafe.As<byte, IntPtr>. The JIT importer marks that widening reinterpretation GTF_IND_UNALIGNED because the source is byte-aligned and the destination requires pointer alignment. Function-pointer instance reads either box through RhBox or use the same unaligned-marked direct path. None of these paths use Volatile, LDAR, or STLR.
NativeAOT ValueType.Equals uses RhBoxAny; its hash-code fallback uses byte spans or Unsafe.As<byte, float/double>, which receives the same unaligned importer treatment. These paths do not share CoreCLR’s reflection volatile accessor.
I built and ran the full packed-field regression as NativeAOT on macOS arm64. The first attempt found that the generic reflected fields were trimmed; the follow-up commit roots public fields on the generic struct parameter. The final NativeAOT build completed with zero warnings/errors and the test passed with expected/actual exit code 100. The test placement predicate now requires a 16-byte atomic-granule crossing only on ARM64 and natural misalignment on other architectures.
The reason will be displayed to describe this comment to others. Learn more.
usingSystem.Runtime.InteropServices;
unsafe
{MyStruct*ms= ... get a pointer to a readonly memory from somewhere...;TypedReferencetr= __makeref(*ms);Console.WriteLine(typeof(MyStruct).GetField("MyField").GetValueDirect(tr));}structMyStruct{publicdoubleMyField;}
The reason will be displayed to describe this comment to others. Learn more.
Agreed. The example with a TypedReference over read-only memory is why the interlocked read is not appropriate.
Fixed in 4691b84380a: GetValueDirect now uses the existing native VolatileLoad for aligned fields and memcpyNoGCRefs for misaligned fields. Neither path writes to or requires writable source memory. The corresponding stores use VolatileStore or the misaligned copy fallback.
This also avoids adding a 64-bit atomicity guarantee on 32-bit systems. Managed cached 8-byte access is disabled on 32-bit so those calls remain on the same native policy.
The reason will be displayed to describe this comment to others. Learn more.
Field.cpp/.h is part of type system. Reflection-specific behaviors do not belong here.
Can we move these helpers that implement reflection-specific behaviors to invokeutil.cpp?
It may be best to delete the methods that try to read the value (like GetValue32/SetValue32) in field.h/field.cpp and just have methods that return the field address like GetInstanceAddress. Leave it to the caller to do the actual reference with the right volatile or unaligned treatment.
Move alignment-aware primitive field access from FieldDesc into InvokeUtil so the reflection layer owns volatile and unaligned access semantics.
Add reflection-specific NativeAOT unaligned boxing, unboxing, and pointer reads without changing the general RhBox and RhUnbox behavior. Extend the regression coverage to include misaligned 64-bit direct field access.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
Instead of introducing this specialized low-level helper, can we deal with the alignment and atomicity in the reflection layer, the same way it is done in regular CoreCLR?
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #133236
On architectures that require natural alignment for acquire/release operations, reflection access to under-aligned fields in packed structs can fault.
FieldDesc::GetInstanceFieldandSetInstanceFielduse volatile scalar operations for primitive fields, which can raiseSIGBUSon arm64 when the field address is misaligned.This also affects reflection consumers such as the field-walking fallback in
ValueType.Equals.This change applies a consistent scalar-access policy across CoreCLR
FieldInfo.GetValue,SetValue,GetValueDirect, andSetValueDirect, including first and cached calls, instance fields, and static fields:memcpyNoGCRefsand intentionally do not provide atomicity or volatile ordering.Testing
SIGBUSon macOS arm64.