[PATCH] intarray: prevent crash in _int_matchsel on invalid query_int input

From: Eugeny Goryachev <gorcom2012(at)gmail(dot)com>
To: pgsql-hackers(at)postgresql(dot)org
Subject: [PATCH] intarray: prevent crash in _int_matchsel on invalid query_int input
Date: 2026-03-04 13:02:51
Message-ID: CABg3sZrVkXuYx5qO4Bw4AihzqWnB4ukqXZpPep7o0-skNNWgLw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

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 <e(dot)goryachev(at)mti-lab(dot)com>
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

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Fujii Masao 2026-03-04 13:25:45 Re: Change COPY ... ON_ERROR ignore to ON_ERROR ignore_row
Previous Message Akshay Joshi 2026-03-04 12:59:53 Re: [PATCH] Add pg_get_database_ddl() function to reconstruct CREATE DATABASE statement