-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Description
Full repro
See https://github.com/elsand/npgsql-dapper-segfault/. The relevant code can also be found below.
Summary
A simple Dapper + Npgsql program crashes the process when binding an array parameter via ICustomQueryParameter and running SELECT unnest(@Ids). This happens on macOS (SIGSEGV, exit 139) and Windows (Fatal error. Internal CLR error 0x80131506). The repro targets net9.0 and net10.0 and uses Npgsql 10.0.1 + Dapper 2.1.66.
Crash seems to occur during Dapper parameter setup (before query execution) when NpgsqlParameter is created for an array via ICustomQueryParameter.
(Moved from npgsql/npgsql#6434, as this does not appear to be a npgsql-issue after all. Not sure if it's Dapper either, or if this belongs to dotnet/runtime).
Steps
- git clone https://github.com/elsand/npgsql-dapper-segfault/
- set PG_CONNECTION_STRING to a PostgreSQL instance
- dotnet run -f net10.0 (or -f net9.0)
Expected
Query returns the unnest’ed values.
Actual
Process crashes. On macOS it’s a SIGSEGV (exit 139). On Windows, it reports “Fatal error. Internal CLR error. (0x80131506)”.
Running on Mac gives no console output, unless minidumps are enabled:
DOTNET_DbgEnableMiniDump=1 \
DOTNET_DbgMiniDumpType=3 \
DOTNET_DbgMiniDumpName=/tmp/npgsql_segfault_%p.dmp \
PG_CONNECTION_STRING='Host=localhost;Database=postgres;Username=postgres;Password=xxxxxx' \
dotnet run -f net10.0
[createdump] Gathering state for process 51920
[createdump] Crashing thread 131147a signal 11 (000b)
[createdump] Writing triage minidump to file /tmp/npgsql_segfault_51920.dmp
[createdump] Written 38621464 bytes (2357 pages) to core file
[createdump] Target process is alive
[createdump] Dump successfully written in 197ms
Minidump can be provided upon request.
Running on Windows gives substantially more output:
PS C:\Users\bjorn\Repos\npgsql-dapper-segfault> dotnet run -f net10.0
Fatal error.
Internal CLR error. (0x80131506)
at Dapper.CommandDefinition.SetupCommand(System.Data.IDbConnection, System.Action`2<System.Data.IDbCommand,System.Object>)
at Dapper.SqlMapper.TrySetupAsyncCommand(Dapper.CommandDefinition, System.Data.IDbConnection, System.Action`2<System.Data.IDbCommand,System.Object>)
at Dapper.SqlMapper+<QueryAsync>d__33`1[[System.__Canon, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[Dapper.SqlMapper+<QueryAsync>d__33`1[[System.__Canon, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null]](<QueryAsync>d__33`1<System.__Canon> ByRef)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[[System.__Canon, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Start[[Dapper.SqlMapper+<QueryAsync>d__33`1[[System.__Canon, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null]](<QueryAsync>d__33`1<System.__Canon> ByRef)
at Dapper.SqlMapper.QueryAsync[[System.__Canon, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Data.IDbConnection, System.Type, Dapper.CommandDefinition)
at Dapper.SqlMapper.QueryAsync[[System.__Canon, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Data.IDbConnection, Dapper.CommandDefinition)
at Program+<<Main>$>d__0.MoveNext()
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext(System.Threading.Thread)
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Runtime.CompilerServices.IAsyncStateMachineBox, Boolean)
at System.Threading.Tasks.Task.RunContinuations(System.Object)
at System.Threading.Tasks.Task`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TrySetResult(System.Threading.Tasks.VoidTaskResult)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].SetExistingTaskResult(System.Threading.Tasks.Task`1<System.Threading.Tasks.VoidTaskResult>, System.Threading.Tasks.VoidTaskResult)
at Npgsql.NpgsqlConnection+<<Open>g__OpenAsync|42_0>d.MoveNext()
(rest of output omitted)
Environment
- Hardware: Apple M3 Pro / AMD Ryzen 7 3700X
- OS: macOS 26.2 (Build 25C56) / Windows 11 Pro (Build 26200.7623)
- PostgreSQL: 16.10
- SDKs tested: .NET 9.0.310, .NET 10.0.102
- Packages: Npgsql 10.0.1, Dapper 2.1.66
Relevant code
using System.Data;
using Dapper;
using Npgsql;
await using var connection = new NpgsqlConnection(Environment.GetEnvironmentVariable("PG_CONNECTION_STRING")
?? "Host=localhost;Database=postgres;Username=postgres;Password=postgres");
await connection.OpenAsync();
var parameters = new
{
Ids = new PostgresArray<string>(["a"]) // Also tested with `int` and `Guid` type parameters - same result
};
var command = new CommandDefinition("SELECT unnest(@Ids) AS Value", parameters, cancellationToken: CancellationToken.None);
var rows = await connection.QueryAsync<string>(command); // <--- Segfault happens here
foreach (var value in rows) // <--- This line is never reached
{
Console.WriteLine(value);
}
internal readonly struct PostgresArray<T>(T[] value) : SqlMapper.ICustomQueryParameter
{
public void AddParameter(IDbCommand command, string name)
{
var param = new NpgsqlParameter(name, value);
command.Parameters.Add(param);
}
}