Re: BUG #19511: contrib/dblink: NULL dereference in dblink_get_notify() when called without a prior connection
Hi
This was originally reported to security@postgresql.org, 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 <noreply@postgresql.org>
wrote:
Show quoted text
The following bug has been logged on the website:
Bug reference: 19511
Logged by: Amjad Shahzad
Email address: amjadshahzad2000@gmail.com
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 availableTHE 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: 0Tested on: PostgreSQL master 0392fb900eb, Ubuntu 24.04, x86_64.
Attachments:
v1-fix-dblink-get-notify-null-crash.patchapplication/octet-stream; name=v1-fix-dblink-get-notify-null-crash.patchDownload+4-1
Import Notes
Reply to msg id not found: 19511-f9f251767b658232@postgresql.orgReference msg id not found: 19511-f9f251767b658232@postgresql.org
On Fri, Jun 5, 2026 at 10:20 AM Amjad Shahzad
<amjadshahzad2000@gmail.com> 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
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 <masao.fujii@gmail.com> wrote:
Show quoted text
On Fri, Jun 5, 2026 at 10:20 AM Amjad Shahzad
<amjadshahzad2000@gmail.com> 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 againstmaster
commit 0392fb900eb.
WHAT IS THE ISSUE
=================
dblink_get_notify() retrieves async notifications from a remoteconnection.
When called with no arguments it uses the default
(unnamed) connection. If no default connection has been establishedfirst,
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
On Fri, Jun 5, 2026 at 7:55 PM Amjad Shahzad <amjadshahzad2000@gmail.com> 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