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
