Commit eb0a7735 authored by Andrew Dunstan's avatar Andrew Dunstan

Perform post-escaping encoding validity checks on SQL literals and COPY input

so that invalidly encoded data cannot enter the database by these means.
parent 22b613eb
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.286 2007/09/07 20:59:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.287 2007/09/12 20:49:27 adunstan Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2685,6 +2685,7 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals) ...@@ -2685,6 +2685,7 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals)
char *start_ptr; char *start_ptr;
char *end_ptr; char *end_ptr;
int input_len; int input_len;
bool saw_high_bit = false;
/* Make sure space remains in fieldvals[] */ /* Make sure space remains in fieldvals[] */
if (fieldno >= maxfields) if (fieldno >= maxfields)
...@@ -2749,6 +2750,8 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals) ...@@ -2749,6 +2750,8 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals)
} }
} }
c = val & 0377; c = val & 0377;
if (IS_HIGHBIT_SET(c))
saw_high_bit = true;
} }
break; break;
case 'x': case 'x':
...@@ -2772,6 +2775,8 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals) ...@@ -2772,6 +2775,8 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals)
} }
} }
c = val & 0xff; c = val & 0xff;
if (IS_HIGHBIT_SET(c))
saw_high_bit = true;
} }
} }
break; break;
...@@ -2799,7 +2804,7 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals) ...@@ -2799,7 +2804,7 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals)
* literally * literally
*/ */
} }
} }
/* Add c to output string */ /* Add c to output string */
*output_ptr++ = c; *output_ptr++ = c;
...@@ -2808,6 +2813,16 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals) ...@@ -2808,6 +2813,16 @@ CopyReadAttributesText(CopyState cstate, int maxfields, char **fieldvals)
/* Terminate attribute value in output area */ /* Terminate attribute value in output area */
*output_ptr++ = '\0'; *output_ptr++ = '\0';
/* If we de-escaped a char with the high bit set, make sure
* we still have valid data for the db encoding. Avoid calling strlen
* here for the sake of efficiency.
*/
if (saw_high_bit)
{
char *fld = fieldvals[fieldno];
pg_verifymbstr(fld, output_ptr - (fld + 1), false);
}
/* Check whether raw input matched null marker */ /* Check whether raw input matched null marker */
input_len = end_ptr - start_ptr; input_len = end_ptr - start_ptr;
if (input_len == cstate->null_print_len && if (input_len == cstate->null_print_len &&
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/scan.l,v 1.140 2007/08/12 20:18:06 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/scan.l,v 1.141 2007/09/12 20:49:27 adunstan Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -60,6 +60,7 @@ bool escape_string_warning = true; ...@@ -60,6 +60,7 @@ bool escape_string_warning = true;
bool standard_conforming_strings = false; bool standard_conforming_strings = false;
static bool warn_on_first_escape; static bool warn_on_first_escape;
static bool saw_high_bit = false;
/* /*
* literalbuf is used to accumulate literal values when multiple rules * literalbuf is used to accumulate literal values when multiple rules
...@@ -426,6 +427,7 @@ other . ...@@ -426,6 +427,7 @@ other .
{xqstart} { {xqstart} {
warn_on_first_escape = true; warn_on_first_escape = true;
saw_high_bit = false;
SET_YYLLOC(); SET_YYLLOC();
if (standard_conforming_strings) if (standard_conforming_strings)
BEGIN(xq); BEGIN(xq);
...@@ -435,6 +437,7 @@ other . ...@@ -435,6 +437,7 @@ other .
} }
{xestart} { {xestart} {
warn_on_first_escape = false; warn_on_first_escape = false;
saw_high_bit = false;
SET_YYLLOC(); SET_YYLLOC();
BEGIN(xe); BEGIN(xe);
startlit(); startlit();
...@@ -443,6 +446,11 @@ other . ...@@ -443,6 +446,11 @@ other .
<xq,xe>{quotefail} { <xq,xe>{quotefail} {
yyless(1); yyless(1);
BEGIN(INITIAL); BEGIN(INITIAL);
/* check that the data remains valid if it might have been
* made invalid by unescaping any chars.
*/
if (saw_high_bit)
pg_verifymbstr(literalbuf, literallen, false);
yylval.str = litbufdup(); yylval.str = litbufdup();
return SCONST; return SCONST;
} }
...@@ -475,12 +483,16 @@ other . ...@@ -475,12 +483,16 @@ other .
check_escape_warning(); check_escape_warning();
addlitchar(c); addlitchar(c);
if (IS_HIGHBIT_SET(c))
saw_high_bit = true;
} }
<xe>{xehexesc} { <xe>{xehexesc} {
unsigned char c = strtoul(yytext+2, NULL, 16); unsigned char c = strtoul(yytext+2, NULL, 16);
check_escape_warning(); check_escape_warning();
addlitchar(c); addlitchar(c);
if (IS_HIGHBIT_SET(c))
saw_high_bit = true;
} }
<xq,xe>{quotecontinue} { <xq,xe>{quotecontinue} {
/* ignore */ /* ignore */
...@@ -892,6 +904,14 @@ litbufdup(void) ...@@ -892,6 +904,14 @@ litbufdup(void)
static unsigned char static unsigned char
unescape_single_char(unsigned char c) unescape_single_char(unsigned char c)
{ {
/* Normally we wouldn't expect to see \n where n has its high bit set
* but we set the flag to check the string if we do get it, so
* that this doesn't become a way of getting around the coding validity
* checks.
*/
if (IS_HIGHBIT_SET(c))
saw_high_bit = true;
switch (c) switch (c)
{ {
case 'b': case 'b':
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment