public inbox for [email protected]  
help / color / mirror / Atom feed
From: Amjad Shahzad <[email protected]>
To: [email protected]
To: [email protected]
Subject: Re: BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
Date: Fri, 5 Jun 2026 06:19:48 +0500
Message-ID: <CADHzGZRAxFYoxMC+g6pEJ0FL0bfVhFi+y0jQ0cRNCC5hFjvJ3Q@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>

Hi

This was originally reported to [email protected], redirected here by
the security team as this does not meet the security
vulnerability threshold.

Patch attached. Applies cleanly against master 0392fb900eb.
Adds the same NULL guard used by every other function in dblink.c that
accesses the default connection.

Regards
Amjad

On Fri, Jun 5, 2026 at 6:16 AM PG Bug reporting form <[email protected]>
wrote:

> The following bug has been logged on the website:
>
> Bug reference:      19511
> Logged by:          Amjad Shahzad
> Email address:      [email protected]
> PostgreSQL version: 18.4
> Operating system:   Ubuntu 24.04 x86_64
> Description:
>
> I found a NULL pointer dereference in contrib/dblink/dblink.c in the
> dblink_get_notify() function. Any user with EXECUTE on the function
> can crash their backend process with a single call. Confirmed against
> master
> commit 0392fb900eb.
>
> WHAT IS THE ISSUE
> =================
> dblink_get_notify() retrieves async notifications from a remote connection.
> When called with no arguments it uses the default
> (unnamed) connection. If no default connection has been established first,
> pconn->conn is NULL. The code assigns this NULL to conn and
> then passes it directly to PQconsumeInput() and PQnotifies():
>
>     /* line 1893 (master) */
>     else
>         conn = pconn->conn;      /* NULL — no connection established */
>
>     InitMaterializedSRF(fcinfo, 0);
>
>     PQconsumeInput(conn);        /* passes NULL to libpq */
>     while ((notify = PQnotifies(conn)) != NULL)  /* NULL dereference */
>
> PQnotifies(NULL) dereferences a null pointer internally, causing a backend
> SIGSEGV.
>
> Every other function in dblink.c that uses the default connection already
> has an explicit NULL guard:
>
>     if (!conn)
>         dblink_conn_not_avail(conname);
>
> dblink_get_notify() is the only function that skips this guard.
>
> WHAT CAN BE COMPROMISED
> =========================
> Any user with EXECUTE on dblink_get_notify(), granted to PUBLIC by default
> can crash their backend process
> on demand. No password, no connection, no special privileges required.
>
> In a shared server environment this can be used as a denial-of-service
> against a specific session. Combined with
> connection pooling or persistent connections it could repeatedly crash
> backend processes.
>
> PREREQUISITES
> ===============
>   1. Any connected database user
>   2. EXECUTE on dblink_get_notify (granted to PUBLIC by default)
>   3. contrib/dblink installed (CREATE EXTENSION dblink)
>
> No dblink connection needed. No password needed.
>
> STEPS TO REPRODUCE
> ====================
>     -- STEP 1: Install dblink
>     CREATE EXTENSION dblink;
>
>     -- STEP 2: As any user, call without connecting first
>     SELECT * FROM dblink_get_notify();
>
>     -- Result before fix:
>     -- server closed the connection unexpectedly
>     -- SIGSEGV in server log
>
>     -- Result after fix:
>     -- ERROR:  connection not available
>
> THE FIX
> =======
> Add the same NULL guard that every other dblink function already has:
>
>     /* BEFORE */
>     else
>         conn = pconn->conn;
>
>     /* AFTER */
>     else
>     {
>         conn = pconn->conn;
>         if (!conn)
>             dblink_conn_not_avail(NULL);
>     }
>
> 4 lines added. Patch attached.
>
> BEHAVIOUR AFTER THE FIX
> ========================
>     -- No prior connection:
>     SELECT * FROM dblink_get_notify();
>     -- ERROR:  connection not available   (clean error, no crash)
>
>     -- With valid connection:
>     SELECT * FROM dblink_get_notify('myconn');
>     -- (0 rows)   (works exactly as before)
>
> REGRESSION TEST RESULTS
> =========================
>     $ meson test --suite dblink
>     1/2 dblink - postgresql:dblink/regress        OK  (all SQL tests pass)
>     2/2 dblink - postgresql:dblink/001_auth_scram OK  12 subtests passed
>     Ok: 2   Fail: 0
>
>     $ meson test --suite regress
>     1/1 regress - postgresql:regress/regress OK   245 subtests passed
>     Ok: 1   Fail: 0
>
> Tested on: PostgreSQL master 0392fb900eb, Ubuntu 24.04, x86_64.
>
>
>


Attachments:

  [application/octet-stream] v1-fix-dblink-get-notify-null-crash.patch (1.9K, 3-v1-fix-dblink-get-notify-null-crash.patch)
  download | inline diff:
From: Amjad Shahzad <[email protected]>
Date: Fri, 05 Jun 2026 00:00:00 +0500
Subject: [PATCH v1] dblink: Fix NULL dereference crash in dblink_get_notify()

dblink_get_notify() retrieves async notifications from a remote
connection. When called with no arguments it uses the default
(unnamed) connection. If no default connection has been established,
pconn->conn is NULL. The code assigned this NULL to conn and then
passed it directly to PQconsumeInput() and PQnotifies() without any
check:

    else
        conn = pconn->conn;   /* NULL if no connection established */

    InitMaterializedSRF(fcinfo, 0);

    PQconsumeInput(conn);     /* passes NULL to libpq */
    while ((notify = PQnotifies(conn)) != NULL)  /* NULL dereference */

PQnotifies(NULL) dereferences a null pointer internally, causing a
backend SIGSEGV. Any user with EXECUTE on the function (granted to
PUBLIC by default) can trigger this with a single call:

    SELECT * FROM dblink_get_notify();

Every other function in dblink.c that uses the default connection
already has an explicit NULL guard via dblink_conn_not_avail().
dblink_get_notify() was the only exception.

Fix by adding the same NULL check used everywhere else in the file.

Regression tests: all 2/2 dblink tests pass with this patch applied
(meson test --suite dblink on master 0392fb900eb).

Reported-by: Amjad Shahzad <[email protected]>
---
 contrib/dblink/dblink.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index d843eee7e97..6e17f86113d 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1890,7 +1890,11 @@ dblink_get_notify(PG_FUNCTION_ARGS)
 	if (PG_NARGS() == 1)
 		conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0)));
 	else
+	{
 		conn = pconn->conn;
+		if (!conn)
+			dblink_conn_not_avail(NULL);
+	}

 	InitMaterializedSRF(fcinfo, 0);
--
PostgreSQL master-0392fb900eb


view thread (5+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected]
  Subject: Re: BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
  In-Reply-To: <CADHzGZRAxFYoxMC+g6pEJ0FL0bfVhFi+y0jQ0cRNCC5hFjvJ3Q@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox