Skip to content

Add LOCKREADIFFAILRET to unlocked MDInternalRW read methods#127958

Open
kevingosse wants to merge 2 commits intodotnet:mainfrom
kevingosse:fix/mdinternalrw-lockread
Open

Add LOCKREADIFFAILRET to unlocked MDInternalRW read methods#127958
kevingosse wants to merge 2 commits intodotnet:mainfrom
kevingosse:fix/mdinternalrw-lockread

Conversation

@kevingosse
Copy link
Copy Markdown
Contributor

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 segmentation faults.

Fix: add LOCKREADIFFAILRET() to GetSigOfMethodDef, GetSigOfFieldDef, GetNameAndSigOfMemberRef, and GetSigFromToken.

Fixes #127957

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.
Copilot AI review requested due to automatic review settings May 8, 2026 14:30
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label May 8, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds read-locking to metadata signature/name accessors and simplifies error handling to return directly instead of using ErrExit/IfFailGo.

Changes:

  • Add LOCKREADIFFAILRET() in GetSigOfMethodDef, GetSigOfFieldDef, and GetNameAndSigOfMemberRef.
  • In GetSigFromToken, acquire read locks for mdtSignature/mdtTypeSpec and replace IfFailGo + ErrExit with IfFailRet + direct returns.
  • Simplify invalid-token handling by returning META_E_INVALID_TOKEN_TYPE directly.

Comment thread src/coreclr/md/enc/mdinternalrw.cpp Outdated
Comment on lines 2287 to 2291
HRESULT hr;
// We don't change MethodDef signature. No need to lock.
LOCKREADIFFAILRET();

MethodRec *pMethodRec;
*ppSig = NULL;
Comment thread src/coreclr/md/enc/mdinternalrw.cpp Outdated
Comment on lines 2312 to 2316
HRESULT hr;
// We don't change Field's signature. No need to lock.
LOCKREADIFFAILRET();

FieldRec *pFieldRec;
*ppSig = NULL;
@@ -2332,34 +2332,35 @@ MDInternalRW::GetSigFromToken(
PCCOR_SIGNATURE * ppSig)
{
HRESULT hr;
@@ -2669,8 +2667,7 @@ MDInternalRW::GetNameAndSigOfMemberRef( // meberref's name
LPCSTR *pszMemberRefName)
{
HRESULT hr;
@steveisok steveisok requested a review from a team May 8, 2026 14:46
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

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.
@noahfalk
Copy link
Copy Markdown
Member

noahfalk commented May 9, 2026

Thanks @kevingosse - this is a nice find!

Fix: add LOCKREADIFFAILRET() to GetSigOfMethodDef, GetSigOfFieldDef, GetNameAndSigOfMemberRef, and GetSigFromToken.

How did you land on this list methods? I ask because I am wondering if there are more methods also affected by the missing lock. For example is this one another case of the same missing lock problem?

@agocke - do we have a dev owner for the metadata code these days? I know it doesn't change often and I'm happy to review but if we do have an owner I wanted them to be in the loop on this too.

@kevingosse
Copy link
Copy Markdown
Contributor Author

You're right, I focused on the set of methods that appeared in the crashes but it looks like there are a bunch more with the same pattern.
I asked Claude to list them, and it came with this list, though they need to be individually verified:

# Method Line Record source Dereference(s)
1 GetCustomAttributeProps 1564 GetCustomAttributeRecord getTypeOfCustomAttribute(pRec)
2 GetCustomAttributeAsBlob 1589 GetCustomAttributeRecord getValueOfCustomAttribute(pRec, …)
3 GetNameOfTypeDef 1803 GetTypeDefRecord getNameOfTypeDef(pRec, …), getNamespaceOfTypeDef(pRec, …)
4 GetNameOfMethodDef 1897 GetMethodRecord getNameOfMethod(pRec, …)
5 GetNameOfFieldDef 1945 GetFieldRecord getNameOfField(pRec, …)
6 GetNameOfTypeRef 1965 GetTypeRefRecord getNamespaceOfTypeRef(pRec, …), getNameOfTypeRef(pRec, …)
7 GetTypeOfInterfaceImpl 2582 GetInterfaceImplRecord getInterfaceOfInterfaceImpl(pRec)
8 GetMethodSpecProps 2603 GetMethodSpecRecord getMethodOfMethodSpec(pRec), getInstantiationOfMethodSpec(pRec, …)
9 GetTypeSpecFromToken 3770 GetTypeSpecRecord getSignatureOfTypeSpec(pRec, …)

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

Labels

area-Diagnostics-coreclr community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Race condition in MDInternalRW

4 participants