Skip to content

[release/8.0-staging] Add LOCKREADIFFAILRET to unlocked MDInternalRW read methods#128733

Closed
github-actions[bot] wants to merge 4 commits into
release/8.0-stagingfrom
backport/pr-127958-to-release/8.0-staging
Closed

[release/8.0-staging] Add LOCKREADIFFAILRET to unlocked MDInternalRW read methods#128733
github-actions[bot] wants to merge 4 commits into
release/8.0-stagingfrom
backport/pr-127958-to-release/8.0-staging

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented May 28, 2026

Backport of #127958 to release/8.0-staging

/cc @elinor-fung @kevingosse

Customer Impact

  • Customer reported
  • Found internally

Issue: #127957

Some read operations for metadata tables were not done under a lock. When the tables are expanded - for example via profiler emit operations - concurrent reads can end up with invalid pointers resulting in a segfault or a MissingMethodException.

DataDog has been hitting it multiple times a week in CI and has gotten customer reports as well.

Regression

  • Yes
  • No

Testing

@andrewlock validated against CI that was hitting this issue without this fix: #127958 (comment)
@kevingosse validated against local changes that were reproing the issue before his fix: #127958 (comment)

Risk

Low.

kevingosse and others added 4 commits May 28, 2026 23:16
Several read methods in MDInternalRW skip locking because they
assume row contents are never modified. However, ExpandTables()
reallocates the backing buffer of every metadata table, invalidating
pointers returned by GetMethodRecord/GetFieldRecord/GetMemberRefRecord.

This races with profiler emit operations (DefineMemberRef, etc.) that
trigger ExpandTables under LOCKWRITE, causing use-after-free reads
that manifest as sporadic MissingMethodException or access violations.

Fix: add LOCKREADIFFAILRET() to GetSigOfMethodDef, GetSigOfFieldDef,
GetNameAndSigOfMemberRef, and GetSigFromToken.
Address review feedback: if LOCKREADIFFAILRET fails, callers should
still see deterministic out-param values (NULL/0) rather than
uninitialized memory. Move out-param initialization above the lock.
Same race as the prior commits: read methods skip locking with
"no need to lock" comments, but ExpandTables() reallocates the
backing buffer of every metadata table under LOCKWRITE,
invalidating record pointers from Get*Record().

Fix the remaining methods that dereference record pointers without
holding the read lock:

- GetCustomAttributeProps
- GetCustomAttributeAsBlob
- GetNameOfTypeDef
- GetNameOfMethodDef
- GetNameOfFieldDef
- GetNameOfTypeRef
- GetTypeOfInterfaceImpl
- GetMethodSpecProps
- GetTypeSpecFromToken

Out-params are initialized before the lock so callers see
deterministic NULL/0 if LockRead fails.
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

@elinor-fung
Copy link
Copy Markdown
Member

elinor-fung commented May 29, 2026

.NET 8 is in maintenance support, which is security fixes only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-VM-coreclr Servicing-consider Issue for next servicing release review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants