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:
21
relaxng.c
21
relaxng.c
@ -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
0
result/relaxng/492317_0
Normal file
1
result/relaxng/492317_0.err
Normal file
1
result/relaxng/492317_0.err
Normal file
@ -0,0 +1 @@
|
|||||||
|
./test/relaxng/492317_0.xml validates
|
0
result/relaxng/492317_1
Normal file
0
result/relaxng/492317_1
Normal file
1
result/relaxng/492317_1.err
Normal file
1
result/relaxng/492317_1.err
Normal file
@ -0,0 +1 @@
|
|||||||
|
./test/relaxng/492317_1.xml validates
|
0
result/relaxng/492317_2
Normal file
0
result/relaxng/492317_2
Normal file
3
result/relaxng/492317_2.err
Normal file
3
result/relaxng/492317_2.err
Normal 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
|
1
result/relaxng/492317_err
Normal file
1
result/relaxng/492317_err
Normal file
@ -0,0 +1 @@
|
|||||||
|
./test/relaxng/492317.rng validates
|
0
result/relaxng/492317_valid
Normal file
0
result/relaxng/492317_valid
Normal file
16
test/relaxng/492317.rng
Normal file
16
test/relaxng/492317.rng
Normal 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>
|
4
test/relaxng/492317_0.xml
Normal file
4
test/relaxng/492317_0.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<root>
|
||||||
|
<child type="Foo">
|
||||||
|
</child>
|
||||||
|
</root>
|
4
test/relaxng/492317_1.xml
Normal file
4
test/relaxng/492317_1.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<root>
|
||||||
|
<child type="Bar">
|
||||||
|
</child>
|
||||||
|
</root>
|
4
test/relaxng/492317_2.xml
Normal file
4
test/relaxng/492317_2.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<root>
|
||||||
|
<child type="">
|
||||||
|
</child>
|
||||||
|
</root>
|
68
xmlregexp.c
68
xmlregexp.c
@ -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
|
||||||
|
Reference in New Issue
Block a user