Skip to content

Commit 371731c

Browse files
EgorBoCopilot
andauthored
Fix alignment of 8-byte thread statics on direct TLS path (#129749)
Fixes #129733 ## Problem `Interlocked.Increment(ref threadStaticLong)` on a `[ThreadStatic] static long` throws `System.DataMisalignedException` on osx-arm64. ## Root cause In `GetTLSIndexForThreadStatic` (`src/coreclr/vm/threadstatics.cpp`), small non-collectible thread statics of already-initialized classes are placed via a top-down bump allocator on the direct-on-thread-local-data path. The per-field alignment was computed with a missing `else`: ```cpp uint32_t alignment; if (bytesNeeded >= 8) alignment = 8; if (bytesNeeded >= 4) // <-- missing 'else' alignment = 4; else if (bytesNeeded >= 2) alignment = 2; else alignment = 1; ``` For an 8-byte field (`long`/`double`), the first `if` sets `alignment = 8`, but the second `if (bytesNeeded >= 4)` is also true and overwrites it with `alignment = 4`. The offset is then chosen via `AlignDown(indexOffsetWithoutAlignment, alignment)`, so with `alignment == 4` it can land on a 4-mod-8 boundary. Since `ThreadLocalData` (and thus `ExtendedDirectThreadLocalTLSData`) is only 8-byte aligned, the resulting absolute address of the `long` can be 4-byte but not 8-byte aligned. ## Why it only reproduces on arm64 (and intermittently) - x64 tolerates misaligned atomic operations; arm64 `Interlocked.Increment` lowers to `ldaxr`/`stlxr`, which fault on a misaligned address → `DataMisalignedException`. - It is layout-dependent: whether the 4-aligned offset also happens to be 8-aligned depends on how many bytes earlier thread statics already consumed in the bump allocator. This is why a real app (e.g. ASP.NET/Kestrel, which registers other thread statics first) trips it while a trivial repro may not. ## Fix Add the missing `else` so an 8-byte field keeps `alignment = 8`. ## Testing I was unable to build/run locally for this change (development box is linux-x64 while the failure manifests on osx-arm64, where misaligned atomics fault). The fix is a one-line correction to the alignment selection logic and was verified by source inspection. CI will exercise the relevant platforms. > [!NOTE] > This pull request was authored with the assistance of GitHub Copilot. Co-authored-by: EgorBo <egorbo@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d1efd0b commit 371731c

1 file changed

Lines changed: 1 addition & 1 deletion

File tree

‎src/coreclr/vm/threadstatics.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde
738738
uint32_t alignment;
739739
if (bytesNeeded >= 8)
740740
alignment = 8;
741-
if (bytesNeeded >= 4)
741+
else if (bytesNeeded >= 4)
742742
alignment = 4;
743743
else if (bytesNeeded >= 2)
744744
alignment = 2;

0 commit comments

Comments
 (0)