Skip to content

Unhandled native exception by SniNativeWrapper.SniOpenSyncEx / SNI_Spn::MakeSpn when opening a connection #3661

@ManuelRin

Description

@ManuelRin

Describe the bug

Opening a (pooled) database connection "sometimes" throws a native exception in unmanaged code Microsoft.Data.SqlClient.SNI.dll function SNI_Spn::MakeSpn (according to debug symbols).
This does not happen every time but once in a while (maybe every 100th or 5000th call).

There is no managed exception and the application crashes, leaving an entry in the Windows Event Log like the following.

Log Name: Application
Source: Application Error
Event ID: 1000

Faulting application name: MyApplication.exe, version: 1.0.0.0, time stamp: 0x68760000
Faulting module name: ntdll.dll, version: 10.0.20348.3932, time stamp: 0xc75bbaae
Exception code: 0xc0000409
Fault offset: 0x000000000008ebff
Faulting process id: 0x1208
Faulting application start time: 0x01dc1b72db812087
Faulting application path: C:\SomePath\MyApplication.exe
Faulting module path: C:\Windows\SYSTEM32\ntdll.dll
Report Id: cd9ba991-be98-4342-8c08-f46a6493ede5
Faulting package full name: 
Faulting package-relative application ID: 

A detailed exception cannot be seen. But with Windows Error Reporting (WER) configured to collect user-mode full dumps on such errors according to Collecting User-Mode Dumps on Microsoft Learn a full dump can be collected.

Full crash dump mixed mode debugging showed the following native exception (Windows x64, debug symbols loaded):

Unhandled exception at 0x00007FFAB886EBFF (ntdll.dll) in MyApplication.exe.14380.dmp: Indirect call guard check detected invalid control transfer.

Exception message:
Stack trace:

ntdll.dll!LdrpICallHandler()
ntdll.dll!RtlpExecuteHandlerForException()
ntdll.dll!RtlDispatchException()
ntdll.dll!KiUserExceptionDispatch()
ntdll.dll!LdrpDispatchUserCallTarget()
Microsoft.Data.SqlClient.SNI.dll!SNI_Spn::MakeSpn(unsigned short *,unsigned short *,unsigned short,unsigned short *,unsigned long)
Microsoft.Data.SqlClient.SNI.dll!Connect(class ConnectParameter *,struct SNI_CLIENT_CONSUMER_INFO *,class ProtElem *,class SNI_Conn * *,int)
Microsoft.Data.SqlClient.SNI.dll!SNIOpenSyncEx()
Microsoft.Data.SqlClient.SNI.dll!SNIOpenSyncExWrapper()
[Managed to Native Transition]
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.SniNativeWrapper.SniOpenSyncEx(Interop.Windows.Sni.ConsumerInfo consumerInfo, string connString, ref nint pConn, ref string spn, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, Microsoft.Data.SqlClient.SqlConnectionIPAddressPreference ipPreference, Microsoft.Data.SqlClient.SQLDNSInfo cachedDnsInfo, string hostNameInCertificate)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.SNIHandle.SNIHandle(Interop.Windows.Sni.ConsumerInfo myInfo, string serverName, ref string spn, int timeout, out byte[] instanceName, bool flushCache, bool fSync, bool fParallel, Microsoft.Data.SqlClient.SqlConnectionIPAddressPreference ipPreference, Microsoft.Data.SqlClient.SQLDNSInfo cachedDNSInfo, string hostNameInCertificate)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.TdsParserStateObjectNative.CreatePhysicalSNIHandle(string serverName, Microsoft.Data.ProviderBase.TimeoutTimer timeout, out byte[] instanceName, out Microsoft.Data.SqlClient.ManagedSni.ResolvedServerSpn resolvedSpn, bool flushCache, bool async, bool fParallel, Microsoft.Data.SqlClient.SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref Microsoft.Data.SqlClient.SQLDNSInfo pendingDNSInfo, string serverSPN, bool isIntegratedSecurity, bool tlsFirst, string hostNameInCertificate, string serverCertificateFilename)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.TdsParser.Connect(Microsoft.Data.SqlClient.ServerInfo serverInfo, Microsoft.Data.SqlClient.SqlInternalConnectionTds connHandler, Microsoft.Data.ProviderBase.TimeoutTimer timeout, Microsoft.Data.SqlClient.SqlConnectionString connectionOptions, bool withFailover)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(Microsoft.Data.SqlClient.ServerInfo serverInfo, string newPassword, System.Security.SecureString newSecurePassword, Microsoft.Data.ProviderBase.TimeoutTimer timeout, bool withFailover)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(Microsoft.Data.SqlClient.ServerInfo serverInfo, string newPassword, System.Security.SecureString newSecurePassword, bool redirectedUserInstance, Microsoft.Data.SqlClient.SqlConnectionString connectionOptions, Microsoft.Data.SqlClient.SqlCredential credential, Microsoft.Data.ProviderBase.TimeoutTimer timeout)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(Microsoft.Data.ProviderBase.TimeoutTimer timeout, Microsoft.Data.SqlClient.SqlConnectionString connectionOptions, Microsoft.Data.SqlClient.SqlCredential credential, string newPassword, System.Security.SecureString newSecurePassword, bool redirectedUserInstance)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.SqlInternalConnectionTds.SqlInternalConnectionTds(Microsoft.Data.SqlClient.ConnectionPool.DbConnectionPoolIdentity identity, Microsoft.Data.SqlClient.SqlConnectionString connectionOptions, Microsoft.Data.SqlClient.SqlCredential credential, object providerInfo, string newPassword, System.Security.SecureString newSecurePassword, bool redirectedUserInstance, Microsoft.Data.SqlClient.SqlConnectionString userConnectionOptions, Microsoft.Data.SqlClient.SessionData reconnectSessionData, bool applyTransientFaultHandling, string accessToken, Microsoft.Data.SqlClient.ConnectionPool.IDbConnectionPool pool, System.Func<Microsoft.Data.SqlClient.SqlAuthenticationParameters, System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Data.SqlClient.SqlAuthenticationToken>> accessTokenCallback)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(Microsoft.Data.Common.ConnectionString.DbConnectionOptions options, Microsoft.Data.SqlClient.ConnectionPool.DbConnectionPoolKey poolKey, Microsoft.Data.SqlClient.ConnectionPool.DbConnectionPoolGroupProviderInfo poolGroupProviderInfo, Microsoft.Data.SqlClient.ConnectionPool.IDbConnectionPool pool, System.Data.Common.DbConnection owningConnection, Microsoft.Data.Common.ConnectionString.DbConnectionOptions userOptions)
Microsoft.Data.SqlClient.dll!Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(Microsoft.Data.SqlClient.ConnectionPool.IDbConnectionPool pool, System.Data.Common.DbConnection owningObject, Microsoft.Data.Common.ConnectionString.DbConnectionOptions options, Microsoft.Data.SqlClient.ConnectionPool.DbConnectionPoolKey poolKey, Microsoft.Data.Common.ConnectionString.DbConnectionOptions userOptions)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.ConnectionPool.WaitHandleDbConnectionPool.CreateObject(System.Data.Common.DbConnection owningObject, Microsoft.Data.Common.ConnectionString.DbConnectionOptions userOptions, Microsoft.Data.ProviderBase.DbConnectionInternal oldConnection)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.ConnectionPool.WaitHandleDbConnectionPool.UserCreateRequest(System.Data.Common.DbConnection owningObject, Microsoft.Data.Common.ConnectionString.DbConnectionOptions userOptions, Microsoft.Data.ProviderBase.DbConnectionInternal oldConnection)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.ConnectionPool.WaitHandleDbConnectionPool.TryGetConnection(System.Data.Common.DbConnection owningObject, uint waitForMultipleObjectsTimeout, bool allowCreate, bool onlyOneCheckConnection, Microsoft.Data.Common.ConnectionString.DbConnectionOptions userOptions, out Microsoft.Data.ProviderBase.DbConnectionInternal connection)
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.ConnectionPool.WaitHandleDbConnectionPool.WaitForPendingOpen()
[Native to Managed Transition]
kernel32.dll!00007ffab7584cb0()
ntdll.dll!00007ffab885edcb()

To reproduce

Prerequisites (may or may not have influence):

  • Have a DNS Alias for the database

  • Have a SQL Server Always On availability group

  • Have setup Kerberos authentication enforced

  • Have setup SPNs for the alias associated with the gMSAs (Group Managed Service Accounts) running the cluster nodes: MSSQLSvc/myDbServerDnsAlias.example.com:1433 and MSSQLSvc/myDbServerDnsAlias:1433 (note that the casing may be different form the casing of the server alias used in the connection string)

  • Compile and publish the application as Release build with .NET R2R Ready-to-Run compilation

  • Setup WER (Windows Error Reporting) to collect user-mode full dumps on such errors according to Collecting User-Mode Dumps on Microsoft Learn

Use the below simple code to connect.
This typically works, but throws the native exception once in a while.

using Microsoft.Data.SqlClient;

string connectionString = new SqlConnectionStringBuilder
    {
        DataSource = "MyDbServerDnsAlias", // no FQDN
        InitialCatalog = "MyDatabase",
        IntegratedSecurity = true,
        MultipleActiveResultSets = true,
        ApplicationName = AppDomain.CurrentDomain.FriendlyName,
        ConnectTimeout = 5,
    }.ConnectionString;

await using var sqlConnection = new SqlConnection(connectionString);
await sqlConnection.OpenAsync();
Console.WriteLine("SUCCESS.");

Expected behavior

In any case, no unmanaged exception from SNI should bubble up through managed code.
In case of incorrect usage, the input should be validated and managed exceptions should be thrown.
In case of a native exception state, this should be handled orderly.
Exceptions should not happen randomly / sometimes.

Further technical details

Microsoft.Data.SqlClient version: 6.1.1
Microsoft.Data.SqlClient.SNI.runtime version: 6.0.2
.NET target: .NET 8
SQL Server version: SQL Server 2016 (13.0.6465.1)
Operating system: Windows Server 2022 (server and client)

Additional context
DLL versions:

  • Microsoft.Data.SqlClient.dll: 6.11.25226.3
  • Microsoft.Data.SqlClient.SNI.dll: 6.2.0.0
  • ntdll.dll: 10.0.20348.3932 (WinBuild.160101.0800)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions