Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit42f94f5

Browse files
committed
Fix incautious handling of possibly-miscoded strings in client code.
An incorrectly-encoded multibyte character near the end of a stringcould cause various processing loops to run past the string'sterminating NUL, with results ranging from no detectable issue toa program crash, depending on what happens to be in the followingmemory.This isn't an issue in the server, because we take care to verifythe encoding of strings before doing any interesting processingon them. However, that lack of care leaked into client-side codewhich shouldn't assume that anyone has validated the encoding ofits input.Although this is certainly a bug worth fixing, the PG security teamelected not to regard it as a security issue, primarily becauseany untrusted text should be sanitized by PQescapeLiteral orthe like before being incorporated into a SQL or psql command.(If an app fails to do so, the same technique can be used tocause SQL injection, with probably much more dire consequencesthan a mere client-program crash.) Those functions were alreadymade proof against this class of problem, cfCVE-2006-2313.To fix, invent PQmblenBounded() which is like PQmblen() except itwon't return more than the number of bytes remaining in the string.In HEAD we can make this a new libpq function, as PQmblen() is.It seems imprudent to change libpq's API in stable branches though,so in the back branches define PQmblenBounded as a macro in the filesthat need it. (Note that just changing PQmblen's behavior would notbe a good idea; notably, it would completely break the escapingfunctions' defense against this exact problem. So we just want aversion for those callers that don't have any better way of handlingthis issue.)Per private report from houjingyi. Back-patch to all supported branches.
1 parent68a6d8a commit42f94f5

File tree

15 files changed

+68
-33
lines changed

15 files changed

+68
-33
lines changed

‎src/bin/psql/common.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,7 +1846,7 @@ skip_white_space(const char *query)
18461846

18471847
while (*query)
18481848
{
1849-
intmblen=PQmblen(query,pset.encoding);
1849+
intmblen=PQmblenBounded(query,pset.encoding);
18501850

18511851
/*
18521852
* Note: we assume the encoding is a superset of ASCII, so that for
@@ -1883,7 +1883,7 @@ skip_white_space(const char *query)
18831883
query++;
18841884
break;
18851885
}
1886-
query+=PQmblen(query,pset.encoding);
1886+
query+=PQmblenBounded(query,pset.encoding);
18871887
}
18881888
}
18891889
elseif (cnestlevel>0)
@@ -1918,7 +1918,7 @@ command_no_begin(const char *query)
19181918
*/
19191919
wordlen=0;
19201920
while (isalpha((unsignedchar)query[wordlen]))
1921-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
1921+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
19221922

19231923
/*
19241924
* Transaction control commands. These should include every keyword that
@@ -1949,7 +1949,7 @@ command_no_begin(const char *query)
19491949

19501950
wordlen=0;
19511951
while (isalpha((unsignedchar)query[wordlen]))
1952-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
1952+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
19531953

19541954
if (wordlen==11&&pg_strncasecmp(query,"transaction",11)==0)
19551955
return true;
@@ -1983,7 +1983,7 @@ command_no_begin(const char *query)
19831983

19841984
wordlen=0;
19851985
while (isalpha((unsignedchar)query[wordlen]))
1986-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
1986+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
19871987

19881988
if (wordlen==8&&pg_strncasecmp(query,"database",8)==0)
19891989
return true;
@@ -1999,7 +1999,7 @@ command_no_begin(const char *query)
19991999

20002000
wordlen=0;
20012001
while (isalpha((unsignedchar)query[wordlen]))
2002-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
2002+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
20032003
}
20042004

20052005
if (wordlen==5&&pg_strncasecmp(query,"index",5)==0)
@@ -2010,7 +2010,7 @@ command_no_begin(const char *query)
20102010

20112011
wordlen=0;
20122012
while (isalpha((unsignedchar)query[wordlen]))
2013-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
2013+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
20142014

20152015
if (wordlen==12&&pg_strncasecmp(query,"concurrently",12)==0)
20162016
return true;
@@ -2027,7 +2027,7 @@ command_no_begin(const char *query)
20272027

20282028
wordlen=0;
20292029
while (isalpha((unsignedchar)query[wordlen]))
2030-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
2030+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
20312031

20322032
/* ALTER SYSTEM isn't allowed in xacts */
20332033
if (wordlen==6&&pg_strncasecmp(query,"system",6)==0)
@@ -2050,7 +2050,7 @@ command_no_begin(const char *query)
20502050

20512051
wordlen=0;
20522052
while (isalpha((unsignedchar)query[wordlen]))
2053-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
2053+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
20542054

20552055
if (wordlen==8&&pg_strncasecmp(query,"database",8)==0)
20562056
return true;
@@ -2065,7 +2065,7 @@ command_no_begin(const char *query)
20652065
query=skip_white_space(query);
20662066
wordlen=0;
20672067
while (isalpha((unsignedchar)query[wordlen]))
2068-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
2068+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
20692069

20702070
/*
20712071
* REINDEX [ TABLE | INDEX ] CONCURRENTLY are not allowed in
@@ -2084,7 +2084,7 @@ command_no_begin(const char *query)
20842084

20852085
wordlen=0;
20862086
while (isalpha((unsignedchar)query[wordlen]))
2087-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
2087+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
20882088

20892089
if (wordlen==12&&pg_strncasecmp(query,"concurrently",12)==0)
20902090
return true;
@@ -2104,7 +2104,7 @@ command_no_begin(const char *query)
21042104

21052105
wordlen=0;
21062106
while (isalpha((unsignedchar)query[wordlen]))
2107-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
2107+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
21082108

21092109
if (wordlen==3&&pg_strncasecmp(query,"all",3)==0)
21102110
return true;
@@ -2140,7 +2140,7 @@ is_select_command(const char *query)
21402140
*/
21412141
wordlen=0;
21422142
while (isalpha((unsignedchar)query[wordlen]))
2143-
wordlen+=PQmblen(&query[wordlen],pset.encoding);
2143+
wordlen+=PQmblenBounded(&query[wordlen],pset.encoding);
21442144

21452145
if (wordlen==6&&pg_strncasecmp(query,"select",6)==0)
21462146
return true;

‎src/bin/psql/psqlscanslash.l

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ dequote_downcase_identifier(char *str, bool downcase, int encoding)
753753
{
754754
if (downcase && !inquotes)
755755
*cp =pg_tolower((unsignedchar) *cp);
756-
cp +=PQmblen(cp, encoding);
756+
cp +=PQmblenBounded(cp, encoding);
757757
}
758758
}
759759
}

‎src/bin/psql/stringutils.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ strtokx(const char *s,
143143
/* okay, we have a quoted token, now scan for the closer */
144144
charthisquote=*p++;
145145

146-
for (;*p;p+=PQmblen(p,encoding))
146+
for (;*p;p+=PQmblenBounded(p,encoding))
147147
{
148148
if (*p==escape&&p[1]!='\0')
149149
p++;/* process escaped anything */
@@ -262,7 +262,7 @@ strip_quotes(char *source, char quote, char escape, int encoding)
262262
elseif (c==escape&&src[1]!='\0')
263263
src++;/* process escaped character */
264264

265-
i=PQmblen(src,encoding);
265+
i=PQmblenBounded(src,encoding);
266266
while (i--)
267267
*dst++=*src++;
268268
}
@@ -324,7 +324,7 @@ quote_if_needed(const char *source, const char *entails_quote,
324324
elseif (strchr(entails_quote,c))
325325
need_quotes= true;
326326

327-
i=PQmblen(src,encoding);
327+
i=PQmblenBounded(src,encoding);
328328
while (i--)
329329
*dst++=*src++;
330330
}

‎src/bin/psql/tab-complete.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4397,7 +4397,7 @@ _complete_from_query(const char *simple_query,
43974397
while (*pstr)
43984398
{
43994399
char_length++;
4400-
pstr+=PQmblen(pstr,pset.encoding);
4400+
pstr+=PQmblenBounded(pstr,pset.encoding);
44014401
}
44024402

44034403
/* Free any prior result */

‎src/bin/scripts/common.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ splitTableColumnsSpec(const char *spec, int encoding,
5252
cp++;
5353
}
5454
else
55-
cp+=PQmblen(cp,encoding);
55+
cp+=PQmblenBounded(cp,encoding);
5656
}
5757
*table=pnstrdup(spec,cp-spec);
5858
*columns=cp;

‎src/common/jsonapi.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ json_lex_string(JsonLexContext *lex)
740740
ch= (ch*16)+ (*s-'A')+10;
741741
else
742742
{
743-
lex->token_terminator=s+pg_encoding_mblen(lex->input_encoding,s);
743+
lex->token_terminator=s+pg_encoding_mblen_bounded(lex->input_encoding,s);
744744
returnJSON_UNICODE_ESCAPE_FORMAT;
745745
}
746746
}
@@ -846,7 +846,7 @@ json_lex_string(JsonLexContext *lex)
846846
default:
847847
/* Not a valid string escape, so signal error. */
848848
lex->token_start=s;
849-
lex->token_terminator=s+pg_encoding_mblen(lex->input_encoding,s);
849+
lex->token_terminator=s+pg_encoding_mblen_bounded(lex->input_encoding,s);
850850
returnJSON_ESCAPING_INVALID;
851851
}
852852
}
@@ -860,7 +860,7 @@ json_lex_string(JsonLexContext *lex)
860860
* shown it's not a performance win.
861861
*/
862862
lex->token_start=s;
863-
lex->token_terminator=s+pg_encoding_mblen(lex->input_encoding,s);
863+
lex->token_terminator=s+pg_encoding_mblen_bounded(lex->input_encoding,s);
864864
returnJSON_ESCAPING_INVALID;
865865
}
866866

‎src/common/wchar.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,6 +1911,11 @@ const pg_wchar_tbl pg_wchar_table[] = {
19111911

19121912
/*
19131913
* Returns the byte length of a multibyte character.
1914+
*
1915+
* Caution: when dealing with text that is not certainly valid in the
1916+
* specified encoding, the result may exceed the actual remaining
1917+
* string length. Callers that are not prepared to deal with that
1918+
* should use pg_encoding_mblen_bounded() instead.
19141919
*/
19151920
int
19161921
pg_encoding_mblen(intencoding,constchar*mbstr)
@@ -1920,6 +1925,16 @@ pg_encoding_mblen(int encoding, const char *mbstr)
19201925
pg_wchar_table[PG_SQL_ASCII].mblen((constunsignedchar*)mbstr));
19211926
}
19221927

1928+
/*
1929+
* Returns the byte length of a multibyte character; but not more than
1930+
* the distance to end of string.
1931+
*/
1932+
int
1933+
pg_encoding_mblen_bounded(intencoding,constchar*mbstr)
1934+
{
1935+
returnstrnlen(mbstr,pg_encoding_mblen(encoding,mbstr));
1936+
}
1937+
19231938
/*
19241939
* Returns the display length of a multibyte character.
19251940
*/

‎src/fe_utils/print.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3636,6 +3636,9 @@ strlen_max_width(unsigned char *str, int *target_width, int encoding)
36363636
curr_width+=char_width;
36373637

36383638
str+=PQmblen((char*)str,encoding);
3639+
3640+
if (str>end)/* Don't overrun invalid string */
3641+
str=end;
36393642
}
36403643

36413644
*target_width=curr_width;

‎src/fe_utils/string_utils.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,12 +1072,9 @@ patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf,
10721072
appendPQExpBufferChar(curbuf,'\\');
10731073
elseif (ch=='['&&cp[1]==']')
10741074
appendPQExpBufferChar(curbuf,'\\');
1075-
i=PQmblen(cp,encoding);
1076-
while (i--&&*cp)
1077-
{
1078-
appendPQExpBufferChar(curbuf,*cp);
1079-
cp++;
1080-
}
1075+
i=PQmblenBounded(cp,encoding);
1076+
while (i--)
1077+
appendPQExpBufferChar(curbuf,*cp++);
10811078
}
10821079
}
10831080
appendPQExpBufferStr(curbuf,")$");

‎src/include/mb/pg_wchar.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ extern intpg_valid_server_encoding_id(int encoding);
574574
* earlier in this file are also available from libpgcommon.
575575
*/
576576
externintpg_encoding_mblen(intencoding,constchar*mbstr);
577+
externintpg_encoding_mblen_bounded(intencoding,constchar*mbstr);
577578
externintpg_encoding_dsplen(intencoding,constchar*mbstr);
578579
externintpg_encoding_verifymbchar(intencoding,constchar*mbstr,intlen);
579580
externintpg_encoding_verifymbstr(intencoding,constchar*mbstr,intlen);

‎src/interfaces/libpq/exports.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,4 @@ PQexitPipelineMode 181
184184
PQpipelineSync 182
185185
PQpipelineStatus 183
186186
PQtraceSetFlags 184
187+
PQmblenBounded 185

‎src/interfaces/libpq/fe-misc.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,8 +1180,13 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time)
11801180
*/
11811181

11821182
/*
1183-
*returns the byte length of the character beginning at s, using the
1183+
*Returns the byte length of the character beginning at s, using the
11841184
* specified encoding.
1185+
*
1186+
* Caution: when dealing with text that is not certainly valid in the
1187+
* specified encoding, the result may exceed the actual remaining
1188+
* string length. Callers that are not prepared to deal with that
1189+
* should use PQmblenBounded() instead.
11851190
*/
11861191
int
11871192
PQmblen(constchar*s,intencoding)
@@ -1190,7 +1195,17 @@ PQmblen(const char *s, int encoding)
11901195
}
11911196

11921197
/*
1193-
* returns the display length of the character beginning at s, using the
1198+
* Returns the byte length of the character beginning at s, using the
1199+
* specified encoding; but not more than the distance to end of string.
1200+
*/
1201+
int
1202+
PQmblenBounded(constchar*s,intencoding)
1203+
{
1204+
returnstrnlen(s,pg_encoding_mblen(encoding,s));
1205+
}
1206+
1207+
/*
1208+
* Returns the display length of the character beginning at s, using the
11941209
* specified encoding.
11951210
*/
11961211
int

‎src/interfaces/libpq/fe-print.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ do_field(const PQprintOpt *po, const PGresult *res,
365365
/* Detect whether field contains non-numeric data */
366366
charch='0';
367367

368-
for (p=pval;*p;p+=PQmblen(p,res->client_encoding))
368+
for (p=pval;*p;p+=PQmblenBounded(p,res->client_encoding))
369369
{
370370
ch=*p;
371371
if (!((ch >='0'&&ch <='9')||

‎src/interfaces/libpq/fe-protocol3.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,7 @@ reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding)
12961296
if (w <=0)
12971297
w=1;
12981298
scroffset+=w;
1299-
qoffset+=pg_encoding_mblen(encoding,&wquery[qoffset]);
1299+
qoffset+=PQmblenBounded(&wquery[qoffset],encoding);
13001300
}
13011301
else
13021302
{
@@ -1364,7 +1364,7 @@ reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding)
13641364
* width.
13651365
*/
13661366
scroffset=0;
1367-
for (;i<msg->len;i+=pg_encoding_mblen(encoding,&msg->data[i]))
1367+
for (;i<msg->len;i+=PQmblenBounded(&msg->data[i],encoding))
13681368
{
13691369
intw=pg_encoding_dsplen(encoding,&msg->data[i]);
13701370

‎src/interfaces/libpq/libpq-fe.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,9 @@ extern intPQlibVersion(void);
625625
/* Determine length of multibyte encoded char at *s */
626626
externintPQmblen(constchar*s,intencoding);
627627

628+
/* Same, but not more than the distance to the end of string s */
629+
externintPQmblenBounded(constchar*s,intencoding);
630+
628631
/* Determine display length of multibyte encoded char at *s */
629632
externintPQdsplen(constchar*s,intencoding);
630633

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp