Try to fix endless loop in ecpg with informix mode

Started by 高增琦about 8 years ago9 messages
#1高增琦
pgf00a@gmail.com
2 attachment(s)

Hi,

I tried some tests with ecpg informix mode.
When trying to store float data into a integer var, I got endless loop.

The reason is:
In informix mode, ecpg can accept
string form of float number when processing query result.
During checking the string form of float number, it seems
that ecpg forgot to skip characters after '.'.
Then outer loop will never stop because it hopes to see '\0'.

The first patch will reproduce the problem in ecpg's regress test.
The second patch tries to fix it in simple way.

--
GaoZengqi
pgf00a@gmail.com
zengqigao@gmail.com

Attachments:

0001-Edit-ecpg-regress-test-to-trigger-endless-loop.patchapplication/octet-stream; name=0001-Edit-ecpg-regress-test-to-trigger-endless-loop.patchDownload
From a4001dfd27abaa4de95249bd6a5c4f1256bbb31b Mon Sep 17 00:00:00 2001
From: Gao Zengqi <pgf00a@gmail.com>
Date: Thu, 26 Oct 2017 07:32:21 +0000
Subject: [PATCH 1/2] Edit ecpg regress test to trigger endless loop.

---
 src/interfaces/ecpg/test/compat_informix/test_informix.pgc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/interfaces/ecpg/test/compat_informix/test_informix.pgc b/src/interfaces/ecpg/test/compat_informix/test_informix.pgc
index 8b7692b..16fcddf 100644
--- a/src/interfaces/ecpg/test/compat_informix/test_informix.pgc
+++ b/src/interfaces/ecpg/test/compat_informix/test_informix.pgc
@@ -21,7 +21,7 @@ int main(void)
 	$connect to REGRESSDB1;
 	if (sqlca.sqlcode != 0) exit(1);
 
-	$create table test(i int primary key, j int, c text);
+	$create table test(i float primary key, j int, c text);
 
 	/* this INSERT works */
 	rsetnull(CDECIMALTYPE, (char *)&j);
@@ -29,7 +29,7 @@ int main(void)
 	$commit;
 
 	/* this INSERT should fail because i is a unique column */
-	$insert into test (i, j, c) values (7, NUMBER, 'a');
+	$insert into test (i, j, c) values (7.1, NUMBER, 'a');
 	printf("INSERT: %ld=%s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
 	if (sqlca.sqlcode != 0) $rollback;
 
-- 
2.7.4

0002-Fix-endless-loop-in-ecpg-with-informix-mode.patchapplication/octet-stream; name=0002-Fix-endless-loop-in-ecpg-with-informix-mode.patchDownload
From e2940f9fc9245f479258ad345356f13ec4aedbb0 Mon Sep 17 00:00:00 2001
From: Gao Zengqi <pgf00a@gmail.com>
Date: Thu, 26 Oct 2017 07:34:31 +0000
Subject: [PATCH 2/2] Fix endless loop in ecpg with informix mode

---
 src/interfaces/ecpg/ecpglib/data.c | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index a2f3916..ab447d8 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -44,7 +44,7 @@ array_boundary(enum ARRAY_TYPE isarray, char c)
 
 /* returns true if some garbage is found at the end of the scanned string */
 static bool
-garbage_left(enum ARRAY_TYPE isarray, char *scan_length, enum COMPAT_MODE compat)
+garbage_left(enum ARRAY_TYPE isarray, char **scan_length, enum COMPAT_MODE compat)
 {
 	/*
 	 * INFORMIX allows for selecting a numeric into an int, the result is
@@ -52,13 +52,19 @@ garbage_left(enum ARRAY_TYPE isarray, char *scan_length, enum COMPAT_MODE compat
 	 */
 	if (isarray == ECPG_ARRAY_NONE)
 	{
-		if (INFORMIX_MODE(compat) && *scan_length == '.')
+		if (INFORMIX_MODE(compat) && **scan_length == '.')
+		{
+			/* skip invalid characters */
+			do {
+				(*scan_length)++;
+			} while (**scan_length != ' ' && **scan_length != '\0');
 			return false;
+		}
 
-		if (*scan_length != ' ' && *scan_length != '\0')
+		if (**scan_length != ' ' && **scan_length != '\0')
 			return true;
 	}
-	else if (ECPG_IS_ARRAY(isarray) && !array_delimiter(isarray, *scan_length) && !array_boundary(isarray, *scan_length))
+	else if (ECPG_IS_ARRAY(isarray) && !array_delimiter(isarray, **scan_length) && !array_boundary(isarray, **scan_length))
 		return true;
 
 	return false;
@@ -303,7 +309,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 				case ECPGt_int:
 				case ECPGt_long:
 					res = strtol(pval, &scan_length, 10);
-					if (garbage_left(isarray, scan_length, compat))
+					if (garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_INT_FORMAT,
 								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
@@ -332,7 +338,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 				case ECPGt_unsigned_int:
 				case ECPGt_unsigned_long:
 					ures = strtoul(pval, &scan_length, 10);
-					if (garbage_left(isarray, scan_length, compat))
+					if (garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_UINT_FORMAT,
 								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
@@ -361,7 +367,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 #ifdef HAVE_STRTOLL
 				case ECPGt_long_long:
 					*((long long int *) (var + offset * act_tuple)) = strtoll(pval, &scan_length, 10);
-					if (garbage_left(isarray, scan_length, compat))
+					if (garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return false;
@@ -373,7 +379,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 #ifdef HAVE_STRTOULL
 				case ECPGt_unsigned_long_long:
 					*((unsigned long long int *) (var + offset * act_tuple)) = strtoull(pval, &scan_length, 10);
-					if (garbage_left(isarray, scan_length, compat))
+					if (garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return false;
@@ -395,7 +401,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if (isarray && *scan_length == '"')
 						scan_length++;
 
-					if (garbage_left(isarray, scan_length, compat))
+					if (garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_FLOAT_FORMAT,
 								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
@@ -593,7 +599,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					}
 					else
 					{
-						if (!isarray && garbage_left(isarray, scan_length, compat))
+						if (!isarray && garbage_left(isarray, &scan_length, compat))
 						{
 							free(nres);
 							ecpg_raise(lineno, ECPG_NUMERIC_FORMAT,
@@ -651,7 +657,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 						if (*scan_length == '"')
 							scan_length++;
 
-						if (!isarray && garbage_left(isarray, scan_length, compat))
+						if (!isarray && garbage_left(isarray, &scan_length, compat))
 						{
 							free(ires);
 							ecpg_raise(lineno, ECPG_INTERVAL_FORMAT,
@@ -701,7 +707,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 						if (*scan_length == '"')
 							scan_length++;
 
-						if (!isarray && garbage_left(isarray, scan_length, compat))
+						if (!isarray && garbage_left(isarray, &scan_length, compat))
 						{
 							ecpg_raise(lineno, ECPG_DATE_FORMAT,
 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
@@ -749,7 +755,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 						if (*scan_length == '"')
 							scan_length++;
 
-						if (!isarray && garbage_left(isarray, scan_length, compat))
+						if (!isarray && garbage_left(isarray, &scan_length, compat))
 						{
 							ecpg_raise(lineno, ECPG_TIMESTAMP_FORMAT,
 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
-- 
2.7.4

#2高增琦
pgf00a@gmail.com
In reply to: 高增琦 (#1)
Re: Try to fix endless loop in ecpg with informix mode

Any comments?

2017-10-26 16:03 GMT+08:00 高增琦 <pgf00a@gmail.com>:

Hi,

I tried some tests with ecpg informix mode.
When trying to store float data into a integer var, I got endless loop.

The reason is:
In informix mode, ecpg can accept
string form of float number when processing query result.
During checking the string form of float number, it seems
that ecpg forgot to skip characters after '.'.
Then outer loop will never stop because it hopes to see '\0'.

The first patch will reproduce the problem in ecpg's regress test.
The second patch tries to fix it in simple way.

--
GaoZengqi
pgf00a@gmail.com
zengqigao@gmail.com

--
GaoZengqi
pgf00a@gmail.com
zengqigao@gmail.com

#3Julien Rouhaud
rjuju123@gmail.com
In reply to: 高增琦 (#2)
Re: Try to fix endless loop in ecpg with informix mode

On Wed, Nov 1, 2017 at 12:22 PM, 高增琦 <pgf00a@gmail.com> wrote:

Any comments?

Hi,

You should register these patches for the next commitfest at
https://commitfest.postgresql.org/15/. As Michael pointed out earlier,
this commitfest will start soon so you should add your patches
quickly.

Regards.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Michael Meskes
meskes@postgresql.org
In reply to: 高增琦 (#2)
Re: Try to fix endless loop in ecpg with informix mode

Any comments?

Sorry, I've been working through the backlog of three weeks of
traveling.

I tried some tests with ecpg informix mode.
When trying to store float data into a integer var, I got endless
loop.

The reason is:
In informix mode, ecpg can accept
string form of float number when processing query result.
During checking the string form of float number, it seems
that ecpg forgot to skip characters after '.'.
Then outer loop will never stop because it hopes to see '\0'.

The first patch will reproduce the problem in ecpg's regress test.
The second patch tries to fix it in simple way.

Thanks for spotting and fixing. I changed your patch slightly and made
it check if the rest of the data is indeed digits, or else it would
accept something like "7.hello" as "7".

Committed.

Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5高增琦
pgf00a@gmail.com
In reply to: Michael Meskes (#4)
Re: Try to fix endless loop in ecpg with informix mode

Thanks for commit.

I am afraid the changes may separate "7.a" to "7" and "a", then error out
with "invalid input syntax for type int" for "a".

How about changes as below? (use following the if to decide true or false)

```
-            } while (**scan_length != ' ' && **scan_length != '\0');
-            return false;
+            } while (isdigit(**scan_length));
```

2017-11-01 20:35 GMT+08:00 Michael Meskes <meskes@postgresql.org>:

Any comments?

Sorry, I've been working through the backlog of three weeks of
traveling.

I tried some tests with ecpg informix mode.
When trying to store float data into a integer var, I got endless
loop.

The reason is:
In informix mode, ecpg can accept
string form of float number when processing query result.
During checking the string form of float number, it seems
that ecpg forgot to skip characters after '.'.
Then outer loop will never stop because it hopes to see '\0'.

The first patch will reproduce the problem in ecpg's regress test.
The second patch tries to fix it in simple way.

Thanks for spotting and fixing. I changed your patch slightly and made
it check if the rest of the data is indeed digits, or else it would
accept something like "7.hello" as "7".

Committed.

Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL

--
GaoZengqi
pgf00a@gmail.com
zengqigao@gmail.com

#6高增琦
pgf00a@gmail.com
In reply to: 高增琦 (#5)
Re: Try to fix endless loop in ecpg with informix mode

Diff from the head:
(use the following if to decide true or false)

```
diff --git a/src/interfaces/ecpg/ecpglib/data.c
b/src/interfaces/ecpg/ecpglib/data.c
index 5375934..1621e7b 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -57,8 +57,7 @@ garbage_left(enum ARRAY_TYPE isarray, char **scan_length,
enum COMPAT_MODE compa
             /* skip invalid characters */
             do {
                 (*scan_length)++;
-            } while (**scan_length != ' ' && **scan_length != '\0' &&
isdigit(**scan_length));
-            return false;
+            } while (isdigit(**scan_length));
         }

if (**scan_length != ' ' && **scan_length != '\0')

```

2017-11-02 11:07 GMT+08:00 高增琦 <pgf00a@gmail.com>:

Thanks for commit.

I am afraid the changes may separate "7.a" to "7" and "a", then error out
with "invalid input syntax for type int" for "a".

How about changes as below? (use following the if to decide true or false)

```
-            } while (**scan_length != ' ' && **scan_length != '\0');
-            return false;
+            } while (isdigit(**scan_length));
```

2017-11-01 20:35 GMT+08:00 Michael Meskes <meskes@postgresql.org>:

Any comments?

Sorry, I've been working through the backlog of three weeks of
traveling.

I tried some tests with ecpg informix mode.
When trying to store float data into a integer var, I got endless
loop.

The reason is:
In informix mode, ecpg can accept
string form of float number when processing query result.
During checking the string form of float number, it seems
that ecpg forgot to skip characters after '.'.
Then outer loop will never stop because it hopes to see '\0'.

The first patch will reproduce the problem in ecpg's regress test.
The second patch tries to fix it in simple way.

Thanks for spotting and fixing. I changed your patch slightly and made
it check if the rest of the data is indeed digits, or else it would
accept something like "7.hello" as "7".

Committed.

Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL

--
GaoZengqi
pgf00a@gmail.com
zengqigao@gmail.com

--
GaoZengqi
pgf00a@gmail.com
zengqigao@gmail.com

#7Michael Meskes
meskes@postgresql.org
In reply to: 高增琦 (#5)
Re: Try to fix endless loop in ecpg with informix mode

I am afraid the changes may separate "7.a" to "7" and "a", then error
out
with "invalid input syntax for type int" for "a".

Which is correct, is it not?

How about changes as below? (use following the if to decide true or
false)
...
return false;
+ } while (isdigit(**scan_length));

Yes, this is certainly correct and better than what I committed. What
was I thinking yesterday?

I think the same function is used for identifying garbage in floats
which might ask for different logic. Let me check.

Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8高增琦
pgf00a@gmail.com
In reply to: Michael Meskes (#7)
Re: Try to fix endless loop in ecpg with informix mode

Hi,

I found the last commit changed as:

```
            /* skip invalid characters */
            do {
                (*scan_length)++;
-           } while (**scan_length != ' ' && **scan_length != '\0' &&
isdigit(**scan_length));
+           } while (isdigit(**scan_length));
            return false;
        }
```

It will still return false if we got non-digital characters after ".",
then it will error out "invalid input syntax for type int" for "a" . (if
input is "7.a")

Although this error message is not wrong, I think it should be better to
give error message as "invalid input syntax for type int" for "7.a".
This could be done by delete "return false;" after "while(...)", let
the following if to decide which to return.

2017-11-02 15:25 GMT+08:00 Michael Meskes <meskes@postgresql.org>:

I am afraid the changes may separate "7.a" to "7" and "a", then error
out
with "invalid input syntax for type int" for "a".

Which is correct, is it not?

How about changes as below? (use following the if to decide true or
false)
...
return false;
+ } while (isdigit(**scan_length));

Yes, this is certainly correct and better than what I committed. What
was I thinking yesterday?

I think the same function is used for identifying garbage in floats
which might ask for different logic. Let me check.

Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL

--
GaoZengqi
pgf00a@gmail.com
zengqigao@gmail.com

#9Michael Meskes
meskes@postgresql.org
In reply to: 高增琦 (#8)
Re: Try to fix endless loop in ecpg with informix mode

Although this error message is not wrong, I think it should be better
to
give error message as "invalid input syntax for type int" for "7.a".
This could be done by delete "return false;" after "while(...)", let
the following if to decide which to return.

Ah, now I understand. Sorry, I completely misunderstood your prior
email. Yes, you're right, I will change the code accordingly.

Thanks.

Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers