Re: Why is citext/regress failing on hamerkop?

2024-08-01 Thread Oleg Tselebrovskiy

Thomas Munro wrote 2024-05-12 06:31:

Hamerkop is already green on the 15 and 16 branches, apparently
because it's using the pre-meson test stuff that I guess just didn't
run the relevant test.  In other words, nobody would notice the
difference anyway, and a master-only fix would be enough to end this
44-day red streak.


Sorry for necroposting, but in our automated testing system we have
found some fails of this test. The most recent one was a couple of
days ago (see attached files) on PostgreSQL 15.7. Also I've reported
this bug some time ago [1], but provided an example only for
PostgreSQL 17. Back then the bug was actually found on 15 or 16
branches (no logs remain from couple of months back), but i wanted
to show that it was reproducible on 17.

I would appreciate if you would backpatch this change to 15 and 16
branches.

[1] 
https://www.postgresql.org/message-id/6885a0b52d06f7e5910d2b6276bbb4e8%40postgrespro.ru


Oleg Tselebrovskiy, Postgres ProThe files belonging to this database system will be owned by user 
"GitLabRunner".
This user must also own the server process.

The database cluster will be initialized with locale "English_United 
States.1252".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory 
C:/gr-builds/TaKFe3FF/2/pgpro-dev/postgrespro/ci_base ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... windows
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Europe/Moscow
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
initdb: hint: You can change this by editing pg_hba.conf or using the option 
-A, or --auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

pg_ctl -D 
^"C^:^\gr^-builds^\TaKFe3FF^\2^\pgpro^-dev^\postgrespro^\ci^_base^" -l logfile 
start

2024-07-29 14:41:21.649 MSK [14344] LOG:  starting PostgreSQL 15.7, compiled by 
Visual C++ build 1929, 64-bit
2024-07-29 14:41:21.653 MSK [14344] LOG:  listening on IPv6 address "::1", port 
5432
2024-07-29 14:41:21.653 MSK [14344] LOG:  listening on IPv4 address 
"127.0.0.1", port 5432
2024-07-29 14:41:21.697 MSK [8052] LOG:  database system was shut down at 
2024-07-29 14:41:17 MSK
2024-07-29 14:41:21.735 MSK [14344] LOG:  database system is ready to accept 
connections
2024-07-29 14:41:22.859 MSK [9348] ERROR:  tablespace location must be an 
absolute path
2024-07-29 14:41:22.859 MSK [9348] STATEMENT:  CREATE TABLESPACE 
regress_tblspace LOCATION 'relative';
diff -w -U3 C:/gr-builds/TaKFe3FF/2/pgpro-dev/postgrespro/contrib/citext/expected/citext_utf8.out C:/gr-builds/TaKFe3FF/2/pgpro-dev/postgrespro/contrib/citext/results/citext_utf8.out
--- C:/gr-builds/TaKFe3FF/2/pgpro-dev/postgrespro/contrib/citext/expected/citext_utf8.out	2024-07-29 13:53:45.259126600 +0300
+++ C:/gr-builds/TaKFe3FF/2/pgpro-dev/postgrespro/contrib/citext/results/citext_utf8.out	2024-07-29 14:43:38.772857200 +0300
@@ -54,7 +54,7 @@
 SELECT 'i'::citext = 'Ä°'::citext AS t;
  t 
 ---
- t
+ f
 (1 row)
 
 -- Regression.


Re: [PROPOSAL] Skip test citext_utf8 on Windows

2024-03-11 Thread Oleg Tselebrovskiy

Michael Paquier писал(а) 2024-03-12 06:24:

On Mon, Mar 11, 2024 at 03:21:11PM +0700, Oleg Tselebrovskiy wrote:

The proposed patch for skipping test is attached


Your attached patch seems to be in binary format.
--
Michael

Right, I had it saved in not-UTF-8 encoding. Kind of ironic

Here's a fixed versiondiff --git a/contrib/citext/expected/citext_utf8.out b/contrib/citext/expected/citext_utf8.out
index 5d988dcd485..6c4069f9469 100644
--- a/contrib/citext/expected/citext_utf8.out
+++ b/contrib/citext/expected/citext_utf8.out
@@ -10,7 +10,8 @@
 SELECT getdatabaseencoding() <> 'UTF8' OR
(SELECT (datlocprovider = 'c' AND datctype = 'C') OR datlocprovider = 'i'
 FROM pg_database
-WHERE datname=current_database())
+WHERE datname=current_database()) OR
+	   (version() ~ 'windows' OR version() ~ 'Visual C\+\+' OR version() ~ 'mingw32')
AS skip_test \gset
 \if :skip_test
 \quit
diff --git a/contrib/citext/expected/citext_utf8_1.out b/contrib/citext/expected/citext_utf8_1.out
index 7065a5da190..d4472b1c36a 100644
--- a/contrib/citext/expected/citext_utf8_1.out
+++ b/contrib/citext/expected/citext_utf8_1.out
@@ -10,7 +10,8 @@
 SELECT getdatabaseencoding() <> 'UTF8' OR
(SELECT (datlocprovider = 'c' AND datctype = 'C') OR datlocprovider = 'i'
 FROM pg_database
-WHERE datname=current_database())
+WHERE datname=current_database()) OR
+	   (version() ~ 'windows' OR version() ~ 'Visual C\+\+' OR version() ~ 'mingw32')
AS skip_test \gset
 \if :skip_test
 \quit
diff --git a/contrib/citext/sql/citext_utf8.sql b/contrib/citext/sql/citext_utf8.sql
index 34b232d64e2..53775cdcd35 100644
--- a/contrib/citext/sql/citext_utf8.sql
+++ b/contrib/citext/sql/citext_utf8.sql
@@ -11,7 +11,8 @@
 SELECT getdatabaseencoding() <> 'UTF8' OR
(SELECT (datlocprovider = 'c' AND datctype = 'C') OR datlocprovider = 'i'
 FROM pg_database
-WHERE datname=current_database())
+WHERE datname=current_database()) OR
+	   (version() ~ 'windows' OR version() ~ 'Visual C\+\+' OR version() ~ 'mingw32')
AS skip_test \gset
 \if :skip_test
 \quit


[PROPOSAL] Skip test citext_utf8 on Windows

2024-03-11 Thread Oleg Tselebrovskiy

Greetings, everyone!

While running "installchecks" on databases with UTF-8 encoding the test
citext_utf8 fails because of Turkish dotted I like this:

 SELECT 'i'::citext = 'İ'::citext AS t;
  t
 ---
- t
+ f
 (1 row)

I tried to replicate the test's results by hand and with any collation
that I tried (including --locale="Turkish") this test failed

Also an interesing result of my tesing. If you initialize you DB
with -E utf-8 --locale="Turkish" and then run select LOWER('İ');
the output will be this:
 lower
---
 İ
(1 row)

Which I find strange since lower() uses collation that was passed
(default in this case but still)

My PostgreSQL version is this:
postgres=# select version();
   version
--
 PostgreSQL 17devel on x86_64-windows, compiled by gcc-13.1.0, 64-bit

The proposed patch for skipping test is attached

Oleg Tselebrovskiy, Postgres Proÿþdiff --git a/contrib/citext/expected/citext_utf8.out b/contrib/citext/expected/citext_utf8.out

index 5d988dcd485..6c4069f9469 100644

--- a/contrib/citext/expected/citext_utf8.out

+++ b/contrib/citext/expected/citext_utf8.out

@@ -10,7 +10,8 @@

 SELECT getdatabaseencoding() <> 'UTF8' OR

        (SELECT (datlocprovider = 'c' AND datctype = 'C') OR datlocprovider = 'i'

         FROM pg_database

-        WHERE datname=current_database())

+        WHERE datname=current_database()) OR

+	   (version() ~ 'windows' OR version() ~ 'Visual C\+\+' OR version() ~ 'mingw32')

        AS skip_test \gset

 \if :skip_test

 \quit

diff --git a/contrib/citext/expected/citext_utf8_1.out b/contrib/citext/expected/citext_utf8_1.out

index 7065a5da190..d4472b1c36a 100644

--- a/contrib/citext/expected/citext_utf8_1.out

+++ b/contrib/citext/expected/citext_utf8_1.out

@@ -10,7 +10,8 @@

 SELECT getdatabaseencoding() <> 'UTF8' OR

        (SELECT (datlocprovider = 'c' AND datctype = 'C') OR datlocprovider = 'i'

         FROM pg_database

-        WHERE datname=current_database())

+        WHERE datname=current_database()) OR

+	   (version() ~ 'windows' OR version() ~ 'Visual C\+\+' OR version() ~ 'mingw32')

        AS skip_test \gset

 \if :skip_test

 \quit

diff --git a/contrib/citext/sql/citext_utf8.sql b/contrib/citext/sql/citext_utf8.sql

index 34b232d64e2..53775cdcd35 100644

--- a/contrib/citext/sql/citext_utf8.sql

+++ b/contrib/citext/sql/citext_utf8.sql

@@ -11,7 +11,8 @@

 SELECT getdatabaseencoding() <> 'UTF8' OR

        (SELECT (datlocprovider = 'c' AND datctype = 'C') OR datlocprovider = 'i'

         FROM pg_database

-        WHERE datname=current_database())

+        WHERE datname=current_database()) OR

+	   (version() ~ 'windows' OR version() ~ 'Visual C\+\+' OR version() ~ 'mingw32')

        AS skip_test \gset

 \if :skip_test

 \quit



BRIN integer overflow

2024-02-20 Thread Oleg Tselebrovskiy

Greetings, everyone!

While analyzing output of Svace static analyzer [1] I've found a bug

Function bringetbitmap that is used in BRIN's IndexAmRoutine should 
return an
int64 value, but the actual return value is int, since totalpages is int 
and

totalpages * 10 is also int. This could lead to integer overflow

I suggest to change totalpages to be int64 to avoid potential overflow.
Also in all other "amgetbitmap functions" (such as hashgetbitmap, 
gistgetbitmap,

gingetbitmap, blgetbitmap) the return value is of correct int64 type

The proposed patch is attached

[1] - https://svace.pages.ispras.ru/svace-website/en/

Oleg Tselebrovskiy, Postgres Prodiff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 1087a9011ea..f72667e484e 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -559,7 +559,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 	BrinOpaque *opaque;
 	BlockNumber nblocks;
 	BlockNumber heapBlk;
-	int			totalpages = 0;
+	int64		totalpages = 0;
 	FmgrInfo   *consistentFn;
 	MemoryContext oldcxt;
 	MemoryContext perRangeCxt;


Re: Returning non-terminated string in ECPG Informix-compatible function

2024-02-15 Thread Oleg Tselebrovskiy

Thanks for review!

I added a regression test that is based on code from previous email

New patch is attached

Oleg Tselebrovskiy, Postgres Prodiff --git a/src/interfaces/ecpg/compatlib/informix.c b/src/interfaces/ecpg/compatlib/informix.c
index dccf39582da..80d40aa3e09 100644
--- a/src/interfaces/ecpg/compatlib/informix.c
+++ b/src/interfaces/ecpg/compatlib/informix.c
@@ -654,7 +654,7 @@ intoasc(interval * i, char *str)
 	if (!tmp)
 		return -errno;
 
-	memcpy(str, tmp, strlen(tmp));
+	strcpy(str, tmp);
 	free(tmp);
 	return 0;
 }
diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
index 99bdc94d6d7..d4ca0cbff6e 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -2659,6 +2659,8 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
  */
 pfmt++;
 tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
+if(!tmp)
+	return 1;
 strcpy(tmp, "%m/%d/%y");
 strcat(tmp, pfmt);
 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
@@ -2784,6 +2786,8 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 			case 'r':
 pfmt++;
 tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
+if(!tmp)
+	return 1;
 strcpy(tmp, "%I:%M:%S %p");
 strcat(tmp, pfmt);
 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
@@ -2792,6 +2796,8 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 			case 'R':
 pfmt++;
 tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
+if(!tmp)
+	return 1;
 strcpy(tmp, "%H:%M");
 strcat(tmp, pfmt);
 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
@@ -2837,6 +2843,8 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 			case 'T':
 pfmt++;
 tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
+if(!tmp)
+	return 1;
 strcpy(tmp, "%H:%M:%S");
 strcat(tmp, pfmt);
 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
diff --git a/src/interfaces/ecpg/test/compat_informix/.gitignore b/src/interfaces/ecpg/test/compat_informix/.gitignore
index f97706ba4be..6967ae77cd2 100644
--- a/src/interfaces/ecpg/test/compat_informix/.gitignore
+++ b/src/interfaces/ecpg/test/compat_informix/.gitignore
@@ -4,6 +4,8 @@
 /dec_test.c
 /describe
 /describe.c
+/intoasc
+/intoasc.c
 /rfmtdate
 /rfmtdate.c
 /rfmtlong
diff --git a/src/interfaces/ecpg/test/compat_informix/Makefile b/src/interfaces/ecpg/test/compat_informix/Makefile
index d50fdc29fd1..638b4e0af78 100644
--- a/src/interfaces/ecpg/test/compat_informix/Makefile
+++ b/src/interfaces/ecpg/test/compat_informix/Makefile
@@ -16,7 +16,8 @@ TESTS = test_informix test_informix.c \
 rnull rnull.c \
 sqlda sqlda.c \
 describe describe.c \
-charfuncs charfuncs.c
+charfuncs charfuncs.c \
+intoasc intoasc.c
 
 all: $(TESTS)
 
diff --git a/src/interfaces/ecpg/test/compat_informix/intoasc.pgc b/src/interfaces/ecpg/test/compat_informix/intoasc.pgc
new file mode 100644
index 000..d13c83bb7a7
--- /dev/null
+++ b/src/interfaces/ecpg/test/compat_informix/intoasc.pgc
@@ -0,0 +1,21 @@
+#include 
+#include 
+
+#include "pgtypes_interval.h"
+
+EXEC SQL BEGIN DECLARE SECTION;
+char dirty_str[100] = "a__c_d_";
+interval *interval_ptr;
+EXEC SQL END DECLARE SECTION;
+
+int main()
+{
+interval_ptr = (interval *) malloc(sizeof(interval));
+interval_ptr->time = 1;
+interval_ptr->month = 240;
+
+printf("dirty_str contents before intoasc: %s\n", dirty_str);
+intoasc(interval_ptr, dirty_str);
+printf("dirty_str contents after intoasc: %s\n", dirty_str);
+return 0;
+}
diff --git a/src/interfaces/ecpg/test/compat_informix/meson.build b/src/interfaces/ecpg/test/compat_informix/meson.build
index e2f8802330d..7e4790933ad 100644
--- a/src/interfaces/ecpg/test/compat_informix/meson.build
+++ b/src/interfaces/ecpg/test/compat_informix/meson.build
@@ -4,6 +4,7 @@ pgc_files = [
   'charfuncs',
   'dec_test',
   'describe',
+  'intoasc',
   'rfmtdate',
   'rfmtlong',
   'rnull',
diff --git a/src/interfaces/ecpg/test/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule
index 39814a39c17..f9c0a0e3c00 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule
+++ b/src/interfaces/ecpg/test/ecpg_schedule
@@ -7,6 +7,7 @@ test: compat_informix/sqlda
 test: compat_informix/describe
 test: compat_informix/test_informix
 test: compat_informix/test_informix2
+test: compat_informix/i

xmlBufferCreate return value not checked in pgxmlNodeSetToText

2024-02-14 Thread Oleg Tselebrovskiy

Greetings, everyone!

While analyzing output of Svace static analyzer [1] I've found a bug.

In function pgxmlNodeSetToText there is a call of xmlBufferCreate that 
doesn't
have its return value checked. In all four other calls of 
xmlBufferCreate there

is a try...catch that checks the return value inside.

I suggest to add the same checks here that are used in other four calls 
of

xmlBufferCreate.

The proposed patch is attached.

[1] - https://svace.pages.ispras.ru/svace-website/en/

Oleg Tselebrovskiy, Postgres Prodiff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 94641930f7b..2d72ade9c20 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -122,62 +122,85 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
    xmlChar *septagname,
    xmlChar *plainsep)
 {
-	xmlBufferPtr buf;
+	xmlBufferPtr buf = NULL;
 	xmlChar*result;
 	int			i;
+	PgXmlErrorContext *xmlerrcxt;
 
-	buf = xmlBufferCreate();
+	xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_LEGACY);
 
-	if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
-	{
-		xmlBufferWriteChar(buf, "<");
-		xmlBufferWriteCHAR(buf, toptagname);
-		xmlBufferWriteChar(buf, ">");
-	}
-	if (nodeset != NULL)
+	PG_TRY();
 	{
-		for (i = 0; i < nodeset->nodeNr; i++)
-		{
-			if (plainsep != NULL)
-			{
-xmlBufferWriteCHAR(buf,
-   xmlXPathCastNodeToString(nodeset->nodeTab[i]));
+		buf = xmlBufferCreate();
 
-/* If this isn't the last entry, write the plain sep. */
-if (i < (nodeset->nodeNr) - 1)
-	xmlBufferWriteChar(buf, (char *) plainsep);
-			}
-			else
+		if (buf == NULL)
+			xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+		"could not allocate xmlBuffer");
+
+		if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
+		{
+			xmlBufferWriteChar(buf, "<");
+			xmlBufferWriteCHAR(buf, toptagname);
+			xmlBufferWriteChar(buf, ">");
+		}
+		if (nodeset != NULL)
+		{
+			for (i = 0; i < nodeset->nodeNr; i++)
 			{
-if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
+if (plainsep != NULL)
 {
-	xmlBufferWriteChar(buf, "<");
-	xmlBufferWriteCHAR(buf, septagname);
-	xmlBufferWriteChar(buf, ">");
-}
-xmlNodeDump(buf,
-			nodeset->nodeTab[i]->doc,
-			nodeset->nodeTab[i],
-			1, 0);
+	xmlBufferWriteCHAR(buf,
+	xmlXPathCastNodeToString(nodeset->nodeTab[i]));
 
-if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
+	/* If this isn't the last entry, write the plain sep. */
+	if (i < (nodeset->nodeNr) - 1)
+		xmlBufferWriteChar(buf, (char *) plainsep);
+}
+else
 {
-	xmlBufferWriteChar(buf, "");
+	if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
+	{
+		xmlBufferWriteChar(buf, "<");
+		xmlBufferWriteCHAR(buf, septagname);
+		xmlBufferWriteChar(buf, ">");
+	}
+	xmlNodeDump(buf,
+nodeset->nodeTab[i]->doc,
+nodeset->nodeTab[i],
+1, 0);
+
+	if ((septagname != NULL) && (xmlStrlen(septagname) > 0))
+	{
+		xmlBufferWriteChar(buf, "");
+	}
 }
 			}
 		}
-	}
 
-	if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
+		if ((toptagname != NULL) && (xmlStrlen(toptagname) > 0))
+		{
+			xmlBufferWriteChar(buf, "");
+		}
+		result = xmlStrdup(buf->content);
+	}
+	PG_CATCH();
 	{
-		xmlBufferWriteChar(buf, "");
+		if (buf)
+			xmlBufferFree(buf);
+
+		pg_xml_done(xmlerrcxt, true);
+
+		PG_RE_THROW();
 	}
-	result = xmlStrdup(buf->content);
+	PG_END_TRY();
+
 	xmlBufferFree(buf);
+	pg_xml_done(xmlerrcxt, false);
+
 	return result;
 }
 


Re: Returning non-terminated string in ECPG Informix-compatible function

2024-02-14 Thread Oleg Tselebrovskiy

Greetings again.
I was looking through more static analyzer output and found another 
problem.

In ecpg/pgtypeslib/dt_common.c there are 4 calls of pgtypes_alloc.
This function uses calloc and returns NULL if OOM, but we don't check 
its
return value and immediately pass it to strcpy, which could lead to 
segfault.


I suggest adding a check for a return value since all other calls of
pgtypes_alloc are checked for NULL.

A proposed patch (with previous and current changes) is attached

Oleg Tselebrovskiy, Postgres Prodiff --git a/src/interfaces/ecpg/compatlib/informix.c b/src/interfaces/ecpg/compatlib/informix.c
index dccf39582da..80d40aa3e09 100644
--- a/src/interfaces/ecpg/compatlib/informix.c
+++ b/src/interfaces/ecpg/compatlib/informix.c
@@ -654,7 +654,7 @@ intoasc(interval * i, char *str)
 	if (!tmp)
 		return -errno;
 
-	memcpy(str, tmp, strlen(tmp));
+	strcpy(str, tmp);
 	free(tmp);
 	return 0;
 }
diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
index 99bdc94d6d7..d4ca0cbff6e 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -2659,6 +2659,8 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
  */
 pfmt++;
 tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
+if(!tmp)
+	return 1;
 strcpy(tmp, "%m/%d/%y");
 strcat(tmp, pfmt);
 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
@@ -2784,6 +2786,8 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 			case 'r':
 pfmt++;
 tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
+if(!tmp)
+	return 1;
 strcpy(tmp, "%I:%M:%S %p");
 strcat(tmp, pfmt);
 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
@@ -2792,6 +2796,8 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 			case 'R':
 pfmt++;
 tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
+if(!tmp)
+	return 1;
 strcpy(tmp, "%H:%M");
 strcat(tmp, pfmt);
 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
@@ -2837,6 +2843,8 @@ PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
 			case 'T':
 pfmt++;
 tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
+if(!tmp)
+	return 1;
 strcpy(tmp, "%H:%M:%S");
 strcat(tmp, pfmt);
 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);


Re: Returning non-terminated string in ECPG Informix-compatible function

2024-01-30 Thread Oleg Tselebrovskiy

Here's the code for bug reproduction:
#include 
#include 

EXEC SQL INCLUDE pgtypes_interval.h;
EXEC SQL INCLUDE ecpg_informix.h;

EXEC SQL BEGIN DECLARE SECTION;
char dirty_str[100] = "a__c_d_";
interval *interval_ptr;
EXEC SQL END DECLARE SECTION;

int main()
{
interval_ptr = (interval *) malloc(sizeof(interval));
interval_ptr->time = 1;
interval_ptr->month = 240;

printf("dirty_str contents before intoasc: %s\n", dirty_str);
intoasc(interval_ptr, dirty_str);
printf("dirty_str contents after intoasc: %s\n", dirty_str);
return 0;
}

And here's the output:

dirty_str contents before intoasc: 
a__c_d_
dirty_str contents after intoasc: @ 20 years 1 min 40 
secs_d_


I compiled it with following commands (provided for quicker 
reproduction):

/path/to/pgsql/bin/ecpg informix_bug_example.pgc
gcc -I/path/to/pgsql/include -c informix_bug_example.c
gcc -o informix_bug_example informix_bug_example.o -L/path/to/pgsql/lib 
-lecpg -lecpg_compat


I've also found at least one project that uses intoasc() in it - 
https://github.com/credativ/informix_fdw/


Oleg Tselebrovskiy, Postgres Pro