Skip to content

Segfault when binding array parameter via Dapper ICustomQueryParameter #2189

@elsand

Description

@elsand

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

  1. git clone https://github.com/elsand/npgsql-dapper-segfault/
  2. set PG_CONNECTION_STRING to a PostgreSQL instance
  3. 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)

Full output of Windows run

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);
    }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions