diff mbox

[for-2.8,2/2] loader: fix undefined behavior in rom_order_compare()

Message ID 20161128195701.24912-3-lersek@redhat.com
State New
Headers show

Commit Message

Laszlo Ersek Nov. 28, 2016, 7:57 p.m. UTC
According to ISO C99 / N1256 (referenced in HACKING):

> 6.5.8 Relational operators
>
> 4 For the purposes of these operators, a pointer to an object that is
>   not an element of an array behaves the same as a pointer to the first
>   element of an array of length one with the type of the object as its
>   element type.
>
> 5 When two pointers are compared, the result depends on the relative
>   locations in the address space of the objects pointed to. If two
>   pointers to object or incomplete types both point to the same object,
>   or both point one past the last element of the same array object, they
>   compare equal. If the objects pointed to are members of the same
>   aggregate object, pointers to structure members declared later compare
>   greater than pointers to members declared earlier in the structure,
>   and pointers to array elements with larger subscript values compare
>   greater than pointers to elements of the same array with lower
>   subscript values. All pointers to members of the same union object
>   compare equal. If the expression /P/ points to an element of an array
>   object and the expression /Q/ points to the last element of the same
>   array object, the pointer expression /Q+1/ compares greater than /P/.
>   In all other cases, the behavior is undefined.

Our AddressSpace objects are allocated generally individually, and kept in
the "address_spaces" linked list, so we mustn't compare their addresses
with relops.

Convert the pointers subjected to the relop in rom_order_compare() to
"uintptr_t":

> 7.18.1.4 Integer types capable of holding object pointers
>
> 1 [...]
>
>   The following type designates an unsigned integer type with the
>   property that any valid pointer to void can be converted to this type,
>   then converted back to pointer to void, and the result will compare
>   equal to the original pointer:
>
>   /uintptr_t/
>
>   These types are optional.

Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Alistair Francis <alistair.francis@xilinx.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: qemu-devel@nongnu.org
Fixes: 3e76099aacb4dae0d37ebf95305369e03d1491e6
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
 hw/core/loader.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Alistair Francis Nov. 28, 2016, 11:13 p.m. UTC | #1
On Mon, Nov 28, 2016 at 11:57 AM, Laszlo Ersek <lersek@redhat.com> wrote:
> According to ISO C99 / N1256 (referenced in HACKING):
>
>> 6.5.8 Relational operators
>>
>> 4 For the purposes of these operators, a pointer to an object that is
>>   not an element of an array behaves the same as a pointer to the first
>>   element of an array of length one with the type of the object as its
>>   element type.
>>
>> 5 When two pointers are compared, the result depends on the relative
>>   locations in the address space of the objects pointed to. If two
>>   pointers to object or incomplete types both point to the same object,
>>   or both point one past the last element of the same array object, they
>>   compare equal. If the objects pointed to are members of the same
>>   aggregate object, pointers to structure members declared later compare
>>   greater than pointers to members declared earlier in the structure,
>>   and pointers to array elements with larger subscript values compare
>>   greater than pointers to elements of the same array with lower
>>   subscript values. All pointers to members of the same union object
>>   compare equal. If the expression /P/ points to an element of an array
>>   object and the expression /Q/ points to the last element of the same
>>   array object, the pointer expression /Q+1/ compares greater than /P/.
>>   In all other cases, the behavior is undefined.
>
> Our AddressSpace objects are allocated generally individually, and kept in
> the "address_spaces" linked list, so we mustn't compare their addresses
> with relops.
>
> Convert the pointers subjected to the relop in rom_order_compare() to
> "uintptr_t":
>
>> 7.18.1.4 Integer types capable of holding object pointers
>>
>> 1 [...]
>>
>>   The following type designates an unsigned integer type with the
>>   property that any valid pointer to void can be converted to this type,
>>   then converted back to pointer to void, and the result will compare
>>   equal to the original pointer:
>>
>>   /uintptr_t/
>>
>>   These types are optional.
>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Alistair Francis <alistair.francis@xilinx.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: qemu-devel@nongnu.org
> Fixes: 3e76099aacb4dae0d37ebf95305369e03d1491e6
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>

Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>

Thanks,

Alistair

> ---
>  hw/core/loader.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index c0d645a87134..766e48f2aec2 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -818,7 +818,7 @@ static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
>
>  static inline bool rom_order_compare(Rom *rom, Rom *item)
>  {
> -    return (rom->as > item->as) ||
> +    return ((uintptr_t)(void*)rom->as > (uintptr_t)(void*)item->as) ||
>             (rom->as == item->as && rom->addr >= item->addr);
>  }
>
> --
> 2.9.2
>
>
Michael S. Tsirkin Nov. 29, 2016, 4:29 p.m. UTC | #2
On Mon, Nov 28, 2016 at 08:57:01PM +0100, Laszlo Ersek wrote:
> According to ISO C99 / N1256 (referenced in HACKING):
> 
> > 6.5.8 Relational operators
> >
> > 4 For the purposes of these operators, a pointer to an object that is
> >   not an element of an array behaves the same as a pointer to the first
> >   element of an array of length one with the type of the object as its
> >   element type.
> >
> > 5 When two pointers are compared, the result depends on the relative
> >   locations in the address space of the objects pointed to. If two
> >   pointers to object or incomplete types both point to the same object,
> >   or both point one past the last element of the same array object, they
> >   compare equal. If the objects pointed to are members of the same
> >   aggregate object, pointers to structure members declared later compare
> >   greater than pointers to members declared earlier in the structure,
> >   and pointers to array elements with larger subscript values compare
> >   greater than pointers to elements of the same array with lower
> >   subscript values. All pointers to members of the same union object
> >   compare equal. If the expression /P/ points to an element of an array
> >   object and the expression /Q/ points to the last element of the same
> >   array object, the pointer expression /Q+1/ compares greater than /P/.
> >   In all other cases, the behavior is undefined.
> 
> Our AddressSpace objects are allocated generally individually, and kept in
> the "address_spaces" linked list, so we mustn't compare their addresses
> with relops.
> 
> Convert the pointers subjected to the relop in rom_order_compare() to
> "uintptr_t":
> 
> > 7.18.1.4 Integer types capable of holding object pointers
> >
> > 1 [...]
> >
> >   The following type designates an unsigned integer type with the
> >   property that any valid pointer to void can be converted to this type,
> >   then converted back to pointer to void, and the result will compare
> >   equal to the original pointer:
> >
> >   /uintptr_t/
> >
> >   These types are optional.
> 
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Alistair Francis <alistair.francis@xilinx.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: qemu-devel@nongnu.org
> Fixes: 3e76099aacb4dae0d37ebf95305369e03d1491e6
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
>  hw/core/loader.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index c0d645a87134..766e48f2aec2 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -818,7 +818,7 @@ static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
>  
>  static inline bool rom_order_compare(Rom *rom, Rom *item)
>  {
> -    return (rom->as > item->as) ||
> +    return ((uintptr_t)(void*)rom->as > (uintptr_t)(void*)item->as) ||
>             (rom->as == item->as && rom->addr >= item->addr);
>  }

Can't hurt but why cast to void *?
Should not be needed.

> -- 
> 2.9.2
Laszlo Ersek Nov. 29, 2016, 6:40 p.m. UTC | #3
On 11/29/16 17:29, Michael S. Tsirkin wrote:
> On Mon, Nov 28, 2016 at 08:57:01PM +0100, Laszlo Ersek wrote:
>> According to ISO C99 / N1256 (referenced in HACKING):
>>
>>> 6.5.8 Relational operators
>>>
>>> 4 For the purposes of these operators, a pointer to an object that is
>>>   not an element of an array behaves the same as a pointer to the first
>>>   element of an array of length one with the type of the object as its
>>>   element type.
>>>
>>> 5 When two pointers are compared, the result depends on the relative
>>>   locations in the address space of the objects pointed to. If two
>>>   pointers to object or incomplete types both point to the same object,
>>>   or both point one past the last element of the same array object, they
>>>   compare equal. If the objects pointed to are members of the same
>>>   aggregate object, pointers to structure members declared later compare
>>>   greater than pointers to members declared earlier in the structure,
>>>   and pointers to array elements with larger subscript values compare
>>>   greater than pointers to elements of the same array with lower
>>>   subscript values. All pointers to members of the same union object
>>>   compare equal. If the expression /P/ points to an element of an array
>>>   object and the expression /Q/ points to the last element of the same
>>>   array object, the pointer expression /Q+1/ compares greater than /P/.
>>>   In all other cases, the behavior is undefined.
>>
>> Our AddressSpace objects are allocated generally individually, and kept in
>> the "address_spaces" linked list, so we mustn't compare their addresses
>> with relops.
>>
>> Convert the pointers subjected to the relop in rom_order_compare() to
>> "uintptr_t":
>>
>>> 7.18.1.4 Integer types capable of holding object pointers
>>>
>>> 1 [...]
>>>
>>>   The following type designates an unsigned integer type with the
>>>   property that any valid pointer to void can be converted to this type,
>>>   then converted back to pointer to void, and the result will compare
>>>   equal to the original pointer:
>>>
>>>   /uintptr_t/
>>>
>>>   These types are optional.
>>
>> Cc: "Michael S. Tsirkin" <mst@redhat.com>
>> Cc: Alistair Francis <alistair.francis@xilinx.com>
>> Cc: Paolo Bonzini <pbonzini@redhat.com>
>> Cc: Peter Maydell <peter.maydell@linaro.org>
>> Cc: qemu-devel@nongnu.org
>> Fixes: 3e76099aacb4dae0d37ebf95305369e03d1491e6
>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>> ---
>>  hw/core/loader.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/hw/core/loader.c b/hw/core/loader.c
>> index c0d645a87134..766e48f2aec2 100644
>> --- a/hw/core/loader.c
>> +++ b/hw/core/loader.c
>> @@ -818,7 +818,7 @@ static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
>>  
>>  static inline bool rom_order_compare(Rom *rom, Rom *item)
>>  {
>> -    return (rom->as > item->as) ||
>> +    return ((uintptr_t)(void*)rom->as > (uintptr_t)(void*)item->as) ||
>>             (rom->as == item->as && rom->addr >= item->addr);
>>  }
> 
> Can't hurt but why cast to void *?
> Should not be needed.

Just to comply with the word of the standard above; it says "any valid
pointer to void".

> 
>> -- 
>> 2.9.2
diff mbox

Patch

diff --git a/hw/core/loader.c b/hw/core/loader.c
index c0d645a87134..766e48f2aec2 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -818,7 +818,7 @@  static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
 
 static inline bool rom_order_compare(Rom *rom, Rom *item)
 {
-    return (rom->as > item->as) ||
+    return ((uintptr_t)(void*)rom->as > (uintptr_t)(void*)item->as) ||
            (rom->as == item->as && rom->addr >= item->addr);
 }