Hi!

PostgreSQL build under Windows with MS Visual Studio has functions to work with links (unlike msys2 that has own functions). If a database has link pointing to location longer 130 chars, function pgreadlink fails to recognise this
link and cancels query.
The reason of the error - small buffer for link name - MAX_PATH symbols, though this buffer must have the place for at least 2 MAX_PATH : substitution and print names
of the link.

 How to reproduce:

If build directory has length ~100 chars or longer, pg_rewind/004_pg_xlog_symlink
test will fail (Windows, MSVS build)

 Steps to reproduce:

call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
SET LC_MESSAGES=C

SET BUILDDIR=c:\postgresql-builds\length_30\length_40\length_50\length_60\length_70\length_80\length_90\length100\
meson setup %BUILDDIR%  --prefix=c:\postgresql-builds\install
c:
cd %BUILDDIR%
meson compile -C %BUILDDIR%
meson install -C %BUILDDIR%
meson test -C %BUILDDIR%

 Memory leak:
In case of error the function in the code branch reporting the error does not return
Windows file handle and Windows heap allocation for error message text.

 Solution:
Proposed patch fixes link information buffer size changing it to the documented value
MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
and fixes memory leaks - the message buffer copied to a buffer on stack
with maximal message size 64Kb.

--
Best regards,

Vladlen Popolitov.
From 4eb8afe77064a0cf3d815aa0ffa1ebf815fd1165 Mon Sep 17 00:00:00 2001
From: Vladlen Popolitov <v.popoli...@postgrespro.ru>
Date: Fri, 27 Dec 2024 15:23:30 +0300
Subject: [PATCH v1] Fix bug with access to file links with long name (Windows,
 MSVC)

---
 src/port/dirmod.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/src/port/dirmod.c b/src/port/dirmod.c
index f98d5a7bf2..dbdd8a6e9f 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -310,7 +310,12 @@ pgreadlink(const char *path, char *buf, size_t size)
 {
 	DWORD		attr;
 	HANDLE		h;
-	char		buffer[MAX_PATH * sizeof(WCHAR) + offsetof(REPARSE_JUNCTION_DATA_BUFFER, PathBuffer)];
+/*
+ * The buffer size described in documentation
+ * https://learn.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-set-reparse-point
+ * It stores paths for SubstitutaName and PrintName.
+ */
+	char		buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
 	REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
 	DWORD		len;
 	int			r;
@@ -350,6 +355,8 @@ pgreadlink(const char *path, char *buf, size_t size)
 						 NULL))
 	{
 		LPSTR		msg;
+		/* the maximal size of the message returned by FormatMessage is 64Kb */
+		char		msgBuffer[0x10000];
 
 		errno = 0;
 		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
@@ -358,17 +365,24 @@ pgreadlink(const char *path, char *buf, size_t size)
 					  NULL, GetLastError(),
 					  MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
 					  (LPSTR) &msg, 0, NULL);
+		strncpy(msgBuffer, msg, sizeof(msgBuffer));
+		if (strlen(msg) >= sizeof(msgBuffer))
+		{
+			msgBuffer[sizeof(msgBuffer) - 1] = 0;
+		}
 #ifndef FRONTEND
+		LocalFree(msg);
+		CloseHandle(h);
 		ereport(ERROR,
 				(errcode_for_file_access(),
 				 errmsg("could not get junction for \"%s\": %s",
-						path, msg)));
+						path, msgBuffer)));
 #else
 		fprintf(stderr, _("could not get junction for \"%s\": %s\n"),
 				path, msg);
-#endif
 		LocalFree(msg);
 		CloseHandle(h);
+#endif
 		errno = EINVAL;
 		return -1;
 	}
-- 
2.42.0.windows.2

Reply via email to