[libcamera-devel,v2] libcamera: media_device: prevent sign extension on casts

Message ID 20200218141839.5574-1-kieran.bingham@ideasonboard.com
State Accepted
Commit 90240a79506a3c400f3af4cb0b08746ae87c79e2
Headers show
Series
  • [libcamera-devel,v2] libcamera: media_device: prevent sign extension on casts
Related show

Commit Message

Kieran Bingham Feb. 18, 2020, 2:18 p.m. UTC
The conversion of pointers to integers is implementation defined and
differs between g++ and clang++ when utilising a uint64_t type.

    #include <iostream>

    int main(int argc, char **argv)
    {
        void *ptr = reinterpret_cast<void *>(0xf1234567);
        uint64_t u64 = reinterpret_cast<uint64_t>(ptr);
	uint64_t uint64 = reinterpret_cast<uintptr_t>(ptr);

        std::cout << "ptr " << ptr
		  << " u64 0x" << std::hex << u64
		  << " uint64 0x" << std::hex << uint64
		  << std::endl;

	return 0;
    }

When compiled with g++ produces the following unexpected output:

  ptr 0xf1234567 u64 0xfffffffff1234567 uint64 0xf1234567

The standards states:

"A pointer can be explicitly converted to any integral type large enough
to hold all values of its type. The mapping function is
implementation-defined. [Note: It is intended to be unsurprising to
those who know the addressing structure of the underlying machine. — end
note]"

And as such the g++ implementation appears to be little more surprising
than expected in this situation.

The MediaDevice passes pointers to the kernel via the struct
media_v2_topology in which pointers are cast using a uint64 type (__u64),
which is affected by the sign extension described above when BIT(32) is
set and causes an invalid address to be given to the kernel.

Ensure that we cast using uintptr_t which is not affected by the sign
extension issue.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
v2:
 - Expand commit message to explain the underlying issue.
 - include stdint.h
 - use uintptr_t instead of std::uintptr_t

 src/libcamera/media_device.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

Comments

Laurent Pinchart Feb. 18, 2020, 3:17 p.m. UTC | #1
Hi Kieran,

Thank you for the patch.

On Tue, Feb 18, 2020 at 02:18:39PM +0000, Kieran Bingham wrote:
> The conversion of pointers to integers is implementation defined and
> differs between g++ and clang++ when utilising a uint64_t type.
> 
>     #include <iostream>
> 
>     int main(int argc, char **argv)
>     {
>         void *ptr = reinterpret_cast<void *>(0xf1234567);
>         uint64_t u64 = reinterpret_cast<uint64_t>(ptr);
> 	uint64_t uint64 = reinterpret_cast<uintptr_t>(ptr);
> 
>         std::cout << "ptr " << ptr
> 		  << " u64 0x" << std::hex << u64
> 		  << " uint64 0x" << std::hex << uint64

Maybe " ptr -> u64 0x" and " ptr -> uintptr_t -> u64 0x" ?

> 		  << std::endl;
> 
> 	return 0;
>     }

That's a weird mix of tabs and spaces :-)

> 
> When compiled with g++ produces the following unexpected output:

s/with g++/with g++ for a 32-bit platform/

> 
>   ptr 0xf1234567 u64 0xfffffffff1234567 uint64 0xf1234567
> 
> The standards states:
> 
> "A pointer can be explicitly converted to any integral type large enough
> to hold all values of its type. The mapping function is
> implementation-defined. [Note: It is intended to be unsurprising to
> those who know the addressing structure of the underlying machine. — end
> note]"
> 
> And as such the g++ implementation appears to be little more surprising
> than expected in this situation.
> 
> The MediaDevice passes pointers to the kernel via the struct
> media_v2_topology in which pointers are cast using a uint64 type (__u64),
> which is affected by the sign extension described above when BIT(32) is
> set and causes an invalid address to be given to the kernel.
> 
> Ensure that we cast using uintptr_t which is not affected by the sign
> extension issue.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
> v2:
>  - Expand commit message to explain the underlying issue.
>  - include stdint.h
>  - use uintptr_t instead of std::uintptr_t
> 
>  src/libcamera/media_device.cpp | 9 +++++----
>  1 file changed, 5 insertions(+), 4 deletions(-)
> 
> diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
> index fad475b9ac76..0d6b5efd9e7a 100644
> --- a/src/libcamera/media_device.cpp
> +++ b/src/libcamera/media_device.cpp
> @@ -9,6 +9,7 @@
>  
>  #include <errno.h>
>  #include <fcntl.h>
> +#include <stdint.h>
>  #include <string>
>  #include <string.h>
>  #include <sys/ioctl.h>
> @@ -236,10 +237,10 @@ int MediaDevice::populate()
>  	 */
>  	while (true) {
>  		topology.topology_version = 0;
> -		topology.ptr_entities = reinterpret_cast<__u64>(ents);
> -		topology.ptr_interfaces = reinterpret_cast<__u64>(interfaces);
> -		topology.ptr_links = reinterpret_cast<__u64>(links);
> -		topology.ptr_pads = reinterpret_cast<__u64>(pads);
> +		topology.ptr_entities = reinterpret_cast<uintptr_t>(ents);
> +		topology.ptr_interfaces = reinterpret_cast<uintptr_t>(interfaces);
> +		topology.ptr_links = reinterpret_cast<uintptr_t>(links);
> +		topology.ptr_pads = reinterpret_cast<uintptr_t>(pads);
>  
>  		ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology);
>  		if (ret < 0) {
Kieran Bingham Feb. 18, 2020, 5:07 p.m. UTC | #2
Hi Laurent,

On 18/02/2020 15:17, Laurent Pinchart wrote:
> Hi Kieran,
> 
> Thank you for the patch.
> 
> On Tue, Feb 18, 2020 at 02:18:39PM +0000, Kieran Bingham wrote:
>> The conversion of pointers to integers is implementation defined and
>> differs between g++ and clang++ when utilising a uint64_t type.
>>
>>     #include <iostream>
>>
>>     int main(int argc, char **argv)
>>     {
>>         void *ptr = reinterpret_cast<void *>(0xf1234567);
>>         uint64_t u64 = reinterpret_cast<uint64_t>(ptr);
>> 	uint64_t uint64 = reinterpret_cast<uintptr_t>(ptr);
>>
>>         std::cout << "ptr " << ptr
>> 		  << " u64 0x" << std::hex << u64
>> 		  << " uint64 0x" << std::hex << uint64
> 
> Maybe " ptr -> u64 0x" and " ptr -> uintptr_t -> u64 0x" ?
> 
>> 		  << std::endl;
>>
>> 	return 0;
>>     }
> 
> That's a weird mix of tabs and spaces :-)

Agh, I indented the left hand items by 4 to indent the whole code block
for the commit message.

Indenting the whole lot by a tab fits, but it looks a bit weird and too
far to the right ... but I don't think it's important.


> 
>>
>> When compiled with g++ produces the following unexpected output:
> 
> s/with g++/with g++ for a 32-bit platform/

No, I don't think that's true.
This issue affects a 64-bit platform just the same.

If a pointer is passed through here which happens to be
0x00000000f1234567, it will be incorrect when it gets to the kernel.

(or any pointer with leading zeros up to a set bit 32...)

>>   ptr 0xf1234567 u64 0xfffffffff1234567 uint64 0xf1234567
>>
>> The standards states:
>>
>> "A pointer can be explicitly converted to any integral type large enough
>> to hold all values of its type. The mapping function is
>> implementation-defined. [Note: It is intended to be unsurprising to
>> those who know the addressing structure of the underlying machine. — end
>> note]"
>>
>> And as such the g++ implementation appears to be little more surprising
>> than expected in this situation.
>>
>> The MediaDevice passes pointers to the kernel via the struct
>> media_v2_topology in which pointers are cast using a uint64 type (__u64),
>> which is affected by the sign extension described above when BIT(32) is
>> set and causes an invalid address to be given to the kernel.
>>
>> Ensure that we cast using uintptr_t which is not affected by the sign
>> extension issue.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
>> ---
>> v2:
>>  - Expand commit message to explain the underlying issue.
>>  - include stdint.h
>>  - use uintptr_t instead of std::uintptr_t
>>
>>  src/libcamera/media_device.cpp | 9 +++++----
>>  1 file changed, 5 insertions(+), 4 deletions(-)
>>
>> diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
>> index fad475b9ac76..0d6b5efd9e7a 100644
>> --- a/src/libcamera/media_device.cpp
>> +++ b/src/libcamera/media_device.cpp
>> @@ -9,6 +9,7 @@
>>  
>>  #include <errno.h>
>>  #include <fcntl.h>
>> +#include <stdint.h>
>>  #include <string>
>>  #include <string.h>
>>  #include <sys/ioctl.h>
>> @@ -236,10 +237,10 @@ int MediaDevice::populate()
>>  	 */
>>  	while (true) {
>>  		topology.topology_version = 0;
>> -		topology.ptr_entities = reinterpret_cast<__u64>(ents);
>> -		topology.ptr_interfaces = reinterpret_cast<__u64>(interfaces);
>> -		topology.ptr_links = reinterpret_cast<__u64>(links);
>> -		topology.ptr_pads = reinterpret_cast<__u64>(pads);
>> +		topology.ptr_entities = reinterpret_cast<uintptr_t>(ents);
>> +		topology.ptr_interfaces = reinterpret_cast<uintptr_t>(interfaces);
>> +		topology.ptr_links = reinterpret_cast<uintptr_t>(links);
>> +		topology.ptr_pads = reinterpret_cast<uintptr_t>(pads);
>>  
>>  		ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology);
>>  		if (ret < 0) {
>
Laurent Pinchart Feb. 18, 2020, 5:33 p.m. UTC | #3
Hi Kieran,

On Tue, Feb 18, 2020 at 05:07:41PM +0000, Kieran Bingham wrote:
> On 18/02/2020 15:17, Laurent Pinchart wrote:
> > On Tue, Feb 18, 2020 at 02:18:39PM +0000, Kieran Bingham wrote:
> >> The conversion of pointers to integers is implementation defined and
> >> differs between g++ and clang++ when utilising a uint64_t type.
> >>
> >>     #include <iostream>
> >>
> >>     int main(int argc, char **argv)
> >>     {
> >>         void *ptr = reinterpret_cast<void *>(0xf1234567);
> >>         uint64_t u64 = reinterpret_cast<uint64_t>(ptr);
> >> 	uint64_t uint64 = reinterpret_cast<uintptr_t>(ptr);
> >>
> >>         std::cout << "ptr " << ptr
> >> 		  << " u64 0x" << std::hex << u64
> >> 		  << " uint64 0x" << std::hex << uint64
> > 
> > Maybe " ptr -> u64 0x" and " ptr -> uintptr_t -> u64 0x" ?
> > 
> >> 		  << std::endl;
> >>
> >> 	return 0;
> >>     }
> > 
> > That's a weird mix of tabs and spaces :-)
> 
> Agh, I indented the left hand items by 4 to indent the whole code block
> for the commit message.
> 
> Indenting the whole lot by a tab fits, but it looks a bit weird and too
> far to the right ... but I don't think it's important.

No big deal, no. I was however referring to lines starting with
"uint64_t uint64" and "return" that seem to have a different combination
of spaces and tabs than the other ones.

> >> When compiled with g++ produces the following unexpected output:
> > 
> > s/with g++/with g++ for a 32-bit platform/
> 
> No, I don't think that's true.
> This issue affects a 64-bit platform just the same.
> 
> If a pointer is passed through here which happens to be
> 0x00000000f1234567, it will be incorrect when it gets to the kernel.
> 
> (or any pointer with leading zeros up to a set bit 32...)

Really ? Testing the above code on x86-64 without -m32 prints

ptr 0xf1234567 u64 0xf1234567 uint64 0xf1234567

for me.

> >>   ptr 0xf1234567 u64 0xfffffffff1234567 uint64 0xf1234567
> >>
> >> The standards states:
> >>
> >> "A pointer can be explicitly converted to any integral type large enough
> >> to hold all values of its type. The mapping function is
> >> implementation-defined. [Note: It is intended to be unsurprising to
> >> those who know the addressing structure of the underlying machine. — end
> >> note]"
> >>
> >> And as such the g++ implementation appears to be little more surprising
> >> than expected in this situation.
> >>
> >> The MediaDevice passes pointers to the kernel via the struct
> >> media_v2_topology in which pointers are cast using a uint64 type (__u64),
> >> which is affected by the sign extension described above when BIT(32) is
> >> set and causes an invalid address to be given to the kernel.
> >>
> >> Ensure that we cast using uintptr_t which is not affected by the sign
> >> extension issue.
> >>
> >> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > 
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > 
> >> ---
> >> v2:
> >>  - Expand commit message to explain the underlying issue.
> >>  - include stdint.h
> >>  - use uintptr_t instead of std::uintptr_t
> >>
> >>  src/libcamera/media_device.cpp | 9 +++++----
> >>  1 file changed, 5 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
> >> index fad475b9ac76..0d6b5efd9e7a 100644
> >> --- a/src/libcamera/media_device.cpp
> >> +++ b/src/libcamera/media_device.cpp
> >> @@ -9,6 +9,7 @@
> >>  
> >>  #include <errno.h>
> >>  #include <fcntl.h>
> >> +#include <stdint.h>
> >>  #include <string>
> >>  #include <string.h>
> >>  #include <sys/ioctl.h>
> >> @@ -236,10 +237,10 @@ int MediaDevice::populate()
> >>  	 */
> >>  	while (true) {
> >>  		topology.topology_version = 0;
> >> -		topology.ptr_entities = reinterpret_cast<__u64>(ents);
> >> -		topology.ptr_interfaces = reinterpret_cast<__u64>(interfaces);
> >> -		topology.ptr_links = reinterpret_cast<__u64>(links);
> >> -		topology.ptr_pads = reinterpret_cast<__u64>(pads);
> >> +		topology.ptr_entities = reinterpret_cast<uintptr_t>(ents);
> >> +		topology.ptr_interfaces = reinterpret_cast<uintptr_t>(interfaces);
> >> +		topology.ptr_links = reinterpret_cast<uintptr_t>(links);
> >> +		topology.ptr_pads = reinterpret_cast<uintptr_t>(pads);
> >>  
> >>  		ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology);
> >>  		if (ret < 0) {
Kieran Bingham Feb. 18, 2020, 5:38 p.m. UTC | #4
Hi Laurent,

On 18/02/2020 17:33, Laurent Pinchart wrote:
> Hi Kieran,
> 
> On Tue, Feb 18, 2020 at 05:07:41PM +0000, Kieran Bingham wrote:
>> On 18/02/2020 15:17, Laurent Pinchart wrote:
>>> On Tue, Feb 18, 2020 at 02:18:39PM +0000, Kieran Bingham wrote:
>>>> The conversion of pointers to integers is implementation defined and
>>>> differs between g++ and clang++ when utilising a uint64_t type.
>>>>
>>>>     #include <iostream>
>>>>
>>>>     int main(int argc, char **argv)
>>>>     {
>>>>         void *ptr = reinterpret_cast<void *>(0xf1234567);
>>>>         uint64_t u64 = reinterpret_cast<uint64_t>(ptr);
>>>> 	uint64_t uint64 = reinterpret_cast<uintptr_t>(ptr);
>>>>
>>>>         std::cout << "ptr " << ptr
>>>> 		  << " u64 0x" << std::hex << u64
>>>> 		  << " uint64 0x" << std::hex << uint64
>>>
>>> Maybe " ptr -> u64 0x" and " ptr -> uintptr_t -> u64 0x" ?
>>>
>>>> 		  << std::endl;
>>>>
>>>> 	return 0;
>>>>     }
>>>
>>> That's a weird mix of tabs and spaces :-)
>>
>> Agh, I indented the left hand items by 4 to indent the whole code block
>> for the commit message.
>>
>> Indenting the whole lot by a tab fits, but it looks a bit weird and too
>> far to the right ... but I don't think it's important.
> 
> No big deal, no. I was however referring to lines starting with
> "uint64_t uint64" and "return" that seem to have a different combination
> of spaces and tabs than the other ones.
> 
>>>> When compiled with g++ produces the following unexpected output:
>>>
>>> s/with g++/with g++ for a 32-bit platform/
>>
>> No, I don't think that's true.
>> This issue affects a 64-bit platform just the same.
>>
>> If a pointer is passed through here which happens to be
>> 0x00000000f1234567, it will be incorrect when it gets to the kernel.
>>
>> (or any pointer with leading zeros up to a set bit 32...)
> 
> Really ? Testing the above code on x86-64 without -m32 prints
> 
> ptr 0xf1234567 u64 0xf1234567 uint64 0xf1234567
> 
> for me.

Ah, I /may/ have been building the snippit code on the target, when I
thought I had done it on the host... :-D

I.e. I hadn't specified -m32 'explicitly' ... ahem...

So you are correct, this is only an issue when compiled for 32 bit systems.

--
Kieran

> 
>>>>   ptr 0xf1234567 u64 0xfffffffff1234567 uint64 0xf1234567
>>>>
>>>> The standards states:
>>>>
>>>> "A pointer can be explicitly converted to any integral type large enough
>>>> to hold all values of its type. The mapping function is
>>>> implementation-defined. [Note: It is intended to be unsurprising to
>>>> those who know the addressing structure of the underlying machine. — end
>>>> note]"
>>>>
>>>> And as such the g++ implementation appears to be little more surprising
>>>> than expected in this situation.
>>>>
>>>> The MediaDevice passes pointers to the kernel via the struct
>>>> media_v2_topology in which pointers are cast using a uint64 type (__u64),
>>>> which is affected by the sign extension described above when BIT(32) is
>>>> set and causes an invalid address to be given to the kernel.
>>>>
>>>> Ensure that we cast using uintptr_t which is not affected by the sign
>>>> extension issue.
>>>>
>>>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>>
>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>
>>>> ---
>>>> v2:
>>>>  - Expand commit message to explain the underlying issue.
>>>>  - include stdint.h
>>>>  - use uintptr_t instead of std::uintptr_t
>>>>
>>>>  src/libcamera/media_device.cpp | 9 +++++----
>>>>  1 file changed, 5 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
>>>> index fad475b9ac76..0d6b5efd9e7a 100644
>>>> --- a/src/libcamera/media_device.cpp
>>>> +++ b/src/libcamera/media_device.cpp
>>>> @@ -9,6 +9,7 @@
>>>>  
>>>>  #include <errno.h>
>>>>  #include <fcntl.h>
>>>> +#include <stdint.h>
>>>>  #include <string>
>>>>  #include <string.h>
>>>>  #include <sys/ioctl.h>
>>>> @@ -236,10 +237,10 @@ int MediaDevice::populate()
>>>>  	 */
>>>>  	while (true) {
>>>>  		topology.topology_version = 0;
>>>> -		topology.ptr_entities = reinterpret_cast<__u64>(ents);
>>>> -		topology.ptr_interfaces = reinterpret_cast<__u64>(interfaces);
>>>> -		topology.ptr_links = reinterpret_cast<__u64>(links);
>>>> -		topology.ptr_pads = reinterpret_cast<__u64>(pads);
>>>> +		topology.ptr_entities = reinterpret_cast<uintptr_t>(ents);
>>>> +		topology.ptr_interfaces = reinterpret_cast<uintptr_t>(interfaces);
>>>> +		topology.ptr_links = reinterpret_cast<uintptr_t>(links);
>>>> +		topology.ptr_pads = reinterpret_cast<uintptr_t>(pads);
>>>>  
>>>>  		ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology);
>>>>  		if (ret < 0) {
>

Patch

diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
index fad475b9ac76..0d6b5efd9e7a 100644
--- a/src/libcamera/media_device.cpp
+++ b/src/libcamera/media_device.cpp
@@ -9,6 +9,7 @@ 
 
 #include <errno.h>
 #include <fcntl.h>
+#include <stdint.h>
 #include <string>
 #include <string.h>
 #include <sys/ioctl.h>
@@ -236,10 +237,10 @@  int MediaDevice::populate()
 	 */
 	while (true) {
 		topology.topology_version = 0;
-		topology.ptr_entities = reinterpret_cast<__u64>(ents);
-		topology.ptr_interfaces = reinterpret_cast<__u64>(interfaces);
-		topology.ptr_links = reinterpret_cast<__u64>(links);
-		topology.ptr_pads = reinterpret_cast<__u64>(pads);
+		topology.ptr_entities = reinterpret_cast<uintptr_t>(ents);
+		topology.ptr_interfaces = reinterpret_cast<uintptr_t>(interfaces);
+		topology.ptr_links = reinterpret_cast<uintptr_t>(links);
+		topology.ptr_pads = reinterpret_cast<uintptr_t>(pads);
 
 		ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology);
 		if (ret < 0) {