Skip to content

Conversation

@davidwrighton
Copy link
Member

@davidwrighton davidwrighton commented Jan 23, 2026

We have logic which loads a typedef, and if the typedef is loaded then we assume the method can be loaded via lookup in the MethodDef/FieldDef token map tables. HOWEVER, what this boils down to is we do a load from the TypeDef table, and if that succeeds we will do a load from the FieldDef/MethodDef token map tables, but that second load is done without an explicit memory barrier. Unfortunately, through the wonder of memory ordering behavior, its legal (and not actually all that uncommon) for the CPU to do the load from the FieldDef/MethodDef tables BEFORE it actually loads from the TypeDef table. So what can happen is that the fielddef table read can happen, then the typedef table read can happen, then the CPU does the if check to see if its ok to do the fielddef table read, then we use the value from the fielddef table read which happened earlier.

This fix should fix that problem by using a VolatileLoad for all reads from the lookup maps. This is slightly slower but avoids the extremely easy to mess up dependency ordering rules.

Fixes #120754

…ef token tables

We have logic which loads a typedef, and if the typedef is loaded then we assume the method can be loaded via lookup in the MethodDef/FieldDef token map tables. HOWEVER, what this boils down to is we do a load from the TypeDef table, and if that succeeds we will do a load from the FieldDef/MethodDef token map tables, but that second load is done without an explicit memory barrier. Unfortunately, through the wonder of memory ordering behavior, its legal (and not actually all that uncommon) for the CPU to do the load from the FieldDef/MethodDef tables BEFORE it actually loads from the TypeDef table. So what can happen is that the fielddef table read can happen, then the typedef table read can happen, then the CPU does the if check to see if its ok to do the fielddef table read, then we use the value from the fielddef table read which happened earlier.

This fix should fix that problem by inserting a memory barrier if there is ever a read of NULL from either of those tables, and retrying. Reads of NULL are significantly rarer than reads with values filled in, so this fix should not cause any meaningful performance issues.

Fixes dotnet#120754
Copilot AI review requested due to automatic review settings January 23, 2026 19:18
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jan 23, 2026
Copy link
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

This pull request fixes a critical memory ordering issue in the MethodDef and FieldDef token lookup functions that could cause sporadic MissingMethodException errors. The root cause is that these lookup maps are populated before the TypeDef table entry is stored, and due to CPU memory reordering, a read from these maps could happen before the TypeDef check, causing a null value to be used even when the entry was actually populated.

Changes:

  • Added memory barrier protection in LookupMethodDef to prevent CPU reordering when NULL is read
  • Moved LookupFieldDef from header/cpp to inline file and added the same memory barrier protection
  • Unified DAC and non-DAC implementations of LookupFieldDef into a single implementation

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/coreclr/vm/ceeload.inl Added NULL-check with memory barrier retry logic to LookupMethodDef; moved LookupFieldDef implementation here with same memory barrier fix
src/coreclr/vm/ceeload.h Removed conditional compilation guards and inline implementation of LookupFieldDef, keeping only forward declaration
src/coreclr/vm/ceeload.cpp Removed DACCESS_COMPILE-specific LookupFieldDef implementation

@davidwrighton davidwrighton added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jan 23, 2026
@dotnet-policy-service
Copy link
Contributor

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

Copy link
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

Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.

@davidwrighton davidwrighton requested a review from Copilot January 24, 2026 01:00
Copy link
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

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@davidwrighton davidwrighton merged commit 9baed28 into dotnet:main Jan 24, 2026
109 of 111 checks passed
@davidwrighton
Copy link
Member Author

/backport to release/10.0

@github-actions
Copy link
Contributor

Started backporting to release/10.0 (link to workflow run)

@github-actions
Copy link
Contributor

@davidwrighton backporting to release/10.0 failed, the patch most likely resulted in conflicts. Please backport manually!

git am output
$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch

Applying: Fix token memory ordering issue reading from the MethodDef and FieldDef token tables
Using index info to reconstruct a base tree...
M	src/coreclr/vm/ceeload.cpp
M	src/coreclr/vm/ceeload.h
M	src/coreclr/vm/ceeload.inl
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/ceeload.cpp
Auto-merging src/coreclr/vm/ceeload.h
Auto-merging src/coreclr/vm/ceeload.inl
Applying: Update src/coreclr/vm/ceeload.inl
Applying: Revert "Fix token memory ordering issue reading from the MethodDef and FieldDef token tables"
Using index info to reconstruct a base tree...
M	src/coreclr/vm/ceeload.cpp
M	src/coreclr/vm/ceeload.h
M	src/coreclr/vm/ceeload.inl
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/ceeload.cpp
Auto-merging src/coreclr/vm/ceeload.h
Auto-merging src/coreclr/vm/ceeload.inl
CONFLICT (content): Merge conflict in src/coreclr/vm/ceeload.inl
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0003 Revert "Fix token memory ordering issue reading from the MethodDef and FieldDef token tables"
Error: The process '/usr/bin/git' failed with exit code 128

Link to workflow output

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

Projects

None yet

3 participants