Hi, The selectivity function _int_matchsel() in contrib/intarray assumes that the right-hand argument is a valid query_int datum. If a malformed or binary-incompatible value is passed (for example, via an implicit cast from a user-defined type created WITHOUT FUNCTION), the function may dereference an invalid pointer and crash.
Specifically, _int_matchsel() calls DatumGetQueryTypeP() and immediately accesses the resulting structure without validating the datum size or structure integrity. This patch adds a minimal size check before dereferencing the query pointer. If the datum is not a valid query_int value, an ERROR with ERRCODE_DATATYPE_MISMATCH is raised instead of causing a backend crash. A regression test is included to demonstrate the issue using a fake type that is implicitly cast to query_int. Patch attached. Regards, Eugeny Goryachev >From f7f108be286872e7e380e0f11b636d03407758c4 Mon Sep 17 00:00:00 2001 From: Eugeny Goryachev <[email protected]> Date: Wed, 4 Mar 2026 12:22:33 +0300 Subject: [PATCH] Fix intarray segfault --- contrib/intarray/Makefile | 4 +- contrib/intarray/_int_selfuncs.c | 5 +++ contrib/intarray/expected/fake_query_type.out | 38 ++++++++++++++++ contrib/intarray/sql/fake_query_type.sql | 43 +++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 contrib/intarray/expected/fake_query_type.out create mode 100644 contrib/intarray/sql/fake_query_type.sql diff --git a/contrib/intarray/Makefile b/contrib/intarray/Makefile index 3817c1669ab..c064c091047 100644 --- a/contrib/intarray/Makefile +++ b/contrib/intarray/Makefile @@ -17,7 +17,9 @@ DATA = intarray--1.4--1.5.sql intarray--1.3--1.4.sql intarray--1.2--1.3.sql \ intarray--1.0--1.1.sql PGFILEDESC = "intarray - functions and operators for arrays of integers" -REGRESS = _int +REGRESS = \ + _int \ + fake_query_type ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/intarray/_int_selfuncs.c b/contrib/intarray/_int_selfuncs.c index f75da3a09d2..26e715cfce6 100644 --- a/contrib/intarray/_int_selfuncs.c +++ b/contrib/intarray/_int_selfuncs.c @@ -186,6 +186,11 @@ _int_matchsel(PG_FUNCTION_ARGS) query = DatumGetQueryTypeP(((Const *) other)->constvalue); + if (VARSIZE(query) < HDRSIZEQT) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument must be of type querytype"))); + /* Empty query matches nothing */ if (query->size == 0) { diff --git a/contrib/intarray/expected/fake_query_type.out b/contrib/intarray/expected/fake_query_type.out new file mode 100644 index 00000000000..c3cc64eb2dd --- /dev/null +++ b/contrib/intarray/expected/fake_query_type.out @@ -0,0 +1,38 @@ +-- test for missing type validation in _int_matchsel +CREATE FUNCTION fake_query_int_in(cstring) +RETURNS fake_query_int +AS 'textin' +LANGUAGE internal IMMUTABLE STRICT; +NOTICE: type "fake_query_int" is not yet defined +DETAIL: Creating a shell type definition. +CREATE FUNCTION fake_query_int_out(fake_query_int) +RETURNS cstring +AS 'textout' +LANGUAGE internal IMMUTABLE STRICT; +NOTICE: argument type fake_query_int is only a shell +CREATE TYPE fake_query_int ( + INPUT = fake_query_int_in, + OUTPUT = fake_query_int_out, + LIKE = text +); +CREATE CAST (fake_query_int AS query_int) +WITHOUT FUNCTION +AS IMPLICIT; +CREATE OR REPLACE FUNCTION fake_query_int_match(integer[], fake_query_int) +RETURNS boolean +AS $$ + SELECT $1 @@ $2::query_int; +$$ LANGUAGE sql IMMUTABLE; +CREATE OPERATOR @@ ( + LEFTARG = integer[], + RIGHTARG = fake_query_int, + PROCEDURE = fake_query_int_match, + RESTRICT = _int_matchsel +); +CREATE TABLE test_arr ( + id serial, + arr integer[] +); +SELECT * FROM test_arr +WHERE arr @@ '1&2'::fake_query_int; +ERROR: argument must be of type querytype diff --git a/contrib/intarray/sql/fake_query_type.sql b/contrib/intarray/sql/fake_query_type.sql new file mode 100644 index 00000000000..a36c6eee1d4 --- /dev/null +++ b/contrib/intarray/sql/fake_query_type.sql @@ -0,0 +1,43 @@ +-- test for missing type validation in _int_matchsel + +CREATE FUNCTION fake_query_int_in(cstring) +RETURNS fake_query_int +AS 'textin' +LANGUAGE internal IMMUTABLE STRICT; + +CREATE FUNCTION fake_query_int_out(fake_query_int) +RETURNS cstring +AS 'textout' +LANGUAGE internal IMMUTABLE STRICT; + +CREATE TYPE fake_query_int ( + INPUT = fake_query_int_in, + OUTPUT = fake_query_int_out, + LIKE = text +); + +CREATE CAST (fake_query_int AS query_int) +WITHOUT FUNCTION +AS IMPLICIT; + +CREATE OR REPLACE FUNCTION fake_query_int_match(integer[], fake_query_int) +RETURNS boolean +AS $$ + SELECT $1 @@ $2::query_int; +$$ LANGUAGE sql IMMUTABLE; + +CREATE OPERATOR @@ ( + LEFTARG = integer[], + RIGHTARG = fake_query_int, + PROCEDURE = fake_query_int_match, + RESTRICT = _int_matchsel +); + + +CREATE TABLE test_arr ( + id serial, + arr integer[] +); + +SELECT * FROM test_arr +WHERE arr @@ '1&2'::fake_query_int; -- 2.42.4
