From 97fd8f946e76a3bbe878bab4dfce8f64adfe35c6 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 22 Oct 2024 14:52:11 +0900
Subject: [PATCH] ecpg: Fix out-of-bound read in DecodeDateTime()

blah.

Backpatch-through: 12
---
 src/interfaces/ecpg/pgtypeslib/dt_common.c    |  6 +-
 .../ecpg/test/expected/pgtypeslib-dt_test.c   | 76 +++++++++++++------
 .../test/expected/pgtypeslib-dt_test.stderr   | 42 +++++-----
 .../test/expected/pgtypeslib-dt_test.stdout   |  3 +-
 .../ecpg/test/pgtypeslib/dt_test.pgc          | 30 ++++++++
 5 files changed, 109 insertions(+), 48 deletions(-)

diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
index 0d63fe52c5..c4119ab793 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -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;
 
 		/*
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c
index ee867b6dd8..ae27b0d433 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c
@@ -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>
 
@@ -22,9 +23,11 @@
 
 
 
-#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;
+	}
+}
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr
index 6e9ed3d3db..2a109ee7fa 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr
@@ -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
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout
index 4b582fd7a2..6b8bcc9fc2 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout
@@ -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
diff --git a/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc b/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc
index f81a392665..b34e1d2f06 100644
--- a/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc
+++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc
@@ -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;
+	}
+}
-- 
2.45.2

