CVE-2026-46239
Description
In the Linux kernel, the following vulnerability has been resolved:
media: i2c: ov5647: Fix runtime PM refcount leak in s_ctrl
Three control cases (AUTOGAIN, EXPOSURE_AUTO, ANALOGUE_GAIN) directly return without calling pm_runtime_put(), causing runtime PM reference count leaks.
Change these cases from 'return' to 'ret = ... break' pattern to ensure pm_runtime_put() is always called before function exit.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Linux kernel's ov5647 driver, three control cases in s_ctrl cause runtime PM refcount leaks, fixed by ensuring pm_runtime_put().
Vulnerability
In the Linux kernel's ov5647 media driver (drivers/media/i2c/ov5647.c), the s_ctrl function handles power management using runtime PM. Three control cases (AUTOGAIN, EXPOSURE_AUTO, ANALOGUE_GAIN) directly return without calling pm_runtime_put(), causing a runtime PM reference count leak. This affects all kernel versions containing the buggy code.
Exploitation
An attacker with the ability to control the sensor's controls via V4L2 IOCTL (e.g., a local user with access to the video device) can repeatedly trigger these control paths to deplete the PM reference count, eventually preventing the device from entering low-power states.
Impact
The vulnerability leads to a runtime PM reference count leak, which may cause the device to remain powered unnecessarily, potentially leading to increased power consumption and denial of service by exhausting system resources.
Mitigation
The fix was committed in the Linux kernel stable tree as commits [1] and [2]. Users should apply the corresponding patched kernel or update to a version containing the fix. If patching is not possible, avoid using the affected controls (AUTOGAIN, EXPOSURE_AUTO, ANALOGUE_GAIN) until a fix is applied.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
4f11ae9c04f83media: i2c: ov5647: Fix runtime PM refcount leak in s_ctrl
2 files changed · +12 −14
drivers/media/i2c/ov5647.c+6 −7 modifieddiff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac3f..db9bd2892140de 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* -- cgit 1.3-korg
drivers/media/i2c/ov5647.c+6 −7 modifieddiff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac3f..db9bd2892140de 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* -- cgit 1.3-korg
6b03ecf75bdamedia: i2c: ov5647: Fix runtime PM refcount leak in s_ctrl
2 files changed · +12 −14
drivers/media/i2c/ov5647.c+6 −7 modifieddiff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac3f..db9bd2892140de 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* -- cgit 1.3-korg
drivers/media/i2c/ov5647.c+6 −7 modifieddiff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac3f..db9bd2892140de 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* -- cgit 1.3-korg
6b03ecf75bdamedia: i2c: ov5647: Fix runtime PM refcount leak in s_ctrl
2 files changed · +12 −14
drivers/media/i2c/ov5647.c+6 −7 modifieddiff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac3f..db9bd2892140de 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* -- cgit 1.3-korg
drivers/media/i2c/ov5647.c+6 −7 modifieddiff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac3f..db9bd2892140de 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* -- cgit 1.3-korg
f11ae9c04f83media: i2c: ov5647: Fix runtime PM refcount leak in s_ctrl
2 files changed · +12 −14
drivers/media/i2c/ov5647.c+6 −7 modifieddiff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac3f..db9bd2892140de 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* -- cgit 1.3-korg
drivers/media/i2c/ov5647.c+6 −7 modifieddiff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 6a46ef7233ac3f..db9bd2892140de 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -967,21 +967,21 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: /* Non-zero turns on AGC by clearing bit 1.*/ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), - ctrl->val ? 0 : BIT(1), NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: /* * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by * clearing bit 0. */ - return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), - ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); + ret = cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: /* 10 bits of gain, 2 in the high register. */ - return cci_write(sensor->regmap, OV5647_REG_GAIN, - ctrl->val & 0x3ff, NULL); + ret = cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: /* -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing pm_runtime_put() call on early return paths in ov5647_s_ctrl for three V4L2 control cases (AUTOGAIN, EXPOSURE_AUTO, ANALOGUE_GAIN), causing runtime PM reference count leaks."
Attack vector
An attacker with access to the V4L2 device node (e.g., via a video4linux userspace API) can set the AUTOGAIN, EXPOSURE_AUTO, or ANALOGUE_GAIN controls. Each time one of these controls is set, the function ov5647_s_ctrl returns early without calling pm_runtime_put(), which was previously called by pm_runtime_get_sync() at the top of the function [patch_id=2897487]. Repeatedly setting these controls will increment the runtime PM usage counter without decrementing it, eventually preventing the device from suspending and potentially causing power management instability or denial of service.
Affected code
The bug is in the ov5647_s_ctrl() function in drivers/media/i2c/ov5647.c [patch_id=2897487]. Three case labels within the switch statement — V4L2_CID_AUTOGAIN, V4L2_CID_EXPOSURE_AUTO, and V4L2_CID_ANALOGUE_GAIN — used direct 'return' statements that bypassed the function's common exit path where pm_runtime_put() is called.
What the fix does
The patch changes the three offending case blocks from directly returning the result of the register I/O call to assigning the result to the local variable 'ret' and then falling through to the existing 'break' statement [patch_id=2897487]. After the switch statement, the function's common exit path calls pm_runtime_put() unconditionally. This ensures that every code path through the switch correctly balances the pm_runtime_get_sync() call made at the start of ov5647_s_ctrl, eliminating the reference count leak.
Preconditions
- inputThe attacker must be able to open the V4L2 device node and call S_CTRL (set control) ioctls.
- configThe ov5647 driver must be bound to an OV5647 sensor device and runtime PM must be enabled.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.