Skip to content

Fix eager native SNI initialization on non-Windows platforms - #4700

Open
shreyarao4 wants to merge 1 commit into
dotnet:mainfrom
shreyarao4:fix1
Open

shreyarao4 wants to merge 1 commit into
dotnet:mainfrom
shreyarao4:fix1

Conversation

@shreyarao4

@shreyarao4 shreyarao4 commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Description

After #4465, the same SqlClient code is built for both Windows and non-Windows platforms, with native or managed SNI selected at runtime.

SNILoadHandle contains a static singleton:
internal static readonly SNILoadHandle SingletonInstance = new SNILoadHandle();
Because SNILoadHandle has no explicit static constructor, its treated as beforefieldinit. This allows the runtime to initialize the type before SingletonInstance is actually accessed.

The SNILoadHandle constructor calls the Windows-only native SNI initialization:
SniNativeWrapper.SniInitialize();

On non-Windows platforms, managed SNI is selected and SNILoadHandle should not be initialized. However, because of beforefieldinit, the runtime can initialize SNILoadHandle merely because it is referenced by JIT-compiled code.
This causes the Windows-only native SNI library to be loaded on non-Windows platforms and results in the following DllNotFoundException.

System.TypeInitializationException: The type initializer for 'Microsoft.Data.SqlClient.SNILoadHandle' threw an exception.
 ---> System.DllNotFoundException: Microsoft.Data.SqlClient.SNI.dll
   at Interop.Windows.Sni.SniNativeMethods.SniInitialize(IntPtr pmo)
   at Microsoft.Data.SqlClient.SniNativeWrapper.SniInitialize()
   at Microsoft.Data.SqlClient.SNILoadHandle..ctor()
   at Microsoft.Data.SqlClient.SNILoadHandle..cctor()
 --- End of inner exception stack trace ---
   at System.Runtime.InteropServices.SafeHandle.InternalRelease(Boolean disposeOrFinalizeOperation)
   at System.Runtime.InteropServices.SafeHandle.Dispose(Boolean disposing)
   at System.Runtime.InteropServices.SafeHandle.Finalize()
   at System.GC.GuardedFinalize(Object o)

Solution

Added an explicit static constructor to SNILoadHandle.
This removes the beforefieldinit behavior and ensures that static initialization happens only when SNILoadHandle is actually accessed.
Therefore, on Linux, SNILoadHandle is not initialized when the managed SNI path is selected, and the Windows-only native SNI library is never loaded.

Testing

Verified on Linux/s390x: ManualTests previously crashed with the DllNotFoundException above; after this change, tests run sucessfully.

Environment

Linux s390x
Mono runtime

@giritrivedi

Fixes a crash where SNILoadHandle's native SNI initialization (SniInitialize(), Windows-only) can run on non-Windows platforms, throwing DllNotFoundException
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@edwardneal

Copy link
Copy Markdown
Contributor

That load behaviour is very eager - the code paths are unreachable as a result of OsConstants.IsWindows returning false, I'm surprised that the JIT is even able to see the native SNI to try and initialize it. Tests have been continuing to pass on Linux and macOS using the updated package for the last two months, has this been seen on platforms besides s390x?

@shreyarao4

Copy link
Copy Markdown
Contributor Author

I have not verified on any other platforms, but please note this is using Mono runtime. dotnet/runtime#77513 confirms this behavior on Mono. Mono eagerly runs a beforefieldinit type's static constructor even when the accessing code path is never taken.

@mdaigle mdaigle added this to the 8.0.0-preview1 milestone Sep 18, 2026
@cheenamalhotra cheenamalhotra moved this from To triage to In review in SqlClient Board Sep 23, 2026
@mdaigle

mdaigle commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).
1 pipeline(s) were filtered out due to trigger conditions.

@codecov

codecov Bot commented Sep 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 64.33%. Comparing base (8387aeb) to head (d10918c).
⚠️ Report is 13 commits behind head on main.

Files with missing lines Patch % Lines
...c/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4700      +/-   ##
==========================================
- Coverage   66.21%   64.33%   -1.89%     
==========================================
  Files         290      285       -5     
  Lines       45063    68064   +23001     
==========================================
+ Hits        29838    43788   +13950     
- Misses      15225    24276    +9051     
Flag Coverage Δ
CI-SqlClient ?
PR-SqlClient-Project 64.33% <0.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

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

Labels

None yet

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

4 participants