Bug 1205288

Summary: Coverity-detected defect: null pointer dereferences in uri.c
Product: Red Hat Enterprise Linux 6 Reporter: Jeff Nelson <jen>
Component: qemu-kvmAssignee: Markus Armbruster <armbru>
Status: CLOSED ERRATA QA Contact: Virtualization Bugs <virt-bugs>
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: 6.7CC: armbru, chayang, juzhang, mkenneth, qzhang, rbalakri, rpacheco, virt-maint
Target Milestone: rc   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: qemu-kvm-0.12.1.2-2.474.el6 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
: 1218919 (view as bug list) Environment:
Last Closed: 2015-07-22 06:09:37 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1218919    

Description Jeff Nelson 2015-03-24 15:44:25 UTC
Description of problem:
A Coverity scan reports the following issue:

Defect type: OVERRUN
1. qemu-kvm-0.12.1.2/uri.c:1935: assignment: Assigning: "pos" = "0".
26. qemu-kvm-0.12.1.2/uri.c:1997: alias: Assigning: "ref->path" = ""/"". "ref->path" now points to byte 0 of ""/"" (which consists of 2 bytes).
33. qemu-kvm-0.12.1.2/uri.c:2030: assignment: Assigning: "pos" += "2". The value of "pos" is now 2.
38. qemu-kvm-0.12.1.2/uri.c:2035: overrun-local: Overrunning array of 2 bytes at byte offset 2 by dereferencing pointer "ref->path + pos".

This analysis appears to be correct.

Version-Release number of selected component (if applicable):
qemu-kvm-0.12.1.2-2.459.el6

Comment 3 Markus Armbruster 2015-03-30 12:07:19 UTC
Let's backport wholesale:

7f303ad clean unnecessary code: don't check g_strdup arg for NULL
2458810 Drop superfluous conditionals around g_strdup()
44c2286 util: Drop superfluous conditionals around g_free()
9be71be util: Fuse g_malloc(); memset() into g_new0()
c89c6e8 util/uri: uri_new() can't fail, drop dead error handling
afd5ea3 util/uri: realloc2n() can't fail, drop dead error handling
afb30dd util/uri: URI member path can be null, compare more carfully
2b21233 util/uri: Add overflow check to rfc3986_parse_port

Comment 4 Jeff Nelson 2015-05-06 18:06:03 UTC
Fix included in qemu-kvm-0.12.1.2-2.474.el6

Comment 7 Markus Armbruster 2015-05-13 17:01:39 UTC
I ran local Coverity scans for qemu-kvm-0.12.1.2-2.473.el6 (unpatched)
qemu-kvm-0.12.1.2-2.475.el6 and (patched).

csdiff reports two out of three defects in uri.c fixed:

Error: REVERSE_INULL:
uri.c:1988: deref_ptr_in_call: Dereferencing pointer "bas->path".
uri.c:1992: check_after_deref: Null-checking "bas->path" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.

Error: REVERSE_INULL:
uri.c:1988: deref_ptr_in_call: Dereferencing pointer "ref->path".
uri.c:1996: check_after_deref: Null-checking "ref->path" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.

Unchanged:

Error: OVERRUN:
uri.c:2035: overrun-local: Overrunning array of 2 bytes at byte offset 2 by dereferencing pointer "ref->path + pos".

This is actually a false positive.  Coverity worries that after

    int pos = 0;

and

    if (ref->path == NULL) {
        ref->path = (char *) "/";
	remove_path = 1;
    }

we increment pos to 2 in

	if ((ref->path[pos] == '.') && (ref->path[pos+1] == '/'))
            pos += 2;

and then access ref->path[2] out of bounds in

	while ((bptr[pos] == ref->path[pos]) && (bptr[pos] != 0))
	    pos++;

Can't happen, because the conditional guarding pos += 2 can't be true:
ref->path[0] is '/', hence != '.'.

The defects we actually fixed are real.  Adjusting bug description.

Comment 9 Jeff Nelson 2015-05-14 15:59:27 UTC
I conclude that this is not a defect, but using a different analysis. Please review and let me know if you disagree.

pos starts at 0. The next reference to pos occurs in this statement:

    if ((ref->path[pos] == '.') && (ref->path[pos+1] == '/'))
        pos += 2;

For pos to be incremented to 2, the conditional must be true. So what can we say about ref->path? It is either:

a) the string "/" as assigned here:

    if (ref->path == NULL) {
        ref->path = (char *) "/";
        remove_path = 1;
    }

in which case the value of pos is not incremented. Or it is:

b) a string of unknown length (initialized from actual parameter 'uri'). So let's reason about the string and whether or not pos gets incremented by:

    if ((ref->path[pos] == '.') && (ref->path[pos+1] == '/'))
        pos += 2;

* It could be a string of length 0; that is,

    ref->path[0] == '\0'

If so, the expression 'ref->path[pos+1]' references undefined (and possibly unallocated) memory beyond the end of the string (a separate defect not noted by Coverity). pos won't be incremented because 'ref->path[pos]' is '\0' not '.'.

* It could be a string of length 1, but if so that means ref->path[1]=='\0'.  pos won't be incremented because '\0' != '/'.

* It could be a string of length 2, in which case ref->path[2]=='\0'. pos *might* be incremented because we don't know anything about ref->path[0] or ref->path[1].

* It could be a string of length > 2. pos *might* be incremented.

Now we come to the statement that Coverity claims is a defect:

    while ((bptr[pos] == ref->path[pos]) && (bptr[pos] != 0))
        pos++;

This statement is OK; the value 'ref->path[pos]' is valid for pos==2 because for pos to be 2, we know that the string has to be at least 2 characters long.

Finally, I happened to notice that uri_resolve_relative, the function in question, has NO callers. Code that doesn't execute can't have side effects, whether good or bad.

Comment 10 Jeff Nelson 2015-05-14 16:01:55 UTC
The remarks in Comment 9 are directed at the analysis in Comment 7, not at the BZ in general.

Comment 11 Markus Armbruster 2015-05-14 16:33:48 UTC
> I conclude that this is not a defect, but using a different
> analysis. Please review and let me know if you disagree.
>
> pos starts at 0. The next reference to pos occurs in this statement:
>
>     if ((ref->path[pos] == '.') && (ref->path[pos+1] == '/'))
>         pos += 2;
>
> For pos to be incremented to 2, the conditional must be true. So what
> can we say about ref->path? It is either:
>
> a) the string "/" as assigned here:
>
>     if (ref->path == NULL) {
>         ref->path = (char *) "/";
>         remove_path = 1;
>     }
>
> in which case the value of pos is not incremented. Or it is:
>
> b) a string of unknown length (initialized from actual parameter
> 'uri'). So let's reason about the string and whether or not pos gets
> incremented by:
>
>     if ((ref->path[pos] == '.') && (ref->path[pos+1] == '/'))
>         pos += 2;
>
> * It could be a string of length 0; that is,
>
>     ref->path[0] == '\0'
>
> If so, the expression 'ref->path[pos+1]' references undefined (and
> possibly unallocated) memory beyond the end of the string (a separate
> defect not noted by Coverity).

Since the right operand of && is executed only when the left operand
is true.  It isn't in this case, so there's no defect.

> pos won't be incremented because 'ref->path[pos]' is '\0' not '.'.

Correct.

> * It could be a string of length 1, but if so that means
>   ref->path[1]=='\0'.  pos won't be incremented because '\0' != '/'.

Correct.

> * It could be a string of length 2, in which case
>   ref->path[2]=='\0'. pos *might* be incremented because we don't know
>   anything about ref->path[0] or ref->path[1].

Correct.

> * It could be a string of length > 2. pos *might* be incremented.

Correct.

> Now we come to the statement that Coverity claims is a defect:
>
>     while ((bptr[pos] == ref->path[pos]) && (bptr[pos] != 0))
>         pos++;
>
> This statement is OK; the value 'ref->path[pos]' is valid for pos==2
> because for pos to be 2, we know that the string has to be at least 2
> characters long.

Correct.

> Finally, I happened to notice that uri_resolve_relative, the function
> in question, has NO callers. Code that doesn't execute can't have side
> effects, whether good or bad.

Good point :)

Comment 14 errata-xmlrpc 2015-07-22 06:09:37 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://rhn.redhat.com/errata/RHBA-2015-1275.html