After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 752902 - signed integer overflow and index out of bound in uri.c and parser.c
signed integer overflow and index out of bound in uri.c and parser.c
Status: RESOLVED NOTABUG
Product: libxml2
Classification: Platform
Component: general
git master
Other Linux
: Normal normal
: ---
Assigned To: Daniel Veillard
libxml QA maintainers
Depends on:
Blocks:
 
 
Reported: 2015-07-26 18:13 UTC by Dingbao Xie
Modified: 2016-05-24 03:39 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
inputs to reproduce the undefined behaviors (377 bytes, application/gzip)
2015-07-26 18:13 UTC, Dingbao Xie
Details

Description Dingbao Xie 2015-07-26 18:13:32 UTC
Created attachment 308184 [details]
inputs to reproduce the undefined behaviors

Hey, after fuzzing libxml2 with afl for a night, I found one signed integer overflow and several out of bound access to array xmlChar*.

To reproduce the bug, you need to build the source code with flag '-fsanitize=undefined'
Then execute command:
./xmllint $file (see attachment)
You will see the following error information:
uri.c:334:25: runtime error: signed integer overflow: 555555555 * 10 cannot be represented in type 'int'
parser.c:1273:5: runtime error: index 5 out of bounds for type 'const xmlChar *[5]'
parser.c:1274:5: runtime error: index 6 out of bounds for type 'const xmlChar *[5]'
parser.c:1278:5: runtime error: index 7 out of bounds for type 'const xmlChar *[5]'
parser.c:1279:5: runtime error: index 8 out of bounds for type 'const xmlChar *[5]'
parser.c:1283:9: runtime error: index 9 out of bounds for type 'const xmlChar *[5]'
parser.c:9584:20: runtime error: index 5 out of bounds for type 'const xmlChar *[5]'
parser.c:9585:13: runtime error: index 6 out of bounds for type 'const xmlChar *[5]'
parser.c:9643:24: runtime error: index 7 out of bounds for type 'const xmlChar *[5]'
parser.c:9644:24: runtime error: index 8 out of bounds for type 'const xmlChar *[5]'
parser.c:9600:21: runtime error: index 7 out of bounds for type 'const xmlChar *[5]'
parser.c:9602:15: runtime error: index 7 out of bounds for type 'const xmlChar *[5]'
Comment 1 Dingbao Xie 2015-07-26 18:14:14 UTC
Version information:
./local/bin/xmllint: using libxml version 20902-GITv2.9.2-30-g3eaedba
   compiled with: Threads Tree Output Push Reader Patterns Writer SAXv1 FTP HTTP DTDValid HTML Legacy C14N Catalog XPath XPointer XInclude Iconv ISO8859X Unicode Regexps Automata Expr Schemas Schematron Modules Debug Zlib
Comment 2 Gaurav 2015-10-05 10:05:33 UTC
Malloc is done for "defaults":
 1233         defaults = (xmlDefAttrsPtr) xmlMalloc(sizeof(xmlDefAttrs) +
 1234                            (4 * 5) * sizeof(const xmlChar *));
But accessed is default->values which is :
const xmlChar *values[5]; /* array of localname/prefix/values/external */
It is accessed at positions 5 & above.
 1273     defaults->values[5 * defaults->nbAttrs] = name;
 1274     defaults->values[5 * defaults->nbAttrs + 1] = prefix;
 1275     /* intern the string and precompute the end */
 1276     len = xmlStrlen(value);
 1277     value = xmlDictLookup(ctxt->dict, value, len);
 1278     defaults->values[5 * defaults->nbAttrs + 2] = value;
 1279     defaults->values[5 * defaults->nbAttrs + 3] = value + len;
 1280     if (ctxt->external)
 1281         defaults->values[5 * defaults->nbAttrs + 4] = BAD_CAST "external";
 1282     else
 1283         defaults->values[5 * defaults->nbAttrs + 4] = NULL;

Is this correct?
Comment 3 David Kilzer 2016-05-20 23:00:12 UTC
(In reply to Dingbao Xie from comment #0)
> Created attachment 308184 [details]
> inputs to reproduce the undefined behaviors
> 
> Hey, after fuzzing libxml2 with afl for a night, I found one signed integer
> overflow and several out of bound access to array xmlChar*.
> 
> To reproduce the bug, you need to build the source code with flag
> '-fsanitize=undefined'
> Then execute command:
> ./xmllint $file (see attachment)
> You will see the following error information:
> uri.c:334:25: runtime error: signed integer overflow: 555555555 * 10 cannot
> be represented in type 'int'

This issue is tracked here:

Bug 765566: Integer overflow parsing port number in uri.c
Comment 4 Daniel Veillard 2016-05-21 08:23:19 UTC
I don't think it's a bug, we use 

struct _xmlDefAttrs {
    int nbAttrs;        /* number of defaulted attributes on that element */
    int maxAttrs;       /* the size of the array */
    const xmlChar *values[5]; /* array of localname/prefix/values/external */
};

as the base struct but store more than 5 strings and the allocation is done
accordingly as Gaurav pointed. Your compiler may barf but the memory access and allocations are a priori correct.

Daniel
Comment 5 David Kilzer 2016-05-24 03:27:12 UTC
(In reply to Gaurav from comment #2)
> Malloc is done for "defaults":
>  1233         defaults = (xmlDefAttrsPtr) xmlMalloc(sizeof(xmlDefAttrs) +
>  1234                            (4 * 5) * sizeof(const xmlChar *));
> But accessed is default->values which is :
> const xmlChar *values[5]; /* array of localname/prefix/values/external */
> It is accessed at positions 5 & above.
>  1273     defaults->values[5 * defaults->nbAttrs] = name;
>  1274     defaults->values[5 * defaults->nbAttrs + 1] = prefix;
>  1275     /* intern the string and precompute the end */
>  1276     len = xmlStrlen(value);
>  1277     value = xmlDictLookup(ctxt->dict, value, len);
>  1278     defaults->values[5 * defaults->nbAttrs + 2] = value;
>  1279     defaults->values[5 * defaults->nbAttrs + 3] = value + len;
>  1280     if (ctxt->external)
>  1281         defaults->values[5 * defaults->nbAttrs + 4] = BAD_CAST
> "external";
>  1282     else
>  1283         defaults->values[5 * defaults->nbAttrs + 4] = NULL;
> 
> Is this correct?

TL;DR: This is a false positive.

In practice, there is no undefined behavior here since enough memory is allocated earlier in the method to store all the values, but the field is not defined the way it's used since it's a one-dimensional array of 5 items, and having more than one attribute means the code will write past the first 5 items off the "end" of the array:

typedef struct _xmlDefAttrs xmlDefAttrs;
typedef xmlDefAttrs *xmlDefAttrsPtr;
struct _xmlDefAttrs {
    int nbAttrs;	/* number of defaulted attributes on that element */
    int maxAttrs;       /* the size of the array */
    const xmlChar *values[5]; /* array of localname/prefix/values/external */
};

What you really want is a two-dimensional array with 5 items for each row:

typedef struct _xmlDefAttrs xmlDefAttrs;
typedef xmlDefAttrs *xmlDefAttrsPtr;
struct _xmlDefAttrs {
    int nbAttrs;	/* number of defaulted attributes on that element */
    int maxAttrs;       /* the size of the array */
    const xmlChar *values[][5]; /* array of localname/prefix/values/external */
};

Making this change forces one to clean up the code in xmlAddDefAttrs() and xmlParseStartTag2().

In fact, making this change and cleaning up the code leads to a fix for the following buggy code:

		    nsname = xmlGetNamespace(ctxt, attname);
		    if (nsname != defaults->values[2]) {
			if (nsPush(ctxt, attname,
			           defaults->values[5 * i + 2]) > 0)
			    nbNs++;
		    }

The second line above is missing the calculation to get the correct value out of defaults->values.  The correct version is this:

		    nsname = xmlGetNamespace(ctxt, attname);
		    if (nsname != defaults->values[i][2]) {
			if (nsPush(ctxt, attname,
			           defaults->values[i][2]) > 0)
			    nbNs++;
		    }

Sadly, there doesn't appear to be any test coverage for this code as it doesn't cause any existing tests to change their results.

I can open a new bug for the issue noted above.
Comment 6 David Kilzer 2016-05-24 03:39:25 UTC
I filed:

Bug 766828: xmlParseStartTag2() contains typo when checking for default definitions for an attribute in a namespace
<https://bugzilla.gnome.org/show_bug.cgi?id=766828>