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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To triage

Development

Successfully merging this pull request may close these issues.

4 participants