Subject: BUG #19491: Segmentation fault triggered by IS NULL

From: PG Bug reporting form <[email-masked]>
Date: Fri, 22 May 2026 06:27:28 +0000

The following bug has been logged on the website:

Bug reference:      19491
Logged by:          Chi Zhang
Email address:      [email-masked]
PostgreSQL version: 18.4
Operating system:   Ubuntu 24.04
Description:        

Hi,

I found that the following test case triggers a segmentation fault.

```
CREATE SCHEMA IF NOT EXISTS poc;

CREATE FUNCTION poc.mystring_in(cstring)
    RETURNS poc.mystring
    AS 'textin' LANGUAGE internal IMMUTABLE STRICT;

CREATE FUNCTION poc.mystring_out(poc.mystring)
    RETURNS cstring
    AS 'textout' LANGUAGE internal IMMUTABLE STRICT;

CREATE TYPE poc.mystring (
    INPUT = poc.mystring_in,
    OUTPUT = poc.mystring_out,
    LIKE = text,
    CATEGORY = 'S'
);

SELECT '{"a":1}'::poc.mystring IS JSON;

DROP SCHEMA IF EXISTS poc CASCADE;
```

This is the output:

```
sqlancer=# CREATE SCHEMA IF NOT EXISTS poc;
CREATE SCHEMA
sqlancer=# CREATE FUNCTION poc.mystring_in(cstring)
    RETURNS poc.mystring
    AS 'textin' LANGUAGE internal IMMUTABLE STRICT;
NOTICE:  type "poc.mystring" is not yet defined
DETAIL:  Creating a shell type definition.
CREATE FUNCTION
sqlancer=# CREATE FUNCTION poc.mystring_out(poc.mystring)
    RETURNS cstring
    AS 'textout' LANGUAGE internal IMMUTABLE STRICT;
NOTICE:  argument type poc.mystring is only a shell
LINE 1: CREATE FUNCTION poc.mystring_out(poc.mystring)
                                         ^
CREATE FUNCTION
sqlancer=# CREATE TYPE poc.mystring (
    INPUT = poc.mystring_in,
    OUTPUT = poc.mystring_out,
    LIKE = text,
    CATEGORY = 'S'
);
CREATE TYPE
sqlancer=# SELECT '{"a":1}'::poc.mystring IS JSON;
server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.
```

This is the log:

```
2026-05-22 06:19:00.811 UTC [1] LOG:  starting PostgreSQL 18.1 (Debian
18.1-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19)
14.2.0, 64-bit
2026-05-22 06:19:00.811 UTC [1] LOG:  listening on IPv4 address "0.0.0.0",
port 5432
2026-05-22 06:19:00.811 UTC [1] LOG:  listening on IPv6 address "::", port
5432
2026-05-22 06:19:00.816 UTC [1] LOG:  listening on Unix socket
"/var/run/postgresql/.s.PGSQL.5432"
2026-05-22 06:19:00.825 UTC [32] LOG:  database system was shut down at
2026-05-22 06:18:54 UTC
2026-05-22 06:19:00.830 UTC [1] LOG:  database system is ready to accept
connections
2026-05-22 06:21:48.272 UTC [1] LOG:  client backend (PID 42) was terminated
by signal 11: Segmentation fault
2026-05-22 06:21:48.272 UTC [1] DETAIL:  Failed process was running: SELECT
'{"a":1}'::poc.mystring IS JSON;
2026-05-22 06:21:48.272 UTC [1] LOG:  terminating any other active server
processes
2026-05-22 06:21:48.273 UTC [1] LOG:  all server processes terminated;
reinitializing
2026-05-22 06:21:48.324 UTC [49] LOG:  database system was interrupted; last
known up at 2026-05-22 06:19:00 UTC
2026-05-22 06:21:48.428 UTC [49] LOG:  database system was not properly shut
down; automatic recovery in progress
2026-05-22 06:21:48.431 UTC [49] LOG:  redo starts at 0/1C07F50
2026-05-22 06:21:48.432 UTC [49] LOG:  invalid record length at 0/1C1AE88:
expected at least 24, got 0
2026-05-22 06:21:48.432 UTC [49] LOG:  redo done at 0/1C1AE00 system usage:
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
2026-05-22 06:21:48.437 UTC [50] LOG:  checkpoint starting: end-of-recovery
immediate wait
2026-05-22 06:21:48.450 UTC [50] LOG:  checkpoint complete: wrote 15 buffers
(0.1%), wrote 3 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled;
write=0.003 s, sync=0.004 s, total=0.016 s; sync files=16, longest=0.002 s,
average=0.001 s; distance=75 kB, estimate=75 kB; lsn=0/1C1AE88, redo
lsn=0/1C1AE88
2026-05-22 06:21:48.455 UTC [1] LOG:  database system is ready to accept
connections
```

I can reproduce this on the github commit
d8af73010033cb8ad6c941942d6b03d74d7e4f7e

---

From: Ayush Tiwari <[email-masked]>
Date: Mon, 25 May 2026 20:42:41 +0530

Hi,

> The connection to the server was lost. Attempting reset: Succeeded.

Thanks for the report.  I was able to reproduce this on HEAD with your SQL.
The crash is in the executor while building expression state for the IS JSON
predicate -- ExecInitExprRec() ends up being called with a NULL node:

  #0  ExecInitExprRec(node=0x0, ...)        execExpr.c:966
  #1  ExecInitExprRec(<JsonIsPredicate>)    execExpr.c:2507
  #2  ExecBuildProjectionInfo(...)          execExpr.c:511

The NULL comes from transformJsonParseArg().  For UNKNOWN or string-category
input types it implicitly coerces to text via coerce_to_target_type() and
then sets exprtype to TEXTOID  without checking if the coercion succeeded.
poc.mystring is in CATEGORY = 'S' but has no implicit cast to text, so
coerce_to_target_type() returns NULL; the parser still claims it's text and
that NULL ends up as the JsonIsPredicate subject.

String-category alone isn't a promise of text-coercibility -- adding
CREATE CAST (poc.mystring AS text) ... AS IMPLICIT makes the same query
works fine.  I guess the parser just shouldn't assume the coercion worked?

The smallest fix I could see is to only update expr / exprtype when
coerce_to_target_type() returns non-NULL.  Then transformJsonIsPredicate()
(and the similar JSON() WITH UNIQUE KEYS path) raise their existing
"cannot use type X" errors instead of crashing:

  ERROR:  cannot use type poc.mystring in IS JSON predicate

Attached is a small patch with that fix and a regression test in sqljson.

Blame points at 6ee30209a6f1 (March 2023), so this looks like it goes back
to v16.

Regards,
Ayush

---

From: Srinath Reddy Sadipiralla <[email-masked]>
Date: Mon, 25 May 2026 22:06:01 +0530

Hi,

On Mon, May 25, 2026 at 8:43 PM Ayush Tiwari <[email-masked]>
wrote:

> Hi,
>
> On Mon, 25 May 2026 at 18:57, PG Bug reporting form <
> [email-masked]> wrote:
>

yeah , this was the reason.

>   ERROR:  cannot use type poc.mystring in IS JSON predicate

I have reviewed the patch , i think the error message might be
something like

diff --git a/src/backend/parser/parse_expr.c
b/src/backend/parser/parse_expr.c
index 89d0f348303..59e34f082e4 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -4203,6 +4203,13 @@ transformJsonParseArg(ParseState *pstate, Node
*jsexpr, JsonFormat *format,

     TEXTOID, -1,

     COERCION_IMPLICIT,

     COERCE_IMPLICIT_CAST, -1);
+                       if(expr == NULL)
+                               ereport(ERROR,
+                               (errcode(ERRCODE_CANNOT_COERCE),
+                                errmsg("cannot cast type %s to %s",
+                                               format_type_be(*exprtype),
+                                               format_type_be(TEXTOID)),
+                                parser_errposition(pstate,
exprLocation(raw_expr))));
                        *exprtype = TEXTOID;
                }

-- 
Thanks,
Srinath Reddy Sadipiralla
EDB: https://www.enterprisedb.com/

---

From: Ayush Tiwari <[email-masked]>
Date: Fri, 5 Jun 2026 21:41:34 +0530

Hi,

> exprLocation(raw_expr))));

Thanks, that reads better.  v2 attached, raising ERRCODE_CANNOT_COERCE
right where the coercion fails:

  ERROR:  cannot cast type sqljson_mystr to text

which also covers the JSON() WITH UNIQUE KEYS path.

Regards,
Ayush

---

From: Srinath Reddy Sadipiralla <[email-masked]>
Date: Sat, 6 Jun 2026 11:57:38 +0530

Hi,

> which also covers the JSON() WITH UNIQUE KEYS path.

Thanks for the updated patch, LGTM.

-- 
Thanks :)

---

From: Álvaro Herrera <[email-masked]>
Date: Mon, 8 Jun 2026 20:40:20 +0200

> Thanks for the updated patch, LGTM.

Thanks -- will review, hoping to push soon.

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
"That sort of implies that there are Emacs keystrokes which aren't obscure.
I've been using it daily for 2 years now and have yet to discover any key
sequence which makes any sense."                        (Paul Thomas)

---

From: Álvaro Herrera <[email-masked]>
Date: Thu, 11 Jun 2026 16:27:17 +0200

> Thanks for the updated patch, LGTM.

Thank you, I have pushed this to all branches since 16.

I also added an Assert() to makeJsonIsPredicate() that 'expr' is not
null.  This wouldn't have caught this problem, mind, because what was
really missing is an example case where the problem appears.  However,
it seems good manners to disallow that case explicitly rather than
have it return a Expr node with a embedded null pointer that's only
going to cause indirect problems.

BTW the subject of this thread is somehow comically bogus.  The problem
is IS JSON causing a NULL pointer dereference, not IS NULL.