From eeb8e68b79e38d925ce7cac6ab2a356df9799bb9 Mon Sep 17 00:00:00 2001
From: Sivirilova Maria <m.sivirilova@ftdata.ru>
Date: Wed, 19 Aug 2026 16:05:32 +0700
Subject: [PATCH] Fix heap-buffer-overflow in PGTYPEStimestamp_defmt_scan()

When parsing format tokens '%D', '%r', '%R', or '%T', the function
allocated memory for the temporary buffer based on the length of the
remaining input string (pstr). However, it subsequently appended the
remaining format string (pfmt) to that buffer via strcat().

If the tail of the format string happened to be longer than the remaining
input string, a heap-buffer-overflow occurred.

Fix this by calculating the required buffer size using the length of
the format string (pfmt) instead of the input string (pstr).

Author: Maria Sivirilova <m.sivirilova@ftdata.ru>
Co-authored-by: Kanatbek Kanybekov <k.kanybekov@ftdata.ru>
---
 src/interfaces/ecpg/pgtypeslib/dt_common.c       | 8 ++++----
 src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc | 9 +++++++++
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
index f0889dddaf9..6d58dee7890 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -2658,7 +2658,7 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 				 * find the end of the substitution
 				 */
 				pfmt++;
-				tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
+				tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pfmt) + 1);
 				if (!tmp)
 					return 1;
 				strcpy(tmp, "%m/%d/%y");
@@ -2785,7 +2785,7 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 				break;
 			case 'r':
 				pfmt++;
-				tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
+				tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pfmt) + 1);
 				if (!tmp)
 					return 1;
 				strcpy(tmp, "%I:%M:%S %p");
@@ -2795,7 +2795,7 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 				return err;
 			case 'R':
 				pfmt++;
-				tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
+				tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pfmt) + 1);
 				if (!tmp)
 					return 1;
 				strcpy(tmp, "%H:%M");
@@ -2842,7 +2842,7 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 				break;
 			case 'T':
 				pfmt++;
-				tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
+				tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pfmt) + 1);
 				if (!tmp)
 					return 1;
 				strcpy(tmp, "%H:%M:%S");
diff --git a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
index 62b934b07ee..101fccce6a8 100644
--- a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
+++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
@@ -66,6 +66,12 @@ main(void)
 		date *dc;
 	exec sql end declare section;
 
+	char large_fmt[10000] = {
+		[0] = '%',
+		[1] = 'D',
+		[2 ... 9998] = 'A'
+	};
+
 	int i, j;
 	char *endptr;
 
@@ -147,5 +153,8 @@ main(void)
 		PGTYPESinterval_free(i1);
 	}
 
+	PGTYPEStimestamp_defmt_asc("1 minute", large_fmt, &ts1);
+	printf("Success parsing large string\n");
+
 	return 0;
 }
-- 
2.43.0

