1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-39210 ExtractValue/UpdateXML crash.

XML parser fixed to handle <! and <? correctly.
This commit is contained in:
Alexey Botchkov
2026-04-01 02:02:39 +04:00
parent f12ff3600f
commit 8d0feb2072
4 changed files with 284 additions and 48 deletions

View File

@@ -68,6 +68,10 @@ typedef struct xml_stack_st
int (*enter)(struct xml_stack_st *st,const char *val, size_t len);
int (*value)(struct xml_stack_st *st,const char *val, size_t len);
int (*leave_xml)(struct xml_stack_st *st,const char *val, size_t len);
int (*processing_instruction)(struct xml_stack_st *st,
const char *target, size_t target_len,
const char *value, size_t value_len);
} MY_XML_PARSER;
void my_xml_parser_create(MY_XML_PARSER *st);
@@ -83,6 +87,8 @@ void my_xml_set_enter_handler(MY_XML_PARSER *st, int (*)(MY_XML_PARSER *,
void my_xml_set_leave_handler(MY_XML_PARSER *st, int (*)(MY_XML_PARSER *,
const char *,
size_t len));
void my_xml_set_processing_instruction_handler(MY_XML_PARSER *st,
int (*)(MY_XML_PARSER *, const char *, size_t, const char *, size_t));
void my_xml_set_user_data(MY_XML_PARSER *st, void *);
size_t my_xml_error_pos(MY_XML_PARSER *st);

View File

@@ -1322,3 +1322,95 @@ foo
#
# Start of 10.5 tests
#
#
# ExtractValue/UpdateXML crash
#
SELECT ExtractValue('<!a/><!b/>', '/a');
ExtractValue('<!a/><!b/>', '/a')
NULL
Warnings:
Warning 1525 Incorrect XML value: 'parse error at line 1 pos 2: unknown token unexpected (ident or '/' wanted)'
SELECT UpdateXML('<!a/><!b/>', '/a', '<new/>');
UpdateXML('<!a/><!b/>', '/a', '<new/>')
NULL
Warnings:
Warning 1525 Incorrect XML value: 'parse error at line 1 pos 2: unknown token unexpected (ident or '/' wanted)'
SELECT ExtractValue('<!a/><!b/><!c/>', '/a');
ExtractValue('<!a/><!b/><!c/>', '/a')
NULL
Warnings:
Warning 1525 Incorrect XML value: 'parse error at line 1 pos 2: unknown token unexpected (ident or '/' wanted)'
SELECT ExtractValue('<?a/?><?b/?>', '/a');
ExtractValue('<?a/?><?b/?>', '/a')
NULL
Warnings:
Warning 1525 Incorrect XML value: 'parse error at line 1 pos 5: unknown token unexpected (ident wanted)'
SELECT ExtractValue('<!a/><?b/?>', '/a');
ExtractValue('<!a/><?b/?>', '/a')
NULL
Warnings:
Warning 1525 Incorrect XML value: 'parse error at line 1 pos 2: unknown token unexpected (ident or '/' wanted)'
SET @xml='<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>See you this weekend!</body>
</note>
';
SELECT ExtractValue(@xml, '/note/from');
ExtractValue(@xml, '/note/from')
Jani
set @xml= '<?xml version="1.0"?>
<employee>
<dt1>--01--+01:00</dt1>
<dt2>--12--+01:00</dt2>
<?dt3 123456 ?>
<dt3>--02--</dt3>
<dt4>--03--Z</dt4>
<?dt4 123456 ?>
</employee>
<?employee ABSDEV !@#$ ?>';
SELECT ExtractValue(@xml, '/employee/dt3');
ExtractValue(@xml, '/employee/dt3')
--02--
set @xml= '<? xml version="1.0"?>
<employee>
<dt1>--01--+01:00</dt1>
<dt2>--12--+01:00</dt2>
<?dt3 123456 ?>
<dt3>--02--</dt3>
<dt4>--03--Z</dt4>
<?dt4 123456 ?>
</employee>
<? employee ABSDEV !@#$ ?>';
SELECT ExtractValue(@xml, '/employee/dt3');
ExtractValue(@xml, '/employee/dt3')
NULL
Warnings:
Warning 1525 Incorrect XML value: 'parse error at line 1 pos 21: unknown token unexpected ('>' wanted)'
set @xml= '<?xml version="1.0"?>
<employee>
<dt1>--01--+01:00</dt1>
<dt2>--12--+01:00</dt2>
<?dt3 123456 ?>
<dt3>--02--</dt3>
<dt4>--03--Z</dt4>
<?dt4 123456 ? >
</employee>
<?employee ABSDEV !@#$ ?>';
SELECT ExtractValue(@xml, '/employee/dt3');
ExtractValue(@xml, '/employee/dt3')
NULL
Warnings:
Warning 1525 Incorrect XML value: 'parse error at line 10 pos 27: unexpected END-OF-INPUT'
#
# Start of 10.6 tests
#

View File

@@ -824,3 +824,76 @@ SELECT 'foo' AS f UNION SELECT BINARY( UpdateXML('<a></a>', '/a', '<b></b>')) AS
--echo #
--echo # Start of 10.5 tests
--echo #
--echo #
--echo # ExtractValue/UpdateXML crash
--echo #
#view protocol generates additional warning
--disable_view_protocol
SELECT ExtractValue('<!a/><!b/>', '/a');
SELECT UpdateXML('<!a/><!b/>', '/a', '<new/>');
SELECT ExtractValue('<!a/><!b/><!c/>', '/a');
SELECT ExtractValue('<?a/?><?b/?>', '/a');
SELECT ExtractValue('<!a/><?b/?>', '/a');
--enable_view_protocol
SET @xml='<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>See you this weekend!</body>
</note>
';
SELECT ExtractValue(@xml, '/note/from');
set @xml= '<?xml version="1.0"?>
<employee>
<dt1>--01--+01:00</dt1>
<dt2>--12--+01:00</dt2>
<?dt3 123456 ?>
<dt3>--02--</dt3>
<dt4>--03--Z</dt4>
<?dt4 123456 ?>
</employee>
<?employee ABSDEV !@#$ ?>';
SELECT ExtractValue(@xml, '/employee/dt3');
set @xml= '<? xml version="1.0"?>
<employee>
<dt1>--01--+01:00</dt1>
<dt2>--12--+01:00</dt2>
<?dt3 123456 ?>
<dt3>--02--</dt3>
<dt4>--03--Z</dt4>
<?dt4 123456 ?>
</employee>
<? employee ABSDEV !@#$ ?>';
SELECT ExtractValue(@xml, '/employee/dt3');
set @xml= '<?xml version="1.0"?>
<employee>
<dt1>--01--+01:00</dt1>
<dt2>--12--+01:00</dt2>
<?dt3 123456 ?>
<dt3>--02--</dt3>
<dt4>--03--Z</dt4>
<?dt4 123456 ? >
</employee>
<?employee ABSDEV !@#$ ?>';
SELECT ExtractValue(@xml, '/employee/dt3');
--echo #
--echo # Start of 10.6 tests
--echo #

View File

@@ -30,9 +30,9 @@
#define MY_XML_SLASH '/'
#define MY_XML_COMMENT 'C'
#define MY_XML_TEXT 'T'
#define MY_XML_QUESTION '?'
#define MY_XML_EXCLAM '!'
#define MY_XML_CDATA 'D'
#define MY_XML_PROCESSING_INSTRUCTION '?'
#define MY_XML_CDATA 'A'
#define MY_XML_DTD 'D'
typedef struct xml_attr_st
{
@@ -95,8 +95,8 @@ static const char *lex2str(int lex)
case MY_XML_SLASH: return "'/'";
case MY_XML_COMMENT: return "COMMENT";
case MY_XML_TEXT: return "TEXT";
case MY_XML_QUESTION: return "'?'";
case MY_XML_EXCLAM: return "'!'";
case MY_XML_PROCESSING_INSTRUCTION: return "'?'";
case MY_XML_DTD: return "DTD";
}
return "unknown token";
}
@@ -145,6 +145,43 @@ static int my_xml_scan(MY_XML_PARSER *p,MY_XML_ATTR *a)
a->end=p->cur;
lex=MY_XML_COMMENT;
}
else if (!my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("<?")))
{
lex= MY_XML_UNKNOWN;
p->cur+= 2;
a->beg= p->cur;
if (my_xml_is_id0(p->cur[0]))
{
int termination_expected= FALSE;
p->cur++;
while (p->cur < p->end && my_xml_is_id1(p->cur[0]))
p->cur++;
a->end=p->cur;
/*
After the target name the syntax requires the
terminating '?>' or some spaces then an
arbitrary string to the '>?'.
*/
if (p->cur < p->end && !my_xml_is_space(p->cur[0]))
termination_expected= TRUE;
while (p->cur < p->end)
{
if (my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("?>")) == 0)
{
lex= MY_XML_PROCESSING_INSTRUCTION;
p->cur+= 2;
break;
}
else if (termination_expected)
break;
p->cur++;
}
}
}
else if (!my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("<![CDATA[")))
{
p->cur+= 9;
@@ -159,7 +196,56 @@ static int my_xml_scan(MY_XML_PARSER *p,MY_XML_ATTR *a)
}
lex= MY_XML_CDATA;
}
else if (strchr("?=/<>!",p->cur[0]))
else if (!my_xml_parser_prefix_cmp(p, C_STRING_WITH_LEN("<!DOCTYPE")))
{
/* skip <!DOCTYPE part. TODO: add parser for the DTD */
int in_quote= 0;
int level= 1;
p->cur+= 9;
lex= MY_XML_UNKNOWN;
for (; p->cur < p->end; p->cur++)
{
if (p->cur[0] == '"' || p->cur[0] == '\'')
{
if (!in_quote)
in_quote= p->cur[0];
else
{
if (p->cur[0] == in_quote)
in_quote= 0;
}
}
if (in_quote)
continue;
switch (p->cur[0])
{
case MY_XML_LT:
{
level++;
break;
}
case MY_XML_GT:
{
level--;
if (level == 0)
{
lex= MY_XML_DTD;
p->cur++;
goto dtd_end;
}
}
default:;
}
}
dtd_end:
a->end= p->cur;
}
else if (strchr("=/<>",p->cur[0]))
{
p->cur++;
a->end=p->cur;
@@ -340,14 +426,23 @@ int my_xml_parse(MY_XML_PARSER *p,const char *str, size_t len)
if (p->cur[0] == '<')
{
int lex;
int question=0;
int exclam=0;
lex=my_xml_scan(p,&a);
if (MY_XML_COMMENT == lex)
if (MY_XML_COMMENT == lex ||
MY_XML_DTD == lex)
continue;
if (MY_XML_PROCESSING_INSTRUCTION == lex)
{
if (p->processing_instruction &&
MY_XML_OK != p->processing_instruction(p,
a.beg, (size_t) (a.end-a.beg),
a.end, (size_t) (p->cur - a.end - 2)))
return MY_XML_ERROR;
continue;
}
if (lex == MY_XML_CDATA)
{
a.beg+= 9;
@@ -371,16 +466,6 @@ int my_xml_parse(MY_XML_PARSER *p,const char *str, size_t len)
goto gt;
}
if (MY_XML_EXCLAM == lex)
{
lex=my_xml_scan(p,&a);
exclam=1;
}
else if (MY_XML_QUESTION == lex)
{
lex=my_xml_scan(p,&a);
question=1;
}
if (MY_XML_IDENT == lex)
{
@@ -395,8 +480,7 @@ int my_xml_parse(MY_XML_PARSER *p,const char *str, size_t len)
return MY_XML_ERROR;
}
while ((MY_XML_IDENT == (lex=my_xml_scan(p,&a))) ||
((MY_XML_STRING == lex && exclam)))
while (MY_XML_IDENT == (lex=my_xml_scan(p,&a)))
{
MY_XML_ATTR b;
if (MY_XML_EQ == (lex=my_xml_scan(p,&b)))
@@ -424,15 +508,6 @@ int my_xml_parse(MY_XML_PARSER *p,const char *str, size_t len)
(MY_XML_OK != my_xml_leave(p,a.beg,(size_t) (a.end-a.beg))))
return MY_XML_ERROR;
}
else if ((MY_XML_STRING == lex) && exclam)
{
/*
We are in <!DOCTYPE>, e.g.
<!DOCTYPE name SYSTEM "SystemLiteral">
<!DOCTYPE name PUBLIC "PublidLiteral" "SystemLiteral">
Just skip "SystemLiteral" and "PublicidLiteral"
*/
}
else
break;
}
@@ -445,24 +520,6 @@ int my_xml_parse(MY_XML_PARSER *p,const char *str, size_t len)
}
gt:
if (question)
{
if (lex != MY_XML_QUESTION)
{
snprintf(p->errstr,sizeof(p->errstr),"%s unexpected ('?' wanted)",lex2str(lex));
return MY_XML_ERROR;
}
if (MY_XML_OK != my_xml_leave(p,NULL,0))
return MY_XML_ERROR;
lex=my_xml_scan(p,&a);
}
if (exclam)
{
if (MY_XML_OK != my_xml_leave(p,NULL,0))
return MY_XML_ERROR;
}
if (lex != MY_XML_GT)
{
snprintf(p->errstr,sizeof(p->errstr),"%s unexpected ('>' wanted)",lex2str(lex));
@@ -537,6 +594,14 @@ void my_xml_set_leave_handler(MY_XML_PARSER *p,
}
void my_xml_set_processing_instruction_handler(MY_XML_PARSER *p,
int (*p_i)(MY_XML_PARSER *, const char *, size_t,
const char *, size_t))
{
p->processing_instruction= p_i;
}
void my_xml_set_user_data(MY_XML_PARSER *p, void *user_data)
{
p->user_data=user_data;