diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 8169672a285..d6909cf1c51 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -28,6 +28,8 @@ #include "fe_utils/mbprint.h" +#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) + static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec); static bool command_no_begin(const char *query); static bool is_select_command(const char *query); @@ -1700,7 +1702,7 @@ skip_white_space(const char *query) while (*query) { - int mblen = PQmblen(query, pset.encoding); + int mblen = PQmblenBounded(query, pset.encoding); /* * Note: we assume the encoding is a superset of ASCII, so that for @@ -1737,7 +1739,7 @@ skip_white_space(const char *query) query++; break; } - query += PQmblen(query, pset.encoding); + query += PQmblenBounded(query, pset.encoding); } } else if (cnestlevel > 0) @@ -1772,7 +1774,7 @@ command_no_begin(const char *query) */ wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); /* * Transaction control commands. These should include every keyword that @@ -1803,7 +1805,7 @@ command_no_begin(const char *query) wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); if (wordlen == 11 && pg_strncasecmp(query, "transaction", 11) == 0) return true; @@ -1837,7 +1839,7 @@ command_no_begin(const char *query) wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0) return true; @@ -1853,7 +1855,7 @@ command_no_begin(const char *query) wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); } if (wordlen == 5 && pg_strncasecmp(query, "index", 5) == 0) @@ -1864,7 +1866,7 @@ command_no_begin(const char *query) wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); if (wordlen == 12 && pg_strncasecmp(query, "concurrently", 12) == 0) return true; @@ -1881,7 +1883,7 @@ command_no_begin(const char *query) wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); /* ALTER SYSTEM isn't allowed in xacts */ if (wordlen == 6 && pg_strncasecmp(query, "system", 6) == 0) @@ -1904,7 +1906,7 @@ command_no_begin(const char *query) wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0) return true; @@ -1922,7 +1924,7 @@ command_no_begin(const char *query) wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); if (wordlen == 12 && pg_strncasecmp(query, "concurrently", 12) == 0) return true; @@ -1942,7 +1944,7 @@ command_no_begin(const char *query) wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); if (wordlen == 3 && pg_strncasecmp(query, "all", 3) == 0) return true; @@ -1978,7 +1980,7 @@ is_select_command(const char *query) */ wordlen = 0; while (isalpha((unsigned char) query[wordlen])) - wordlen += PQmblen(&query[wordlen], pset.encoding); + wordlen += PQmblenBounded(&query[wordlen], pset.encoding); if (wordlen == 6 && pg_strncasecmp(query, "select", 6) == 0) return true; diff --git a/src/bin/psql/psqlscanslash.l b/src/bin/psql/psqlscanslash.l index afa1d3be96c..2a86fa4e33a 100644 --- a/src/bin/psql/psqlscanslash.l +++ b/src/bin/psql/psqlscanslash.l @@ -26,6 +26,8 @@ %{ #include "fe_utils/psqlscan_int.h" +#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) + /* * We must have a typedef YYSTYPE for yylex's first argument, but this lexer * doesn't presently make use of that argument, so just declare it as int. @@ -681,7 +683,7 @@ dequote_downcase_identifier(char *str, bool downcase, int encoding) { if (downcase && !inquotes) *cp = pg_tolower((unsigned char) *cp); - cp += PQmblen(cp, encoding); + cp += PQmblenBounded(cp, encoding); } } } diff --git a/src/bin/psql/stringutils.c b/src/bin/psql/stringutils.c index 9c6034cf824..a8dcee2288f 100644 --- a/src/bin/psql/stringutils.c +++ b/src/bin/psql/stringutils.c @@ -12,6 +12,8 @@ #include "common.h" #include "stringutils.h" +#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) + /* * Replacement for strtok() (a.k.a. poor man's flex) @@ -143,7 +145,7 @@ strtokx(const char *s, /* okay, we have a quoted token, now scan for the closer */ char thisquote = *p++; - for (; *p; p += PQmblen(p, encoding)) + for (; *p; p += PQmblenBounded(p, encoding)) { if (*p == escape && p[1] != '\0') p++; /* process escaped anything */ @@ -262,7 +264,7 @@ strip_quotes(char *source, char quote, char escape, int encoding) else if (c == escape && src[1] != '\0') src++; /* process escaped character */ - i = PQmblen(src, encoding); + i = PQmblenBounded(src, encoding); while (i--) *dst++ = *src++; } @@ -322,7 +324,7 @@ quote_if_needed(const char *source, const char *entails_quote, else if (strchr(entails_quote, c)) need_quotes = true; - i = PQmblen(src, encoding); + i = PQmblenBounded(src, encoding); while (i--) *dst++ = *src++; } diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 272fca1d0ba..aaa57eedec1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -57,6 +57,8 @@ extern char *filename_completion_function(); #define completion_matches rl_completion_matches #endif +#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) + /* word break characters */ #define WORD_BREAKS "\t\n@$><=;|&{() " @@ -3308,7 +3310,7 @@ _complete_from_query(int is_schema_query, const char *text, int state) while (*pstr) { char_length++; - pstr += PQmblen(pstr, pset.encoding); + pstr += PQmblenBounded(pstr, pset.encoding); } /* Free any prior result */ diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c index 64bf1f42270..49d7ebe8379 100644 --- a/src/bin/scripts/common.c +++ b/src/bin/scripts/common.c @@ -21,6 +21,8 @@ #include "fe_utils/connect.h" #include "fe_utils/string_utils.h" +#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) + static PGcancel *volatile cancelConn = NULL; bool CancelRequested = false; @@ -304,7 +306,7 @@ split_table_columns_spec(const char *spec, int encoding, cp++; } else - cp += PQmblen(cp, encoding); + cp += PQmblenBounded(cp, encoding); } *table = pg_strdup(spec); (*table)[cp - spec] = '\0'; /* no strndup */ diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c index 5b56d31faa3..6f773b93b29 100644 --- a/src/fe_utils/print.c +++ b/src/fe_utils/print.c @@ -3527,6 +3527,9 @@ strlen_max_width(unsigned char *str, int *target_width, int encoding) curr_width += char_width; str += PQmblen((char *) str, encoding); + + if (str > end) /* Don't overrun invalid string */ + str = end; } *target_width = curr_width; diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c index e596a514087..4988778f814 100644 --- a/src/interfaces/libpq/fe-print.c +++ b/src/interfaces/libpq/fe-print.c @@ -36,6 +36,7 @@ #include "libpq-fe.h" #include "libpq-int.h" +#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) static void do_field(const PQprintOpt *po, const PGresult *res, const int i, const int j, const int fs_len, @@ -358,7 +359,7 @@ do_field(const PQprintOpt *po, const PGresult *res, /* Detect whether field contains non-numeric data */ char ch = '0'; - for (p = pval; *p; p += PQmblen(p, res->client_encoding)) + for (p = pval; *p; p += PQmblenBounded(p, res->client_encoding)) { ch = *p; if (!((ch >= '0' && ch <= '9') || diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 013274f6111..8e399d597c1 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -42,6 +42,8 @@ ((id) == 'T' || (id) == 'D' || (id) == 'd' || (id) == 'V' || \ (id) == 'E' || (id) == 'N' || (id) == 'A') +#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) + static void handleSyncLoss(PGconn *conn, char id, int msgLength); static int getRowDescriptions(PGconn *conn, int msgLength); @@ -1228,7 +1230,7 @@ reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding) if (w <= 0) w = 1; scroffset += w; - qoffset += pg_encoding_mblen(encoding, &wquery[qoffset]); + qoffset += PQmblenBounded(&wquery[qoffset], encoding); } else { @@ -1296,7 +1298,7 @@ reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding) * width. */ scroffset = 0; - for (; i < msg->len; i += pg_encoding_mblen(encoding, &msg->data[i])) + for (; i < msg->len; i += PQmblenBounded(&msg->data[i], encoding)) { int w = pg_encoding_dsplen(encoding, &msg->data[i]);