Skip to content

Commit 4334cbb

Browse files
committed
parser: Fix downstream code that swaps DTDs
Downstream code like the nginx xslt module can change the document's DTD pointers in a SAX callback. If an entity from a separate DTD is parsed lazily, its content must not reference the current document. Regressed with commit d025cfb. Fixes #815.
1 parent 9292977 commit 4334cbb

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

parser.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12213,6 +12213,15 @@ xmlCtxtParseEntity(xmlParserCtxtPtr ctxt, xmlEntityPtr ent) {
1221312213

1221412214
while (list != NULL) {
1221512215
list->parent = (xmlNodePtr) ent;
12216+
12217+
/*
12218+
* Downstream code like the nginx xslt module can set
12219+
* ctxt->myDoc->extSubset to a separate DTD, so the entity
12220+
* might have a different or a NULL document.
12221+
*/
12222+
if (list->doc != ent->doc)
12223+
xmlSetTreeDoc(list, ent->doc);
12224+
1221612225
if (list->next == NULL)
1221712226
ent->last = list;
1221812227
list = list->next;

testparser.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,57 @@ testCFileIO(void) {
125125
return err;
126126
}
127127

128+
#ifdef LIBXML_VALID_ENABLED
129+
static void
130+
testSwitchDtdExtSubset(void *vctxt, const xmlChar *name ATTRIBUTE_UNUSED,
131+
const xmlChar *externalId ATTRIBUTE_UNUSED,
132+
const xmlChar *systemId ATTRIBUTE_UNUSED) {
133+
xmlParserCtxtPtr ctxt = vctxt;
134+
135+
ctxt->myDoc->extSubset = ctxt->_private;
136+
}
137+
138+
static int
139+
testSwitchDtd(void) {
140+
const char dtdContent[] =
141+
"<!ENTITY test '<elem1/><elem2/>'>\n";
142+
const char docContent[] =
143+
"<!DOCTYPE doc SYSTEM 'entities.dtd'>\n"
144+
"<doc>&test;</doc>\n";
145+
xmlParserInputBufferPtr input;
146+
xmlParserCtxtPtr ctxt;
147+
xmlDtdPtr dtd;
148+
xmlDocPtr doc;
149+
xmlEntityPtr ent;
150+
int err = 0;
151+
152+
input = xmlParserInputBufferCreateStatic(dtdContent,
153+
sizeof(dtdContent) - 1,
154+
XML_CHAR_ENCODING_NONE);
155+
dtd = xmlIOParseDTD(NULL, input, XML_CHAR_ENCODING_NONE);
156+
157+
ctxt = xmlNewParserCtxt();
158+
ctxt->_private = dtd;
159+
ctxt->sax->externalSubset = testSwitchDtdExtSubset;
160+
doc = xmlCtxtReadMemory(ctxt, docContent, sizeof(docContent) - 1, NULL,
161+
NULL, XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
162+
xmlFreeParserCtxt(ctxt);
163+
164+
ent = xmlGetDocEntity(doc, BAD_CAST "test");
165+
if (ent->children->doc != NULL) {
166+
fprintf(stderr, "Entity content should have NULL doc\n");
167+
err = 1;
168+
}
169+
170+
/* Free doc before DTD */
171+
doc->extSubset = NULL;
172+
xmlFreeDoc(doc);
173+
xmlFreeDtd(dtd);
174+
175+
return err;
176+
}
177+
#endif /* LIBXML_VALID_ENABLED */
178+
128179
#ifdef LIBXML_SAX1_ENABLED
129180
static int
130181
testBalancedChunk(void) {
@@ -571,6 +622,9 @@ main(void) {
571622
err |= testUnsupportedEncoding();
572623
err |= testNodeGetContent();
573624
err |= testCFileIO();
625+
#ifdef LIBXML_VALID_ENABLED
626+
err |= testSwitchDtd();
627+
#endif
574628
#ifdef LIBXML_SAX1_ENABLED
575629
err |= testBalancedChunk();
576630
#endif

0 commit comments

Comments
 (0)