Fix P/Invoke IL stub race in DoPrestub - #124579

Merged
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race
Feb 20, 2026
Merged

Fix P/Invoke IL stub race in DoPrestub#124579
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Summary

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to prevent racing threads from independently creating and JIT-ing duplicate IL stubs for the same target method.

Root Cause

PR #117901 made IsSharedStubScenario return false for forward P/Invokes, which disabled the ILStubCache lookup. Multiple threads could then each create their own DynamicMethodDesc and JIT it, producing different PCODE values that violated the assert in DoPrestub.

Fix

  • Remove the forward P/Invoke exclusion from IsSharedStubScenario so these stubs use the ILStubCache for de-duplication.
  • In CreateHashBlob, create a minimal hash blob containing just the target MethodDesc pointer for forward P/Invoke stubs, so different P/Invoke methods get distinct cache entries while racing threads for the same method converge on the same DynamicMethodDesc.

Fixes#124530

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing
duplicate IL stubs for the same target method.
PR dotnet#117901 made IsSharedStubScenario return false for forward
P/Invokes, which disabled the cache. Multiple threads could then
each create their own DynamicMethodDesc and JIT it, producing
different PCODE values that violated the assert in DoPrestub.
The fix:
- Remove the forward P/Invoke exclusion from IsSharedStubScenario
so these stubs use the ILStubCache for de-duplication.
- In CreateHashBlob, create a minimal hash blob containing just the
target MethodDesc pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads
for the same method converge on the same DynamicMethodDesc.
Fixesdotnet#124530
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race condition in P/Invoke IL stub generation that was introduced by PR #117901. The root cause was that forward P/Invoke stubs (non-CALLI, non-vararg) were excluded from IsSharedStubScenario, disabling the ILStubCache lookup/insert mechanism. This allowed multiple threads to independently create and JIT duplicate IL stubs for the same target method, resulting in different PCODE values and violating the assert in DoPrestub.

Changes:

  • Re-enables ILStubCache usage for forward P/Invoke stubs by removing their exclusion from IsSharedStubScenario
  • Creates minimal hash blobs keyed by target MethodDesc pointer for forward P/Invoke stubs to ensure proper cache de-duplication
  • Updates comments to reflect the new caching behavior

@jkoritzinsky

Copy link
Copy Markdown
Member

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

@jtschusterjtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment threadsrc/coreclr/vm/dllimport.cpp
Comment threadsrc/coreclr/vm/dllimport.cpp
@AaronRobinsonMSFT

Copy link
Copy Markdown
MemberAuthor

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

I think we could, but I really dislike the idea of having multiple places for this sort of data. Instead of hiding it on the PInvokeMethodDesc for this one instance, I would prefer to fix the entire MethodDesc heirarchy and make it less bespoke. Unless there is a compelling reason to add it there, especially since it is a single lookup cost, I'm inclined to leave it as-is.

@jkoritzinsky

Copy link
Copy Markdown
Member

Yeah this can wait until we do a greater refactoring of the MethodDesc hierarchy if that's the preference.

@jkotas

Copy link
Copy Markdown
Member

refactoring of the MethodDesc hierarchy

There should not be a DynamicMethodDesc for these in the first place. It can be all transient IL.

CopilotAI review requested due to automatic review settings February 19, 2026 14:55

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 1 comment.

Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
CopilotAI review requested due to automatic review settings February 20, 2026 00:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings February 20, 2026 15:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 503a82e into dotnet:mainFeb 20, 2026
107 of 109 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the fix-pinvoke-stub-race branch February 20, 2026 18:34
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
## Summary
Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing duplicate
IL stubs for the same target method.
## Root Cause
PR dotnet#117901 made `IsSharedStubScenario` return `false` for forward
P/Invokes, which disabled the `ILStubCache` lookup. Multiple threads
could then each create their own `DynamicMethodDesc` and JIT it,
producing different `PCODE` values that violated the assert in
`DoPrestub`.
## Fix
- Remove the forward P/Invoke exclusion from `IsSharedStubScenario` so
these stubs use the `ILStubCache` for de-duplication.
- In `CreateHashBlob`, create a minimal hash blob containing just the
target `MethodDesc` pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads for the
same method converge on the same `DynamicMethodDesc`.
Fixesdotnet#124530
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Race in PInvoke stub generation

5 participants

@AaronRobinsonMSFT@jkoritzinsky@jkotas@jtschuster
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix P/Invoke IL stub race in DoPrestub - #124579

Merged
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race
Feb 20, 2026
Merged

Fix P/Invoke IL stub race in DoPrestub#124579
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Summary

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to prevent racing threads from independently creating and JIT-ing duplicate IL stubs for the same target method.

Root Cause

PR #117901 made IsSharedStubScenario return false for forward P/Invokes, which disabled the ILStubCache lookup. Multiple threads could then each create their own DynamicMethodDesc and JIT it, producing different PCODE values that violated the assert in DoPrestub.

Fix

  • Remove the forward P/Invoke exclusion from IsSharedStubScenario so these stubs use the ILStubCache for de-duplication.
  • In CreateHashBlob, create a minimal hash blob containing just the target MethodDesc pointer for forward P/Invoke stubs, so different P/Invoke methods get distinct cache entries while racing threads for the same method converge on the same DynamicMethodDesc.

Fixes#124530

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing
duplicate IL stubs for the same target method.
PR dotnet#117901 made IsSharedStubScenario return false for forward
P/Invokes, which disabled the cache. Multiple threads could then
each create their own DynamicMethodDesc and JIT it, producing
different PCODE values that violated the assert in DoPrestub.
The fix:
- Remove the forward P/Invoke exclusion from IsSharedStubScenario
so these stubs use the ILStubCache for de-duplication.
- In CreateHashBlob, create a minimal hash blob containing just the
target MethodDesc pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads
for the same method converge on the same DynamicMethodDesc.
Fixesdotnet#124530
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race condition in P/Invoke IL stub generation that was introduced by PR #117901. The root cause was that forward P/Invoke stubs (non-CALLI, non-vararg) were excluded from IsSharedStubScenario, disabling the ILStubCache lookup/insert mechanism. This allowed multiple threads to independently create and JIT duplicate IL stubs for the same target method, resulting in different PCODE values and violating the assert in DoPrestub.

Changes:

  • Re-enables ILStubCache usage for forward P/Invoke stubs by removing their exclusion from IsSharedStubScenario
  • Creates minimal hash blobs keyed by target MethodDesc pointer for forward P/Invoke stubs to ensure proper cache de-duplication
  • Updates comments to reflect the new caching behavior

@jkoritzinsky

Copy link
Copy Markdown
Member

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

@jtschusterjtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment threadsrc/coreclr/vm/dllimport.cpp
Comment threadsrc/coreclr/vm/dllimport.cpp
@AaronRobinsonMSFT

Copy link
Copy Markdown
MemberAuthor

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

I think we could, but I really dislike the idea of having multiple places for this sort of data. Instead of hiding it on the PInvokeMethodDesc for this one instance, I would prefer to fix the entire MethodDesc heirarchy and make it less bespoke. Unless there is a compelling reason to add it there, especially since it is a single lookup cost, I'm inclined to leave it as-is.

@jkoritzinsky

Copy link
Copy Markdown
Member

Yeah this can wait until we do a greater refactoring of the MethodDesc hierarchy if that's the preference.

@jkotas

Copy link
Copy Markdown
Member

refactoring of the MethodDesc hierarchy

There should not be a DynamicMethodDesc for these in the first place. It can be all transient IL.

CopilotAI review requested due to automatic review settings February 19, 2026 14:55

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 1 comment.

Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
CopilotAI review requested due to automatic review settings February 20, 2026 00:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings February 20, 2026 15:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 503a82e into dotnet:mainFeb 20, 2026
107 of 109 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the fix-pinvoke-stub-race branch February 20, 2026 18:34
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
## Summary
Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing duplicate
IL stubs for the same target method.
## Root Cause
PR dotnet#117901 made `IsSharedStubScenario` return `false` for forward
P/Invokes, which disabled the `ILStubCache` lookup. Multiple threads
could then each create their own `DynamicMethodDesc` and JIT it,
producing different `PCODE` values that violated the assert in
`DoPrestub`.
## Fix
- Remove the forward P/Invoke exclusion from `IsSharedStubScenario` so
these stubs use the `ILStubCache` for de-duplication.
- In `CreateHashBlob`, create a minimal hash blob containing just the
target `MethodDesc` pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads for the
same method converge on the same `DynamicMethodDesc`.
Fixesdotnet#124530
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Race in PInvoke stub generation

5 participants

@AaronRobinsonMSFT@jkoritzinsky@jkotas@jtschuster
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix P/Invoke IL stub race in DoPrestub - #124579

Merged
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race
Feb 20, 2026
Merged

Fix P/Invoke IL stub race in DoPrestub#124579
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Summary

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to prevent racing threads from independently creating and JIT-ing duplicate IL stubs for the same target method.

Root Cause

PR #117901 made IsSharedStubScenario return false for forward P/Invokes, which disabled the ILStubCache lookup. Multiple threads could then each create their own DynamicMethodDesc and JIT it, producing different PCODE values that violated the assert in DoPrestub.

Fix

  • Remove the forward P/Invoke exclusion from IsSharedStubScenario so these stubs use the ILStubCache for de-duplication.
  • In CreateHashBlob, create a minimal hash blob containing just the target MethodDesc pointer for forward P/Invoke stubs, so different P/Invoke methods get distinct cache entries while racing threads for the same method converge on the same DynamicMethodDesc.

Fixes#124530

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing
duplicate IL stubs for the same target method.
PR dotnet#117901 made IsSharedStubScenario return false for forward
P/Invokes, which disabled the cache. Multiple threads could then
each create their own DynamicMethodDesc and JIT it, producing
different PCODE values that violated the assert in DoPrestub.
The fix:
- Remove the forward P/Invoke exclusion from IsSharedStubScenario
so these stubs use the ILStubCache for de-duplication.
- In CreateHashBlob, create a minimal hash blob containing just the
target MethodDesc pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads
for the same method converge on the same DynamicMethodDesc.
Fixesdotnet#124530
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race condition in P/Invoke IL stub generation that was introduced by PR #117901. The root cause was that forward P/Invoke stubs (non-CALLI, non-vararg) were excluded from IsSharedStubScenario, disabling the ILStubCache lookup/insert mechanism. This allowed multiple threads to independently create and JIT duplicate IL stubs for the same target method, resulting in different PCODE values and violating the assert in DoPrestub.

Changes:

  • Re-enables ILStubCache usage for forward P/Invoke stubs by removing their exclusion from IsSharedStubScenario
  • Creates minimal hash blobs keyed by target MethodDesc pointer for forward P/Invoke stubs to ensure proper cache de-duplication
  • Updates comments to reflect the new caching behavior

@jkoritzinsky

Copy link
Copy Markdown
Member

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

@jtschusterjtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment threadsrc/coreclr/vm/dllimport.cpp
Comment threadsrc/coreclr/vm/dllimport.cpp
@AaronRobinsonMSFT

Copy link
Copy Markdown
MemberAuthor

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

I think we could, but I really dislike the idea of having multiple places for this sort of data. Instead of hiding it on the PInvokeMethodDesc for this one instance, I would prefer to fix the entire MethodDesc heirarchy and make it less bespoke. Unless there is a compelling reason to add it there, especially since it is a single lookup cost, I'm inclined to leave it as-is.

@jkoritzinsky

Copy link
Copy Markdown
Member

Yeah this can wait until we do a greater refactoring of the MethodDesc hierarchy if that's the preference.

@jkotas

Copy link
Copy Markdown
Member

refactoring of the MethodDesc hierarchy

There should not be a DynamicMethodDesc for these in the first place. It can be all transient IL.

CopilotAI review requested due to automatic review settings February 19, 2026 14:55

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 1 comment.

Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
CopilotAI review requested due to automatic review settings February 20, 2026 00:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings February 20, 2026 15:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 503a82e into dotnet:mainFeb 20, 2026
107 of 109 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the fix-pinvoke-stub-race branch February 20, 2026 18:34
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
## Summary
Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing duplicate
IL stubs for the same target method.
## Root Cause
PR dotnet#117901 made `IsSharedStubScenario` return `false` for forward
P/Invokes, which disabled the `ILStubCache` lookup. Multiple threads
could then each create their own `DynamicMethodDesc` and JIT it,
producing different `PCODE` values that violated the assert in
`DoPrestub`.
## Fix
- Remove the forward P/Invoke exclusion from `IsSharedStubScenario` so
these stubs use the `ILStubCache` for de-duplication.
- In `CreateHashBlob`, create a minimal hash blob containing just the
target `MethodDesc` pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads for the
same method converge on the same `DynamicMethodDesc`.
Fixesdotnet#124530
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Race in PInvoke stub generation

5 participants

@AaronRobinsonMSFT@jkoritzinsky@jkotas@jtschuster
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix P/Invoke IL stub race in DoPrestub - #124579

Merged
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race
Feb 20, 2026
Merged

Fix P/Invoke IL stub race in DoPrestub#124579
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Summary

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to prevent racing threads from independently creating and JIT-ing duplicate IL stubs for the same target method.

Root Cause

PR #117901 made IsSharedStubScenario return false for forward P/Invokes, which disabled the ILStubCache lookup. Multiple threads could then each create their own DynamicMethodDesc and JIT it, producing different PCODE values that violated the assert in DoPrestub.

Fix

  • Remove the forward P/Invoke exclusion from IsSharedStubScenario so these stubs use the ILStubCache for de-duplication.
  • In CreateHashBlob, create a minimal hash blob containing just the target MethodDesc pointer for forward P/Invoke stubs, so different P/Invoke methods get distinct cache entries while racing threads for the same method converge on the same DynamicMethodDesc.

Fixes#124530

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing
duplicate IL stubs for the same target method.
PR dotnet#117901 made IsSharedStubScenario return false for forward
P/Invokes, which disabled the cache. Multiple threads could then
each create their own DynamicMethodDesc and JIT it, producing
different PCODE values that violated the assert in DoPrestub.
The fix:
- Remove the forward P/Invoke exclusion from IsSharedStubScenario
so these stubs use the ILStubCache for de-duplication.
- In CreateHashBlob, create a minimal hash blob containing just the
target MethodDesc pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads
for the same method converge on the same DynamicMethodDesc.
Fixesdotnet#124530
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race condition in P/Invoke IL stub generation that was introduced by PR #117901. The root cause was that forward P/Invoke stubs (non-CALLI, non-vararg) were excluded from IsSharedStubScenario, disabling the ILStubCache lookup/insert mechanism. This allowed multiple threads to independently create and JIT duplicate IL stubs for the same target method, resulting in different PCODE values and violating the assert in DoPrestub.

Changes:

  • Re-enables ILStubCache usage for forward P/Invoke stubs by removing their exclusion from IsSharedStubScenario
  • Creates minimal hash blobs keyed by target MethodDesc pointer for forward P/Invoke stubs to ensure proper cache de-duplication
  • Updates comments to reflect the new caching behavior

@jkoritzinsky

Copy link
Copy Markdown
Member

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

@jtschusterjtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment threadsrc/coreclr/vm/dllimport.cpp
Comment threadsrc/coreclr/vm/dllimport.cpp
@AaronRobinsonMSFT

Copy link
Copy Markdown
MemberAuthor

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

I think we could, but I really dislike the idea of having multiple places for this sort of data. Instead of hiding it on the PInvokeMethodDesc for this one instance, I would prefer to fix the entire MethodDesc heirarchy and make it less bespoke. Unless there is a compelling reason to add it there, especially since it is a single lookup cost, I'm inclined to leave it as-is.

@jkoritzinsky

Copy link
Copy Markdown
Member

Yeah this can wait until we do a greater refactoring of the MethodDesc hierarchy if that's the preference.

@jkotas

Copy link
Copy Markdown
Member

refactoring of the MethodDesc hierarchy

There should not be a DynamicMethodDesc for these in the first place. It can be all transient IL.

CopilotAI review requested due to automatic review settings February 19, 2026 14:55

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 1 comment.

Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
CopilotAI review requested due to automatic review settings February 20, 2026 00:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings February 20, 2026 15:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 503a82e into dotnet:mainFeb 20, 2026
107 of 109 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the fix-pinvoke-stub-race branch February 20, 2026 18:34
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
## Summary
Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing duplicate
IL stubs for the same target method.
## Root Cause
PR dotnet#117901 made `IsSharedStubScenario` return `false` for forward
P/Invokes, which disabled the `ILStubCache` lookup. Multiple threads
could then each create their own `DynamicMethodDesc` and JIT it,
producing different `PCODE` values that violated the assert in
`DoPrestub`.
## Fix
- Remove the forward P/Invoke exclusion from `IsSharedStubScenario` so
these stubs use the `ILStubCache` for de-duplication.
- In `CreateHashBlob`, create a minimal hash blob containing just the
target `MethodDesc` pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads for the
same method converge on the same `DynamicMethodDesc`.
Fixesdotnet#124530
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Race in PInvoke stub generation

5 participants

@AaronRobinsonMSFT@jkoritzinsky@jkotas@jtschuster
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix P/Invoke IL stub race in DoPrestub - #124579

Merged
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race
Feb 20, 2026
Merged

Fix P/Invoke IL stub race in DoPrestub#124579
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Summary

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to prevent racing threads from independently creating and JIT-ing duplicate IL stubs for the same target method.

Root Cause

PR #117901 made IsSharedStubScenario return false for forward P/Invokes, which disabled the ILStubCache lookup. Multiple threads could then each create their own DynamicMethodDesc and JIT it, producing different PCODE values that violated the assert in DoPrestub.

Fix

  • Remove the forward P/Invoke exclusion from IsSharedStubScenario so these stubs use the ILStubCache for de-duplication.
  • In CreateHashBlob, create a minimal hash blob containing just the target MethodDesc pointer for forward P/Invoke stubs, so different P/Invoke methods get distinct cache entries while racing threads for the same method converge on the same DynamicMethodDesc.

Fixes#124530

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing
duplicate IL stubs for the same target method.
PR dotnet#117901 made IsSharedStubScenario return false for forward
P/Invokes, which disabled the cache. Multiple threads could then
each create their own DynamicMethodDesc and JIT it, producing
different PCODE values that violated the assert in DoPrestub.
The fix:
- Remove the forward P/Invoke exclusion from IsSharedStubScenario
so these stubs use the ILStubCache for de-duplication.
- In CreateHashBlob, create a minimal hash blob containing just the
target MethodDesc pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads
for the same method converge on the same DynamicMethodDesc.
Fixesdotnet#124530
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race condition in P/Invoke IL stub generation that was introduced by PR #117901. The root cause was that forward P/Invoke stubs (non-CALLI, non-vararg) were excluded from IsSharedStubScenario, disabling the ILStubCache lookup/insert mechanism. This allowed multiple threads to independently create and JIT duplicate IL stubs for the same target method, resulting in different PCODE values and violating the assert in DoPrestub.

Changes:

  • Re-enables ILStubCache usage for forward P/Invoke stubs by removing their exclusion from IsSharedStubScenario
  • Creates minimal hash blobs keyed by target MethodDesc pointer for forward P/Invoke stubs to ensure proper cache de-duplication
  • Updates comments to reflect the new caching behavior

@jkoritzinsky

Copy link
Copy Markdown
Member

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

@jtschusterjtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment threadsrc/coreclr/vm/dllimport.cpp
Comment threadsrc/coreclr/vm/dllimport.cpp
@AaronRobinsonMSFT

Copy link
Copy Markdown
MemberAuthor

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

I think we could, but I really dislike the idea of having multiple places for this sort of data. Instead of hiding it on the PInvokeMethodDesc for this one instance, I would prefer to fix the entire MethodDesc heirarchy and make it less bespoke. Unless there is a compelling reason to add it there, especially since it is a single lookup cost, I'm inclined to leave it as-is.

@jkoritzinsky

Copy link
Copy Markdown
Member

Yeah this can wait until we do a greater refactoring of the MethodDesc hierarchy if that's the preference.

@jkotas

Copy link
Copy Markdown
Member

refactoring of the MethodDesc hierarchy

There should not be a DynamicMethodDesc for these in the first place. It can be all transient IL.

CopilotAI review requested due to automatic review settings February 19, 2026 14:55

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 1 comment.

Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
CopilotAI review requested due to automatic review settings February 20, 2026 00:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings February 20, 2026 15:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 503a82e into dotnet:mainFeb 20, 2026
107 of 109 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the fix-pinvoke-stub-race branch February 20, 2026 18:34
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
## Summary
Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing duplicate
IL stubs for the same target method.
## Root Cause
PR dotnet#117901 made `IsSharedStubScenario` return `false` for forward
P/Invokes, which disabled the `ILStubCache` lookup. Multiple threads
could then each create their own `DynamicMethodDesc` and JIT it,
producing different `PCODE` values that violated the assert in
`DoPrestub`.
## Fix
- Remove the forward P/Invoke exclusion from `IsSharedStubScenario` so
these stubs use the `ILStubCache` for de-duplication.
- In `CreateHashBlob`, create a minimal hash blob containing just the
target `MethodDesc` pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads for the
same method converge on the same `DynamicMethodDesc`.
Fixesdotnet#124530
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Race in PInvoke stub generation

5 participants

@AaronRobinsonMSFT@jkoritzinsky@jkotas@jtschuster
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix P/Invoke IL stub race in DoPrestub - #124579

Merged
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race
Feb 20, 2026
Merged

Fix P/Invoke IL stub race in DoPrestub#124579
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Summary

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to prevent racing threads from independently creating and JIT-ing duplicate IL stubs for the same target method.

Root Cause

PR #117901 made IsSharedStubScenario return false for forward P/Invokes, which disabled the ILStubCache lookup. Multiple threads could then each create their own DynamicMethodDesc and JIT it, producing different PCODE values that violated the assert in DoPrestub.

Fix

  • Remove the forward P/Invoke exclusion from IsSharedStubScenario so these stubs use the ILStubCache for de-duplication.
  • In CreateHashBlob, create a minimal hash blob containing just the target MethodDesc pointer for forward P/Invoke stubs, so different P/Invoke methods get distinct cache entries while racing threads for the same method converge on the same DynamicMethodDesc.

Fixes#124530

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing
duplicate IL stubs for the same target method.
PR dotnet#117901 made IsSharedStubScenario return false for forward
P/Invokes, which disabled the cache. Multiple threads could then
each create their own DynamicMethodDesc and JIT it, producing
different PCODE values that violated the assert in DoPrestub.
The fix:
- Remove the forward P/Invoke exclusion from IsSharedStubScenario
so these stubs use the ILStubCache for de-duplication.
- In CreateHashBlob, create a minimal hash blob containing just the
target MethodDesc pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads
for the same method converge on the same DynamicMethodDesc.
Fixesdotnet#124530
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race condition in P/Invoke IL stub generation that was introduced by PR #117901. The root cause was that forward P/Invoke stubs (non-CALLI, non-vararg) were excluded from IsSharedStubScenario, disabling the ILStubCache lookup/insert mechanism. This allowed multiple threads to independently create and JIT duplicate IL stubs for the same target method, resulting in different PCODE values and violating the assert in DoPrestub.

Changes:

  • Re-enables ILStubCache usage for forward P/Invoke stubs by removing their exclusion from IsSharedStubScenario
  • Creates minimal hash blobs keyed by target MethodDesc pointer for forward P/Invoke stubs to ensure proper cache de-duplication
  • Updates comments to reflect the new caching behavior

@jkoritzinsky

Copy link
Copy Markdown
Member

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

@jtschusterjtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment threadsrc/coreclr/vm/dllimport.cpp
Comment threadsrc/coreclr/vm/dllimport.cpp
@AaronRobinsonMSFT

Copy link
Copy Markdown
MemberAuthor

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

I think we could, but I really dislike the idea of having multiple places for this sort of data. Instead of hiding it on the PInvokeMethodDesc for this one instance, I would prefer to fix the entire MethodDesc heirarchy and make it less bespoke. Unless there is a compelling reason to add it there, especially since it is a single lookup cost, I'm inclined to leave it as-is.

@jkoritzinsky

Copy link
Copy Markdown
Member

Yeah this can wait until we do a greater refactoring of the MethodDesc hierarchy if that's the preference.

@jkotas

Copy link
Copy Markdown
Member

refactoring of the MethodDesc hierarchy

There should not be a DynamicMethodDesc for these in the first place. It can be all transient IL.

CopilotAI review requested due to automatic review settings February 19, 2026 14:55

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 1 comment.

Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
CopilotAI review requested due to automatic review settings February 20, 2026 00:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings February 20, 2026 15:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 503a82e into dotnet:mainFeb 20, 2026
107 of 109 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the fix-pinvoke-stub-race branch February 20, 2026 18:34
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
## Summary
Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing duplicate
IL stubs for the same target method.
## Root Cause
PR dotnet#117901 made `IsSharedStubScenario` return `false` for forward
P/Invokes, which disabled the `ILStubCache` lookup. Multiple threads
could then each create their own `DynamicMethodDesc` and JIT it,
producing different `PCODE` values that violated the assert in
`DoPrestub`.
## Fix
- Remove the forward P/Invoke exclusion from `IsSharedStubScenario` so
these stubs use the `ILStubCache` for de-duplication.
- In `CreateHashBlob`, create a minimal hash blob containing just the
target `MethodDesc` pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads for the
same method converge on the same `DynamicMethodDesc`.
Fixesdotnet#124530
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Race in PInvoke stub generation

5 participants

@AaronRobinsonMSFT@jkoritzinsky@jkotas@jtschuster
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix P/Invoke IL stub race in DoPrestub - #124579

Merged
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race
Feb 20, 2026
Merged

Fix P/Invoke IL stub race in DoPrestub#124579
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Summary

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to prevent racing threads from independently creating and JIT-ing duplicate IL stubs for the same target method.

Root Cause

PR #117901 made IsSharedStubScenario return false for forward P/Invokes, which disabled the ILStubCache lookup. Multiple threads could then each create their own DynamicMethodDesc and JIT it, producing different PCODE values that violated the assert in DoPrestub.

Fix

  • Remove the forward P/Invoke exclusion from IsSharedStubScenario so these stubs use the ILStubCache for de-duplication.
  • In CreateHashBlob, create a minimal hash blob containing just the target MethodDesc pointer for forward P/Invoke stubs, so different P/Invoke methods get distinct cache entries while racing threads for the same method converge on the same DynamicMethodDesc.

Fixes#124530

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing
duplicate IL stubs for the same target method.
PR dotnet#117901 made IsSharedStubScenario return false for forward
P/Invokes, which disabled the cache. Multiple threads could then
each create their own DynamicMethodDesc and JIT it, producing
different PCODE values that violated the assert in DoPrestub.
The fix:
- Remove the forward P/Invoke exclusion from IsSharedStubScenario
so these stubs use the ILStubCache for de-duplication.
- In CreateHashBlob, create a minimal hash blob containing just the
target MethodDesc pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads
for the same method converge on the same DynamicMethodDesc.
Fixesdotnet#124530
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race condition in P/Invoke IL stub generation that was introduced by PR #117901. The root cause was that forward P/Invoke stubs (non-CALLI, non-vararg) were excluded from IsSharedStubScenario, disabling the ILStubCache lookup/insert mechanism. This allowed multiple threads to independently create and JIT duplicate IL stubs for the same target method, resulting in different PCODE values and violating the assert in DoPrestub.

Changes:

  • Re-enables ILStubCache usage for forward P/Invoke stubs by removing their exclusion from IsSharedStubScenario
  • Creates minimal hash blobs keyed by target MethodDesc pointer for forward P/Invoke stubs to ensure proper cache de-duplication
  • Updates comments to reflect the new caching behavior

@jkoritzinsky

Copy link
Copy Markdown
Member

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

@jtschusterjtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment threadsrc/coreclr/vm/dllimport.cpp
Comment threadsrc/coreclr/vm/dllimport.cpp
@AaronRobinsonMSFT

Copy link
Copy Markdown
MemberAuthor

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

I think we could, but I really dislike the idea of having multiple places for this sort of data. Instead of hiding it on the PInvokeMethodDesc for this one instance, I would prefer to fix the entire MethodDesc heirarchy and make it less bespoke. Unless there is a compelling reason to add it there, especially since it is a single lookup cost, I'm inclined to leave it as-is.

@jkoritzinsky

Copy link
Copy Markdown
Member

Yeah this can wait until we do a greater refactoring of the MethodDesc hierarchy if that's the preference.

@jkotas

Copy link
Copy Markdown
Member

refactoring of the MethodDesc hierarchy

There should not be a DynamicMethodDesc for these in the first place. It can be all transient IL.

CopilotAI review requested due to automatic review settings February 19, 2026 14:55

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 1 comment.

Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
CopilotAI review requested due to automatic review settings February 20, 2026 00:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings February 20, 2026 15:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 503a82e into dotnet:mainFeb 20, 2026
107 of 109 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the fix-pinvoke-stub-race branch February 20, 2026 18:34
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
## Summary
Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing duplicate
IL stubs for the same target method.
## Root Cause
PR dotnet#117901 made `IsSharedStubScenario` return `false` for forward
P/Invokes, which disabled the `ILStubCache` lookup. Multiple threads
could then each create their own `DynamicMethodDesc` and JIT it,
producing different `PCODE` values that violated the assert in
`DoPrestub`.
## Fix
- Remove the forward P/Invoke exclusion from `IsSharedStubScenario` so
these stubs use the `ILStubCache` for de-duplication.
- In `CreateHashBlob`, create a minimal hash blob containing just the
target `MethodDesc` pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads for the
same method converge on the same `DynamicMethodDesc`.
Fixesdotnet#124530
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Race in PInvoke stub generation

5 participants

@AaronRobinsonMSFT@jkoritzinsky@jkotas@jtschuster
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix P/Invoke IL stub race in DoPrestub - #124579

Merged
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race
Feb 20, 2026
Merged

Fix P/Invoke IL stub race in DoPrestub#124579
AaronRobinsonMSFT merged 8 commits into
dotnet:mainfrom
AaronRobinsonMSFT:fix-pinvoke-stub-race

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Summary

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to prevent racing threads from independently creating and JIT-ing duplicate IL stubs for the same target method.

Root Cause

PR #117901 made IsSharedStubScenario return false for forward P/Invokes, which disabled the ILStubCache lookup. Multiple threads could then each create their own DynamicMethodDesc and JIT it, producing different PCODE values that violated the assert in DoPrestub.

Fix

  • Remove the forward P/Invoke exclusion from IsSharedStubScenario so these stubs use the ILStubCache for de-duplication.
  • In CreateHashBlob, create a minimal hash blob containing just the target MethodDesc pointer for forward P/Invoke stubs, so different P/Invoke methods get distinct cache entries while racing threads for the same method converge on the same DynamicMethodDesc.

Fixes#124530

Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing
duplicate IL stubs for the same target method.
PR dotnet#117901 made IsSharedStubScenario return false for forward
P/Invokes, which disabled the cache. Multiple threads could then
each create their own DynamicMethodDesc and JIT it, producing
different PCODE values that violated the assert in DoPrestub.
The fix:
- Remove the forward P/Invoke exclusion from IsSharedStubScenario
so these stubs use the ILStubCache for de-duplication.
- In CreateHashBlob, create a minimal hash blob containing just the
target MethodDesc pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads
for the same method converge on the same DynamicMethodDesc.
Fixesdotnet#124530
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a race condition in P/Invoke IL stub generation that was introduced by PR #117901. The root cause was that forward P/Invoke stubs (non-CALLI, non-vararg) were excluded from IsSharedStubScenario, disabling the ILStubCache lookup/insert mechanism. This allowed multiple threads to independently create and JIT duplicate IL stubs for the same target method, resulting in different PCODE values and violating the assert in DoPrestub.

Changes:

  • Re-enables ILStubCache usage for forward P/Invoke stubs by removing their exclusion from IsSharedStubScenario
  • Creates minimal hash blobs keyed by target MethodDesc pointer for forward P/Invoke stubs to ensure proper cache de-duplication
  • Updates comments to reflect the new caching behavior

@jkoritzinsky

Copy link
Copy Markdown
Member

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

@jtschusterjtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment threadsrc/coreclr/vm/dllimport.cpp
Comment threadsrc/coreclr/vm/dllimport.cpp
@AaronRobinsonMSFT

Copy link
Copy Markdown
MemberAuthor

Could we store the DynamicMethodDesc on the PInvokeMethodDesc directly instead of going through the cache if we always have a 1:1 relationship here?

I think we could, but I really dislike the idea of having multiple places for this sort of data. Instead of hiding it on the PInvokeMethodDesc for this one instance, I would prefer to fix the entire MethodDesc heirarchy and make it less bespoke. Unless there is a compelling reason to add it there, especially since it is a single lookup cost, I'm inclined to leave it as-is.

@jkoritzinsky

Copy link
Copy Markdown
Member

Yeah this can wait until we do a greater refactoring of the MethodDesc hierarchy if that's the preference.

@jkotas

Copy link
Copy Markdown
Member

refactoring of the MethodDesc hierarchy

There should not be a DynamicMethodDesc for these in the first place. It can be all transient IL.

CopilotAI review requested due to automatic review settings February 19, 2026 14:55

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 1 comment.

Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
Comment threadsrc/coreclr/vm/dllimport.cpp Outdated
CopilotAI review requested due to automatic review settings February 20, 2026 00:33

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings February 20, 2026 15:21

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 3 out of 3 changed files in this pull request and generated no new comments.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit 503a82e into dotnet:mainFeb 20, 2026
107 of 109 checks passed
@AaronRobinsonMSFT
AaronRobinsonMSFT deleted the fix-pinvoke-stub-race branch February 20, 2026 18:34
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
## Summary
Re-enable ILStubCache lookup/insert for forward P/Invoke stubs to
prevent racing threads from independently creating and JIT-ing duplicate
IL stubs for the same target method.
## Root Cause
PR dotnet#117901 made `IsSharedStubScenario` return `false` for forward
P/Invokes, which disabled the `ILStubCache` lookup. Multiple threads
could then each create their own `DynamicMethodDesc` and JIT it,
producing different `PCODE` values that violated the assert in
`DoPrestub`.
## Fix
- Remove the forward P/Invoke exclusion from `IsSharedStubScenario` so
these stubs use the `ILStubCache` for de-duplication.
- In `CreateHashBlob`, create a minimal hash blob containing just the
target `MethodDesc` pointer for forward P/Invoke stubs, so different
P/Invoke methods get distinct cache entries while racing threads for the
same method converge on the same `DynamicMethodDesc`.
Fixesdotnet#124530
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Race in PInvoke stub generation

5 participants

@AaronRobinsonMSFT@jkoritzinsky@jkotas@jtschuster