Bug 13357 - [PATCH] uvcvideo: VIDIOC_G_EXT_CTRLS does not handle NULL pointer correctly
Summary: [PATCH] uvcvideo: VIDIOC_G_EXT_CTRLS does not handle NULL pointer correctly
Status: RESOLVED OBSOLETE
Alias: None
Product: Drivers
Classification: Unclassified
Component: Video(Other) (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: drivers_video-other
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-05-21 19:22 UTC by Márton Németh
Modified: 2012-11-20 16:54 UTC (History)
3 users (show)

See Also:
Kernel Version: 2.6.30-rc6
Subsystem:
Regression: No
Bisected commit-id:


Attachments
full dmesg (38.17 KB, text/plain)
2009-05-21 19:22 UTC, Márton Németh
Details
test case to trigger VIDIOC_G_EXT_CTRLS bug (1.02 KB, text/x-csrc)
2009-05-21 19:25 UTC, Márton Németh
Details
Fix integer overflow when copying extended controls from userspace to kernelspace (1008 bytes, patch)
2009-05-24 16:33 UTC, Laurent Pinchart
Details | Diff

Description Márton Németh 2009-05-21 19:22:11 UTC
Created attachment 21470 [details]
full dmesg

When calling the VIDIOC_G_EXT_CTRLS with controls.count = 0x80000000 and with controls.controls = NULL together with CNF7129 webcam (on Asus EeePC 901) then I get the followin error message in "dmesg":

[   57.850663] BUG: unable to handle kernel NULL pointer dereference at 00000010
[   57.850681] IP: [<f9110b8a>] uvc_ctrl_get+0x10/0xae [uvcvideo]
[   57.850705] *pde = 00000000 
[   57.850713] Oops: 0000 [#1] PREEMPT SMP 
[   57.850724] last sysfs file: /sys/class/backlight/eeepc/brightness
[   57.850732] Modules linked in: rfkill_input ipv6 snd_hda_codec_realtek snd_hda_intel snd_hda_codec snd_pcm_oss snd_mixer_oss snd_pcm uvcvideo usbhid videodev v4l1_comp
at hid snd_seq_dummy snd_seq_oss snd_seq_midi snd_rawmidi psmouse i2c_i801 ehci_hcd i2c_core uhci_hcd serio_raw rng_core snd_seq_midi_event iTCO_wdt usbcore video output 
snd_seq atl1e battery snd_timer snd_seq_device rtc_cmos rtc_core rtc_lib eeepc_laptop thermal ac processor rfkill thermal_sys button intel_agp agpgart snd soundcore snd_p
age_alloc evdev [last unloaded: pci_hotplug]
[   57.850871] 
[   57.850880] Pid: 2283, comm: a.out Not tainted (2.6.30-rc6 #1) 901
[   57.850887] EIP: 0060:[<f9110b8a>] EFLAGS: 00010292 CPU: 1
[   57.850901] EIP is at uvc_ctrl_get+0x10/0xae [uvcvideo]
[   57.850909] EAX: f665a054 EBX: f6abe040 ECX: 00000000 EDX: 00000010
[   57.850916] ESI: 00000000 EDI: f665a054 EBP: f0ea9dbc ESP: f0ea9da8
[   57.850923]  DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
[   57.850931] Process a.out (pid: 2283, ti=f0ea8000 task=f0ea27a0 task.ti=f0ea8000)
[   57.850937] Stack:
[   57.850942]  00000010 00000000 f6abe040 00000000 00000010 f0ea9e2c f910e62c f0ea9e50
[   57.850965]  f665a054 00000000 00000000 f0ea9e00 00000246 00000001 f0ea2c80 00000001
[   57.850990]  f0ea27a0 f6a7750c c018a9d3 f0ea9e10 00000246 00000000 00000246 00000000
[   57.851017] Call Trace:
[   57.851023]  [<f910e62c>] ? uvc_v4l2_do_ioctl+0x3de/0xedb [uvcvideo]
[   57.851041]  [<c018a9d3>] ? might_fault+0x4b/0x88
[   57.851056]  [<c018aa0e>] ? might_fault+0x86/0x88
[   57.851067]  [<c02284cd>] ? copy_from_user+0x2a/0x111
[   57.851079]  [<c0185647>] ? try_to_free_pages+0x201/0x352
[   57.851090]  [<f8f7fa6b>] ? video_usercopy+0x1a8/0x248 [videodev]
[   57.851108]  [<c022b5aa>] ? _raw_spin_lock+0x53/0xfa
[   57.851119]  [<c018af00>] ? __do_fault+0x219/0x393
[   57.851130]  [<c022b551>] ? _raw_spin_unlock+0x78/0x7e
[   57.851141]  [<c017ae6d>] ? unlock_page+0x3e/0x41
[   57.851152]  [<c018b04d>] ? __do_fault+0x366/0x393
[   57.851163]  [<c0185647>] ? try_to_free_pages+0x201/0x352
[   57.851174]  [<f910dfa7>] ? uvc_v4l2_ioctl+0x42/0x4a [uvcvideo]
[   57.851191]  [<f910e24e>] ? uvc_v4l2_do_ioctl+0x0/0xedb [uvcvideo]
[   57.851207]  [<f910df65>] ? uvc_v4l2_ioctl+0x0/0x4a [uvcvideo]
[   57.851223]  [<f8f7f129>] ? v4l2_ioctl+0x33/0x37 [videodev]
[   57.851239]  [<c01ac1b9>] ? vfs_ioctl+0x50/0x69
[   57.851250]  [<c0185647>] ? try_to_free_pages+0x201/0x352
[   57.851261]  [<c01ac632>] ? do_vfs_ioctl+0x460/0x499
[   57.851272]  [<c01192a1>] ? do_page_fault+0xe7/0x219
[   57.851285]  [<c01ac6ab>] ? sys_ioctl+0x40/0x5a
[   57.851295]  [<c01031a4>] ? sysenter_do_call+0x12/0x38
[   57.851307]  [<c0185647>] ? try_to_free_pages+0x201/0x352
[   57.851318] Code: ec 89 f0 ff 56 58 31 c0 80 4b 09 05 eb 05 b8 ea ff ff ff 8d 65 f4 5b 5e 5f 5d c3 55 89 e5 57 89 c7 56 89 f8 53 83 ec 08 89 55 ec <8b> 12 8d 4d f0 e8 6f fb ff ff 85 c0 89 c6 74 7d 8b 58 04 f6 43 
[   57.851483] EIP: [<f9110b8a>] uvc_ctrl_get+0x10/0xae [uvcvideo] SS:ESP 0068:f0ea9da8
[   57.851503] CR2: 0000000000000010
[   57.851512] ---[ end trace c6df2638671a879c ]---

Expected result would be a return value of -1 and the errno set to EFAULT to tell the user-space software that the ioctl() parameter was not accepted.
Comment 1 Márton Németh 2009-05-21 19:25:27 UTC
Created attachment 21471 [details]
test case to trigger VIDIOC_G_EXT_CTRLS bug

The attached test case was developed as a part of v4l-test 0.15 ( http://v4l-test.sourceforge.net/ ). This simple ioctl() call triggers the mentioned behaviour.
Comment 2 Márton Németh 2009-05-21 19:41:22 UTC
The V4L2 specification revision 0.24 on this topic can be found at http://v4l2spec.bytesex.org/spec/r10386.htm .
Comment 3 Laurent Pinchart 2009-05-23 14:37:18 UTC
It seems the problem comes from the v4l core, not the uvcvideo driver. Both video_usercopy and video_ioctl2 fail to check controls.count correctly, resulting in an integer overflow. Can you reproduce the problem if you set controls.control to NULL and controls.count to a sensible value (let's say 10) ?

On a side note, the stack trace seems to be corrupt.
Comment 4 Márton Németh 2009-05-24 15:52:02 UTC
It seems that controls.count=0x80000000 is a special value necessary to trigger this problem. I tested the following values:

controls.  | return  | errno       | expected  | expected    | test
   count   | value   |             | ret. val. | errno       | result
-----------+---------+-------------+-----------+-------------+--------
0          | 0       | (not set)   | 0         | -           | passed
1          | -1      | 14 (EFAULT) | -1        | 14 (EFAULT) | passed
10         | -1      | 14 (EFAULT) | -1        | 14 (EFAULT) | passed
0x7FFFFFFF | -1      | 12 (ENOMEM) | -1        | 14 (EFAULT) | FAILED
0x80000000 | Segmentation fault    | -1        | 14 (EFAULT) | FAILED
0x80000001 | -1      | 14 (EFAULT) | -1        | 14 (EFAULT) | passed
0x8000000A | -1      | 14 (EFAULT) | -1        | 14 (EFAULT) | passed
0xFFFFFFFF | -1      | 12 (ENOMEM) | -1        | 14 (EFAULT) | FAILED

I would expect EFAULT in all cases where controls.count != 0 .

There is a similar problem with VIDIOC_S_EXT_CTRLS and with VIDIOC_TRY_EXT_CTRLS what is mentioned in comment #1.
Comment 5 Laurent Pinchart 2009-05-24 16:33:33 UTC
Created attachment 21519 [details]
Fix integer overflow when copying extended controls from userspace to kernelspace

I suspect that controls.count = 0x40000000 will crash the kernel as well.

Could you please try the attached patch ?

Is it acceptable to return -ENOMEM instead of -EFAULT when controls.controls is an invalid userspace pointer and controls.count is higher than the amount of memory that can be allocated by kmalloc ? The video_ioctl2 and video_usercopy both allocate memory to copy controls.controls from userspace to kernelspace, and only check is the pointer is valid after allocating the memory. If memory can't be allocated (because controls.count is too high) they just return -ENOMEM.

Restricting controls.count to values smaller than KMALLOC_MAX_SIZE / sizeof(struct v4l2_ext_control) will likely fix the problem you noticed. It might make sense to restrict it even further. Any opinion on that ?
Comment 6 Márton Németh 2009-05-25 06:27:29 UTC
(In reply to comment #5)
> Created an attachment (id=21519) [details]
> Fix integer overflow when copying extended controls from userspace to
> kernelspace
> 
> I suspect that controls.count = 0x40000000 will crash the kernel as well.

I tried the value 0x20000000 which also crashes the kernel.

> Could you please try the attached patch ?

I tested your patch with Linux kernel 2.6.30-rc7 with CNF7129 and with v4l-test 0.15 from http://v4l-test.sourceforge.net/ . The patch fixes the problem.

Tested-by: Márton Németh <nm127@freemail.hu>

> Is it acceptable to return -ENOMEM instead of -EFAULT when controls.controls
> is
> an invalid userspace pointer and controls.count is higher than the amount of
> memory that can be allocated by kmalloc ? The video_ioctl2 and video_usercopy
> both allocate memory to copy controls.controls from userspace to kernelspace,
> and only check is the pointer is valid after allocating the memory. If memory
> can't be allocated (because controls.count is too high) they just return
> -ENOMEM.

The error value shall point out the cause of the error. The return value -ENOMEM would point to the controls.count so the application programmer would check that field hopefully. The second thing is that the code shall be optimized for normal operation and not for error handling. So I think that the -ENOMEM instead of -EFAULT would be acceptable for me. I implemented v4l-test version 0.15 so that it expects -ENOMEM or -EFAULT.
Comment 7 Andrew Morton 2009-05-27 00:03:08 UTC
I don't get it.

If p->count is too large then the kmalloc() will fail and the driver
should have detected that, cleaned things up and returned a failure.

So I suspect that there's a different bug, and this p->count check
will just cover it up?
Comment 8 Laurent Pinchart 2009-05-27 00:24:05 UTC
kmalloc is passed p->count * sizeof(struct v4l2_ext_control). The size of the structure might depend on the architecture but it will always be a multiple of 4 bytes. Large p->count values will trigger an integer overflow that will make kmalloc succeed.

For instance, on x86 struct v4l2_ext_control seems to be 20 bytes long. Setting p->count to 0x80000001 will kmalloc 20 bytes of memory.

p->count = 0x80000000 will result in a different issue. ctrls_size will be equal to 0, and kmalloc will return ZERO_SIZE_PTR (which is equal to ((void *)16)). copy_from_user will succeed, as we ask it to copy 0 bytes, and control will be passed to the driver which will receive bogus values.
Comment 9 Andrew Morton 2009-05-27 00:41:04 UTC
ah, OK, right you are.

A nice way to fix this is to use kcalloc(), which has the multiplicative
overflow check built-in.

Unfortunately kcalloc() also zeroes the memory, which is often undesired.  I'd
merge a patch which adds a kcalloc_no_zero(), only I can't think of a good
name for it :(
Comment 10 Márton Németh 2009-12-06 18:23:48 UTC
Was this patch merged to any repository? I am facing with the same problem under Ubuntu 9.10 with the following kernel version:

$ cat /proc/version
Linux version 2.6.31-14-generic (buildd@rothera) (gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu8) ) #48-Ubuntu SMP Fri Oct 16 14:04:26 UTC 2009

The details about that problem can be found at https://bugs.launchpad.net/ubuntu/+source/linux/+bug/493246 .
Comment 11 Laurent Pinchart 2009-12-10 11:12:59 UTC
No it hasn't been merged, thanks for reminding me. I've just pinged Mauro.

Note You need to log in before you can comment on or make changes to this bug.