Repository: gnome/libxml2
Fuzzing engine: libfuzzer
Fuzz target: schema
Sanitizer: address
Crash type: Global-buffer-overflow READ 1

Sanitizer report:
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 8715661
INFO: Loaded 1 modules   (52213 inline 8-bit counters): 52213 [0x55e45e287a18, 0x55e45e29460d), 
INFO: Loaded 1 PC tables (52213 PCs): 52213 [0x55e45e294610,0x55e45e360560), 
/out/schema: Running 1 inputs 1 time(s) each.
Running: /testcase/OSV-2023-212.poc
=================================================================
==7==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55e45e1c76c6 at pc 0x55e45de9f579 bp 0x7ffd41ebc660 sp 0x7ffd41ebc658
READ of size 1 at 0x55e45e1c76c6 thread T0
SCARINESS: 12 (1-byte-read-global-buffer-overflow)
    #0 0x55e45de9f578 in xmlParseEntityDecl /src/libxml2/parser.c:5364:2
    #1 0x55e45deaaed4 in xmlParseMarkupDecl /src/libxml2/parser.c:6831:4
    #2 0x55e45dec7e8d in xmlParseInternalSubset /src/libxml2/parser.c:8335:10
    #3 0x55e45dec682f in xmlParseDocument /src/libxml2/parser.c:10763:6
    #4 0x55e45dedd444 in xmlDoRead /src/libxml2/parser.c:14853:5
    #5 0x55e45dedd444 in xmlCtxtReadFile /src/libxml2/parser.c:15086:13
    #6 0x55e45e0430de in xmlSchemaAddSchemaDoc /src/libxml2/xmlschemas.c:10661:12
    #7 0x55e45e0419b6 in xmlSchemaParse /src/libxml2/xmlschemas.c:21489:11
    #8 0x55e45de5d917 in LLVMFuzzerTestOneInput /src/libxml2/fuzz/schema.c:41:19
    #9 0x55e45dd12390 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:614:13
    #10 0x55e45dcfd605 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:327:6
    #11 0x55e45dd0309f in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:862:9
    #12 0x55e45dd2e342 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #13 0x7f9356ffc082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 0702430aef5fa3dda43986563e9ffcc47efbd75e)
    #14 0x55e45dcf57ed in _start (/out/schema+0x1ce7ed)

DEDUP_TOKEN: xmlParseEntityDecl--xmlParseMarkupDecl--xmlParseInternalSubset
0x55e45e1c76c6 is located 26 bytes before global variable '.str.5' defined in 'parserInternals.c' (0x55e45e1c76e0) of size 18
  '.str.5' is ascii string 'Huge input lookup'
0x55e45e1c76c6 is located 5 bytes after global variable '.str.4' defined in 'parserInternals.c' (0x55e45e1c76c0) of size 1
  '.str.4' is ascii string ''
SUMMARY: AddressSanitizer: global-buffer-overflow /src/libxml2/parser.c:5364:2 in xmlParseEntityDecl
Shadow bytes around the buggy address:
  0x55e45e1c7400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x55e45e1c7480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x55e45e1c7500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x55e45e1c7580: 00 00 00 00 00 00 00 03 f9 f9 f9 f9 00 00 00 00
  0x55e45e1c7600: 00 00 00 04 f9 f9 f9 f9 00 00 00 07 f9 f9 f9 f9
=>0x55e45e1c7680: 00 00 00 02 f9 f9 f9 f9[01]f9 f9 f9 00 00 02 f9
  0x55e45e1c7700: f9 f9 f9 f9 00 00 05 f9 f9 f9 f9 f9 00 00 00 00
  0x55e45e1c7780: f9 f9 f9 f9 00 00 00 00 f9 f9 f9 f9 00 00 00 00
  0x55e45e1c7800: 00 00 f9 f9 f9 f9 f9 f9 00 00 00 00 04 f9 f9 f9
  0x55e45e1c7880: f9 f9 f9 f9 00 00 00 00 00 00 02 f9 f9 f9 f9 f9
  0x55e45e1c7900: 00 00 00 07 f9 f9 f9 f9 00 00 00 00 00 00 00 f9
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==7==ABORTING


Fix patch:
diff --git a/HTMLparser.c b/HTMLparser.c
index 81bd11f9..3bebda6e 100644
--- a/HTMLparser.c
+++ b/HTMLparser.c
@@ -3100,7 +3100,6 @@ htmlParseScript(htmlParserCtxtPtr ctxt) {
     int nbchar = 0;
     int cur,l;
 
-    SHRINK;
     cur = CUR_CHAR(l);
     while (cur != 0) {
 	if ((cur == '<') && (NXT(1) == '/')) {
@@ -3358,7 +3357,6 @@ htmlParsePI(htmlParserCtxtPtr ctxt) {
 	 * this is a Processing Instruction.
 	 */
 	SKIP(2);
-	SHRINK;
 
 	/*
 	 * Parse the target name and check for special support like
@@ -3481,7 +3479,6 @@ htmlParseComment(htmlParserCtxtPtr ctxt) {
 
     state = ctxt->instate;
     ctxt->instate = XML_PARSER_COMMENT;
-    SHRINK;
     SKIP(4);
     buf = (xmlChar *) xmlMallocAtomic(size);
     if (buf == NULL) {
@@ -4477,8 +4474,8 @@ htmlParseContent(htmlParserCtxtPtr ctxt) {
             htmlParseCharData(ctxt);
         }
 
-        GROW;
         SHRINK;
+        GROW;
     }
     if (currentNode != NULL) xmlFree(currentNode);
 }
@@ -4920,8 +4917,8 @@ htmlParseContentInternal(htmlParserCtxtPtr ctxt) {
             htmlParseCharData(ctxt);
         }
 
-        GROW;
         SHRINK;
+        GROW;
     }
     if (currentNode != NULL) xmlFree(currentNode);
 }
diff --git a/include/private/parser.h b/include/private/parser.h
index 18036db5..820bb587 100644
--- a/include/private/parser.h
+++ b/include/private/parser.h
@@ -27,7 +27,7 @@ XML_HIDDEN void
 xmlHaltParser(xmlParserCtxtPtr ctxt);
 XML_HIDDEN int
 xmlParserGrow(xmlParserCtxtPtr ctxt);
-XML_HIDDEN int
+XML_HIDDEN void
 xmlParserShrink(xmlParserCtxtPtr ctxt);
 
 #endif /* XML_PARSER_H_PRIVATE__ */
diff --git a/parser.c b/parser.c
index 8e548cda..bf4d08bd 100644
--- a/parser.c
+++ b/parser.c
@@ -4183,7 +4183,6 @@ xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) {
     xmlChar stop;
     int state = ctxt->instate;
 
-    SHRINK;
     if (RAW == '"') {
         NEXT;
 	stop = '"';
@@ -4265,7 +4264,6 @@ xmlParsePubidLiteral(xmlParserCtxtPtr ctxt) {
     xmlChar stop;
     xmlParserInputState oldstate = ctxt->instate;
 
-    SHRINK;
     if (RAW == '"') {
         NEXT;
 	stop = '"';
@@ -4387,7 +4385,6 @@ xmlParseCharData(xmlParserCtxtPtr ctxt, ATTRIBUTE_UNUSED int cdata) {
     int col = ctxt->input->col;
     int ccol;
 
-    SHRINK;
     GROW;
     /*
      * Accelerated common case where input don't need to be
@@ -4533,7 +4530,6 @@ xmlParseCharDataComplex(xmlParserCtxtPtr ctxt) {
     int nbchar = 0;
     int cur, l;
 
-    SHRINK;
     cur = CUR_CHAR(l);
     while ((cur != '<') && /* checked */
            (cur != '&') &&
@@ -4629,8 +4625,6 @@ xmlChar *
 xmlParseExternalID(xmlParserCtxtPtr ctxt, xmlChar **publicID, int strict) {
     xmlChar *URI = NULL;
 
-    SHRINK;
-
     *publicID = NULL;
     if (CMP6(CUR_PTR, 'S', 'Y', 'S', 'T', 'E', 'M')) {
         SKIP(6);
@@ -4847,7 +4841,6 @@ xmlParseComment(xmlParserCtxtPtr ctxt) {
     ctxt->instate = XML_PARSER_COMMENT;
     inputid = ctxt->input->id;
     SKIP(2);
-    SHRINK;
     GROW;
 
     /*
@@ -5133,7 +5126,6 @@ xmlParsePI(xmlParserCtxtPtr ctxt) {
 	 * this is a Processing Instruction.
 	 */
 	SKIP(2);
-	SHRINK;
 
 	/*
 	 * Parse the target name and check for special support like
@@ -5272,7 +5264,6 @@ xmlParseNotationDecl(xmlParserCtxtPtr ctxt) {
 
     if (CMP8(CUR_PTR, 'N', 'O', 'T', 'A', 'T', 'I', 'O', 'N')) {
 	int inputid = ctxt->input->id;
-	SHRINK;
 	SKIP(8);
 	if (SKIP_BLANKS == 0) {
 	    xmlFatalErrMsg(ctxt, XML_ERR_SPACE_REQUIRED,
@@ -5360,7 +5351,6 @@ xmlParseEntityDecl(xmlParserCtxtPtr ctxt) {
     /* GROW; done in the caller */
     if (CMP6(CUR_PTR, 'E', 'N', 'T', 'I', 'T', 'Y')) {
 	int inputid = ctxt->input->id;
-	SHRINK;
 	SKIP(6);
 	if (SKIP_BLANKS == 0) {
 	    xmlFatalErrMsg(ctxt, XML_ERR_SPACE_REQUIRED,
@@ -5684,7 +5674,6 @@ xmlParseNotationType(xmlParserCtxtPtr ctxt) {
 	xmlFatalErr(ctxt, XML_ERR_NOTATION_NOT_STARTED, NULL);
 	return(NULL);
     }
-    SHRINK;
     do {
         NEXT;
 	SKIP_BLANKS;
@@ -5756,7 +5745,6 @@ xmlParseEnumerationType(xmlParserCtxtPtr ctxt) {
 	xmlFatalErr(ctxt, XML_ERR_ATTLIST_NOT_STARTED, NULL);
 	return(NULL);
     }
-    SHRINK;
     do {
         NEXT;
 	SKIP_BLANKS;
@@ -5885,7 +5873,6 @@ xmlParseEnumeratedType(xmlParserCtxtPtr ctxt, xmlEnumerationPtr *tree) {
  */
 int
 xmlParseAttributeType(xmlParserCtxtPtr ctxt, xmlEnumerationPtr *tree) {
-    SHRINK;
     if (CMP5(CUR_PTR, 'C', 'D', 'A', 'T', 'A')) {
 	SKIP(5);
 	return(XML_ATTRIBUTE_CDATA);
@@ -6070,7 +6057,6 @@ xmlParseElementMixedContentDecl(xmlParserCtxtPtr ctxt, int inputchk) {
     if (CMP7(CUR_PTR, '#', 'P', 'C', 'D', 'A', 'T', 'A')) {
 	SKIP(7);
 	SKIP_BLANKS;
-	SHRINK;
 	if (RAW == ')') {
 	    if (ctxt->input->id != inputchk) {
 		xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY,
@@ -6242,7 +6228,6 @@ xmlParseElementChildrenContentDeclPriv(xmlParserCtxtPtr ctxt, int inputchk,
 	GROW;
     }
     SKIP_BLANKS;
-    SHRINK;
     while ((RAW != ')') && (ctxt->instate != XML_PARSER_EOF)) {
         /*
 	 * Each loop we parse one separator and one element.
@@ -6787,6 +6772,7 @@ xmlParseConditionalSections(xmlParserCtxtPtr ctxt) {
             break;
 
         SKIP_BLANKS;
+        SHRINK;
         GROW;
     }
 
@@ -7018,6 +7004,7 @@ xmlParseExternalSubset(xmlParserCtxtPtr ctxt, const xmlChar *ExternalID,
             return;
         }
         SKIP_BLANKS;
+        SHRINK;
     }
 
     if (RAW != 0) {
@@ -8343,6 +8330,8 @@ xmlParseInternalSubset(xmlParserCtxtPtr ctxt) {
                 return;
             }
 	    SKIP_BLANKS;
+            SHRINK;
+            GROW;
 	}
 	if (RAW == ']') {
 	    NEXT;
@@ -9229,14 +9218,6 @@ xmlParseStartTag2(xmlParserCtxtPtr ctxt, const xmlChar **pref,
     if (RAW != '<') return(NULL);
     NEXT1;
 
-    /*
-     * NOTE: it is crucial with the SAX2 API to never call SHRINK beyond that
-     *       point since the attribute values may be stored as pointers to
-     *       the buffer and calling SHRINK would destroy them !
-     *       The Shrinking is only possible once the full set of attribute
-     *       callbacks have been done.
-     */
-    SHRINK;
     cur = ctxt->input->cur - ctxt->input->base;
     inputid = ctxt->input->id;
     nbatts = 0;
@@ -9881,8 +9862,8 @@ xmlParseContentInternal(xmlParserCtxtPtr ctxt) {
 	    xmlParseCharData(ctxt, 0);
 	}
 
-	GROW;
 	SHRINK;
+	GROW;
     }
 }
 
diff --git a/parserInternals.c b/parserInternals.c
index dd165790..ce4f75e0 100644
--- a/parserInternals.c
+++ b/parserInternals.c
@@ -409,17 +409,16 @@ xmlParserInputGrow(xmlParserInputPtr in, int len) {
  * xmlParserShrink:
  * @ctxt:  an XML parser context
  */
-int
+void
 xmlParserShrink(xmlParserCtxtPtr ctxt) {
     xmlParserInputPtr in = ctxt->input;
     xmlParserInputBufferPtr buf = in->buf;
     size_t used;
-    int ret = 0;
 
     /* Don't shrink memory buffers. */
     if ((buf == NULL) ||
         ((buf->encoder == NULL) && (buf->readcallback == NULL)))
-        return(0);
+        return;
 
     used = in->cur - in->base;
     /*
@@ -439,18 +438,7 @@ xmlParserShrink(xmlParserCtxtPtr ctxt) {
 	}
     }
 
-    if (xmlBufUse(buf->buffer) < INPUT_CHUNK)
-        ret = xmlParserInputBufferGrow(buf, INPUT_CHUNK);
-
     xmlBufSetInputBaseCur(buf->buffer, in, 0, used);
-
-    /* TODO: Get error code from xmlParserInputBufferGrow */
-    if (ret < 0) {
-        xmlErrInternal(ctxt, "Growing input buffer", NULL);
-        xmlHaltParser(ctxt);
-    }
-
-    return(ret);
 }
 
 /**