Skip to content

Fix ComObject finalizer crash from OLE DB error RCWs#130906

Merged
AaronRobinsonMSFT merged 4 commits into
dotnet:mainfrom
AaronRobinsonMSFT:runtime_125221
Jul 16, 2026
Merged

Fix ComObject finalizer crash from OLE DB error RCWs#130906
AaronRobinsonMSFT merged 4 commits into
dotnet:mainfrom
AaronRobinsonMSFT:runtime_125221

Conversation

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

Fixes #125221.

Problem

Running failing SQL through System.Data.OleDb could crash the process with
an AccessViolationException/NullReferenceException in ComObject.Finalize()
on the GC finalizer thread.

OleDbError's constructor creates source-generated ComObject RCWs for the
per-record IErrorInfo/ISQLErrorInfo objects. The non-[PreserveSig]
extraction methods throw on a provider failure HRESULT, so a throw mid-ctor
skipped the manual release and left the RCW to be finalized. OLE DB error
objects are thread-affine, so releasing one on the finalizer thread faults.

Fix

Release the per-record errorInfo/sqlErrorInfo RCWs in try/finally so they
are always released on the calling thread, even when extraction throws.

Diagnostic

Also adds a DEBUG-only, Windows-only assert in FreeThreadedStrategy: it probes
each COM object for agility (IAgileObject / free-threaded marshaler, mirroring
the built-in RCW) and Debug.Asserts otherwise. [Conditional("DEBUG")], so it
compiles out of release builds. This would have caught the crash's root cause at
its source.

Note

This PR description was generated with the assistance of GitHub Copilot.

AaronRobinsonMSFT and others added 2 commits July 16, 2026 11:21
OleDbError's constructor created per-record IErrorInfo and ISQLErrorInfo
RCWs but released them only on the success path. A provider returning a
failure HRESULT from a non-[PreserveSig] call (GetErrorInfo, GetSQLInfo)
threw mid-extraction, letting the RCW escape to ~ComObject() and be
released on the GC finalizer thread. For a thread-affinitized OLE DB
error object that off-thread Marshal.Release faults (0xC0000005).

Wrap both RCWs in try/finally so they are always FinalRelease'd on the
calling thread regardless of exceptions.

Fixes dotnet#125221

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@AaronRobinsonMSFT

Copy link
Copy Markdown
Member Author

@dotnet/interop-contrib PTAL

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

Copilot AI 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

Fixes a reliability issue in source-generated COM interop usage for OLE DB error handling by ensuring thread-affine COM wrappers are deterministically released on the originating thread (instead of being left for finalization), and adds a DEBUG-only diagnostic to help catch non-agile COM objects being used with the free-threaded COM strategy.

Changes:

  • Ensure per-record IErrorInfo and ISQLErrorInfo COM wrappers in OleDbError are always released via try/finally.
  • Add a DEBUG-only Windows agility probe/assertion to FreeThreadedStrategy to detect non-free-threaded COM objects early.
Show a summary per file
File Description
src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/FreeThreadedStrategy.cs Adds DEBUG-only agility probing/assertion for COM objects used with the free-threaded IUnknown strategy.
src/libraries/System.Data.OleDb/src/OleDbError.cs Wraps COM error object usage in try/finally to ensure RCWs are released on the calling thread even when extraction throws.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Comment thread src/libraries/System.Data.OleDb/src/OleDbError.cs

@jtschuster jtschuster 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.

Not familiar enough with COM to know if IsFreeThreaded is correct in all cases, but it should only be firing in Debug builds / CI, and the try/catch changes LGTM.

…s to ensure safety when called from the GC finalizer thread

Copilot AI 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.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Comment thread src/libraries/System.Data.OleDb/src/OleDbError.cs
Copilot AI review requested due to automatic review settings July 16, 2026 20:25

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@AaronRobinsonMSFT
AaronRobinsonMSFT merged commit e0e5f83 into dotnet:main Jul 16, 2026
26 checks passed
@elinor-fung

Copy link
Copy Markdown
Member

It looks like the test failure got through because runtime pipeline just completely failed to start: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1513486&view=results
And it didn't show up on checks at all, so Build Analysis was green?

AaronRobinsonMSFT added a commit that referenced this pull request Jul 17, 2026
Reverts the DEBUG-only free-threaded assert added to
`FreeThreadedStrategy` in #130906. The assert was overly strict:
`FreeThreadedStrategy` is also valid for apartment-affinitized COM
objects when the current thread already has the correct apartment type,
so the agility probe produced false assert failures.

Also clarifies documentation on
`StrategyBasedComWrappers.DefaultIUnknownStrategy` and adds a class
comment on `FreeThreadedStrategy` describing these use cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

System.Data.OleDb: AccessViolationException in ComObject.Finalize() when OleDbCommand fails on error path

4 participants