From 2b6b3945f2df548b56f2c73c490dda9781f92eb2 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Tue, 3 Jun 2025 16:12:56 +0200 Subject: [PATCH] Revert "SAX1: Align handling of default attributes with SAX2" This reverts commit db65b2fc51ef0d6e4d2e9dc65ba12fe948da49f3. This didn't check for duplicate default attributes. --- SAX2.c | 197 ++++++++++++++++++++++++++++----------- include/private/parser.h | 30 +----- parser.c | 24 ++++- 3 files changed, 169 insertions(+), 82 deletions(-) diff --git a/SAX2.c b/SAX2.c index a2fc6e664..d63b72300 100644 --- a/SAX2.c +++ b/SAX2.c @@ -950,7 +950,7 @@ xmlNsErrMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error, * @param ctxt the parser context * @param fullname the attribute name, including namespace prefix * @param value the attribute value - * @param prefix the namespace prefix of the element + * @param prefix the namespace prefix */ static void xmlSAX1Attribute(xmlParserCtxtPtr ctxt, const xmlChar *fullname, @@ -1242,67 +1242,156 @@ error: static void xmlCheckDefaultedAttributes(xmlParserCtxtPtr ctxt, const xmlChar *name, const xmlChar *prefix, const xmlChar **atts) { - xmlDefAttrsPtr defaults; + xmlElementPtr elemDecl; + const xmlChar *att; + int internal = 1; int i; - if (ctxt->attsDefault == NULL) - return; + elemDecl = xmlGetDtdQElementDesc(ctxt->myDoc->intSubset, name, prefix); + if (elemDecl == NULL) { + elemDecl = xmlGetDtdQElementDesc(ctxt->myDoc->extSubset, name, prefix); + internal = 0; + } - defaults = xmlHashLookup2(ctxt->attsDefault, name, prefix); - if (defaults == NULL) - return; +process_external_subset: - for (i = 0; i < defaults->nbAttrs; i++) { - xmlDefAttr *def = &defaults->attrs[i]; - const xmlChar *attname = def->name.name; - const xmlChar *aprefix = def->prefix.name; + if (elemDecl != NULL) { + xmlAttributePtr attr = elemDecl->attributes; - if (((aprefix != NULL) && - (xmlStrEqual(aprefix, BAD_CAST "xmlns"))) || - ((aprefix == NULL) && - (xmlStrEqual(attname, BAD_CAST "xmlns"))) || - (ctxt->loadsubset & XML_COMPLETE_ATTRS)) { - xmlChar fn[50]; - xmlChar *fulln; - const xmlChar *att; - int j; +#ifdef LIBXML_VALID_ENABLED + /* + * Check against defaulted attributes from the external subset + * if the document is stamped as standalone. + * + * This should be moved to valid.c, but we don't keep track + * whether an attribute was defaulted. + */ + if ((ctxt->myDoc->standalone == 1) && + (ctxt->myDoc->extSubset != NULL) && + (ctxt->validate)) { + while (attr != NULL) { + if ((attr->defaultValue != NULL) && + (xmlGetDtdQAttrDesc(ctxt->myDoc->extSubset, + attr->elem, attr->name, + attr->prefix) == attr) && + (xmlGetDtdQAttrDesc(ctxt->myDoc->intSubset, + attr->elem, attr->name, + attr->prefix) == NULL)) { + xmlChar *fulln; - fulln = xmlBuildQName(attname, aprefix, fn, 50); - if (fulln == NULL) { - xmlSAX2ErrMemory(ctxt); - return; - } - - /* - * Check that the attribute is not declared in the - * serialization - */ - att = NULL; - if (atts != NULL) { - j = 0; - att = atts[j]; - while (att != NULL) { - if (xmlStrEqual(att, fulln)) + if (attr->prefix != NULL) { + fulln = xmlStrdup(attr->prefix); + if (fulln != NULL) + fulln = xmlStrcat(fulln, BAD_CAST ":"); + if (fulln != NULL) + fulln = xmlStrcat(fulln, attr->name); + } else { + fulln = xmlStrdup(attr->name); + } + if (fulln == NULL) { + xmlSAX2ErrMemory(ctxt); break; - j += 2; - att = atts[j]; - } - } - if (att == NULL) { - if ((ctxt->validate) && - (ctxt->myDoc->standalone == 1) && - (def->external)) { - xmlErrValid(ctxt, XML_DTD_STANDALONE_DEFAULTED, - "standalone: attribute %s on %s defaulted " - "from external subset\n", - fulln, name); - } + } - xmlSAX1Attribute(ctxt, fulln, def->value.name, prefix); - } - if ((fulln != fn) && (fulln != attname)) - xmlFree(fulln); - } + /* + * Check that the attribute is not declared in the + * serialization + */ + att = NULL; + if (atts != NULL) { + i = 0; + att = atts[i]; + while (att != NULL) { + if (xmlStrEqual(att, fulln)) + break; + i += 2; + att = atts[i]; + } + } + if (att == NULL) { + xmlErrValid(ctxt, XML_DTD_STANDALONE_DEFAULTED, + "standalone: attribute %s on %s defaulted from external subset\n", + fulln, + attr->elem); + } + xmlFree(fulln); + } + attr = attr->nexth; + } + } +#endif + + /* + * Actually insert defaulted values when needed + */ + attr = elemDecl->attributes; + while (attr != NULL) { + /* + * Make sure that attributes redefinition occurring in the + * internal subset are not overridden by definitions in the + * external subset. + */ + if (attr->defaultValue != NULL) { + /* + * the element should be instantiated in the tree if: + * - this is a namespace prefix + * - the user required for completion in the tree + * like XSLT + * - there isn't already an attribute definition + * in the internal subset overriding it. + */ + if (((attr->prefix != NULL) && + (xmlStrEqual(attr->prefix, BAD_CAST "xmlns"))) || + ((attr->prefix == NULL) && + (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) || + (ctxt->loadsubset & XML_COMPLETE_ATTRS)) { + xmlAttributePtr tst; + + tst = xmlGetDtdQAttrDesc(ctxt->myDoc->intSubset, + attr->elem, attr->name, + attr->prefix); + if ((tst == attr) || (tst == NULL)) { + xmlChar fn[50]; + xmlChar *fulln; + + fulln = xmlBuildQName(attr->name, attr->prefix, fn, 50); + if (fulln == NULL) { + xmlSAX2ErrMemory(ctxt); + return; + } + + /* + * Check that the attribute is not declared in the + * serialization + */ + att = NULL; + if (atts != NULL) { + i = 0; + att = atts[i]; + while (att != NULL) { + if (xmlStrEqual(att, fulln)) + break; + i += 2; + att = atts[i]; + } + } + if (att == NULL) { + xmlSAX1Attribute(ctxt, fulln, + attr->defaultValue, prefix); + } + if ((fulln != fn) && (fulln != attr->name)) + xmlFree(fulln); + } + } + } + attr = attr->nexth; + } + if (internal == 1) { + elemDecl = xmlGetDtdQElementDesc(ctxt->myDoc->extSubset, + name, prefix); + internal = 0; + goto process_external_subset; + } } } diff --git a/include/private/parser.h b/include/private/parser.h index 96fd61591..fd3a0f1ac 100644 --- a/include/private/parser.h +++ b/include/private/parser.h @@ -4,8 +4,6 @@ #include #include -#include "private/dict.h" - #define XML_INVALID_CHAR 0x200000 #define XML_MAX_URI_LENGTH 2000 @@ -58,35 +56,13 @@ */ #define INPUT_CHUNK 250 -#define XML_SCAN_NC 1 -#define XML_SCAN_NMTOKEN 2 -#define XML_SCAN_OLD10 4 - struct _xmlAttrHashBucket { int index; }; -typedef struct { - xmlHashedString prefix; - xmlHashedString name; - xmlHashedString value; - const xmlChar *valueEnd; - int external; - int expandedSize; -} xmlDefAttr; - -typedef struct _xmlDefAttrs xmlDefAttrs; -typedef xmlDefAttrs *xmlDefAttrsPtr; -struct _xmlDefAttrs { - int nbAttrs; /* number of defaulted attributes on that element */ - int maxAttrs; /* the size of the array */ -#if __STDC_VERSION__ >= 199901L - /* Using a C99 flexible array member avoids UBSan errors. */ - xmlDefAttr attrs[] ATTRIBUTE_COUNTED_BY(maxAttrs); -#else - xmlDefAttr attrs[1]; -#endif -}; +#define XML_SCAN_NC 1 +#define XML_SCAN_NMTOKEN 2 +#define XML_SCAN_OLD10 4 XML_HIDDEN const xmlChar * xmlScanName(const xmlChar *buf, size_t maxSize, int flags); diff --git a/parser.c b/parser.c index c96c3bec7..00af64471 100644 --- a/parser.c +++ b/parser.c @@ -966,6 +966,28 @@ xmlCtxtInitializeLate(xmlParserCtxtPtr ctxt) { #endif /* LIBXML_VALID_ENABLED */ } +typedef struct { + xmlHashedString prefix; + xmlHashedString name; + xmlHashedString value; + const xmlChar *valueEnd; + int external; + int expandedSize; +} xmlDefAttr; + +typedef struct _xmlDefAttrs xmlDefAttrs; +typedef xmlDefAttrs *xmlDefAttrsPtr; +struct _xmlDefAttrs { + int nbAttrs; /* number of defaulted attributes on that element */ + int maxAttrs; /* the size of the array */ +#if __STDC_VERSION__ >= 199901L + /* Using a C99 flexible array member avoids UBSan errors. */ + xmlDefAttr attrs[] ATTRIBUTE_COUNTED_BY(maxAttrs); +#else + xmlDefAttr attrs[1]; +#endif +}; + /** * Normalize the space in non CDATA attribute values: * If the attribute type is not CDATA, then the XML processor MUST further @@ -6036,7 +6058,7 @@ xmlParseAttributeListDecl(xmlParserCtxt *ctxt) { else if (tree != NULL) xmlFreeEnumeration(tree); - if ((defaultValue != NULL) && + if ((ctxt->sax2) && (defaultValue != NULL) && (def != XML_ATTRIBUTE_IMPLIED) && (def != XML_ATTRIBUTE_REQUIRED)) { xmlAddDefAttrs(ctxt, elemName, attrName, defaultValue);