From ed94357fbd47b9c29434e92b9ee65b4bb8d3f6f1 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 10 Apr 2026 19:17:12 +0530 Subject: [PATCH] Fix NULL dereference in pg_get_database_ddl() 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 (SIGSEGV). This function was introduced by commit a4f774cf1c7. The patch fixes this by checking for NULL before calling pg_strcasecmp(). In that case, pg_get_database_ddl() simply omits the TABLESPACE clause. A regression test is added in database_ddl.sql that exercises this case by setting dattablespace to a non-existent OID and verifying that the function returns successfully without crashing. --- src/backend/utils/adt/ddlutils.c | 2 +- src/test/regress/expected/database_ddl.out | 24 ++++++++++++++++++++++ src/test/regress/sql/database_ddl.sql | 17 +++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/backend/utils/adt/ddlutils.c b/src/backend/utils/adt/ddlutils.c index b16c277d000..c5885b18958 100644 --- a/src/backend/utils/adt/ddlutils.c +++ b/src/backend/utils/adt/ddlutils.c @@ -976,7 +976,7 @@ pg_get_database_ddl_internal(Oid dbid, bool pretty, { char *spcname = get_tablespace_name(dbform->dattablespace); - if (pg_strcasecmp(spcname, "pg_default") != 0) + if (spcname != NULL && pg_strcasecmp(spcname, "pg_default") != 0) append_ddl_option(&buf, pretty, 4, "TABLESPACE = %s", quote_identifier(spcname)); } diff --git a/src/test/regress/expected/database_ddl.out b/src/test/regress/expected/database_ddl.out index 97657e52cfa..bfd7cdcbacf 100644 --- a/src/test/regress/expected/database_ddl.out +++ b/src/test/regress/expected/database_ddl.out @@ -83,6 +83,30 @@ ERROR: permission denied for database regression_database_ddl RESET ROLE; GRANT CONNECT ON DATABASE regression_database_ddl TO PUBLIC; DROP ROLE regress_db_ddl_noaccess; +-- Test for dropped tablespace: dattablespace pointing to a non-existent OID +-- should not crash (see commit fixing NULL deref in pg_get_database_ddl_internal) +CREATE DATABASE regression_database_ddl2 + ENCODING utf8 LC_COLLATE "C" LC_CTYPE "C" TEMPLATE template0 + OWNER regress_datdba; +SET allow_system_table_mods = on; +UPDATE pg_database SET dattablespace = 99999 + WHERE datname = 'regression_database_ddl2'; +UPDATE 1 +RESET allow_system_table_mods; +SELECT ddl_filter(pg_get_database_ddl) FROM pg_get_database_ddl('regression_database_ddl2'); + ddl_filter +------------------------------------------------------------------------------------------ + CREATE DATABASE regression_database_ddl2 WITH TEMPLATE = template0 ENCODING = 'UTF8'; + ALTER DATABASE regression_database_ddl2 OWNER TO regress_datdba; +(2 rows) + +SET allow_system_table_mods = on; +UPDATE pg_database SET dattablespace = + (SELECT oid FROM pg_tablespace WHERE spcname = 'pg_default') + WHERE datname = 'regression_database_ddl2'; +UPDATE 1 +RESET allow_system_table_mods; +DROP DATABASE regression_database_ddl2; DROP DATABASE regression_database_ddl; DROP FUNCTION ddl_filter(text); DROP ROLE regress_datdba; diff --git a/src/test/regress/sql/database_ddl.sql b/src/test/regress/sql/database_ddl.sql index 89753ac6411..0792a4a9f35 100644 --- a/src/test/regress/sql/database_ddl.sql +++ b/src/test/regress/sql/database_ddl.sql @@ -61,6 +61,23 @@ RESET ROLE; GRANT CONNECT ON DATABASE regression_database_ddl TO PUBLIC; DROP ROLE regress_db_ddl_noaccess; +-- Test for dropped tablespace: dattablespace pointing to a non-existent OID +-- should not crash (see commit fixing NULL deref in pg_get_database_ddl_internal) +CREATE DATABASE regression_database_ddl2 + ENCODING utf8 LC_COLLATE "C" LC_CTYPE "C" TEMPLATE template0 + OWNER regress_datdba; +SET allow_system_table_mods = on; +UPDATE pg_database SET dattablespace = 99999 + WHERE datname = 'regression_database_ddl2'; +RESET allow_system_table_mods; +SELECT ddl_filter(pg_get_database_ddl) FROM pg_get_database_ddl('regression_database_ddl2'); +SET allow_system_table_mods = on; +UPDATE pg_database SET dattablespace = + (SELECT oid FROM pg_tablespace WHERE spcname = 'pg_default') + WHERE datname = 'regression_database_ddl2'; +RESET allow_system_table_mods; +DROP DATABASE regression_database_ddl2; + DROP DATABASE regression_database_ddl; DROP FUNCTION ddl_filter(text); DROP ROLE regress_datdba; -- 2.53.0.windows.2