чт, 20 авг. 2026 г. в 03:17, Zsolt Parragi <[email protected]>:
> Hello! > > -[NO_PID]: ECPGtrans on line 393: action "rollback"; connection > "ecpg1_regression" > +[NO_PID]: ECPGtrans on line 412: action "rollback"; connection > "ecpg1_regression" > > The modified test is failing in my run, it seems this should be 409? > Dear Zsolt, Thanks for the review. After shortening the test comment I updated the expected .c #line markers but forgot the matching stderr line number. Fixed in the attached v2. -- Regards, Rachitskiy Andrey
From: Andrey Rachitskiy <[email protected]> Date: Tue, 11 Aug 2026 18:10:00 +0500 Subject: [PATCH] Fix PGTYPESdate_fmt_asc overflow when a year does not fit "yyyy" PGTYPESdate_fmt_asc() replaces fixed-width tokens in place. The token "yyyy" is four characters and means a four-digit year. When the year needs more digits, snprintf() with "%04u" produces a longer string and memcpy() writes past the token. With the usual outbuf size of strlen(fmtstring)+1 that is a heap buffer overflow. Reject a replacement longer than the token and set errno to PGTYPES_DATE_BAD_DATE. A dt_test case for 10000-01-01 with "yyyy" is included. Author: Andrey Rachitskiy <[email protected]> --- diff --git a/src/interfaces/ecpg/pgtypeslib/datetime.c b/src/interfaces/ecpg/pgtypeslib/datetime.c index f43343b4594..91d2c30615c 100644 --- a/src/interfaces/ecpg/pgtypeslib/datetime.c +++ b/src/interfaces/ecpg/pgtypeslib/datetime.c @@ -263,38 +263,42 @@ PGTYPESdate_fmt_asc(date dDate, const char *fmtstring, char *outbuf) free(replace_val.str_val); break; case PGTYPES_TYPE_UINT: - { - char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS); - - if (!t) - return -1; - snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS, - "%u", replace_val.uint_val); - memcpy(start_pattern, t, strlen(t)); - free(t); - } - break; case PGTYPES_TYPE_UINT_2_LZ: - { - char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS); - - if (!t) - return -1; - snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS, - "%02u", replace_val.uint_val); - memcpy(start_pattern, t, strlen(t)); - free(t); - } - break; case PGTYPES_TYPE_UINT_4_LZ: { char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS); + size_t pat_len; + size_t t_len; + const char *fmt; if (!t) return -1; + + if (replace_type == PGTYPES_TYPE_UINT) + fmt = "%u"; + else if (replace_type == PGTYPES_TYPE_UINT_2_LZ) + fmt = "%02u"; + else + fmt = "%04u"; + snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS, - "%04u", replace_val.uint_val); - memcpy(start_pattern, t, strlen(t)); + fmt, replace_val.uint_val); + t_len = strlen(t); + pat_len = strlen(mapping[i].format); + + /* + * In-place replace over a fixed-width token + * ("yyyy" is four digits). A longer value + * would overrun typical outbufs. + */ + if (t_len > pat_len) + { + free(t); + errno = PGTYPES_DATE_BAD_DATE; + return -1; + } + + memcpy(start_pattern, t, t_len); free(t); } break; diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c index 1f1d341a4ac..b8aa0fc88ac 100644 --- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c +++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c @@ -150,6 +150,22 @@ if (sqlca.sqlcode < 0) sqlprint ( );} PGTYPESchar_free(text); free(out); + /* year 10000 does not fit the four-digit "yyyy" token */ + { + date big_year_date; + char *big_out; + int fmt_rc; + + errno = 0; + big_year_date = PGTYPESdate_from_asc("10000-01-01", NULL); + fmt = "yyyy"; + big_out = (char *) malloc(strlen(fmt) + 1); + fmt_rc = PGTYPESdate_fmt_asc(big_year_date, fmt, big_out); + printf("date_fmt_asc yyyy year>=10000: rc=%d errno=%d\n", + fmt_rc, errno); + free(big_out); + } + out = (char*) malloc(48); i = PGTYPEStimestamp_fmt_asc(&ts1, out, 47, "Which is day number %j in %Y."); printf("%s\n", out); @@ -465,16 +481,16 @@ if (sqlca.sqlcode < 0) sqlprint ( );} free(out); { ECPGtrans(__LINE__, NULL, "rollback"); -#line 393 "dt_test.pgc" +#line 409 "dt_test.pgc" if (sqlca.sqlcode < 0) sqlprint ( );} -#line 393 "dt_test.pgc" +#line 409 "dt_test.pgc" { ECPGdisconnect(__LINE__, "CURRENT"); -#line 394 "dt_test.pgc" +#line 410 "dt_test.pgc" if (sqlca.sqlcode < 0) sqlprint ( );} -#line 394 "dt_test.pgc" +#line 410 "dt_test.pgc" return 0; diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr index 2a109ee7fa1..92a4695bd5d 100644 --- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr +++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr @@ -42,7 +42,7 @@ [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_get_data on line 41: RESULT: 2000-07-12 17:34:29 offset: -1; array: no [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ECPGtrans on line 393: action "rollback"; connection "ecpg1_regression" +[NO_PID]: ECPGtrans on line 409: action "rollback"; connection "ecpg1_regression" [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_finish: connection ecpg1_regression closed [NO_PID]: sqlca: code: 0, state: 00000 diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout index 6b8bcc9fc27..38fb8a343af 100644 --- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout +++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout @@ -6,6 +6,7 @@ date seems to get encoded to julian -622 m: 4, d: 19, y: 1998 date_day of 2003-12-04 17:34:29 is 4 Above date in format "(ddd), mmm. dd, yyyy, repeat: (ddd), mmm. dd, yyyy. end" is "(Thu), Dec. 04, 2003, repeat: (Thu), Dec. 04, 2003. end" +date_fmt_asc yyyy year>=10000: rc=-1 errno=310 Which is day number 338 in 2003. date_defmt_asc1: 1995-12-25 date_defmt_asc2: 0095-12-25 diff --git a/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc b/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc index 645c273e503..8ea7e7839bc 100644 --- a/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc +++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc @@ -76,6 +76,22 @@ main(void) PGTYPESchar_free(text); free(out); + /* year 10000 does not fit the four-digit "yyyy" token */ + { + date big_year_date; + char *big_out; + int fmt_rc; + + errno = 0; + big_year_date = PGTYPESdate_from_asc("10000-01-01", NULL); + fmt = "yyyy"; + big_out = (char *) malloc(strlen(fmt) + 1); + fmt_rc = PGTYPESdate_fmt_asc(big_year_date, fmt, big_out); + printf("date_fmt_asc yyyy year>=10000: rc=%d errno=%d\n", + fmt_rc, errno); + free(big_out); + } + out = (char*) malloc(48); i = PGTYPEStimestamp_fmt_asc(&ts1, out, 47, "Which is day number %j in %Y."); printf("%s\n", out);
