public inbox for [email protected]  
help / color / mirror / Atom feed
BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
5+ messages / 3 participants
[nested] [flat]

* BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
@ 2026-06-05 01:15  PG Bug reporting form <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: PG Bug reporting form @ 2026-06-05 01:15 UTC (permalink / raw)
  To: [email protected]; +Cc: [email protected]

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.







^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
@ 2026-06-05 01:19  Amjad Shahzad <[email protected]>
  parent: PG Bug reporting form <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Amjad Shahzad @ 2026-06-05 01:19 UTC (permalink / raw)
  To: [email protected]; [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


^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
@ 2026-06-05 04:45  Fujii Masao <[email protected]>
  parent: Amjad Shahzad <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Fujii Masao @ 2026-06-05 04:45 UTC (permalink / raw)
  To: Amjad Shahzad <[email protected]>; +Cc: [email protected]

On Fri, Jun 5, 2026 at 10:20 AM Amjad Shahzad
<[email protected]> wrote:
>> 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.

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






^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
@ 2026-06-05 10:55  Amjad Shahzad <[email protected]>
  parent: Fujii Masao <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Amjad Shahzad @ 2026-06-05 10:55 UTC (permalink / raw)
  To: Fujii Masao <[email protected]>; +Cc: [email protected]

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

On Fri, Jun 5, 2026 at 9:45 AM Fujii Masao <[email protected]> wrote:

> On Fri, Jun 5, 2026 at 10:20 AM Amjad Shahzad
> <[email protected]> wrote:
> >> 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.
>
> 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
>


^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
@ 2026-06-05 15:15  Fujii Masao <[email protected]>
  parent: Amjad Shahzad <[email protected]>
  0 siblings, 0 replies; 5+ messages in thread

From: Fujii Masao @ 2026-06-05 15:15 UTC (permalink / raw)
  To: Amjad Shahzad <[email protected]>; +Cc: [email protected]

On Fri, Jun 5, 2026 at 7:55 PM Amjad Shahzad <[email protected]> wrote:
>
> 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.

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






^ permalink  raw  reply  [nested|flat] 5+ messages in thread


end of thread, other threads:[~2026-06-05 15:15 UTC | newest]

Thread overview: 5+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-06-05 01:15 BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection PG Bug reporting form <[email protected]>
2026-06-05 01:19 ` Amjad Shahzad <[email protected]>
2026-06-05 04:45   ` Fujii Masao <[email protected]>
2026-06-05 10:55     ` Amjad Shahzad <[email protected]>
2026-06-05 15:15       ` Fujii Masao <[email protected]>

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