v2-0001-pg_dump-Validate-archive-entry-filenames-in-direc.patch

application/octet-stream

Filename: v2-0001-pg_dump-Validate-archive-entry-filenames-in-direc.patch
Type: application/octet-stream
Part: 0
Message: Re: Path Traversal Vulnerability in pg_dump Directory Format

Patch

Format: format-patch
Series: patch v2-0001
Subject: pg_dump: Validate archive entry filenames in directory format
File+
src/bin/pg_dump/pg_backup_directory.c 21 1
From 4729c63ad1652955d97d2df42409cd6334e13c86 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumarb@google.com>
Date: Fri, 3 Jul 2026 10:52:26 +0530
Subject: [PATCH v2] pg_dump: Validate archive entry filenames in directory
 format

Filenames listed in directory-format TOC files (toc.dat and blobs_*.toc)
are untrusted when restoring an archive. If an entry filename contains
path traversal elements (such as directory separators or "." / "..") or
points to a symbolic link, it could lead to reading arbitrary files
outside the intended archive directory.

Add validation in setFilePath() to refuse empty filenames, exact "." or
".." directory names, filenames containing directory separators (using
first_dir_separator()), or symbolic links (using lstat()), raising a fatal
error if any are found. Checking exact "." and ".." instead of searching for
the substring ".." avoids rejecting valid filenames containing consecutive
dots (e.g., "blob..1.toc").

In addition, update _LoadLOs() to construct large object file paths using
setFilePath() rather than manual snprintf(), ensuring LO filenames read
from blobs_*.toc undergo the same validation checks.

Reported-by: Jordanna Chord <jchord@google.com>
Author: Dilip Kumar <dilipbalaut@gmail.com>
Reviewed-by: Jonathan Gonzalez V. <jonathan.abdiel@gmail.com>
Reviewed-by: Imran Zaheer <imran.zhir@gmail.com>
---
 src/bin/pg_dump/pg_backup_directory.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index c0b50223cec..5de1a83c1f7 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -434,7 +434,7 @@ _LoadLOs(ArchiveHandle *AH, TocEntry *te)
 					 tocfname, line);
 
 		StartRestoreLO(AH, oid, AH->public.ropt->dropSchema);
-		snprintf(path, MAXPGPATH, "%s/%s", ctx->directory, lofname);
+		setFilePath(AH, path, lofname);
 		_PrintFileData(AH, path);
 		EndRestoreLO(AH, oid);
 	}
@@ -683,15 +683,35 @@ setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
 {
 	lclContext *ctx = (lclContext *) AH->formatData;
 	char	   *dname;
+	struct stat st;
 
 	dname = ctx->directory;
 
+	/*
+	 * Per-entry filenames come from the (untrusted) toc.dat / blobs_*.toc.
+	 * Refuse empty names, '.' or '..', or anything containing directory
+	 * separators.
+	 */
+	if (relativeFilename[0] == '\0' ||
+		strcmp(relativeFilename, ".") == 0 ||
+		strcmp(relativeFilename, "..") == 0 ||
+		first_dir_separator(relativeFilename) != NULL)
+		pg_fatal("invalid archive: entry filename \"%s\" is not a plain file name",
+				 relativeFilename);
+
 	if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)
 		pg_fatal("file name too long: \"%s\"", dname);
 
 	strcpy(buf, dname);
 	strcat(buf, "/");
 	strcat(buf, relativeFilename);
+
+	/*
+	 * Refuse symbolic links to prevent path traversal attacks via symlinks.
+	 */
+	if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode))
+		pg_fatal("invalid archive: entry file \"%s\" is a symbolic link",
+				 buf);
 }
 
 /*
-- 
2.49.0