0001-Fix-NULL-dereference-in-pg_get_database_ddl.patch
application/octet-stream
Filename: 0001-Fix-NULL-dereference-in-pg_get_database_ddl.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0001
Subject: Fix NULL dereference in pg_get_database_ddl()
| File | + | − |
|---|---|---|
| src/backend/utils/adt/ddlutils.c | 1 | 1 |
| src/test/regress/expected/database_ddl.out | 24 | 0 |
| src/test/regress/sql/database_ddl.sql | 17 | 0 |
From ed94357fbd47b9c29434e92b9ee65b4bb8d3f6f1 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.sgl01@gmail.com>
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