1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-07-30 22:43:14 +03:00

492317 Fix Relax-NG validation problems

* relaxng.c xmlregexp.c: a subtle problem when checking for compileable
  content model, if using the same elements in cases of choices. Handled
  by adding a special flag to the regexp compilation to detect
  transitions with different atoms using same strings.
* test/relaxng/492317* result/relaxng/492317*: add the test to the
  regression suite
This commit is contained in:
Daniel Veillard
2009-08-31 16:47:39 +02:00
parent ec18c96008
commit 1ba2aca3eb
14 changed files with 113 additions and 10 deletions

View File

@ -2854,6 +2854,10 @@ xmlRelaxNGCleanupTypes(void)
* * * *
************************************************************************/ ************************************************************************/
/* from automata.c but not exported */
void xmlAutomataSetFlags(xmlAutomataPtr am, int flags);
static int xmlRelaxNGTryCompile(xmlRelaxNGParserCtxtPtr ctxt, static int xmlRelaxNGTryCompile(xmlRelaxNGParserCtxtPtr ctxt,
xmlRelaxNGDefinePtr def); xmlRelaxNGDefinePtr def);
@ -3037,6 +3041,17 @@ xmlRelaxNGCompile(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def)
ctxt->am = xmlNewAutomata(); ctxt->am = xmlNewAutomata();
if (ctxt->am == NULL) if (ctxt->am == NULL)
return (-1); return (-1);
/*
* assume identical strings but not same pointer are different
* atoms, needed for non-determinism detection
* That way if 2 elements with the same name are in a choice
* branch the automata is found non-deterministic and
* we fallback to the normal validation which does the right
* thing of exploring both choices.
*/
xmlAutomataSetFlags(ctxt->am, 1);
ctxt->state = xmlAutomataGetInitState(ctxt->am); ctxt->state = xmlAutomataGetInitState(ctxt->am);
while (list != NULL) { while (list != NULL) {
xmlRelaxNGCompile(ctxt, list); xmlRelaxNGCompile(ctxt, list);
@ -3068,6 +3083,7 @@ xmlRelaxNGCompile(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def)
ctxt->am = xmlNewAutomata(); ctxt->am = xmlNewAutomata();
if (ctxt->am == NULL) if (ctxt->am == NULL)
return (-1); return (-1);
xmlAutomataSetFlags(ctxt->am, 1);
ctxt->state = xmlAutomataGetInitState(ctxt->am); ctxt->state = xmlAutomataGetInitState(ctxt->am);
while (list != NULL) { while (list != NULL) {
xmlRelaxNGCompile(ctxt, list); xmlRelaxNGCompile(ctxt, list);
@ -3076,6 +3092,11 @@ xmlRelaxNGCompile(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def)
xmlAutomataSetFinalState(ctxt->am, ctxt->state); xmlAutomataSetFinalState(ctxt->am, ctxt->state);
def->contModel = xmlAutomataCompile(ctxt->am); def->contModel = xmlAutomataCompile(ctxt->am);
if (!xmlRegexpIsDeterminist(def->contModel)) { if (!xmlRegexpIsDeterminist(def->contModel)) {
#ifdef DEBUG_COMPILE
xmlGenericError(xmlGenericErrorContext,
"Content model not determinist %s\n",
def->name);
#endif
/* /*
* we can only use the automata if it is determinist * we can only use the automata if it is determinist
*/ */

0
result/relaxng/492317_0 Normal file
View File

View File

@ -0,0 +1 @@
./test/relaxng/492317_0.xml validates

0
result/relaxng/492317_1 Normal file
View File

View File

@ -0,0 +1 @@
./test/relaxng/492317_1.xml validates

0
result/relaxng/492317_2 Normal file
View File

View File

@ -0,0 +1,3 @@
./test/relaxng/492317_2.xml:2: element child: Relax-NG validity error : Element child failed to validate attributes
./test/relaxng/492317_2.xml:1: element root: Relax-NG validity error : Element root failed to validate content
./test/relaxng/492317_2.xml fails to validate

View File

@ -0,0 +1 @@
./test/relaxng/492317.rng validates

View File

16
test/relaxng/492317.rng Normal file
View File

@ -0,0 +1,16 @@
<element name="root" xmlns="http://relaxng.org/ns/structure/1.0">
<choice>
<element name="child">
<attribute name="type">
<value>Foo</value>
</attribute>
<!-- Define stuff that's only valid when type is "Foo" -->
</element>
<element name="child">
<attribute name="type">
<value>Bar</value>
</attribute>
<!-- Define stuff that's only valid when type is "Bar" -->
</element>
</choice>
</element>

View File

@ -0,0 +1,4 @@
<root>
<child type="Foo">
</child>
</root>

View File

@ -0,0 +1,4 @@
<root>
<child type="Bar">
</child>
</root>

View File

@ -0,0 +1,4 @@
<root>
<child type="">
</child>
</root>

View File

@ -233,6 +233,8 @@ struct _xmlAutomataState {
typedef struct _xmlAutomata xmlRegParserCtxt; typedef struct _xmlAutomata xmlRegParserCtxt;
typedef xmlRegParserCtxt *xmlRegParserCtxtPtr; typedef xmlRegParserCtxt *xmlRegParserCtxtPtr;
#define AM_AUTOMATA_RNG 1
struct _xmlAutomata { struct _xmlAutomata {
xmlChar *string; xmlChar *string;
xmlChar *cur; xmlChar *cur;
@ -260,6 +262,7 @@ struct _xmlAutomata {
int determinist; int determinist;
int negs; int negs;
int flags;
}; };
struct _xmlRegexp { struct _xmlRegexp {
@ -271,6 +274,7 @@ struct _xmlRegexp {
int nbCounters; int nbCounters;
xmlRegCounter *counters; xmlRegCounter *counters;
int determinist; int determinist;
int flags;
/* /*
* That's the compact form for determinists automatas * That's the compact form for determinists automatas
*/ */
@ -353,6 +357,8 @@ static int xmlRegCheckCharacter(xmlRegAtomPtr atom, int codepoint);
static int xmlRegCheckCharacterRange(xmlRegAtomType type, int codepoint, static int xmlRegCheckCharacterRange(xmlRegAtomType type, int codepoint,
int neg, int start, int end, const xmlChar *blockName); int neg, int start, int end, const xmlChar *blockName);
void xmlAutomataSetFlags(xmlAutomataPtr am, int flags);
/************************************************************************ /************************************************************************
* * * *
* Regexp memory error handler * * Regexp memory error handler *
@ -434,6 +440,7 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) {
ret->nbCounters = ctxt->nbCounters; ret->nbCounters = ctxt->nbCounters;
ret->counters = ctxt->counters; ret->counters = ctxt->counters;
ret->determinist = ctxt->determinist; ret->determinist = ctxt->determinist;
ret->flags = ctxt->flags;
if (ret->determinist == -1) { if (ret->determinist == -1) {
xmlRegexpIsDeterminist(ret); xmlRegexpIsDeterminist(ret);
} }
@ -2428,6 +2435,7 @@ xmlFACompareAtomTypes(xmlRegAtomType type1, xmlRegAtomType type2) {
* xmlFAEqualAtoms: * xmlFAEqualAtoms:
* @atom1: an atom * @atom1: an atom
* @atom2: an atom * @atom2: an atom
* @deep: if not set only compare string pointers
* *
* Compares two atoms to check whether they are the same exactly * Compares two atoms to check whether they are the same exactly
* this is used to remove equivalent transitions * this is used to remove equivalent transitions
@ -2435,7 +2443,7 @@ xmlFACompareAtomTypes(xmlRegAtomType type1, xmlRegAtomType type2) {
* Returns 1 if same and 0 otherwise * Returns 1 if same and 0 otherwise
*/ */
static int static int
xmlFAEqualAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2) { xmlFAEqualAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2, int deep) {
int ret = 0; int ret = 0;
if (atom1 == atom2) if (atom1 == atom2)
@ -2450,8 +2458,11 @@ xmlFAEqualAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2) {
ret = 0; ret = 0;
break; break;
case XML_REGEXP_STRING: case XML_REGEXP_STRING:
ret = xmlStrEqual((xmlChar *)atom1->valuep, if (!deep)
(xmlChar *)atom2->valuep); ret = (atom1->valuep == atom2->valuep);
else
ret = xmlStrEqual((xmlChar *)atom1->valuep,
(xmlChar *)atom2->valuep);
break; break;
case XML_REGEXP_CHARVAL: case XML_REGEXP_CHARVAL:
ret = (atom1->codepoint == atom2->codepoint); ret = (atom1->codepoint == atom2->codepoint);
@ -2469,6 +2480,7 @@ xmlFAEqualAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2) {
* xmlFACompareAtoms: * xmlFACompareAtoms:
* @atom1: an atom * @atom1: an atom
* @atom2: an atom * @atom2: an atom
* @deep: if not set only compare string pointers
* *
* Compares two atoms to check whether they intersect in some ways, * Compares two atoms to check whether they intersect in some ways,
* this is used by xmlFAComputesDeterminism and xmlFARecurseDeterminism only * this is used by xmlFAComputesDeterminism and xmlFARecurseDeterminism only
@ -2476,7 +2488,7 @@ xmlFAEqualAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2) {
* Returns 1 if yes and 0 otherwise * Returns 1 if yes and 0 otherwise
*/ */
static int static int
xmlFACompareAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2) { xmlFACompareAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2, int deep) {
int ret = 1; int ret = 1;
if (atom1 == atom2) if (atom1 == atom2)
@ -2502,8 +2514,11 @@ xmlFACompareAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2) {
} }
switch (atom1->type) { switch (atom1->type) {
case XML_REGEXP_STRING: case XML_REGEXP_STRING:
ret = xmlRegStrEqualWildcard((xmlChar *)atom1->valuep, if (!deep)
(xmlChar *)atom2->valuep); ret = (atom1->valuep != atom2->valuep);
else
ret = xmlRegStrEqualWildcard((xmlChar *)atom1->valuep,
(xmlChar *)atom2->valuep);
break; break;
case XML_REGEXP_EPSILON: case XML_REGEXP_EPSILON:
goto not_determinist; goto not_determinist;
@ -2566,9 +2581,14 @@ xmlFARecurseDeterminism(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state,
int res; int res;
int transnr, nbTrans; int transnr, nbTrans;
xmlRegTransPtr t1; xmlRegTransPtr t1;
int deep = 1;
if (state == NULL) if (state == NULL)
return(ret); return(ret);
if (ctxt->flags & AM_AUTOMATA_RNG)
deep = 0;
/* /*
* don't recurse on transitions potentially added in the course of * don't recurse on transitions potentially added in the course of
* the elimination. * the elimination.
@ -2592,7 +2612,7 @@ xmlFARecurseDeterminism(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state,
} }
if (t1->to != to) if (t1->to != to)
continue; continue;
if (xmlFACompareAtoms(t1->atom, atom)) { if (xmlFACompareAtoms(t1->atom, atom, deep)) {
ret = 0; ret = 0;
/* mark the transition as non-deterministic */ /* mark the transition as non-deterministic */
t1->nd = 1; t1->nd = 1;
@ -2616,6 +2636,7 @@ xmlFAComputesDeterminism(xmlRegParserCtxtPtr ctxt) {
xmlRegTransPtr t1, t2, last; xmlRegTransPtr t1, t2, last;
int i; int i;
int ret = 1; int ret = 1;
int deep = 1;
#ifdef DEBUG_REGEXP_GRAPH #ifdef DEBUG_REGEXP_GRAPH
printf("xmlFAComputesDeterminism\n"); printf("xmlFAComputesDeterminism\n");
@ -2624,6 +2645,9 @@ xmlFAComputesDeterminism(xmlRegParserCtxtPtr ctxt) {
if (ctxt->determinist != -1) if (ctxt->determinist != -1)
return(ctxt->determinist); return(ctxt->determinist);
if (ctxt->flags & AM_AUTOMATA_RNG)
deep = 0;
/* /*
* First cleanup the automata removing cancelled transitions * First cleanup the automata removing cancelled transitions
*/ */
@ -2651,7 +2675,11 @@ xmlFAComputesDeterminism(xmlRegParserCtxtPtr ctxt) {
continue; continue;
if (t2->atom != NULL) { if (t2->atom != NULL) {
if (t1->to == t2->to) { if (t1->to == t2->to) {
if (xmlFAEqualAtoms(t1->atom, t2->atom) && /*
* Here we use deep because we want to keep the
* transitions which indicate a conflict
*/
if (xmlFAEqualAtoms(t1->atom, t2->atom, deep) &&
(t1->counter == t2->counter) && (t1->counter == t2->counter) &&
(t1->count == t2->count)) (t1->count == t2->count))
t2->to = -1; /* eliminated */ t2->to = -1; /* eliminated */
@ -2688,8 +2716,11 @@ xmlFAComputesDeterminism(xmlRegParserCtxtPtr ctxt) {
if (t2->to == -1) /* eliminated */ if (t2->to == -1) /* eliminated */
continue; continue;
if (t2->atom != NULL) { if (t2->atom != NULL) {
/* not determinist ! */ /*
if (xmlFACompareAtoms(t1->atom, t2->atom)) { * But here we don't use deep because we want to
* find transitions which indicate a conflict
*/
if (xmlFACompareAtoms(t1->atom, t2->atom, 1)) {
ret = 0; ret = 0;
/* mark the transitions as non-deterministic ones */ /* mark the transitions as non-deterministic ones */
t1->nd = 1; t1->nd = 1;
@ -5477,10 +5508,12 @@ xmlRegexpIsDeterminist(xmlRegexpPtr comp) {
am->nbStates = comp->nbStates; am->nbStates = comp->nbStates;
am->states = comp->states; am->states = comp->states;
am->determinist = -1; am->determinist = -1;
am->flags = comp->flags;
ret = xmlFAComputesDeterminism(am); ret = xmlFAComputesDeterminism(am);
am->atoms = NULL; am->atoms = NULL;
am->states = NULL; am->states = NULL;
xmlFreeAutomata(am); xmlFreeAutomata(am);
comp->determinist = ret;
return(ret); return(ret);
} }
@ -5558,6 +5591,7 @@ xmlNewAutomata(void) {
xmlFreeAutomata(ctxt); xmlFreeAutomata(ctxt);
return(NULL); return(NULL);
} }
ctxt->flags = 0;
return(ctxt); return(ctxt);
} }
@ -5575,6 +5609,20 @@ xmlFreeAutomata(xmlAutomataPtr am) {
xmlRegFreeParserCtxt(am); xmlRegFreeParserCtxt(am);
} }
/**
* xmlAutomataSetFlags
* @am: an automata
* @flags: a set of internal flags
*
* Set some flags on the automata
*/
void
xmlAutomataSetFlags(xmlAutomataPtr am, int flags) {
if (am == NULL)
return;
am->flags |= flags;
}
/** /**
* xmlAutomataGetInitState: * xmlAutomataGetInitState:
* @am: an automata * @am: an automata