postgres.git / summary / log / commit / refs
commit 5737110b60afffb90d1178acfaa0fe6881c2f5a9
Author: Michael Paquier <michael@paquier.xyz>
Commit: Noah Misch <noah@leadboat.com>
Date: Mon Aug 10 13:38:30 2026 +0000
ecpg: Fix out-of-bound writes due to processing of invalid bytea data
ECPG assumes that any bytea data it receives from a backend starts with
'\x' as its first two bytes, but a check was missed to enforce that. A
rogue server sending some garbage bytea data would be able to crash a
client, resulting in a client-side DoS, in the most common cases.
Reported-by: ylwangtju <ylwangtju@qq.com>
Backpatch-through: 14
Security: CVE-2026-16241
src/interfaces/ecpg/ecpglib/data.c | 7 ++++++
src/interfaces/ecpg/ecpglib/error.c | 7 ++++++
src/interfaces/ecpg/include/ecpgerrno.h | 1 +
src/interfaces/ecpg/test/expected/sql-bytea.c | 27 ++++++++++++++++++----
src/interfaces/ecpg/test/expected/sql-bytea.stderr | 24 ++++++++++++++++++-
src/interfaces/ecpg/test/sql/bytea.pgc | 5 ++++
6 files changed, 66 insertions(+), 5 deletions(-)
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index 40cbfc0d078..4b0459094bd 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -529,6 +529,13 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
src_size,
dec_size;
+ if (size < 2 || pval[0] != '\\' || pval[1] != 'x')
+ {
+ ecpg_raise(lineno, ECPG_BYTEA_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+
dst_size = ecpg_hex_enc_len(varcharsize);
src_size = size - 2; /* exclude backslash + 'x' */
dec_size = src_size < dst_size ? src_size : dst_size;
diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c
index 26fdcdb69e9..fba8b4468dd 100644
--- a/src/interfaces/ecpg/ecpglib/error.c
+++ b/src/interfaces/ecpg/ecpglib/error.c
@@ -130,6 +130,13 @@ ecpg_raise(int line, int code, const char *sqlstate, const char *str)
ecpg_gettext("inserting an array of variables is not supported on line %d"), line);
break;
+ case ECPG_BYTEA_FORMAT:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("invalid input syntax for type bytea: \"%s\", on line %d"), str, line);
+ break;
+
case ECPG_NO_CONN:
snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
/*------
diff --git a/src/interfaces/ecpg/include/ecpgerrno.h b/src/interfaces/ecpg/include/ecpgerrno.h
index c4bc526463d..d8216b365b6 100644
--- a/src/interfaces/ecpg/include/ecpgerrno.h
+++ b/src/interfaces/ecpg/include/ecpgerrno.h
@@ -32,6 +32,7 @@
#define ECPG_NO_ARRAY -214
#define ECPG_DATA_NOT_ARRAY -215
#define ECPG_ARRAY_INSERT -216
+#define ECPG_BYTEA_FORMAT -217
#define ECPG_NO_CONN -220
#define ECPG_NOT_CONN -221
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.c b/src/interfaces/ecpg/test/expected/sql-bytea.c
index 8338c6008dd..901594a6f63 100644
--- a/src/interfaces/ecpg/test/expected/sql-bytea.c
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.c
@@ -356,17 +356,36 @@ if (sqlca.sqlcode < 0) sqlprint();}
if (sqlca.sqlcode < 0) sqlprint();}
#line 115 "bytea.pgc"
+
+ /* Test for invalid bytea format */
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select '' :: text", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 118 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 118 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select '\\\\a1234' :: text", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 119 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 119 "bytea.pgc"
+
+
{ ECPGtrans(__LINE__, NULL, "commit");
-#line 116 "bytea.pgc"
+#line 121 "bytea.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 116 "bytea.pgc"
+#line 121 "bytea.pgc"
{ ECPGdisconnect(__LINE__, "CURRENT");
-#line 117 "bytea.pgc"
+#line 122 "bytea.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
-#line 117 "bytea.pgc"
+#line 122 "bytea.pgc"
return 0;
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.stderr b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
index cb828a76020..58589474856 100644
--- a/src/interfaces/ecpg/test/expected/sql-bytea.stderr
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
@@ -181,7 +181,29 @@ SQL error: invalid statement name "cursor1" on line 82
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_process_output on line 115: OK: DROP TABLE
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 116: action "commit"; connection "ecpg1_regression"
+[NO_PID]: ecpg_execute on line 118: query: select '' :: text; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 118: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 118: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 118: RESULT: offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -217 on line 118: invalid input syntax for type bytea: "", on line 118
+[NO_PID]: sqlca: code: -217, state: 42804
+SQL error: invalid input syntax for type bytea: "", on line 118
+[NO_PID]: ecpg_execute on line 119: query: select '\\a1234' :: text; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 119: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 119: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 119: RESULT: \\a1234 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -217 on line 119: invalid input syntax for type bytea: "\\a1234", on line 119
+[NO_PID]: sqlca: code: -217, state: 42804
+SQL error: invalid input syntax for type bytea: "\\a1234", on line 119
+[NO_PID]: ECPGtrans on line 121: action "commit"; connection "ecpg1_regression"
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: deallocate_one on line 0: name sel_stmt
[NO_PID]: sqlca: code: 0, state: 00000
diff --git a/src/interfaces/ecpg/test/sql/bytea.pgc b/src/interfaces/ecpg/test/sql/bytea.pgc
index e8741231194..da9152758a2 100644
--- a/src/interfaces/ecpg/test/sql/bytea.pgc
+++ b/src/interfaces/ecpg/test/sql/bytea.pgc
@@ -113,6 +113,11 @@ while (0)
dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
exec sql drop table test;
+
+ /* Test for invalid bytea format */
+ exec sql select ''::text into :recv_buf[0];
+ exec sql select '\\a1234'::text into :recv_buf[0];
+
exec sql commit;
exec sql disconnect;
[parent: 1eb0d53804a4]