ecpg: Fix out-of-bound read in DecodeDateTime()
authorMichael Paquier <[email protected]>
Tue, 22 Oct 2024 23:33:54 +0000 (08:33 +0900)
committerMichael Paquier <[email protected]>
Tue, 22 Oct 2024 23:33:54 +0000 (08:33 +0900)
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

src/interfaces/ecpg/pgtypeslib/dt_common.c
src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c
src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr
src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout
src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc

index 0d63fe52c5bb382103b03a6f38d39d1b21d275f4..c4119ab79320ae1b1a994732f3c7a8df3b7e3f24 100644 (file)
@@ -2327,10 +2327,10 @@ DecodeDateTime(char **field, int *ftype, int nf,
            return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
 
        /*
-        * check for valid day of month, now that we know for sure the month
-        * and year...
+        * check for valid day of month and month, now that we know for sure
+        * 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;
 
        /*
index ee867b6dd862ec79880aea21462f590f078fc320..1f1d341a4accabfcdf01778057f58889a17980a8 100644 (file)
@@ -11,6 +11,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <pgtypes_date.h>
+#include <pgtypes_error.h>
 #include <pgtypes_timestamp.h>
 #include <pgtypes_interval.h>
 
 
 
 
-#line 8 "dt_test.pgc"
+#line 9 "dt_test.pgc"
 
 
+static void check_errno(void);
+
 int
 main(void)
 {
@@ -34,19 +37,19 @@ main(void)
          
         
    
-#line 14 "dt_test.pgc"
+#line 17 "dt_test.pgc"
  date date1 ;
  
-#line 15 "dt_test.pgc"
+#line 18 "dt_test.pgc"
  timestamp ts1 ;
  
-#line 16 "dt_test.pgc"
+#line 19 "dt_test.pgc"
  interval * iv1 , iv2 ;
  
-#line 17 "dt_test.pgc"
+#line 20 "dt_test.pgc"
  char * text ;
 /* exec sql end declare section */
-#line 18 "dt_test.pgc"
+#line 21 "dt_test.pgc"
 
    date date2;
    int mdy[3] = { 4, 19, 1998 };
@@ -57,31 +60,31 @@ main(void)
 
    ECPGdebug(1, stderr);
    /* 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); 
-#line 28 "dt_test.pgc"
+#line 31 "dt_test.pgc"
 
 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);
-#line 29 "dt_test.pgc"
+#line 32 "dt_test.pgc"
 
 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);
-#line 30 "dt_test.pgc"
+#line 33 "dt_test.pgc"
 
 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);
-#line 31 "dt_test.pgc"
+#line 34 "dt_test.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 31 "dt_test.pgc"
+#line 34 "dt_test.pgc"
 
 
    date1 = PGTYPESdate_from_asc(d1, NULL);
@@ -92,10 +95,10 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
    ECPGt_timestamp,&(ts1),(long)1,(long)1,sizeof(timestamp), 
    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 ( );}
-#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 ", 
@@ -105,10 +108,10 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
    ECPGt_timestamp,&(ts1),(long)1,(long)1,sizeof(timestamp), 
    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
-#line 38 "dt_test.pgc"
+#line 41 "dt_test.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 38 "dt_test.pgc"
+#line 41 "dt_test.pgc"
 
 
    text = PGTYPESdate_to_asc(date1);
@@ -263,10 +266,19 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
    PGTYPESchar_free(text);
 
    ts1 = PGTYPEStimestamp_from_asc("1994-02-11 26:10:35", NULL);
+   /* failure, check error code */
+   check_errno();
    text = PGTYPEStimestamp_to_asc(ts1);
    printf("timestamp_to_asc3: %s\n", 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  */
 /*      12345678901234567890123456789 */
 
@@ -453,17 +465,35 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
    free(out);
 
    { ECPGtrans(__LINE__, NULL, "rollback");
-#line 381 "dt_test.pgc"
+#line 393 "dt_test.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 381 "dt_test.pgc"
+#line 393 "dt_test.pgc"
 
         { ECPGdisconnect(__LINE__, "CURRENT");
-#line 382 "dt_test.pgc"
+#line 394 "dt_test.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 382 "dt_test.pgc"
+#line 394 "dt_test.pgc"
 
 
    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;
+   }
+}
index 6e9ed3d3dbad360239f7cae32056345683ff084b..2a109ee7fa1457bbd9a35a63aa2e8f1d3fd7b920 100644 (file)
@@ -2,47 +2,47 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>  
 [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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: 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]: ecpg_finish: connection ecpg1_regression closed
 [NO_PID]: sqlca: code: 0, state: 00000
index 4b582fd7a28029c5a229db27ed59241b786cf590..6b8bcc9fc273701b8e1ac26b0291a7a070610319 100644 (file)
@@ -20,7 +20,8 @@ date_defmt_asc10: 1995-12-25
 date_defmt_asc12: 0095-12-25
 timestamp_to_asc1: 1996-02-29 00:00:00
 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_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
index f81a3926655055d8145b55a14675c05c5068a821..645c273e503cd32acd65a5fde094e24e94325a00 100644 (file)
@@ -2,11 +2,14 @@
 #include <string.h>
 #include <stdlib.h>
 #include <pgtypes_date.h>
+#include <pgtypes_error.h>
 #include <pgtypes_timestamp.h>
 #include <pgtypes_interval.h>
 
 exec sql include ../regression;
 
+static void check_errno(void);
+
 int
 main(void)
 {
@@ -189,10 +192,19 @@ main(void)
    PGTYPESchar_free(text);
 
    ts1 = PGTYPEStimestamp_from_asc("1994-02-11 26:10:35", NULL);
+   /* failure, check error code */
+   check_errno();
    text = PGTYPEStimestamp_to_asc(ts1);
    printf("timestamp_to_asc3: %s\n", 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  */
 /*      12345678901234567890123456789 */
 
@@ -383,3 +395,21 @@ main(void)
 
    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;
+   }
+}