1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-17 06:41:24 +03:00

ecpg: Fix out-of-bound read in DecodeDateTime()

It was possible for the code to read out-of-bound data from the
"day_tab" table with some crafted input data.  Let's treat these as
invalid input as the month number is incorrect.

A test is added to test this case with a check on the errno returned by
the decoding routine.  A test close to the new one added in this commit
was testing for a failure, but did not look at the errno generated, so
let's use this commit to also change it, adding a check on the errno
returned by DecodeDateTime().

Like the other test scripts, dt_test should likely be expanded to
include more checks based on the errnos generated in these code paths.
This is left as future work.

This issue exists since 2e6f97560a83, so backpatch all the way down.

Reported-by: Pavel Nekrasov
Author: Bruce Momjian, Pavel Nekrasov
Discussion: https://postgr.es/m/18614-6bbe00117352309e@postgresql.org
Backpatch-through: 12
This commit is contained in:
Michael Paquier 2024-10-23 08:35:07 +09:00
parent d20194cead
commit fcafbaadf7
5 changed files with 109 additions and 48 deletions

View File

@ -2325,10 +2325,10 @@ DecodeDateTime(char **field, int *ftype, int nf,
return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1; return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
/* /*
* check for valid day of month, now that we know for sure the month * check for valid day of month and month, now that we know for sure
* and year... * the month and year...
*/ */
if (tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) if (tm->tm_mon < 1 || tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
return -1; return -1;
/* /*

View File

@ -11,6 +11,7 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <pgtypes_date.h> #include <pgtypes_date.h>
#include <pgtypes_error.h>
#include <pgtypes_timestamp.h> #include <pgtypes_timestamp.h>
#include <pgtypes_interval.h> #include <pgtypes_interval.h>
@ -22,9 +23,11 @@
#line 8 "dt_test.pgc" #line 9 "dt_test.pgc"
static void check_errno(void);
int int
main(void) main(void)
{ {
@ -34,19 +37,19 @@ main(void)
#line 14 "dt_test.pgc" #line 17 "dt_test.pgc"
date date1 ; date date1 ;
#line 15 "dt_test.pgc" #line 18 "dt_test.pgc"
timestamp ts1 ; timestamp ts1 ;
#line 16 "dt_test.pgc" #line 19 "dt_test.pgc"
interval * iv1 , iv2 ; interval * iv1 , iv2 ;
#line 17 "dt_test.pgc" #line 20 "dt_test.pgc"
char * text ; char * text ;
/* exec sql end declare section */ /* exec sql end declare section */
#line 18 "dt_test.pgc" #line 21 "dt_test.pgc"
date date2; date date2;
int mdy[3] = { 4, 19, 1998 }; int mdy[3] = { 4, 19, 1998 };
@ -57,31 +60,31 @@ main(void)
ECPGdebug(1, stderr); ECPGdebug(1, stderr);
/* exec sql whenever sqlerror do sqlprint ( ) ; */ /* exec sql whenever sqlerror do sqlprint ( ) ; */
#line 27 "dt_test.pgc" #line 30 "dt_test.pgc"
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
#line 28 "dt_test.pgc" #line 31 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );} if (sqlca.sqlcode < 0) sqlprint ( );}
#line 28 "dt_test.pgc" #line 31 "dt_test.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table date_test ( d date , ts timestamp )", ECPGt_EOIT, ECPGt_EORT); { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table date_test ( d date , ts timestamp )", ECPGt_EOIT, ECPGt_EORT);
#line 29 "dt_test.pgc" #line 32 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );} if (sqlca.sqlcode < 0) sqlprint ( );}
#line 29 "dt_test.pgc" #line 32 "dt_test.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "set datestyle to iso", ECPGt_EOIT, ECPGt_EORT); { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "set datestyle to iso", ECPGt_EOIT, ECPGt_EORT);
#line 30 "dt_test.pgc" #line 33 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );} if (sqlca.sqlcode < 0) sqlprint ( );}
#line 30 "dt_test.pgc" #line 33 "dt_test.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "set intervalstyle to postgres_verbose", ECPGt_EOIT, ECPGt_EORT); { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "set intervalstyle to postgres_verbose", ECPGt_EOIT, ECPGt_EORT);
#line 31 "dt_test.pgc" #line 34 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );} if (sqlca.sqlcode < 0) sqlprint ( );}
#line 31 "dt_test.pgc" #line 34 "dt_test.pgc"
date1 = PGTYPESdate_from_asc(d1, NULL); date1 = PGTYPESdate_from_asc(d1, NULL);
@ -92,10 +95,10 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
ECPGt_timestamp,&(ts1),(long)1,(long)1,sizeof(timestamp), ECPGt_timestamp,&(ts1),(long)1,(long)1,sizeof(timestamp),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT); ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
#line 36 "dt_test.pgc" #line 39 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );} if (sqlca.sqlcode < 0) sqlprint ( );}
#line 36 "dt_test.pgc" #line 39 "dt_test.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select * from date_test where d = $1 ", { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select * from date_test where d = $1 ",
@ -105,10 +108,10 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
ECPGt_timestamp,&(ts1),(long)1,(long)1,sizeof(timestamp), ECPGt_timestamp,&(ts1),(long)1,(long)1,sizeof(timestamp),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT); ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
#line 38 "dt_test.pgc" #line 41 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );} if (sqlca.sqlcode < 0) sqlprint ( );}
#line 38 "dt_test.pgc" #line 41 "dt_test.pgc"
text = PGTYPESdate_to_asc(date1); text = PGTYPESdate_to_asc(date1);
@ -263,10 +266,19 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
PGTYPESchar_free(text); PGTYPESchar_free(text);
ts1 = PGTYPEStimestamp_from_asc("1994-02-11 26:10:35", NULL); ts1 = PGTYPEStimestamp_from_asc("1994-02-11 26:10:35", NULL);
/* failure, check error code */
check_errno();
text = PGTYPEStimestamp_to_asc(ts1); text = PGTYPEStimestamp_to_asc(ts1);
printf("timestamp_to_asc3: %s\n", text); printf("timestamp_to_asc3: %s\n", text);
PGTYPESchar_free(text); PGTYPESchar_free(text);
ts1 = PGTYPEStimestamp_from_asc("AM95000062", NULL);
/* failure, check error code */
check_errno();
text = PGTYPEStimestamp_to_asc(ts1);
printf("timestamp_to_asc4: %s\n", text);
PGTYPESchar_free(text);
/* abc-03:10:35-def-02/11/94-gh */ /* abc-03:10:35-def-02/11/94-gh */
/* 12345678901234567890123456789 */ /* 12345678901234567890123456789 */
@ -453,17 +465,35 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
free(out); free(out);
{ ECPGtrans(__LINE__, NULL, "rollback"); { ECPGtrans(__LINE__, NULL, "rollback");
#line 381 "dt_test.pgc" #line 393 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );} if (sqlca.sqlcode < 0) sqlprint ( );}
#line 381 "dt_test.pgc" #line 393 "dt_test.pgc"
{ ECPGdisconnect(__LINE__, "CURRENT"); { ECPGdisconnect(__LINE__, "CURRENT");
#line 382 "dt_test.pgc" #line 394 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );} if (sqlca.sqlcode < 0) sqlprint ( );}
#line 382 "dt_test.pgc" #line 394 "dt_test.pgc"
return 0; return 0;
} }
static void
check_errno(void)
{
switch(errno)
{
case 0:
printf("(no errno set) - ");
break;
case PGTYPES_TS_BAD_TIMESTAMP:
printf("(errno == PGTYPES_TS_BAD_TIMESTAMP) - ");
break;
default:
printf("(unknown errno (%d))\n", errno);
printf("(libc: (%s)) ", strerror(errno));
break;
}
}

View File

@ -2,47 +2,47 @@
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT> [NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 29: query: create table date_test ( d date , ts timestamp ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: ecpg_execute on line 32: query: create table date_test ( d date , ts timestamp ); with 0 parameter(s) on connection ecpg1_regression
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 29: using PQexec [NO_PID]: ecpg_execute on line 32: using PQexec
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_process_output on line 29: OK: CREATE TABLE [NO_PID]: ecpg_process_output on line 32: OK: CREATE TABLE
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 30: query: set datestyle to iso; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: ecpg_execute on line 33: query: set datestyle to iso; with 0 parameter(s) on connection ecpg1_regression
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 30: using PQexec [NO_PID]: ecpg_execute on line 33: using PQexec
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_process_output on line 30: OK: SET [NO_PID]: ecpg_process_output on line 33: OK: SET
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 31: query: set intervalstyle to postgres_verbose; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: ecpg_execute on line 34: query: set intervalstyle to postgres_verbose; with 0 parameter(s) on connection ecpg1_regression
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 31: using PQexec [NO_PID]: ecpg_execute on line 34: using PQexec
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_process_output on line 31: OK: SET [NO_PID]: ecpg_process_output on line 34: OK: SET
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 36: query: insert into date_test ( d , ts ) values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression [NO_PID]: ecpg_execute on line 39: query: insert into date_test ( d , ts ) values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 36: using PQexecParams [NO_PID]: ecpg_execute on line 39: using PQexecParams
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_free_params on line 36: parameter 1 = 1966-01-17 [NO_PID]: ecpg_free_params on line 39: parameter 1 = 1966-01-17
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_free_params on line 36: parameter 2 = 2000-07-12 17:34:29 [NO_PID]: ecpg_free_params on line 39: parameter 2 = 2000-07-12 17:34:29
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_process_output on line 36: OK: INSERT 0 1 [NO_PID]: ecpg_process_output on line 39: OK: INSERT 0 1
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 38: query: select * from date_test where d = $1 ; with 1 parameter(s) on connection ecpg1_regression [NO_PID]: ecpg_execute on line 41: query: select * from date_test where d = $1 ; with 1 parameter(s) on connection ecpg1_regression
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 38: using PQexecParams [NO_PID]: ecpg_execute on line 41: using PQexecParams
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_free_params on line 38: parameter 1 = 1966-01-17 [NO_PID]: ecpg_free_params on line 41: parameter 1 = 1966-01-17
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_process_output on line 38: correctly got 1 tuples with 2 fields [NO_PID]: ecpg_process_output on line 41: correctly got 1 tuples with 2 fields
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_get_data on line 38: RESULT: 1966-01-17 offset: -1; array: no [NO_PID]: ecpg_get_data on line 41: RESULT: 1966-01-17 offset: -1; array: no
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_get_data on line 38: RESULT: 2000-07-12 17:34:29 offset: -1; array: no [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]: sqlca: code: 0, state: 00000
[NO_PID]: ECPGtrans on line 381: action "rollback"; connection "ecpg1_regression" [NO_PID]: ECPGtrans on line 393: action "rollback"; connection "ecpg1_regression"
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_finish: connection ecpg1_regression closed [NO_PID]: ecpg_finish: connection ecpg1_regression closed
[NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: sqlca: code: 0, state: 00000

View File

@ -20,7 +20,8 @@ date_defmt_asc10: 1995-12-25
date_defmt_asc12: 0095-12-25 date_defmt_asc12: 0095-12-25
timestamp_to_asc1: 1996-02-29 00:00:00 timestamp_to_asc1: 1996-02-29 00:00:00
timestamp_to_asc2: 1994-02-11 03:10:35 timestamp_to_asc2: 1994-02-11 03:10:35
timestamp_to_asc3: 2000-01-01 00:00:00 (errno == PGTYPES_TS_BAD_TIMESTAMP) - timestamp_to_asc3: 2000-01-01 00:00:00
(errno == PGTYPES_TS_BAD_TIMESTAMP) - timestamp_to_asc4: 2000-01-01 00:00:00
timestamp_fmt_asc: 0: abc-00:00:00-def-01/01/00-ghi% timestamp_fmt_asc: 0: abc-00:00:00-def-01/01/00-ghi%
timestamp_defmt_asc(This is a 4/12/80 3-39l12test, This is a %m/%d/%y %H-%Ml%Stest) = 1980-04-12 03:39:12, error: 0 timestamp_defmt_asc(This is a 4/12/80 3-39l12test, This is a %m/%d/%y %H-%Ml%Stest) = 1980-04-12 03:39:12, error: 0
timestamp_defmt_asc(Tue Jul 22 17:28:44 +0200 2003, %a %b %d %H:%M:%S %z %Y) = 2003-07-22 15:28:44, error: 0 timestamp_defmt_asc(Tue Jul 22 17:28:44 +0200 2003, %a %b %d %H:%M:%S %z %Y) = 2003-07-22 15:28:44, error: 0

View File

@ -2,11 +2,14 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <pgtypes_date.h> #include <pgtypes_date.h>
#include <pgtypes_error.h>
#include <pgtypes_timestamp.h> #include <pgtypes_timestamp.h>
#include <pgtypes_interval.h> #include <pgtypes_interval.h>
exec sql include ../regression; exec sql include ../regression;
static void check_errno(void);
int int
main(void) main(void)
{ {
@ -189,10 +192,19 @@ main(void)
PGTYPESchar_free(text); PGTYPESchar_free(text);
ts1 = PGTYPEStimestamp_from_asc("1994-02-11 26:10:35", NULL); ts1 = PGTYPEStimestamp_from_asc("1994-02-11 26:10:35", NULL);
/* failure, check error code */
check_errno();
text = PGTYPEStimestamp_to_asc(ts1); text = PGTYPEStimestamp_to_asc(ts1);
printf("timestamp_to_asc3: %s\n", text); printf("timestamp_to_asc3: %s\n", text);
PGTYPESchar_free(text); PGTYPESchar_free(text);
ts1 = PGTYPEStimestamp_from_asc("AM95000062", NULL);
/* failure, check error code */
check_errno();
text = PGTYPEStimestamp_to_asc(ts1);
printf("timestamp_to_asc4: %s\n", text);
PGTYPESchar_free(text);
/* abc-03:10:35-def-02/11/94-gh */ /* abc-03:10:35-def-02/11/94-gh */
/* 12345678901234567890123456789 */ /* 12345678901234567890123456789 */
@ -383,3 +395,21 @@ main(void)
return 0; return 0;
} }
static void
check_errno(void)
{
switch(errno)
{
case 0:
printf("(no errno set) - ");
break;
case PGTYPES_TS_BAD_TIMESTAMP:
printf("(errno == PGTYPES_TS_BAD_TIMESTAMP) - ");
break;
default:
printf("(unknown errno (%d))\n", errno);
printf("(libc: (%s)) ", strerror(errno));
break;
}
}