Subject: BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection

From: PG Bug reporting form <[email-masked]>
Date: Fri, 05 Jun 2026 01:15:40 +0000

The following bug has been logged on the website:

Bug reference:      19511
Logged by:          Amjad Shahzad
Email address:      [email-masked]
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.

---

From: Amjad Shahzad <[email-masked]>
Date: Fri, 5 Jun 2026 06:19:48 +0500

Hi

This was originally reported to [email-masked], 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

---

From: Fujii Masao <[email-masked]>
Date: Fri, 5 Jun 2026 13:45:06 +0900

>> can crash their backend process with a single call. Confirmed against master

Can this segmentation fault actually happen?

PQconsumeInput() and PQnotifies() both simply return immediately when
conn == NULL. So even if dblink_get_notify() calls them with a NULL conn,
it doesn't seem like that would lead to a segmentation fault.
Am I missing something?

Regards,

-- 
Fujii Masao

---

From: Amjad Shahzad <[email-masked]>
Date: Fri, 5 Jun 2026 15:55:39 +0500

Hi Fujii,

You are correct. I checked the libpq source and both PQconsumeInput() and
PQnotifies() have explicit NULL guards:

    /* fe-exec.c line 2003 */
    if (!conn)
        return 0;

    /* fe-exec.c line 2688 */
    if (!conn)
        return NULL;

So no segmentation fault actually occurs. The function silently returns 0
rows when called without a prior connection instead of crashing.

The real issue is a behavioral inconsistency: every other dblink function
that uses the default connection explicitly checks for NULL
and calls dblink_conn_not_avail() to give a clear error message.

dblink_get_notify() is the only exception, it silently returns an empty
result set, which could mislead callers into thinking no
notifications exist when in fact no connection was established. Whether
this inconsistency is worth fixing with a proper error message
is up to the community's judgment. I apologize for incorrectly
characterizing it as a crash.

Regards,
Amjad Shahzad

---

From: Fujii Masao <[email-masked]>
Date: Sat, 6 Jun 2026 00:15:57 +0900

On Fri, Jun 5, 2026 at 7:55 PM Amjad Shahzad <[email-masked]> wrote:
>
> Hi Fujii,
>
> You are correct. I checked the libpq source and both PQconsumeInput() and PQnotifies() have explicit NULL guards:
>
> So no segmentation fault actually occurs. The function silently returns 0 rows when called without a prior connection instead of crashing.
>
> The real issue is a behavioral inconsistency: every other dblink function that uses the default connection explicitly checks for NULL
> dblink_get_notify() is the only exception, it silently returns an empty result set, which could mislead callers into thinking no
> notifications exist when in fact no connection was established.

dblink_get_notify() was introduced by commit f4095b4c4b2 in 2009,
and it appears to have behaved this way ever since. So there may be
existing systems that depend on the current behavior. Also, there do not
seem to have been any complaints about it over the past 10+ years.

Therefore, unless the current behavior can be shown to cause actual
issues or risks, I'm just feeling tempted to avoid changing it...
At the very least, I don't think we should change the behavior in
the back branches. Thought?

Regards,

-- 
Fujii Masao