Subject: Potential issue in ecpg-informix decimal converting functions

From: [email-masked]
Date: Thu, 22 Feb 2024 19:54:37 +0300

Hi, everyone!

I found a potential bug in dectoint() and dectolong() functions from
informix.c. "Informix Compatibility Mode" doc chapter says that
ECPG_INFORMIX_NUM_OVERFLOW is returned if an overflow occurred. But
check this line in dectoint() or dectolong() (it is present in both):
if (ret == PGTYPES_NUM_OVERFLOW) - condition is always
false because PGTYPESnumeric_to_int() and PGTYPESnumeric_to_long()
functions return only 0 or -1. So ECPG_INFORMIX_NUM_OVERFLOW can never
be returned.

I think dectoint(), dectolong() and PGTYPESnumeric_to_int() functions
should be a little bit different like in proposing patch.
What do you think?

The flaw was catched with the help of Svace static analyzer.
https://svace.pages.ispras.ru/svace-website/en/

Thank you!

diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index 73351a9136..8390a61cf4 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -8882,7 +8882,7 @@ int dectodbl(decimal *np, double *dblp);
       <term><function>dectoint</function></term>
       <listitem>
        <para>
-        Convert a variable to type decimal to an integer.
+        Convert a variable of type decimal to an integer.
 <synopsis>
 int dectoint(decimal *np, int *ip);
 </synopsis>
@@ -8892,8 +8892,9 @@ int dectoint(decimal *np, int *ip);
        </para>
        <para>
         On success, 0 is returned and a negative value if the conversion
-        failed. If an overflow occurred, <literal>ECPG_INFORMIX_NUM_OVERFLOW</literal>
-        is returned.
+        failed. If overflow or underflow occurred, the function returns
+        <literal>ECPG_INFORMIX_NUM_OVERFLOW</literal> or
+        <literal>ECPG_INFORMIX_NUM_UNDERFLOW</literal> respectively.
        </para>
        <para>
         Note that the ECPG implementation differs from the <productname>Informix</productname>
@@ -8908,7 +8909,7 @@ int dectoint(decimal *np, int *ip);
       <term><function>dectolong</function></term>
       <listitem>
        <para>
-        Convert a variable to type decimal to a long integer.
+        Convert a variable of type decimal to a long integer.
 <synopsis>
 int dectolong(decimal *np, long *lngp);
 </synopsis>
@@ -8918,8 +8919,9 @@ int dectolong(decimal *np, long *lngp);
        </para>
        <para>
         On success, 0 is returned and a negative value if the conversion
-        failed. If an overflow occurred, <literal>ECPG_INFORMIX_NUM_OVERFLOW</literal>
-        is returned.
+        failed. If overflow or underflow occurred, the function returns
+        <literal>ECPG_INFORMIX_NUM_OVERFLOW</literal> or
+        <literal>ECPG_INFORMIX_NUM_UNDERFLOW</literal> respectively.
        </para>
        <para>
         Note that the ECPG implementation differs from the <productname>Informix</productname>
diff --git a/src/interfaces/ecpg/compatlib/informix.c b/src/interfaces/ecpg/compatlib/informix.c
index 80d40aa3e0..73e84bc6e3 100644
--- a/src/interfaces/ecpg/compatlib/informix.c
+++ b/src/interfaces/ecpg/compatlib/informix.c
@@ -435,6 +435,7 @@ dectoint(decimal *np, int *ip)
 {
 	int			ret;
 	numeric    *nres = PGTYPESnumeric_new();
+	int			errnum = 0;
 
 	if (nres == NULL)
 		return ECPG_INFORMIX_OUT_OF_MEMORY;
@@ -445,11 +446,18 @@ dectoint(decimal *np, int *ip)
 		return ECPG_INFORMIX_OUT_OF_MEMORY;
 	}
 
+	errno = 0;
 	ret = PGTYPESnumeric_to_int(nres, ip);
+	errnum = errno;
 	PGTYPESnumeric_free(nres);
 
-	if (ret == PGTYPES_NUM_OVERFLOW)
-		ret = ECPG_INFORMIX_NUM_OVERFLOW;
+	if (ret)
+	{
+		if (errnum == PGTYPES_NUM_UNDERFLOW)
+			ret = ECPG_INFORMIX_NUM_UNDERFLOW;
+		else if (errnum == PGTYPES_NUM_OVERFLOW)
+			ret = ECPG_INFORMIX_NUM_OVERFLOW;
+	}
 
 	return ret;
 }
@@ -459,6 +467,7 @@ dectolong(decimal *np, long *lngp)
 {
 	int			ret;
 	numeric    *nres = PGTYPESnumeric_new();
+	int			errnum = 0;
 
 	if (nres == NULL)
 		return ECPG_INFORMIX_OUT_OF_MEMORY;
@@ -469,11 +478,18 @@ dectolong(decimal *np, long *lngp)
 		return ECPG_INFORMIX_OUT_OF_MEMORY;
 	}
 
+	errno = 0;
 	ret = PGTYPESnumeric_to_long(nres, lngp);
+	errnum = errno;
 	PGTYPESnumeric_free(nres);
 
-	if (ret == PGTYPES_NUM_OVERFLOW)
-		ret = ECPG_INFORMIX_NUM_OVERFLOW;
+	if (ret)
+	{
+		if (errnum == PGTYPES_NUM_UNDERFLOW)
+			ret = ECPG_INFORMIX_NUM_UNDERFLOW;
+		else if (errnum == PGTYPES_NUM_OVERFLOW)
+			ret = ECPG_INFORMIX_NUM_OVERFLOW;
+	}
 
 	return ret;
 }
diff --git a/src/interfaces/ecpg/pgtypeslib/numeric.c b/src/interfaces/ecpg/pgtypeslib/numeric.c
index 35e7b92da4..52b49e1ce9 100644
--- a/src/interfaces/ecpg/pgtypeslib/numeric.c
+++ b/src/interfaces/ecpg/pgtypeslib/numeric.c
@@ -1502,7 +1502,12 @@ PGTYPESnumeric_to_int(numeric *nv, int *ip)
 /* silence compilers that might complain about useless tests */
 #if SIZEOF_LONG > SIZEOF_INT
 
-	if (l < INT_MIN || l > INT_MAX)
+	if (l < INT_MIN)
+	{
+		errno = PGTYPES_NUM_UNDERFLOW;
+		return -1;
+	}
+	else if(l > INT_MAX)
 	{
 		errno = PGTYPES_NUM_OVERFLOW;
 		return -1;
diff --git a/src/interfaces/ecpg/test/expected/compat_informix-dec_test.stdout b/src/interfaces/ecpg/test/expected/compat_informix-dec_test.stdout
index 1f8675b3f3..71a5afa4a7 100644
--- a/src/interfaces/ecpg/test/expected/compat_informix-dec_test.stdout
+++ b/src/interfaces/ecpg/test/expected/compat_informix-dec_test.stdout
@@ -3,8 +3,8 @@
 (no errno set) - dec[0,3]: r: -1, *
 (no errno set) - dec[0,4]: r: -1, *
 dec[0,5]: r: 0, 0.00
-(errno == PGTYPES_NUM_OVERFLOW) - dec[0,6]: 0 (r: -1)
-(errno == PGTYPES_NUM_OVERFLOW) - dec[0,8]: 0 (r: -1)
+(errno == PGTYPES_NUM_OVERFLOW) - dec[0,6]: 0 (r: -1200)
+(errno == PGTYPES_NUM_OVERFLOW) - dec[0,8]: 0 (r: -1200)
 (errno == PGTYPES_NUM_OVERFLOW) - dec[0,10]: 0 (r: -1)
 
 dec[1,1]: r: 0, -2
@@ -45,8 +45,8 @@ dec[4,2]: r: 0, 592490000000000000000000
 dec[4,3]: r: 0, 592490000000000000000000.0
 dec[4,4]: r: 0, 592490000000000000000000.00
 dec[4,5]: r: 0, 0.00
-(errno == PGTYPES_NUM_OVERFLOW) - dec[4,6]: 0 (r: -1)
-(errno == PGTYPES_NUM_OVERFLOW) - dec[4,8]: 0 (r: -1)
+(errno == PGTYPES_NUM_OVERFLOW) - dec[4,6]: 0 (r: -1200)
+(errno == PGTYPES_NUM_OVERFLOW) - dec[4,8]: 0 (r: -1200)
 dec[4,10]: 5.9249e+23 (r: 0)
 
 dec[5,1]: r: 0, -328400
@@ -141,8 +141,8 @@ dec[13,2]: r: 0, 1234567890123456789012345679
 dec[13,3]: r: 0, 1234567890123456789012345678.9
 dec[13,4]: r: 0, 1234567890123456789012345678.91
 dec[13,5]: r: 0, 0.00
-(errno == PGTYPES_NUM_OVERFLOW) - dec[13,6]: 0 (r: -1)
-(errno == PGTYPES_NUM_OVERFLOW) - dec[13,8]: 0 (r: -1)
+(errno == PGTYPES_NUM_OVERFLOW) - dec[13,6]: 0 (r: -1200)
+(errno == PGTYPES_NUM_OVERFLOW) - dec[13,8]: 0 (r: -1200)
 dec[13,10]: 1.23457e+27 (r: 0)
 
 (errno == PGTYPES_NUM_OVERFLOW) - dec[14,0]: r: -1200