mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2024-12-23 17:33:50 +03:00
Check for invalid redeclarations of predefined entities
Implement section "4.6 Predefined Entities" of the XML 1.0 spec and check whether redeclarations of predefined entities match the original definitions. Note that some test cases declared <!ENTITY lt "<"> But the XML spec clearly states that this is illegal: > If the entities lt or amp are declared, they MUST be declared as > internal entities whose replacement text is a character reference to > the respective character (less-than sign or ampersand) being escaped; > the double escaping is REQUIRED for these entities so that references > to them produce a well-formed result. Also fixes #217 but the connection is only tangential. The integer overflow discovered by fuzzing was more related to the fact that various parts of the parser disagreed on whether to prefer predefined entities over their redeclarations. The whole situation is a mess and even depends on legacy parser options. But now that redeclarations are validated, it shouldn't make a difference. As noted in the added comment, this is also one of the cases where overly defensive checks can hide interesting logic bugs from fuzzers.
This commit is contained in:
parent
07920b4381
commit
01411e7c5e
39
entities.c
39
entities.c
@ -210,7 +210,7 @@ xmlAddEntity(xmlDtdPtr dtd, const xmlChar *name, int type,
|
||||
const xmlChar *content) {
|
||||
xmlDictPtr dict = NULL;
|
||||
xmlEntitiesTablePtr table = NULL;
|
||||
xmlEntityPtr ret;
|
||||
xmlEntityPtr ret, predef;
|
||||
|
||||
if (name == NULL)
|
||||
return(NULL);
|
||||
@ -223,6 +223,43 @@ xmlAddEntity(xmlDtdPtr dtd, const xmlChar *name, int type,
|
||||
case XML_INTERNAL_GENERAL_ENTITY:
|
||||
case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
|
||||
case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
|
||||
predef = xmlGetPredefinedEntity(name);
|
||||
if (predef != NULL) {
|
||||
int valid = 0;
|
||||
|
||||
/* 4.6 Predefined Entities */
|
||||
if (type == XML_INTERNAL_GENERAL_ENTITY) {
|
||||
int c = predef->content[0];
|
||||
|
||||
if (((content[0] == c) && (content[1] == 0)) &&
|
||||
((c == '>') || (c == '\'') || (c == '"'))) {
|
||||
valid = 1;
|
||||
} else if ((content[0] == '&') && (content[1] == '#')) {
|
||||
if (content[2] == 'x') {
|
||||
xmlChar *hex = BAD_CAST "0123456789ABCDEF";
|
||||
xmlChar ref[] = "00;";
|
||||
|
||||
ref[0] = hex[c / 16 % 16];
|
||||
ref[1] = hex[c % 16];
|
||||
if (xmlStrcasecmp(&content[3], ref) == 0)
|
||||
valid = 1;
|
||||
} else {
|
||||
xmlChar ref[] = "00;";
|
||||
|
||||
ref[0] = '0' + c / 10 % 10;
|
||||
ref[1] = '0' + c % 10;
|
||||
if (xmlStrEqual(&content[2], ref))
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!valid) {
|
||||
xmlEntitiesErr(XML_ERR_ENTITY_PROCESSING,
|
||||
"xmlAddEntity: invalid redeclaration of predefined"
|
||||
" entity");
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
if (dtd->entities == NULL)
|
||||
dtd->entities = xmlHashCreateDict(0, dict);
|
||||
table = dtd->entities;
|
||||
|
9
result/ent6hex
Normal file
9
result/ent6hex
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE doc [
|
||||
<!ENTITY lt "&#x3c;">
|
||||
<!ENTITY gt "&#x3E;">
|
||||
<!ENTITY amp "&#x26;">
|
||||
<!ENTITY apos "&#x27;">
|
||||
<!ENTITY quot "&#x22;">
|
||||
]>
|
||||
<doc/>
|
2
result/ent6hex.rde
Normal file
2
result/ent6hex.rde
Normal file
@ -0,0 +1,2 @@
|
||||
0 10 doc 0 0
|
||||
0 1 doc 1 0
|
2
result/ent6hex.rdr
Normal file
2
result/ent6hex.rdr
Normal file
@ -0,0 +1,2 @@
|
||||
0 10 doc 0 0
|
||||
0 1 doc 1 0
|
17
result/ent6hex.sax
Normal file
17
result/ent6hex.sax
Normal file
@ -0,0 +1,17 @@
|
||||
SAX.setDocumentLocator()
|
||||
SAX.startDocument()
|
||||
SAX.internalSubset(doc, , )
|
||||
SAX.entityDecl(lt, 1, (null), (null), <)
|
||||
SAX.getEntity(lt)
|
||||
SAX.entityDecl(gt, 1, (null), (null), >)
|
||||
SAX.getEntity(gt)
|
||||
SAX.entityDecl(amp, 1, (null), (null), &)
|
||||
SAX.getEntity(amp)
|
||||
SAX.entityDecl(apos, 1, (null), (null), ')
|
||||
SAX.getEntity(apos)
|
||||
SAX.entityDecl(quot, 1, (null), (null), ")
|
||||
SAX.getEntity(quot)
|
||||
SAX.externalSubset(doc, , )
|
||||
SAX.startElement(doc)
|
||||
SAX.endElement(doc)
|
||||
SAX.endDocument()
|
17
result/ent6hex.sax2
Normal file
17
result/ent6hex.sax2
Normal file
@ -0,0 +1,17 @@
|
||||
SAX.setDocumentLocator()
|
||||
SAX.startDocument()
|
||||
SAX.internalSubset(doc, , )
|
||||
SAX.entityDecl(lt, 1, (null), (null), <)
|
||||
SAX.getEntity(lt)
|
||||
SAX.entityDecl(gt, 1, (null), (null), >)
|
||||
SAX.getEntity(gt)
|
||||
SAX.entityDecl(amp, 1, (null), (null), &)
|
||||
SAX.getEntity(amp)
|
||||
SAX.entityDecl(apos, 1, (null), (null), ')
|
||||
SAX.getEntity(apos)
|
||||
SAX.entityDecl(quot, 1, (null), (null), ")
|
||||
SAX.getEntity(quot)
|
||||
SAX.externalSubset(doc, , )
|
||||
SAX.startElementNs(doc, NULL, NULL, 0, 0, 0)
|
||||
SAX.endElementNs(doc, NULL, NULL)
|
||||
SAX.endDocument()
|
@ -1,5 +1,5 @@
|
||||
./test/errors/759398.xml:210: parser error : internal error: detected an error in element content
|
||||
|
||||
need to worry about parsers whi<! don't expand
|
||||
need to worry about parsers whi<! don't expand PErefs finding
|
||||
^
|
||||
./test/errors/759398.xml : failed to parse
|
||||
|
9
result/noent/ent6hex
Normal file
9
result/noent/ent6hex
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE doc [
|
||||
<!ENTITY lt "&#x3c;">
|
||||
<!ENTITY gt "&#x3E;">
|
||||
<!ENTITY amp "&#x26;">
|
||||
<!ENTITY apos "&#x27;">
|
||||
<!ENTITY quot "&#x22;">
|
||||
]>
|
||||
<doc/>
|
17
result/noent/ent6hex.sax2
Normal file
17
result/noent/ent6hex.sax2
Normal file
@ -0,0 +1,17 @@
|
||||
SAX.setDocumentLocator()
|
||||
SAX.startDocument()
|
||||
SAX.internalSubset(doc, , )
|
||||
SAX.entityDecl(lt, 1, (null), (null), <)
|
||||
SAX.getEntity(lt)
|
||||
SAX.entityDecl(gt, 1, (null), (null), >)
|
||||
SAX.getEntity(gt)
|
||||
SAX.entityDecl(amp, 1, (null), (null), &)
|
||||
SAX.getEntity(amp)
|
||||
SAX.entityDecl(apos, 1, (null), (null), ')
|
||||
SAX.getEntity(apos)
|
||||
SAX.entityDecl(quot, 1, (null), (null), ")
|
||||
SAX.getEntity(quot)
|
||||
SAX.externalSubset(doc, , )
|
||||
SAX.startElementNs(doc, NULL, NULL, 0, 0, 0)
|
||||
SAX.endElementNs(doc, NULL, NULL)
|
||||
SAX.endDocument()
|
@ -10,7 +10,7 @@ publication. --><!ENTITY XML.version "1.0">
|
||||
<!ENTITY draft.month "February">
|
||||
<!ENTITY draft.year "1998">
|
||||
<!ENTITY WebSGML "WebSGML Adaptations Annex to ISO 8879">
|
||||
<!ENTITY lt "<">
|
||||
<!ENTITY lt "&#60;">
|
||||
<!ENTITY gt ">">
|
||||
<!ENTITY xmlpio "'<?xml'">
|
||||
<!ENTITY pic "'?>'">
|
||||
|
8
test/ent6hex
Normal file
8
test/ent6hex
Normal file
@ -0,0 +1,8 @@
|
||||
<!DOCTYPE doc [
|
||||
<!ENTITY lt "&#x3c;">
|
||||
<!ENTITY gt "&#x3E;">
|
||||
<!ENTITY amp "&#x26;">
|
||||
<!ENTITY apos "&#x27;">
|
||||
<!ENTITY quot "&#x22;">
|
||||
]>
|
||||
<doc/>
|
@ -18,7 +18,7 @@ publication. -->
|
||||
<!ENTITY WebSGML
|
||||
'WebSGML Adaptations Annex to ISO 8879'>
|
||||
|
||||
<!ENTITY lt "<">
|
||||
<!ENTITY lt "&#60;">
|
||||
<!ENTITY gt ">">
|
||||
<!ENTITY xmlpio "'<?xml'">
|
||||
<!ENTITY pic "'?>'">
|
||||
|
@ -18,7 +18,7 @@ publication. -->
|
||||
<!ENTITY WebSGML
|
||||
'WebSGML Adaptations Annex to ISO 8879'>
|
||||
|
||||
<!ENTITY lt "<">
|
||||
<!ENTITY lt "&#60;">
|
||||
<!ENTITY gt ">">
|
||||
<!ENTITY xmlpio "'<?xml'">
|
||||
<!ENTITY pic "'?>'">
|
||||
|
@ -18,7 +18,7 @@ publication. -->
|
||||
<!ENTITY WebSGML
|
||||
'WebSGML Adaptations Annex to ISO 8879'>
|
||||
|
||||
<!ENTITY lt "<">
|
||||
<!ENTITY lt "&#60;">
|
||||
<!ENTITY gt ">">
|
||||
<!ENTITY xmlpio "'<?xml'">
|
||||
<!ENTITY pic "'?>'">
|
||||
|
13
tree.c
13
tree.c
@ -1310,6 +1310,16 @@ xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) {
|
||||
else
|
||||
tmp = 0;
|
||||
while (tmp != ';') { /* Non input consuming loop */
|
||||
/*
|
||||
* If you find an integer overflow here when fuzzing,
|
||||
* the bug is probably elsewhere. This function should
|
||||
* only receive entities that were already validated by
|
||||
* the parser, typically by xmlParseAttValueComplex
|
||||
* calling xmlStringDecodeEntities.
|
||||
*
|
||||
* So it's better *not* to check for overflow to
|
||||
* potentially discover new bugs.
|
||||
*/
|
||||
if ((tmp >= '0') && (tmp <= '9'))
|
||||
charval = charval * 16 + (tmp - '0');
|
||||
else if ((tmp >= 'a') && (tmp <= 'f'))
|
||||
@ -1338,6 +1348,7 @@ xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) {
|
||||
else
|
||||
tmp = 0;
|
||||
while (tmp != ';') { /* Non input consuming loops */
|
||||
/* Don't check for integer overflow, see above. */
|
||||
if ((tmp >= '0') && (tmp <= '9'))
|
||||
charval = charval * 10 + (tmp - '0');
|
||||
else {
|
||||
@ -1517,6 +1528,7 @@ xmlStringGetNodeList(const xmlDoc *doc, const xmlChar *value) {
|
||||
cur += 3;
|
||||
tmp = *cur;
|
||||
while (tmp != ';') { /* Non input consuming loop */
|
||||
/* Don't check for integer overflow, see above. */
|
||||
if ((tmp >= '0') && (tmp <= '9'))
|
||||
charval = charval * 16 + (tmp - '0');
|
||||
else if ((tmp >= 'a') && (tmp <= 'f'))
|
||||
@ -1539,6 +1551,7 @@ xmlStringGetNodeList(const xmlDoc *doc, const xmlChar *value) {
|
||||
cur += 2;
|
||||
tmp = *cur;
|
||||
while (tmp != ';') { /* Non input consuming loops */
|
||||
/* Don't check for integer overflow, see above. */
|
||||
if ((tmp >= '0') && (tmp <= '9'))
|
||||
charval = charval * 10 + (tmp - '0');
|
||||
else {
|
||||
|
Loading…
Reference in New Issue
Block a user