[libcamera-devel] v4l2: v4l2_camera_proxy: Detect ioctl sign-extensions
diff mbox series

Message ID 20230227203947.3964157-1-kieran.bingham@ideasonboard.com
State Superseded
Headers show
Series
  • [libcamera-devel] v4l2: v4l2_camera_proxy: Detect ioctl sign-extensions
Related show

Commit Message

Kieran Bingham Feb. 27, 2023, 8:39 p.m. UTC
Handling ioctl's within applications is often wrapped with a helper such
as xioctl. Unfortunately, there are many instances of xioctl which
incorrectly handle the correct size of the ioctl request.

This leads to incorrect sign-extension of ioctl's which have bit-31 set,
and can cause values to be passed into the libcamera's v4l2 adaptation
layer which no longer represent the true IOCTL code.

We can easily identify if an application has mistakenly sign-extended an
ioctl command request and mask out those bits.

Do so and report the error loudly, but only once that the application
should be fixed.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---

This is a pain. It's not 'libcamera's' fault. But we can do something
about it.

See
 - https://github.com/kbingham/libcamera/issues/69
and
 - https://github.com/Motion-Project/motion/discussions/1636



 src/v4l2/v4l2_camera_proxy.cpp | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

Comments

Paul Elder Feb. 28, 2023, 6:46 a.m. UTC | #1
On Mon, Feb 27, 2023 at 08:39:47PM +0000, Kieran Bingham via libcamera-devel wrote:
> Handling ioctl's within applications is often wrapped with a helper such
> as xioctl. Unfortunately, there are many instances of xioctl which
> incorrectly handle the correct size of the ioctl request.
> 
> This leads to incorrect sign-extension of ioctl's which have bit-31 set,
> and can cause values to be passed into the libcamera's v4l2 adaptation
> layer which no longer represent the true IOCTL code.
> 
> We can easily identify if an application has mistakenly sign-extended an
> ioctl command request and mask out those bits.
> 
> Do so and report the error loudly, but only once that the application
> should be fixed.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
> 
> This is a pain. It's not 'libcamera's' fault. But we can do something
> about it.

Welp :/

Yeah we can do something about it. I see a future where the error gets
ignored because it "still works" though.


Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

> 
> See
>  - https://github.com/kbingham/libcamera/issues/69
> and
>  - https://github.com/Motion-Project/motion/discussions/1636
> 
> 
> 
>  src/v4l2/v4l2_camera_proxy.cpp | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
> 
> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> index 55ff62cdb430..75c53aedf2fa 100644
> --- a/src/v4l2/v4l2_camera_proxy.cpp
> +++ b/src/v4l2/v4l2_camera_proxy.cpp
> @@ -782,6 +782,24 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
>  {
>  	MutexLocker locker(proxyMutex_);
>  
> +	/*
> +	 * Applications may easily find themselves incorrectly sign-extending
> +	 * ioctls on 64-bit systems which can cause hard to diagnose failures
> +	 * with the v4l2-adatation layer. Identify this failure, and work
> +	 * around it - but report the issue as it should be fixed in the
> +	 * application.
> +	 */
> +	if (request & 0xffffffff00000000) {
> +		static bool warnOnce = true;
> +
> +		if (warnOnce) {
> +			LOG(V4L2Compat, Error) << "ioctl sign extension detected.";
> +			LOG(V4L2Compat, Error) << "Please fix your application.";
> +			warnOnce = false;
> +		}
> +		request &= 0xffffffff;
> +	}
> +
>  	if (!arg && (_IOC_DIR(request) & _IOC_WRITE)) {
>  		errno = EFAULT;
>  		return -1;
> -- 
> 2.34.1
>
Laurent Pinchart Feb. 28, 2023, 11:46 p.m. UTC | #2
Hi Kieran,

Thank you for the patch.

On Mon, Feb 27, 2023 at 08:39:47PM +0000, Kieran Bingham via libcamera-devel wrote:
> Handling ioctl's within applications is often wrapped with a helper such
> as xioctl. Unfortunately, there are many instances of xioctl which
> incorrectly handle the correct size of the ioctl request.
>
> This leads to incorrect sign-extension of ioctl's which have bit-31 set,
> and can cause values to be passed into the libcamera's v4l2 adaptation
> layer which no longer represent the true IOCTL code.
> 
> We can easily identify if an application has mistakenly sign-extended an
> ioctl command request and mask out those bits.
> 
> Do so and report the error loudly, but only once that the application
> should be fixed.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
> 
> This is a pain. It's not 'libcamera's' fault. But we can do something
> about it.
> 
> See
>  - https://github.com/kbingham/libcamera/issues/69
> and
>  - https://github.com/Motion-Project/motion/discussions/1636
>
>  src/v4l2/v4l2_camera_proxy.cpp | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
> 
> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> index 55ff62cdb430..75c53aedf2fa 100644
> --- a/src/v4l2/v4l2_camera_proxy.cpp
> +++ b/src/v4l2/v4l2_camera_proxy.cpp
> @@ -782,6 +782,24 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
>  {
>  	MutexLocker locker(proxyMutex_);
>  
> +	/*
> +	 * Applications may easily find themselves incorrectly sign-extending
> +	 * ioctls on 64-bit systems which can cause hard to diagnose failures
> +	 * with the v4l2-adatation layer. Identify this failure, and work

s/adatation/adaptation/

> +	 * around it - but report the issue as it should be fixed in the
> +	 * application.

As far as I understand, the kernel drops the upper 32 bits (see
fs/ioctl.c which defines the ioctl syscall handler with an unsigned int
cmd argument). I suppose it's nice to hint that sign extension isn't a
great idea, but I'm not sure this can be considered an application
error. You may want to adjust the comment accordingly (and keep in mind
I may well be wrong about the whole thing :-)).

> +	 */
> +	if (request & 0xffffffff00000000) {
> +		static bool warnOnce = true;
> +
> +		if (warnOnce) {
> +			LOG(V4L2Compat, Error) << "ioctl sign extension detected.";
> +			LOG(V4L2Compat, Error) << "Please fix your application.";

You could write this in a single message.

> +			warnOnce = false;
> +		}
> +		request &= 0xffffffff;
> +	}
> +
>  	if (!arg && (_IOC_DIR(request) & _IOC_WRITE)) {
>  		errno = EFAULT;
>  		return -1;
Kieran Bingham March 1, 2023, 1:06 p.m. UTC | #3
Hi Laurent,

On 28/02/2023 23:46, Laurent Pinchart wrote:
> Hi Kieran,
> 
> Thank you for the patch.
> 
> On Mon, Feb 27, 2023 at 08:39:47PM +0000, Kieran Bingham via libcamera-devel wrote:
>> Handling ioctl's within applications is often wrapped with a helper such
>> as xioctl. Unfortunately, there are many instances of xioctl which
>> incorrectly handle the correct size of the ioctl request.
>>
>> This leads to incorrect sign-extension of ioctl's which have bit-31 set,
>> and can cause values to be passed into the libcamera's v4l2 adaptation
>> layer which no longer represent the true IOCTL code.
>>
>> We can easily identify if an application has mistakenly sign-extended an
>> ioctl command request and mask out those bits.
>>
>> Do so and report the error loudly, but only once that the application
>> should be fixed.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>> ---
>>
>> This is a pain. It's not 'libcamera's' fault. But we can do something
>> about it.
>>
>> See
>>   - https://github.com/kbingham/libcamera/issues/69
>> and
>>   - https://github.com/Motion-Project/motion/discussions/1636
>>
>>   src/v4l2/v4l2_camera_proxy.cpp | 18 ++++++++++++++++++
>>   1 file changed, 18 insertions(+)
>>
>> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
>> index 55ff62cdb430..75c53aedf2fa 100644
>> --- a/src/v4l2/v4l2_camera_proxy.cpp
>> +++ b/src/v4l2/v4l2_camera_proxy.cpp
>> @@ -782,6 +782,24 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
>>   {
>>   	MutexLocker locker(proxyMutex_);
>>   
>> +	/*
>> +	 * Applications may easily find themselves incorrectly sign-extending
>> +	 * ioctls on 64-bit systems which can cause hard to diagnose failures
>> +	 * with the v4l2-adatation layer. Identify this failure, and work
> 
> s/adatation/adaptation/
> 
>> +	 * around it - but report the issue as it should be fixed in the
>> +	 * application.
> 
> As far as I understand, the kernel drops the upper 32 bits (see
> fs/ioctl.c which defines the ioctl syscall handler with an unsigned int
> cmd argument). I suppose it's nice to hint that sign extension isn't a
> great idea, but I'm not sure this can be considered an application
> error. You may want to adjust the comment accordingly (and keep in mind
> I may well be wrong about the whole thing :-)).

It's confusing, and I was looking for somewhere explicitly masking out 
the bits - I'd missed that it just gets ignored/masked by the 
compiler/interfaces because that's the type on the argument.

man ioctl, definitely states that ioctl() takes an unsigned long... so 
we can't change our args.

Maybe even silently masking is fine. But I'm happier in this small case 
to report it


>> +	 */
>> +	if (request & 0xffffffff00000000) {

I fear that being invalid or inefficient on 32 bit platforms though. /o\

And we can probably simplify all of this with something more like:

-int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, 
void *arg)
+int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long longReq, 
void *arg)
  {
         MutexLocker locker(proxyMutex_);

+       /*
+        * The Linux Kernel only processes 32 bits of an IOCTL.
+        *
+        * Prevent unexpected sign-extensions that could occur if 
applications
+        * use an unsigned int for the ioctl request, which would 
sign-extend
+        * to an incorrect value for unsigned longs on 64 bit 
architectures by
+        * explicitly casting as an unsigned int here.
+        */
+       unsigned int request = longReq;
+


>> +		static bool warnOnce = true;
>> +
>> +		if (warnOnce) {
>> +			LOG(V4L2Compat, Error) << "ioctl sign extension detected.";
>> +			LOG(V4L2Compat, Error) << "Please fix your application.";
> 
> You could write this in a single message.

I did that intentionally, but I'll keep the first line, and drop the second.

Or move to the unsigned int request type and remove the warning all 
together...


> 
>> +			warnOnce = false;
>> +		}
>> +		request &= 0xffffffff;
>> +	}
>> +
>>   	if (!arg && (_IOC_DIR(request) & _IOC_WRITE)) {
>>   		errno = EFAULT;
>>   		return -1;
>

Patch
diff mbox series

diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index 55ff62cdb430..75c53aedf2fa 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -782,6 +782,24 @@  int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
 {
 	MutexLocker locker(proxyMutex_);
 
+	/*
+	 * Applications may easily find themselves incorrectly sign-extending
+	 * ioctls on 64-bit systems which can cause hard to diagnose failures
+	 * with the v4l2-adatation layer. Identify this failure, and work
+	 * around it - but report the issue as it should be fixed in the
+	 * application.
+	 */
+	if (request & 0xffffffff00000000) {
+		static bool warnOnce = true;
+
+		if (warnOnce) {
+			LOG(V4L2Compat, Error) << "ioctl sign extension detected.";
+			LOG(V4L2Compat, Error) << "Please fix your application.";
+			warnOnce = false;
+		}
+		request &= 0xffffffff;
+	}
+
 	if (!arg && (_IOC_DIR(request) & _IOC_WRITE)) {
 		errno = EFAULT;
 		return -1;