diff --git a/backport-CVE-2022-40303-Fix-integer-overflows-with-XML_PARSE_.patch b/backport-CVE-2022-40303-Fix-integer-overflows-with-XML_PARSE_.patch new file mode 100644 index 0000000000000000000000000000000000000000..e28ff49d9e1b3de568fcbda17d2d90856bef40be --- /dev/null +++ b/backport-CVE-2022-40303-Fix-integer-overflows-with-XML_PARSE_.patch @@ -0,0 +1,618 @@ +From c846986356fc149915a74972bf198abc266bc2c0 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Thu, 25 Aug 2022 17:43:08 +0200 +Subject: [PATCH] [CVE-2022-40303] Fix integer overflows with XML_PARSE_HUGE + +Also impose size limits when XML_PARSE_HUGE is set. Limit size of names +to XML_MAX_TEXT_LENGTH (10 million bytes) and other content to +XML_MAX_HUGE_LENGTH (1 billion bytes). + +Move some the length checks to the end of the respective loop to make +them strict. + +xmlParseEntityValue didn't have a length limitation at all. But without +XML_PARSE_HUGE, this should eventually trigger an error in xmlGROW. + +Thanks to Maddie Stone working with Google Project Zero for the report! +--- + parser.c | 233 +++++++++++++++++++++++++++++-------------------------- + 1 file changed, 121 insertions(+), 112 deletions(-) + +diff --git a/parser.c b/parser.c +index 93f031be..79479979 100644 +--- a/parser.c ++++ b/parser.c +@@ -102,6 +102,8 @@ xmlParseElementEnd(xmlParserCtxtPtr ctxt); + * * + ************************************************************************/ + ++#define XML_MAX_HUGE_LENGTH 1000000000 ++ + #define XML_PARSER_BIG_ENTITY 1000 + #define XML_PARSER_LOT_ENTITY 5000 + +@@ -552,7 +554,7 @@ xmlFatalErr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *info) + errmsg = "Malformed declaration expecting version"; + break; + case XML_ERR_NAME_TOO_LONG: +- errmsg = "Name too long use XML_PARSE_HUGE option"; ++ errmsg = "Name too long"; + break; + #if 0 + case: +@@ -3202,6 +3204,9 @@ xmlParseNameComplex(xmlParserCtxtPtr ctxt) { + int len = 0, l; + int c; + int count = 0; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_TEXT_LENGTH : ++ XML_MAX_NAME_LENGTH; + + #ifdef DEBUG + nbParseNameComplex++; +@@ -3267,7 +3272,8 @@ xmlParseNameComplex(xmlParserCtxtPtr ctxt) { + if (ctxt->instate == XML_PARSER_EOF) + return(NULL); + } +- len += l; ++ if (len <= INT_MAX - l) ++ len += l; + NEXTL(l); + c = CUR_CHAR(l); + } +@@ -3293,13 +3299,13 @@ xmlParseNameComplex(xmlParserCtxtPtr ctxt) { + if (ctxt->instate == XML_PARSER_EOF) + return(NULL); + } +- len += l; ++ if (len <= INT_MAX - l) ++ len += l; + NEXTL(l); + c = CUR_CHAR(l); + } + } +- if ((len > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if (len > maxLength) { + xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "Name"); + return(NULL); + } +@@ -3338,7 +3344,10 @@ const xmlChar * + xmlParseName(xmlParserCtxtPtr ctxt) { + const xmlChar *in; + const xmlChar *ret; +- int count = 0; ++ size_t count = 0; ++ size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_TEXT_LENGTH : ++ XML_MAX_NAME_LENGTH; + + GROW; + +@@ -3362,8 +3371,7 @@ xmlParseName(xmlParserCtxtPtr ctxt) { + in++; + if ((*in > 0) && (*in < 0x80)) { + count = in - ctxt->input->cur; +- if ((count > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if (count > maxLength) { + xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "Name"); + return(NULL); + } +@@ -3384,6 +3392,9 @@ xmlParseNCNameComplex(xmlParserCtxtPtr ctxt) { + int len = 0, l; + int c; + int count = 0; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_TEXT_LENGTH : ++ XML_MAX_NAME_LENGTH; + size_t startPosition = 0; + + #ifdef DEBUG +@@ -3404,17 +3415,13 @@ xmlParseNCNameComplex(xmlParserCtxtPtr ctxt) { + while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */ + (xmlIsNameChar(ctxt, c) && (c != ':'))) { + if (count++ > XML_PARSER_CHUNK_SIZE) { +- if ((len > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NCName"); +- return(NULL); +- } + count = 0; + GROW; + if (ctxt->instate == XML_PARSER_EOF) + return(NULL); + } +- len += l; ++ if (len <= INT_MAX - l) ++ len += l; + NEXTL(l); + c = CUR_CHAR(l); + if (c == 0) { +@@ -3432,8 +3439,7 @@ xmlParseNCNameComplex(xmlParserCtxtPtr ctxt) { + c = CUR_CHAR(l); + } + } +- if ((len > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if (len > maxLength) { + xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NCName"); + return(NULL); + } +@@ -3459,7 +3465,10 @@ static const xmlChar * + xmlParseNCName(xmlParserCtxtPtr ctxt) { + const xmlChar *in, *e; + const xmlChar *ret; +- int count = 0; ++ size_t count = 0; ++ size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_TEXT_LENGTH : ++ XML_MAX_NAME_LENGTH; + + #ifdef DEBUG + nbParseNCName++; +@@ -3484,8 +3493,7 @@ xmlParseNCName(xmlParserCtxtPtr ctxt) { + goto complex; + if ((*in > 0) && (*in < 0x80)) { + count = in - ctxt->input->cur; +- if ((count > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if (count > maxLength) { + xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NCName"); + return(NULL); + } +@@ -3567,6 +3575,9 @@ xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) { + const xmlChar *cur = *str; + int len = 0, l; + int c; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_TEXT_LENGTH : ++ XML_MAX_NAME_LENGTH; + + #ifdef DEBUG + nbParseStringName++; +@@ -3602,12 +3613,6 @@ xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) { + if (len + 10 > max) { + xmlChar *tmp; + +- if ((len > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NCName"); +- xmlFree(buffer); +- return(NULL); +- } + max *= 2; + tmp = (xmlChar *) xmlRealloc(buffer, + max * sizeof(xmlChar)); +@@ -3621,14 +3626,18 @@ xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) { + COPY_BUF(l,buffer,len,c); + cur += l; + c = CUR_SCHAR(cur, l); ++ if (len > maxLength) { ++ xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NCName"); ++ xmlFree(buffer); ++ return(NULL); ++ } + } + buffer[len] = 0; + *str = cur; + return(buffer); + } + } +- if ((len > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if (len > maxLength) { + xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NCName"); + return(NULL); + } +@@ -3655,6 +3664,9 @@ xmlParseNmtoken(xmlParserCtxtPtr ctxt) { + int len = 0, l; + int c; + int count = 0; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_TEXT_LENGTH : ++ XML_MAX_NAME_LENGTH; + + #ifdef DEBUG + nbParseNmToken++; +@@ -3706,12 +3718,6 @@ xmlParseNmtoken(xmlParserCtxtPtr ctxt) { + if (len + 10 > max) { + xmlChar *tmp; + +- if ((max > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NmToken"); +- xmlFree(buffer); +- return(NULL); +- } + max *= 2; + tmp = (xmlChar *) xmlRealloc(buffer, + max * sizeof(xmlChar)); +@@ -3725,6 +3731,11 @@ xmlParseNmtoken(xmlParserCtxtPtr ctxt) { + COPY_BUF(l,buffer,len,c); + NEXTL(l); + c = CUR_CHAR(l); ++ if (len > maxLength) { ++ xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NmToken"); ++ xmlFree(buffer); ++ return(NULL); ++ } + } + buffer[len] = 0; + return(buffer); +@@ -3732,8 +3743,7 @@ xmlParseNmtoken(xmlParserCtxtPtr ctxt) { + } + if (len == 0) + return(NULL); +- if ((len > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if (len > maxLength) { + xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NmToken"); + return(NULL); + } +@@ -3759,6 +3769,9 @@ xmlParseEntityValue(xmlParserCtxtPtr ctxt, xmlChar **orig) { + int len = 0; + int size = XML_PARSER_BUFFER_SIZE; + int c, l; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_HUGE_LENGTH : ++ XML_MAX_TEXT_LENGTH; + xmlChar stop; + xmlChar *ret = NULL; + const xmlChar *cur = NULL; +@@ -3818,6 +3831,12 @@ xmlParseEntityValue(xmlParserCtxtPtr ctxt, xmlChar **orig) { + GROW; + c = CUR_CHAR(l); + } ++ ++ if (len > maxLength) { ++ xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_NOT_FINISHED, ++ "entity value too long\n"); ++ goto error; ++ } + } + buf[len] = 0; + if (ctxt->instate == XML_PARSER_EOF) +@@ -3905,6 +3924,9 @@ xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen, int normalize) { + xmlChar *rep = NULL; + size_t len = 0; + size_t buf_size = 0; ++ size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_HUGE_LENGTH : ++ XML_MAX_TEXT_LENGTH; + int c, l, in_space = 0; + xmlChar *current = NULL; + xmlEntityPtr ent; +@@ -3936,16 +3958,6 @@ xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen, int normalize) { + while (((NXT(0) != limit) && /* checked */ + (IS_CHAR(c)) && (c != '<')) && + (ctxt->instate != XML_PARSER_EOF)) { +- /* +- * Impose a reasonable limit on attribute size, unless XML_PARSE_HUGE +- * special option is given +- */ +- if ((len > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, +- "AttValue length too long\n"); +- goto mem_error; +- } + if (c == '&') { + in_space = 0; + if (NXT(1) == '#') { +@@ -4093,6 +4105,11 @@ xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen, int normalize) { + } + GROW; + c = CUR_CHAR(l); ++ if (len > maxLength) { ++ xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, ++ "AttValue length too long\n"); ++ goto mem_error; ++ } + } + if (ctxt->instate == XML_PARSER_EOF) + goto error; +@@ -4114,16 +4131,6 @@ xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen, int normalize) { + } else + NEXT; + +- /* +- * There we potentially risk an overflow, don't allow attribute value of +- * length more than INT_MAX it is a very reasonable assumption ! +- */ +- if (len >= INT_MAX) { +- xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, +- "AttValue length too long\n"); +- goto mem_error; +- } +- + if (attlen != NULL) *attlen = (int) len; + return(buf); + +@@ -4194,6 +4201,9 @@ xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) { + int len = 0; + int size = XML_PARSER_BUFFER_SIZE; + int cur, l; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_TEXT_LENGTH : ++ XML_MAX_NAME_LENGTH; + xmlChar stop; + int state = ctxt->instate; + int count = 0; +@@ -4221,13 +4231,6 @@ xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) { + if (len + 5 >= size) { + xmlChar *tmp; + +- if ((size > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "SystemLiteral"); +- xmlFree(buf); +- ctxt->instate = (xmlParserInputState) state; +- return(NULL); +- } + size *= 2; + tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar)); + if (tmp == NULL) { +@@ -4256,6 +4259,12 @@ xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) { + SHRINK; + cur = CUR_CHAR(l); + } ++ if (len > maxLength) { ++ xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "SystemLiteral"); ++ xmlFree(buf); ++ ctxt->instate = (xmlParserInputState) state; ++ return(NULL); ++ } + } + buf[len] = 0; + ctxt->instate = (xmlParserInputState) state; +@@ -4283,6 +4292,9 @@ xmlParsePubidLiteral(xmlParserCtxtPtr ctxt) { + xmlChar *buf = NULL; + int len = 0; + int size = XML_PARSER_BUFFER_SIZE; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_TEXT_LENGTH : ++ XML_MAX_NAME_LENGTH; + xmlChar cur; + xmlChar stop; + int count = 0; +@@ -4310,12 +4322,6 @@ xmlParsePubidLiteral(xmlParserCtxtPtr ctxt) { + if (len + 1 >= size) { + xmlChar *tmp; + +- if ((size > XML_MAX_NAME_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "Public ID"); +- xmlFree(buf); +- return(NULL); +- } + size *= 2; + tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar)); + if (tmp == NULL) { +@@ -4343,6 +4349,11 @@ xmlParsePubidLiteral(xmlParserCtxtPtr ctxt) { + SHRINK; + cur = CUR; + } ++ if (len > maxLength) { ++ xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "Public ID"); ++ xmlFree(buf); ++ return(NULL); ++ } + } + buf[len] = 0; + if (cur != stop) { +@@ -4742,6 +4753,9 @@ xmlParseCommentComplex(xmlParserCtxtPtr ctxt, xmlChar *buf, + int r, rl; + int cur, l; + size_t count = 0; ++ size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_HUGE_LENGTH : ++ XML_MAX_TEXT_LENGTH; + int inputid; + + inputid = ctxt->input->id; +@@ -4787,13 +4801,6 @@ xmlParseCommentComplex(xmlParserCtxtPtr ctxt, xmlChar *buf, + if ((r == '-') && (q == '-')) { + xmlFatalErr(ctxt, XML_ERR_HYPHEN_IN_COMMENT, NULL); + } +- if ((len > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErrMsgStr(ctxt, XML_ERR_COMMENT_NOT_FINISHED, +- "Comment too big found", NULL); +- xmlFree (buf); +- return; +- } + if (len + 5 >= size) { + xmlChar *new_buf; + size_t new_size; +@@ -4831,6 +4838,13 @@ xmlParseCommentComplex(xmlParserCtxtPtr ctxt, xmlChar *buf, + GROW; + cur = CUR_CHAR(l); + } ++ ++ if (len > maxLength) { ++ xmlFatalErrMsgStr(ctxt, XML_ERR_COMMENT_NOT_FINISHED, ++ "Comment too big found", NULL); ++ xmlFree (buf); ++ return; ++ } + } + buf[len] = 0; + if (cur == 0) { +@@ -4875,6 +4889,9 @@ xmlParseComment(xmlParserCtxtPtr ctxt) { + xmlChar *buf = NULL; + size_t size = XML_PARSER_BUFFER_SIZE; + size_t len = 0; ++ size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_HUGE_LENGTH : ++ XML_MAX_TEXT_LENGTH; + xmlParserInputState state; + const xmlChar *in; + size_t nbchar = 0; +@@ -4958,8 +4975,7 @@ get_more: + buf[len] = 0; + } + } +- if ((len > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if (len > maxLength) { + xmlFatalErrMsgStr(ctxt, XML_ERR_COMMENT_NOT_FINISHED, + "Comment too big found", NULL); + xmlFree (buf); +@@ -5159,6 +5175,9 @@ xmlParsePI(xmlParserCtxtPtr ctxt) { + xmlChar *buf = NULL; + size_t len = 0; + size_t size = XML_PARSER_BUFFER_SIZE; ++ size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_HUGE_LENGTH : ++ XML_MAX_TEXT_LENGTH; + int cur, l; + const xmlChar *target; + xmlParserInputState state; +@@ -5234,14 +5253,6 @@ xmlParsePI(xmlParserCtxtPtr ctxt) { + return; + } + count = 0; +- if ((len > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErrMsgStr(ctxt, XML_ERR_PI_NOT_FINISHED, +- "PI %s too big found", target); +- xmlFree(buf); +- ctxt->instate = state; +- return; +- } + } + COPY_BUF(l,buf,len,cur); + NEXTL(l); +@@ -5251,15 +5262,14 @@ xmlParsePI(xmlParserCtxtPtr ctxt) { + GROW; + cur = CUR_CHAR(l); + } ++ if (len > maxLength) { ++ xmlFatalErrMsgStr(ctxt, XML_ERR_PI_NOT_FINISHED, ++ "PI %s too big found", target); ++ xmlFree(buf); ++ ctxt->instate = state; ++ return; ++ } + } +- if ((len > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErrMsgStr(ctxt, XML_ERR_PI_NOT_FINISHED, +- "PI %s too big found", target); +- xmlFree(buf); +- ctxt->instate = state; +- return; +- } + buf[len] = 0; + if (cur != '?') { + xmlFatalErrMsgStr(ctxt, XML_ERR_PI_NOT_FINISHED, +@@ -8954,6 +8964,9 @@ xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *len, int *alloc, + const xmlChar *in = NULL, *start, *end, *last; + xmlChar *ret = NULL; + int line, col; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_HUGE_LENGTH : ++ XML_MAX_TEXT_LENGTH; + + GROW; + in = (xmlChar *) CUR_PTR; +@@ -8993,8 +9006,7 @@ xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *len, int *alloc, + start = in; + if (in >= end) { + GROW_PARSE_ATT_VALUE_INTERNAL(ctxt, in, start, end) +- if (((in - start) > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if ((in - start) > maxLength) { + xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, + "AttValue length too long\n"); + return(NULL); +@@ -9007,8 +9019,7 @@ xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *len, int *alloc, + if ((*in++ == 0x20) && (*in == 0x20)) break; + if (in >= end) { + GROW_PARSE_ATT_VALUE_INTERNAL(ctxt, in, start, end) +- if (((in - start) > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if ((in - start) > maxLength) { + xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, + "AttValue length too long\n"); + return(NULL); +@@ -9041,16 +9052,14 @@ xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *len, int *alloc, + last = last + delta; + } + end = ctxt->input->end; +- if (((in - start) > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if ((in - start) > maxLength) { + xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, + "AttValue length too long\n"); + return(NULL); + } + } + } +- if (((in - start) > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if ((in - start) > maxLength) { + xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, + "AttValue length too long\n"); + return(NULL); +@@ -9063,8 +9072,7 @@ xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *len, int *alloc, + col++; + if (in >= end) { + GROW_PARSE_ATT_VALUE_INTERNAL(ctxt, in, start, end) +- if (((in - start) > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if ((in - start) > maxLength) { + xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, + "AttValue length too long\n"); + return(NULL); +@@ -9072,8 +9080,7 @@ xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *len, int *alloc, + } + } + last = in; +- if (((in - start) > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { ++ if ((in - start) > maxLength) { + xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, + "AttValue length too long\n"); + return(NULL); +@@ -9763,6 +9770,9 @@ xmlParseCDSect(xmlParserCtxtPtr ctxt) { + int s, sl; + int cur, l; + int count = 0; ++ int maxLength = (ctxt->options & XML_PARSE_HUGE) ? ++ XML_MAX_HUGE_LENGTH : ++ XML_MAX_TEXT_LENGTH; + + /* Check 2.6.0 was NXT(0) not RAW */ + if (CMP9(CUR_PTR, '<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[')) { +@@ -9796,13 +9806,6 @@ xmlParseCDSect(xmlParserCtxtPtr ctxt) { + if (len + 5 >= size) { + xmlChar *tmp; + +- if ((size > XML_MAX_TEXT_LENGTH) && +- ((ctxt->options & XML_PARSE_HUGE) == 0)) { +- xmlFatalErrMsgStr(ctxt, XML_ERR_CDATA_NOT_FINISHED, +- "CData section too big found", NULL); +- xmlFree (buf); +- return; +- } + tmp = (xmlChar *) xmlRealloc(buf, size * 2 * sizeof(xmlChar)); + if (tmp == NULL) { + xmlFree(buf); +@@ -9829,6 +9832,12 @@ xmlParseCDSect(xmlParserCtxtPtr ctxt) { + } + NEXTL(l); + cur = CUR_CHAR(l); ++ if (len > maxLength) { ++ xmlFatalErrMsg(ctxt, XML_ERR_CDATA_NOT_FINISHED, ++ "CData section too big found\n"); ++ xmlFree(buf); ++ return; ++ } + } + buf[len] = 0; + ctxt->instate = XML_PARSER_CONTENT; +-- +2.27.0 + diff --git a/backport-CVE-2022-40304-Fix-dict-corruption-caused-by-entity-.patch b/backport-CVE-2022-40304-Fix-dict-corruption-caused-by-entity-.patch new file mode 100644 index 0000000000000000000000000000000000000000..50443907c8c2a42da10b39e7df01c26c8b77a601 --- /dev/null +++ b/backport-CVE-2022-40304-Fix-dict-corruption-caused-by-entity-.patch @@ -0,0 +1,101 @@ +From 1b41ec4e9433b05bb0376be4725804c54ef1d80b Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Wed, 31 Aug 2022 22:11:25 +0200 +Subject: [PATCH] [CVE-2022-40304] Fix dict corruption caused by entity + reference cycles + +When an entity reference cycle is detected, the entity content is +cleared by setting its first byte to zero. But the entity content might +be allocated from a dict. In this case, the dict entry becomes corrupted +leading to all kinds of logic errors, including memory errors like +double-frees. + +Stop storing entity content, orig, ExternalID and SystemID in a dict. +These values are unlikely to occur multiple times in a document, so they +shouldn't have been stored in a dict in the first place. + +Thanks to Ned Williamson and Nathan Wachholz working with Google Project +Zero for the report! +--- + entities.c | 55 ++++++++++++++++-------------------------------------- + 1 file changed, 16 insertions(+), 39 deletions(-) + +diff --git a/entities.c b/entities.c +index 84435515..d4e5412e 100644 +--- a/entities.c ++++ b/entities.c +@@ -128,36 +128,19 @@ xmlFreeEntity(xmlEntityPtr entity) + if ((entity->children) && (entity->owner == 1) && + (entity == (xmlEntityPtr) entity->children->parent)) + xmlFreeNodeList(entity->children); +- if (dict != NULL) { +- if ((entity->name != NULL) && (!xmlDictOwns(dict, entity->name))) +- xmlFree((char *) entity->name); +- if ((entity->ExternalID != NULL) && +- (!xmlDictOwns(dict, entity->ExternalID))) +- xmlFree((char *) entity->ExternalID); +- if ((entity->SystemID != NULL) && +- (!xmlDictOwns(dict, entity->SystemID))) +- xmlFree((char *) entity->SystemID); +- if ((entity->URI != NULL) && (!xmlDictOwns(dict, entity->URI))) +- xmlFree((char *) entity->URI); +- if ((entity->content != NULL) +- && (!xmlDictOwns(dict, entity->content))) +- xmlFree((char *) entity->content); +- if ((entity->orig != NULL) && (!xmlDictOwns(dict, entity->orig))) +- xmlFree((char *) entity->orig); +- } else { +- if (entity->name != NULL) +- xmlFree((char *) entity->name); +- if (entity->ExternalID != NULL) +- xmlFree((char *) entity->ExternalID); +- if (entity->SystemID != NULL) +- xmlFree((char *) entity->SystemID); +- if (entity->URI != NULL) +- xmlFree((char *) entity->URI); +- if (entity->content != NULL) +- xmlFree((char *) entity->content); +- if (entity->orig != NULL) +- xmlFree((char *) entity->orig); +- } ++ if ((entity->name != NULL) && ++ ((dict == NULL) || (!xmlDictOwns(dict, entity->name)))) ++ xmlFree((char *) entity->name); ++ if (entity->ExternalID != NULL) ++ xmlFree((char *) entity->ExternalID); ++ if (entity->SystemID != NULL) ++ xmlFree((char *) entity->SystemID); ++ if (entity->URI != NULL) ++ xmlFree((char *) entity->URI); ++ if (entity->content != NULL) ++ xmlFree((char *) entity->content); ++ if (entity->orig != NULL) ++ xmlFree((char *) entity->orig); + xmlFree(entity); + } + +@@ -193,18 +176,12 @@ xmlCreateEntity(xmlDictPtr dict, const xmlChar *name, int type, + ret->SystemID = xmlStrdup(SystemID); + } else { + ret->name = xmlDictLookup(dict, name, -1); +- if (ExternalID != NULL) +- ret->ExternalID = xmlDictLookup(dict, ExternalID, -1); +- if (SystemID != NULL) +- ret->SystemID = xmlDictLookup(dict, SystemID, -1); ++ ret->ExternalID = xmlStrdup(ExternalID); ++ ret->SystemID = xmlStrdup(SystemID); + } + if (content != NULL) { + ret->length = xmlStrlen(content); +- if ((dict != NULL) && (ret->length < 5)) +- ret->content = (xmlChar *) +- xmlDictLookup(dict, content, ret->length); +- else +- ret->content = xmlStrndup(content, ret->length); ++ ret->content = xmlStrndup(content, ret->length); + } else { + ret->length = 0; + ret->content = NULL; +-- +2.27.0 + diff --git a/backport-pre-CVE-2022-40303-Remove-useless-comparisons.patch b/backport-pre-CVE-2022-40303-Remove-useless-comparisons.patch new file mode 100644 index 0000000000000000000000000000000000000000..99111f0eabb8c95d01677bf5c4d541a38b2fa162 --- /dev/null +++ b/backport-pre-CVE-2022-40303-Remove-useless-comparisons.patch @@ -0,0 +1,328 @@ +From 9bd7abfba41ca219ab39cb912f020f8e02116f32 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Thu, 2 Jan 2020 14:14:48 +0100 +Subject: [PATCH] Remove useless comparisons + +Found by lgtm.com +--- + nanoftp.c | 3 +- + parser.c | 1 - + valid.c | 43 ++++++------ + xmlregexp.c | 4 +- + xmlschemas.c | 191 +++++++++++++++++++++++++-------------------------- + 5 files changed, 117 insertions(+), 125 deletions(-) + +diff --git a/nanoftp.c b/nanoftp.c +index 54fa026d..80685da4 100644 +--- a/nanoftp.c ++++ b/nanoftp.c +@@ -1251,8 +1251,7 @@ xmlNanoFTPConnectTo(const char *server, int port) { + xmlNanoFTPFreeCtxt(ctxt); + return(NULL); + } +- if (port != 0) +- ctxt->port = port; ++ ctxt->port = port; + res = xmlNanoFTPConnect(ctxt); + if (res < 0) { + xmlNanoFTPFreeCtxt(ctxt); +diff --git a/parser.c b/parser.c +index a34bb6cd..43a1a0ab 100644 +--- a/parser.c ++++ b/parser.c +@@ -3912,7 +3912,6 @@ xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen, int normalize) { + "AttValue length too long\n"); + goto mem_error; + } +- if (c == 0) break; + if (c == '&') { + in_space = 0; + if (NXT(1) == '#') { +diff --git a/valid.c b/valid.c +index 07963e74..b828bc65 100644 +--- a/valid.c ++++ b/valid.c +@@ -5919,28 +5919,27 @@ xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) { + break; + case XML_ELEMENT_TYPE_MIXED: + break; +- case XML_ELEMENT_TYPE_ELEMENT: +- if (len > 0) { +- int i; +- +- for (i = 0;i < len;i++) { +- if (!IS_BLANK_CH(data[i])) { +- xmlErrValidNode(ctxt, state->node, +- XML_DTD_CONTENT_MODEL, +- "Element %s content does not follow the DTD, Text not allowed\n", +- state->node->name, NULL, NULL); +- ret = 0; +- goto done; +- } +- } +- /* +- * TODO: +- * VC: Standalone Document Declaration +- * element types with element content, if white space +- * occurs directly within any instance of those types. +- */ +- } +- break; ++ case XML_ELEMENT_TYPE_ELEMENT: { ++ int i; ++ ++ for (i = 0;i < len;i++) { ++ if (!IS_BLANK_CH(data[i])) { ++ xmlErrValidNode(ctxt, state->node, ++ XML_DTD_CONTENT_MODEL, ++ "Element %s content does not follow the DTD, Text not allowed\n", ++ state->node->name, NULL, NULL); ++ ret = 0; ++ goto done; ++ } ++ } ++ /* ++ * TODO: ++ * VC: Standalone Document Declaration ++ * element types with element content, if white space ++ * occurs directly within any instance of those types. ++ */ ++ break; ++ } + } + } + } +diff --git a/xmlregexp.c b/xmlregexp.c +index c119ff1f..5a2deb9e 100644 +--- a/xmlregexp.c ++++ b/xmlregexp.c +@@ -6055,7 +6055,7 @@ xmlAutomataNewOnceTrans2(xmlAutomataPtr am, xmlAutomataStatePtr from, + return(NULL); + if (min < 1) + return(NULL); +- if ((max < min) || (max < 1)) ++ if (max < min) + return(NULL); + atom = xmlRegNewAtom(am, XML_REGEXP_STRING); + if (atom == NULL) +@@ -6134,7 +6134,7 @@ xmlAutomataNewOnceTrans(xmlAutomataPtr am, xmlAutomataStatePtr from, + return(NULL); + if (min < 1) + return(NULL); +- if ((max < min) || (max < 1)) ++ if (max < min) + return(NULL); + atom = xmlRegNewAtom(am, XML_REGEXP_STRING); + if (atom == NULL) +diff --git a/xmlschemas.c b/xmlschemas.c +index d19de6df..301c8449 100644 +--- a/xmlschemas.c ++++ b/xmlschemas.c +@@ -24184,7 +24184,7 @@ xmlSchemaValidateFacets(xmlSchemaAbstractCtxtPtr actxt, + unsigned long length, + int fireErrors) + { +- int ret, error = 0; ++ int ret, error = 0, found; + + xmlSchemaTypePtr tmpType; + xmlSchemaFacetLinkPtr facetLink; +@@ -24308,103 +24308,98 @@ WXS_IS_LIST: + } + + pattern_and_enum: +- if (error >= 0) { +- int found = 0; +- /* +- * Process enumerations. Facet values are in the value space +- * of the defining type's base type. This seems to be a bug in the +- * XML Schema 1.0 spec. Use the whitespace type of the base type. +- * Only the first set of enumerations in the ancestor-or-self axis +- * is used for validation. +- */ +- ret = 0; +- tmpType = type; +- do { +- for (facet = tmpType->facets; facet != NULL; facet = facet->next) { +- if (facet->type != XML_SCHEMA_FACET_ENUMERATION) +- continue; +- found = 1; +- ret = xmlSchemaAreValuesEqual(facet->val, val); +- if (ret == 1) +- break; +- else if (ret < 0) { +- AERROR_INT("xmlSchemaValidateFacets", +- "validating against an enumeration facet"); +- return (-1); +- } +- } +- if (ret != 0) +- break; +- /* +- * Break on the first set of enumerations. Any additional +- * enumerations which might be existent on the ancestors +- * of the current type are restricted by this set; thus +- * *must* *not* be taken into account. +- */ +- if (found) +- break; +- tmpType = tmpType->baseType; +- } while ((tmpType != NULL) && +- (tmpType->type != XML_SCHEMA_TYPE_BASIC)); +- if (found && (ret == 0)) { +- ret = XML_SCHEMAV_CVC_ENUMERATION_VALID; +- if (fireErrors) { +- xmlSchemaFacetErr(actxt, ret, node, +- value, 0, type, NULL, NULL, NULL, NULL); +- } else +- return (ret); +- if (error == 0) +- error = ret; +- } +- } +- +- if (error >= 0) { +- int found; +- /* +- * Process patters. Pattern facets are ORed at type level +- * and ANDed if derived. Walk the base type axis. +- */ +- tmpType = type; +- facet = NULL; +- do { +- found = 0; +- for (facetLink = tmpType->facetSet; facetLink != NULL; +- facetLink = facetLink->next) { +- if (facetLink->facet->type != XML_SCHEMA_FACET_PATTERN) +- continue; +- found = 1; +- /* +- * NOTE that for patterns, @value needs to be the +- * normalized value. +- */ +- ret = xmlRegexpExec(facetLink->facet->regexp, value); +- if (ret == 1) +- break; +- else if (ret < 0) { +- AERROR_INT("xmlSchemaValidateFacets", +- "validating against a pattern facet"); +- return (-1); +- } else { +- /* +- * Save the last non-validating facet. +- */ +- facet = facetLink->facet; +- } +- } +- if (found && (ret != 1)) { +- ret = XML_SCHEMAV_CVC_PATTERN_VALID; +- if (fireErrors) { +- xmlSchemaFacetErr(actxt, ret, node, +- value, 0, type, facet, NULL, NULL, NULL); +- } else +- return (ret); +- if (error == 0) +- error = ret; +- break; +- } +- tmpType = tmpType->baseType; +- } while ((tmpType != NULL) && (tmpType->type != XML_SCHEMA_TYPE_BASIC)); +- } ++ found = 0; ++ /* ++ * Process enumerations. Facet values are in the value space ++ * of the defining type's base type. This seems to be a bug in the ++ * XML Schema 1.0 spec. Use the whitespace type of the base type. ++ * Only the first set of enumerations in the ancestor-or-self axis ++ * is used for validation. ++ */ ++ ret = 0; ++ tmpType = type; ++ do { ++ for (facet = tmpType->facets; facet != NULL; facet = facet->next) { ++ if (facet->type != XML_SCHEMA_FACET_ENUMERATION) ++ continue; ++ found = 1; ++ ret = xmlSchemaAreValuesEqual(facet->val, val); ++ if (ret == 1) ++ break; ++ else if (ret < 0) { ++ AERROR_INT("xmlSchemaValidateFacets", ++ "validating against an enumeration facet"); ++ return (-1); ++ } ++ } ++ if (ret != 0) ++ break; ++ /* ++ * Break on the first set of enumerations. Any additional ++ * enumerations which might be existent on the ancestors ++ * of the current type are restricted by this set; thus ++ * *must* *not* be taken into account. ++ */ ++ if (found) ++ break; ++ tmpType = tmpType->baseType; ++ } while ((tmpType != NULL) && ++ (tmpType->type != XML_SCHEMA_TYPE_BASIC)); ++ if (found && (ret == 0)) { ++ ret = XML_SCHEMAV_CVC_ENUMERATION_VALID; ++ if (fireErrors) { ++ xmlSchemaFacetErr(actxt, ret, node, ++ value, 0, type, NULL, NULL, NULL, NULL); ++ } else ++ return (ret); ++ if (error == 0) ++ error = ret; ++ } ++ ++ /* ++ * Process patters. Pattern facets are ORed at type level ++ * and ANDed if derived. Walk the base type axis. ++ */ ++ tmpType = type; ++ facet = NULL; ++ do { ++ found = 0; ++ for (facetLink = tmpType->facetSet; facetLink != NULL; ++ facetLink = facetLink->next) { ++ if (facetLink->facet->type != XML_SCHEMA_FACET_PATTERN) ++ continue; ++ found = 1; ++ /* ++ * NOTE that for patterns, @value needs to be the ++ * normalized value. ++ */ ++ ret = xmlRegexpExec(facetLink->facet->regexp, value); ++ if (ret == 1) ++ break; ++ else if (ret < 0) { ++ AERROR_INT("xmlSchemaValidateFacets", ++ "validating against a pattern facet"); ++ return (-1); ++ } else { ++ /* ++ * Save the last non-validating facet. ++ */ ++ facet = facetLink->facet; ++ } ++ } ++ if (found && (ret != 1)) { ++ ret = XML_SCHEMAV_CVC_PATTERN_VALID; ++ if (fireErrors) { ++ xmlSchemaFacetErr(actxt, ret, node, ++ value, 0, type, facet, NULL, NULL, NULL); ++ } else ++ return (ret); ++ if (error == 0) ++ error = ret; ++ break; ++ } ++ tmpType = tmpType->baseType; ++ } while ((tmpType != NULL) && (tmpType->type != XML_SCHEMA_TYPE_BASIC)); + + return (error); + } +-- +2.27.0 + diff --git a/libxml2.spec b/libxml2.spec index 40dd7a8feb1f97481fc053a6c42deea16109f1cd..f110b8bf3cbb3edd2d0bb764c5027b722c3f5388 100644 --- a/libxml2.spec +++ b/libxml2.spec @@ -1,7 +1,7 @@ Summary: Library providing XML and HTML support Name: libxml2 Version: 2.9.10 -Release: 33 +Release: 34 License: MIT Group: Development/Libraries Source: ftp://xmlsoft.org/libxml2/libxml2-%{version}.tar.gz @@ -123,6 +123,9 @@ Patch110:backport-CVE-2022-29824-Fix-integer-overflows-in-xmlBuf-and-xmlBuffer.p Patch111:Fix-memory-leaks-for-xmlACatalogAdd.patch Patch112:Fix-memory-leaks-in-xmlACatalogAdd-when-xmlHashAddEntry-failed.patch Patch113:backport-CVE-2016-3709.patch +Patch114:backport-pre-CVE-2022-40303-Remove-useless-comparisons.patch +Patch115:backport-CVE-2022-40303-Fix-integer-overflows-with-XML_PARSE_.patch +Patch116:backport-CVE-2022-40304-Fix-dict-corruption-caused-by-entity-.patch BuildRoot: %{_tmppath}/%{name}-%{version}-root BuildRequires: python2-devel @@ -314,6 +317,9 @@ rm -fr %{buildroot} %changelog +* Tue Nov 08 2022 fuanan - 2.9.10-34 +- fix CVE-2022-40303 CVE-2022-40304 + * Wed Sep 14 2022 hubin - 2.9.10-33 - remove recommend in spec