From 4729c63ad1652955d97d2df42409cd6334e13c86 Mon Sep 17 00:00:00 2001 From: Dilip Kumar 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 Author: Dilip Kumar Reviewed-by: Jonathan Gonzalez V. Reviewed-by: Imran Zaheer --- 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