Finding #6

Note : while fuzzing gnustep-base, claude built the needed tools
as source files, scripts and data files in a Tests/fuzz/ folder. I can
send the corresponding patches if you think they should be included
in the repository. It needs clang with libFuzzer and AddressSanitizer.

Hope this helps,
Philippe

=========

Discovery: found by fuzzing GSSloppyXMLParser AddressSanitizer reported
heap-buffer-overflow in -[GSStandaloneXMLParser parse] and in fixBOM()
called from NewUTF8STR().  A minimal reproducer is "4??x<".

Fix: report NSXMLParserPrematureDocumentEndError when the end of the
input is reached while scanning a tag name.  Also make NewUTF8STR()
refuse a negative length, so that any other error in the parser's
position arithmetic cannot turn into a huge read.

Test: Tests/base/NSXMLParser/standalone-truncated-tag.m

Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 Source/Additions/GSStandaloneXMLParser.m      | 18 ++++++
 .../NSXMLParser/standalone-truncated-tag.m    | 55 +++++++++++++++++++
 2 files changed, 73 insertions(+)
 create mode 100644 Tests/base/NSXMLParser/standalone-truncated-tag.m

diff --git a/Source/Additions/GSStandaloneXMLParser.m 
b/Source/Additions/GSStandaloneXMLParser.m
index 80bd5c281..29e185a59 100644
--- a/Source/Additions/GSStandaloneXMLParser.m
+++ b/Source/Additions/GSStandaloneXMLParser.m
@@ -51,6 +51,11 @@ NewUTF8STR(const void *ptr, int len)
 {
   NSString     *s;
 
+  if (len < 0)
+    {
+      NSLog(@"invalid string length %d in XML parser", len);
+      return nil;
+    }
   s = [[NSString alloc] initWithBytes: ptr
                               length: len
                             encoding: NSUTF8StringEncoding];
@@ -1467,6 +1475,12 @@ NSLog(@"_processTag <%@%@ %@>", flag?@"/": @"", tag, 
attributes);
                 {
                   c = cget(); // scan tag until we find a delimiting character
                 }
+              if (c == EOF)
+                {
+                  return [self _parseError:
+                    @"unexpected end of document inside tag"
+                    code: NSXMLParserPrematureDocumentEndError];
+                }
               if (*addr(tp) == '/')
                 {
                   tag = NewUTF8STR(addr(tp + 1), this->cp - tp - 2);
diff --git a/Tests/base/NSXMLParser/standalone-truncated-tag.m 
b/Tests/base/NSXMLParser/standalone-truncated-tag.m
new file mode 100644
index 000000000..31228b7b5
--- /dev/null
+++ b/Tests/base/NSXMLParser/standalone-truncated-tag.m
@@ -0,0 +1,55 @@
+/*
+ * GNUstep's own XML parser (GSStandaloneXMLParser/GSSloppyXMLParser,
+ * also used to read XML property lists) must not read beyond the end of
+ * its input when the document ends inside a tag such as "x<" or "</".
+ * Found by fuzzing (ASan heap-buffer-overflow in
+ * -[GSStandaloneXMLParser parse]).
+ */
+#import <Foundation/Foundation.h>
+#import "ObjectTesting.h"
+
+static BOOL
+parseBytes(Class c, const char *xml, NSInteger *code)
+{
+  NSData       *d = [NSData dataWithBytes: xml length: strlen(xml)];
+  NSXMLParser  *p = [[c alloc] initWithData: d];
+  BOOL         ok;
+
+  ok = [p parse];
+  *code = [[p parserError] code];
+  RELEASE(p);
+  return ok;
+}
+
+int
+main(int argc, char *argv[])
+{
+  NSAutoreleasePool    *arp = [NSAutoreleasePool new];
+  Class                        c = NSClassFromString(@"GSSloppyXMLParser");
+  NSInteger            code;
+  unsigned             i;
+  const char           *truncated[] = {
+    "<", "x<", "<r>text<", "<r></", "<r><?", "<r><abc", "<r></r", 0
+  };
+
+  START_SET("GSStandaloneXMLParser truncated tags")
+
+  if (c == Nil)
+    SKIP("GSSloppyXMLParser not available")
+
+  PASS(parseBytes(c, "<r><a/>text</r>", &code), "well-formed document parses")
+
+  for (i = 0; truncated[i] != 0; i++)
+    {
+      PASS(parseBytes(c, truncated[i], &code) == NO,
+       "document truncated in a tag (\"%s\") is rejected", truncated[i])
+    }
+  PASS(parseBytes(c, "<r><abc", &code) == NO
+    && code == NSXMLParserPrematureDocumentEndError,
+    "truncated tag reports NSXMLParserPrematureDocumentEndError")
+
+  END_SET("GSStandaloneXMLParser truncated tags")
+
+  [arp release];
+  return 0;
+}
-- 
2.39.5

Reply via email to