From: CPANSec Security Scanner Bot Subject: [PATCH] XML::Bare: bounds truncated fixed-advance lookahead (heap OOB read) Heap-buffer-overflow READ (CWE-125) in the hand-rolled C parser (parser.c), reached by the default XML::Bare->new(text=>$xml)->parse on untrusted XML. Several transitions advance `cpos` by a fixed amount past a recognised token without checking the buffer end, then dereference the new position: - the `` follows the `/`, so a truncated tail such as `` when `*(cpos+1)` is non-NUL. Both changes are behaviour-preserving for well-formed input — real CDATA always carries the `[`, and a non-truncated self-close always has a byte after the `/`; they differ only on the truncated-tail case that previously overran the allocation. --- a/parser.c +++ b/parser.c @@ -193,7 +193,8 @@ *(cpos+4) == 'D' && *(cpos+5) == 'A' && *(cpos+6) == 'T' && - *(cpos+7) == 'A' ) { + *(cpos+7) == 'A' && + *(cpos+8) == '[' ) { // require full "type = 1; goto cdata; @@ -344,7 +345,7 @@ temp = nodec_addchildr( curnode, tagname, tagname_len ); temp->z = cpos +1 - xmlin; tagname_len = 0; - cpos+=2; + if( *(cpos+1) ) cpos += 2; else cpos++; // skip assumed '>' only if not the NUL terminator goto val_1; } @@ -368,7 +369,7 @@ curnode->z = cpos+1-xmlin; curnode = curnode->parent; if( !curnode ) goto done; - cpos+=2; // am assuming next char is > + if( *(cpos+1) ) cpos += 2; else cpos++; // was: assume next char is > (skip past NUL on truncated tail) goto val_1; case '=': cpos++; @@ -425,7 +426,7 @@ curnode->z = cpos+1-xmlin; curnode = curnode->parent; if( !curnode ) goto done; - cpos += 2; + if( *(cpos+1) ) cpos += 2; else cpos++; // "/> assumed" — skip '>' only if present, not the NUL goto val_1; case ' ': if( *(cpos+1) == '=' ) {