[PATCH] media: gspca: Handle SENSOR_HV7131R

Philipp Matthias Hahn posted 1 patch 2 months ago
There is a newer version of this series
drivers/media/usb/gspca/sonixb.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
[PATCH] media: gspca: Handle SENSOR_HV7131R
Posted by Philipp Matthias Hahn 2 months ago
I found an old USB webcam 0c45:602d Microdia VideoCAM ExpressII. `vlc`
triggered an OOPS as soon as I opened the device:

> BUG: kernel NULL pointer dereference, address: 0000000000000068
> #PF: supervisor read access in kernel mode
> #PF: error_code(0x0000) - not-present page
> PGD 0 P4D 0
> Oops: Oops: 0000 [#1] PREEMPT SMP NOPTI
> CPU: 8 UID: 1000 PID: 19655 Comm: vlc Tainted: G           O       6.12.74+deb13+1-amd64 #1  Debian 6.12.74-2
> Tainted: [O]=OOT_MODULE
> Hardware name: Micro-Star International Co., Ltd. MS-7B89/B450M MORTAR MAX (MS-7B89), BIOS 2.P1 09/18/2025
> RIP: 0010:do_autogain+0x7d/0x100 [gspca_sonixb]
> Code: 74 21 0f af 90 c4 00 00 00 89 d0 48 63 d2 48 69 d2 09 04 02 81 48 c1 ea 20 01 c2 c1 f8 1f c1 fa 06 29 c2 48 8b 83 48 06 00 00 <48> 81 78 68 f3 01 00 00 7f 34 48 89 df e8 41 f1 3b 00 85 c0 74 07

(The out-of-tree-module is v4l2-loopback.)

Adding addition debug information I found out, that the cam is based on
an SENSOR_HV7131R:

> gspca_main: sonixb-2.14.0 probing 0c45:602d
> sonixb 1-3:1.0: sd_config(sensor=01 bridge=00)

In case of an SENSOR_HV7131R `gspca_dev->exposure` is not setup.
Enabling auto-gain will result in the above OOPS.

1. Check for `gspca_dev->exposure != NULL` before dereferencing it.

Even after that there's 2nd OOPS:

> BUG: kernel NULL pointer dereference, address: 0000000000000034
> #PF: supervisor read access in kernel mode
> #PF: error_code(0x0000) - not-present page
> PGD 0 P4D 0
> Oops: Oops: 0000 [#1] SMP NOPTI
> CPU: 1 UID: 1000 PID: 709 Comm: vlc Tainted: G            E       7.0.0+ #6 PREEMPT(lazy)
> Tainted: [E]=UNSIGNED_MODULE
> Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 2025.02-8+deb13u1 11/08/2025
> RIP: 0010:v4l2_ctrl_g_ctrl+0x36/0x80 [videodev]
> Code: 20 65 48 8b 05 63 17 b1 c8 48 89 44 24 18 31 c0 c7 44 24 14 00 00 00 00 48 c7 44 24 04 00 00 00 00 48 c7 44 24 0c 00 00 00 00 <f6> 47 34 20 74 2e 48 8d 74 24 04 c7 44 24 10 00 00 00 00 e8 72 fd

This is caused by v4l2_ctrl_g_ctrl() dereferencing gspca_dev->autogain,
which remains NULL as gspca_dev->exposure is NULL.

Check for `gspca_dev->autogain != NULL` before dereferencing it via
gspca_expo_autogain().

Signed-off-by: Philipp Matthias Hahn <pmhahn@pmhahn.de>
---
 drivers/media/usb/gspca/sonixb.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c
index 4d655e2da9cb..9a9aba5a35de 100644
--- a/drivers/media/usb/gspca/sonixb.c
+++ b/drivers/media/usb/gspca/sonixb.c
@@ -900,11 +900,11 @@ static void do_autogain(struct gspca_dev *gspca_dev)
 	if (sd->brightness)
 		desired_avg_lum = sd->brightness->val * desired_avg_lum / 127;
 
-	if (gspca_dev->exposure->maximum < 500) {
+	if (gspca_dev->exposure && gspca_dev->exposure->maximum < 500) {
 		if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
 				desired_avg_lum, deadzone))
 			sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
-	} else {
+	} else if (gspca_dev->autogain) {
 		int gain_knee = (s32)gspca_dev->gain->maximum * 9 / 10;
 		if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum,
 				deadzone, gain_knee, sd->exposure_knee))
@@ -927,6 +927,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
 	sd->sensor = id->driver_info >> 8;
 	sd->bridge = id->driver_info & 0xff;
 
+	dev_info(gspca_dev->v4l2_dev.dev, "sd_config(sensor=%02x bridge=%02x)\n", sd->sensor, sd->bridge);
+
 	cam = &gspca_dev->cam;
 	if (!(sensor_data[sd->sensor].flags & F_SIF)) {
 		cam->cam_mode = vga_mode;
@@ -958,7 +960,7 @@ static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 
 	gspca_dev->usb_err = 0;
 
-	if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
+	if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val && gspca_dev->exposure) {
 		/* when switching to autogain set defaults to make sure
 		   we are on a valid point of the autogain gain /
 		   exposure knee graph, and give this change time to
@@ -976,7 +978,7 @@ static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 		setbrightness(gspca_dev);
 		break;
 	case V4L2_CID_AUTOGAIN:
-		if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+		if ((gspca_dev->exposure && gspca_dev->exposure->is_new) || (ctrl->is_new && ctrl->val))
 			setexposure(gspca_dev);
 		if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
 			setgain(gspca_dev);
-- 
2.47.3