From 59a3c0425960483f73b6c352268949a0c44b677a Mon Sep 17 00:00:00 2001
From: Jianghua Yang <yjhjstz@gmail.com>
Date: Thu, 19 Mar 2026 09:06:31 -0700
Subject: [PATCH] Fix file descriptor leak when dup()+fdopen()/gzdopen() fails
 in pg_dump compression backends

When fd >= 0, the compression open functions called fdopen(dup(fd), mode)
or gzdopen(dup(fd), mode) directly. If dup() succeeded but fdopen()/gzdopen()
failed, the duplicated file descriptor was leaked because POSIX specifies that
fdopen() does not close the fd on failure.

Fix by saving the dup() result in a local variable and explicitly calling
close() on it if the subsequent wrapping call fails.

Affected backends: compress_gzip.c, compress_lz4.c, compress_none.c,
compress_zstd.c. The zstd fix also ensures pg_free(zstdcs) is called on all
failure paths after the allocation.
---
 src/bin/pg_dump/compress_gzip.c | 20 ++++++++++++++++----
 src/bin/pg_dump/compress_lz4.c  | 27 ++++++++++++++++++++++-----
 src/bin/pg_dump/compress_none.c | 20 ++++++++++++++++----
 src/bin/pg_dump/compress_zstd.c | 28 ++++++++++++++++++++++------
 4 files changed, 76 insertions(+), 19 deletions(-)

diff --git a/src/bin/pg_dump/compress_gzip.c b/src/bin/pg_dump/compress_gzip.c
index c9ce8a53aaa..60c553ba25a 100644
--- a/src/bin/pg_dump/compress_gzip.c
+++ b/src/bin/pg_dump/compress_gzip.c
@@ -386,12 +386,24 @@ Gzip_open(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
 		strcpy(mode_compression, mode);
 
 	if (fd >= 0)
-		gzfp = gzdopen(dup(fd), mode_compression);
+	{
+		int			dup_fd = dup(fd);
+
+		if (dup_fd < 0)
+			return false;
+		gzfp = gzdopen(dup_fd, mode_compression);
+		if (gzfp == NULL)
+		{
+			close(dup_fd);
+			return false;
+		}
+	}
 	else
+	{
 		gzfp = gzopen(path, mode_compression);
-
-	if (gzfp == NULL)
-		return false;
+		if (gzfp == NULL)
+			return false;
+	}
 
 	CFH->private_data = gzfp;
 
diff --git a/src/bin/pg_dump/compress_lz4.c b/src/bin/pg_dump/compress_lz4.c
index 0aa519fbb67..0a7872116e7 100644
--- a/src/bin/pg_dump/compress_lz4.c
+++ b/src/bin/pg_dump/compress_lz4.c
@@ -715,13 +715,30 @@ LZ4Stream_open(const char *path, int fd, const char *mode,
 	LZ4State   *state = (LZ4State *) CFH->private_data;
 
 	if (fd >= 0)
-		state->fp = fdopen(dup(fd), mode);
+	{
+		int			dup_fd = dup(fd);
+
+		if (dup_fd < 0)
+		{
+			state->errcode = errno;
+			return false;
+		}
+		state->fp = fdopen(dup_fd, mode);
+		if (state->fp == NULL)
+		{
+			state->errcode = errno;
+			close(dup_fd);
+			return false;
+		}
+	}
 	else
-		state->fp = fopen(path, mode);
-	if (state->fp == NULL)
 	{
-		state->errcode = errno;
-		return false;
+		state->fp = fopen(path, mode);
+		if (state->fp == NULL)
+		{
+			state->errcode = errno;
+			return false;
+		}
 	}
 
 	return true;
diff --git a/src/bin/pg_dump/compress_none.c b/src/bin/pg_dump/compress_none.c
index d862d8ca6e9..743e2ce94b5 100644
--- a/src/bin/pg_dump/compress_none.c
+++ b/src/bin/pg_dump/compress_none.c
@@ -231,12 +231,24 @@ open_none(const char *path, int fd, const char *mode, CompressFileHandle *CFH)
 	Assert(CFH->private_data == NULL);
 
 	if (fd >= 0)
-		CFH->private_data = fdopen(dup(fd), mode);
+	{
+		int			dup_fd = dup(fd);
+
+		if (dup_fd < 0)
+			return false;
+		CFH->private_data = fdopen(dup_fd, mode);
+		if (CFH->private_data == NULL)
+		{
+			close(dup_fd);
+			return false;
+		}
+	}
 	else
+	{
 		CFH->private_data = fopen(path, mode);
-
-	if (CFH->private_data == NULL)
-		return false;
+		if (CFH->private_data == NULL)
+			return false;
+	}
 
 	return true;
 }
diff --git a/src/bin/pg_dump/compress_zstd.c b/src/bin/pg_dump/compress_zstd.c
index cf2db2649ac..68f1d815917 100644
--- a/src/bin/pg_dump/compress_zstd.c
+++ b/src/bin/pg_dump/compress_zstd.c
@@ -523,14 +523,30 @@ Zstd_open(const char *path, int fd, const char *mode,
 	}
 
 	if (fd >= 0)
-		fp = fdopen(dup(fd), mode);
-	else
-		fp = fopen(path, mode);
+	{
+		int			dup_fd = dup(fd);
 
-	if (fp == NULL)
+		if (dup_fd < 0)
+		{
+			pg_free(zstdcs);
+			return false;
+		}
+		fp = fdopen(dup_fd, mode);
+		if (fp == NULL)
+		{
+			close(dup_fd);
+			pg_free(zstdcs);
+			return false;
+		}
+	}
+	else
 	{
-		pg_free(zstdcs);
-		return false;
+		fp = fopen(path, mode);
+		if (fp == NULL)
+		{
+			pg_free(zstdcs);
+			return false;
+		}
 	}
 
 	zstdcs->fp = fp;
-- 
2.50.1 (Apple Git-155)

