From ca55dd350412ade8bb07f94d05740f2f7106a6a2 Mon Sep 17 00:00:00 2001 From: Sivirilova Maria Date: Tue, 3 Nov 2026 12:51:46 +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 Co-authored-by: Kanatbek Kanybekov --- src/interfaces/ecpg/pgtypeslib/dt_common.c | 8 ++++---- .../ecpg/test/pgtypeslib/dt_test2.pgc | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c index c4119ab7932..ff345f7712f 100644 --- a/src/interfaces/ecpg/pgtypeslib/dt_common.c +++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c @@ -2660,7 +2660,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"); @@ -2787,7 +2787,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"); @@ -2797,7 +2797,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"); @@ -2845,7 +2845,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..20feaf577d3 100644 --- a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc +++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc @@ -66,6 +66,9 @@ main(void) date *dc; exec sql end declare section; + char large_fmt[10000]; + const char *fmt_tokens = "DrRT"; + int i, j; char *endptr; @@ -147,5 +150,19 @@ main(void) PGTYPESinterval_free(i1); } + /* + * Test parsing a small string with a very large format string. + * These calls are meant to trigger ASAN or Valgrind reports + * if a heap-buffer-overflow regression occurs. + */ + memset(large_fmt, 'A', sizeof(large_fmt) - 1); + large_fmt[sizeof(large_fmt) - 1] = '\0'; + large_fmt[0] = '%'; + for (i = 0; fmt_tokens[i] != '\0'; i++) + { + large_fmt[1] = fmt_tokens[i]; + PGTYPEStimestamp_defmt_asc("1 minute", large_fmt, &ts1); + } + return 0; } -- 2.53.0