[PATCH] Fix NULL dereference in pg_get_database_ddl()
Hi,
pg_get_database_ddl_internal() can dereference a NULL pointer when
pg_database.dattablespace points to a tablespace OID that no longer
exists.
The immediate issue is that get_tablespace_name() may return NULL, but
the result is passed directly to pg_strcasecmp():
spcname = get_tablespace_name(dbform->dattablespace);
if (pg_strcasecmp(spcname, "pg_default") != 0)
...
That leads to a backend crash. I reproduced it on current master as a
SIGSEGV with crash recovery.
This function was introduced by commit a4f774cf1c7.
Deterministic reproduction:
CREATE DATABASE regression_testdb;
SET allow_system_table_mods = on;
UPDATE pg_database
SET dattablespace = 99999
WHERE datname = 'regression_testdb';
RESET allow_system_table_mods;
SELECT * FROM pg_get_database_ddl('regression_testdb');
The attached patch fixes this by checking for NULL before calling
pg_strcasecmp(). In that case, pg_get_database_ddl() simply omits the
TABLESPACE clause.
I also added a regression test in database_ddl.sql that exercises this
case by setting dattablespace to a non-existent OID and verifying that
the function returns successfully.
Patch attached. Please review and let me know if it needs any edits. Thanks!
Regards,
Ayush Tiwari
Attachments:
0001-Fix-NULL-dereference-in-pg_get_database_ddl.patchapplication/octet-stream; name=0001-Fix-NULL-dereference-in-pg_get_database_ddl.patchDownload+42-2
On Fri, 10 Apr 2026 at 19:27, Ayush Tiwari <ayushtiwari.slg01@gmail.com>
wrote:
Hi,
pg_get_database_ddl_internal() can dereference a NULL pointer when
pg_database.dattablespace points to a tablespace OID that no longer
exists.The immediate issue is that get_tablespace_name() may return NULL, but
the result is passed directly to pg_strcasecmp():spcname = get_tablespace_name(dbform->dattablespace);
if (pg_strcasecmp(spcname, "pg_default") != 0)
...That leads to a backend crash. I reproduced it on current master as a
SIGSEGV with crash recovery.This function was introduced by commit a4f774cf1c7.
Deterministic reproduction:
CREATE DATABASE regression_testdb;
SET allow_system_table_mods = on;
UPDATE pg_database
SET dattablespace = 99999
WHERE datname = 'regression_testdb';
RESET allow_system_table_mods;SELECT * FROM pg_get_database_ddl('regression_testdb');
The attached patch fixes this by checking for NULL before calling
pg_strcasecmp(). In that case, pg_get_database_ddl() simply omits the
TABLESPACE clause.I also added a regression test in database_ddl.sql that exercises this
case by setting dattablespace to a non-existent OID and verifying that
the function returns successfully.Patch attached. Please review and let me know if it needs any edits.
Thanks!Regards,
Ayush Tiwari
Re-attaching patch without trailing white-space
Regards,
Ayush
Attachments:
0001-Fix-NULL-dereference-in-pg_get_database_ddl.patchapplication/octet-stream; name=0001-Fix-NULL-dereference-in-pg_get_database_ddl.patchDownload+40-2
On Sat, 11 Apr 2026 at 01:58, Ayush Tiwari <ayushtiwari.slg01@gmail.com> wrote:
Deterministic reproduction:
CREATE DATABASE regression_testdb;
SET allow_system_table_mods = on;
UPDATE pg_database
SET dattablespace = 99999
WHERE datname = 'regression_testdb';
RESET allow_system_table_mods;SELECT * FROM pg_get_database_ddl('regression_testdb');
The attached patch fixes this by checking for NULL before calling
pg_strcasecmp(). In that case, pg_get_database_ddl() simply omits the
TABLESPACE clause.
Can you explain why this method of self-inflicted catalogue corruption
is any more important than any of the just-about-infinite other ways
there are of causing issues by manually updating the catalogue tables?
The typical response to this sort of thing can be seen in the thread
in [1]/messages/by-id/19383-e6b60ec2a4fce5b0@postgresql.org, in particular, the response in [2]/messages/by-id/1538113.1768921841@sss.pgh.pa.us.
David
[1]: /messages/by-id/19383-e6b60ec2a4fce5b0@postgresql.org
[2]: /messages/by-id/1538113.1768921841@sss.pgh.pa.us
Hi,
On Mon, 13 Apr 2026 at 15:02, David Rowley <dgrowleyml@gmail.com> wrote:
On Sat, 11 Apr 2026 at 01:58, Ayush Tiwari <ayushtiwari.slg01@gmail.com>
wrote:Deterministic reproduction:
CREATE DATABASE regression_testdb;
SET allow_system_table_mods = on;
UPDATE pg_database
SET dattablespace = 99999
WHERE datname = 'regression_testdb';
RESET allow_system_table_mods;SELECT * FROM pg_get_database_ddl('regression_testdb');
The attached patch fixes this by checking for NULL before calling
pg_strcasecmp(). In that case, pg_get_database_ddl() simply omits the
TABLESPACE clause.Can you explain why this method of self-inflicted catalogue corruption
is any more important than any of the just-about-infinite other ways
there are of causing issues by manually updating the catalogue tables?The typical response to this sort of thing can be seen in the thread
in [1], in particular, the response in [2].David
[1]
/messages/by-id/19383-e6b60ec2a4fce5b0@postgresql.org
[2]
/messages/by-id/1538113.1768921841@sss.pgh.pa.us
Thanks for the review.
I dug into this further and I don't think I can support the patch as a real
bug fix.
My original thought was that there might be a supported concurrent-DDL path
where
pg_get_database_ddl_internal() reads pg_database.dattablespace, and then a
concurrent change makes get_tablespace_name() return NULL before the second
lookup. I tried to reproduce that by widening the window and testing
ALTER DATABASE ... SET TABLESPACE together with DROP TABLESPACE, but I
could not
make it happen.
The reason seems to be that both catalog reads are governed by the same
statement-level CatalogSnapshot, so the function continues to see a
consistent
catalog view for the duration of the call. On the other side, DROP
TABLESPACE
also doesn't appear able to commit a state where pg_database still points
at a
vanished tablespace row through supported SQL, because the drop fails while
the
database directory is still present.
So at this point the remaining crash case seems to require a broken catalog
state,
which puts this in the same bucket as the precedent you cited rather than a
supported-path bug.
Given that, I don't think this patch is worth pursuing further in its
current
form. I'll drop it here unless I find a supported reproducer or a different,
stronger issue around the missing database/tablespace bookkeeping.
Thanks again for the push to investigate it more carefully.
Regards,
Ayush