[PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter

Ricardo Ribalda posted 4 patches 3 weeks ago
[PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
Posted by Ricardo Ribalda 3 weeks ago
Some camera modules have XU controls that can configure the behaviour of
the privacy LED.

Block mapping of those controls, unless the module is configured with
a new parameter: allow_privacy_override.

This is just an interim solution. Based on the users feedback, we will
either put the privacy controls behind a CONFIG option, or completely
block them.

Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c   | 38 ++++++++++++++++++++++++++++++++++++++
 drivers/media/usb/uvc/uvc_driver.c | 20 ++++++++++++++++++++
 drivers/media/usb/uvc/uvc_v4l2.c   |  7 +++++++
 drivers/media/usb/uvc/uvcvideo.h   |  2 ++
 include/linux/usb/uvc.h            |  4 ++++
 5 files changed, 71 insertions(+)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b6e020b41671..3ca108b83f1d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
 	return ret;
 }
 
+bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector)
+{
+	/*
+	 * This list is not exhaustive, it is a best effort to block access to
+	 * non documented controls that can affect user's privacy.
+	 */
+	struct privacy_control {
+		u8 entity[16];
+		u8 selector;
+	} privacy_control[] = {
+		{
+			.entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
+			.selector = 1,
+		},
+		{
+			.entity = UVC_GUID_LOGITECH_PERIPHERAL,
+			.selector = 9,
+		},
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(privacy_control); i++)
+		if (!memcmp(entity, privacy_control[i].entity, 16) &&
+		    selector == privacy_control[i].selector)
+			return true;
+
+	return false;
+}
+
 int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 	struct uvc_xu_control_query *xqry)
 {
@@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 		return -ENOENT;
 	}
 
+	if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
+	    !uvc_allow_privacy_override_param) {
+		dev_warn_once(&chain->dev->intf->dev,
+			      "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
+		uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
+			entity->guid, xqry->selector);
+		return -EACCES;
+	}
+
 	if (mutex_lock_interruptible(&chain->ctrl_mutex))
 		return -ERESTARTSYS;
 
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index b0ca81d924b6..74c9dea29d36 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1;
 static unsigned int uvc_quirks_param = -1;
 unsigned int uvc_dbg_param;
 unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
+bool uvc_allow_privacy_override_param;
 
 static struct usb_driver uvc_driver;
 
@@ -2505,6 +2506,25 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
 module_param_named(timeout, uvc_timeout_param, uint, 0644);
 MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
 
+static int param_set_privacy(const char *val, const struct kernel_param *kp)
+{
+	pr_warn_once("uvcvideo: " DEPRECATED
+		     "allow_privacy_override parameter will be eventually removed.\n");
+	return param_set_bool(val, kp);
+}
+
+static const struct kernel_param_ops param_ops_privacy = {
+	.set = param_set_privacy,
+	.get = param_get_bool,
+};
+
+param_check_bool(allow_privacy_override, &uvc_allow_privacy_override_param);
+module_param_cb(allow_privacy_override, &param_ops_privacy,
+		&uvc_allow_privacy_override_param, 0644);
+__MODULE_PARM_TYPE(allow_privacy_override, "bool");
+MODULE_PARM_DESC(allow_privacy_override,
+		 "Allow access to privacy related controls");
+
 /* ------------------------------------------------------------------------
  * Driver initialization and cleanup
  */
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index f9049e9c0d3a..6d4f027c8402 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
 		return -EINVAL;
 	}
 
+	if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
+	    !uvc_allow_privacy_override_param) {
+		dev_warn_once(&chain->dev->intf->dev,
+			      "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
+		return -EACCES;
+	}
+
 	map = kzalloc_obj(*map);
 	if (map == NULL)
 		return -ENOMEM;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 8480d65ecb85..362110d58ca3 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -664,6 +664,7 @@ extern unsigned int uvc_no_drop_param;
 extern unsigned int uvc_dbg_param;
 extern unsigned int uvc_timeout_param;
 extern unsigned int uvc_hw_timestamps_param;
+extern bool uvc_allow_privacy_override_param;
 
 #define uvc_dbg(_dev, flag, fmt, ...)					\
 do {									\
@@ -794,6 +795,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 		      struct uvc_xu_control_query *xqry);
 
 void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
+bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
 
 /* Utility functions */
 struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
index dea23aabbad4..70c2a7d25236 100644
--- a/include/linux/usb/uvc.h
+++ b/include/linux/usb/uvc.h
@@ -49,6 +49,10 @@
 #define UVC_GUID_LOGITECH_PERIPHERAL \
 	{0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
 	 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
+#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \
+	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+	 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f }
+
 
 /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
 #define UVC_MSXU_CONTROL_FOCUS			0x01

-- 
2.53.0.851.ga537e3e6e9-goog
Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
Posted by Hans de Goede 1 week ago
Hi,

On 16-Mar-26 14:34, Ricardo Ribalda wrote:
> Some camera modules have XU controls that can configure the behaviour of
> the privacy LED.
> 
> Block mapping of those controls, unless the module is configured with
> a new parameter: allow_privacy_override.
> 
> This is just an interim solution. Based on the users feedback, we will
> either put the privacy controls behind a CONFIG option, or completely
> block them.
> 
> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>

I realize this patch is not without controversy, but I do believe
that this is an important step to safeguard the privacy of Logitech
webcam users.

Discussion in this thread has mentioned libusb / usbfs access as
a workaround. But that can be used to directly access usb-storage
devices directly without going through a filesystem and those
pesky filesystem permission checks, yet we do bother with those.

As with all things related to information security it is all
about defenense in depth and this just makes it that bit harder
for spyware to disable the privacy LED.

I do believe that the RFC Kconfig option likely is a bridge
too far. As discussed in the thread it will likely be at least
a year before many stable distro users actually see the change
adding this module parameter, so switching to permanently
disabling this by default through Kconfig in a year seems much
too soon and depending on the feedback we may end up sticking
with the module parameter and never permanently disabling this.

As such while merging this I've removed this paragraph from the commit
message:

"This is just an interim solution. Based on the users feedback, we will
either put the privacy controls behind a CONFIG option, or completely
block them."

and I've also removed the deprecation warning turning this into
a regular bool module parameter.

I've pushed this to the uvc git repo for-next branch now,
with the discussed changes squashed in.

Regards,

Hans






> ---
>  drivers/media/usb/uvc/uvc_ctrl.c   | 38 ++++++++++++++++++++++++++++++++++++++
>  drivers/media/usb/uvc/uvc_driver.c | 20 ++++++++++++++++++++
>  drivers/media/usb/uvc/uvc_v4l2.c   |  7 +++++++
>  drivers/media/usb/uvc/uvcvideo.h   |  2 ++
>  include/linux/usb/uvc.h            |  4 ++++
>  5 files changed, 71 insertions(+)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index b6e020b41671..3ca108b83f1d 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
>  	return ret;
>  }
>  
> +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector)
> +{
> +	/*
> +	 * This list is not exhaustive, it is a best effort to block access to
> +	 * non documented controls that can affect user's privacy.
> +	 */
> +	struct privacy_control {
> +		u8 entity[16];
> +		u8 selector;
> +	} privacy_control[] = {
> +		{
> +			.entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
> +			.selector = 1,
> +		},
> +		{
> +			.entity = UVC_GUID_LOGITECH_PERIPHERAL,
> +			.selector = 9,
> +		},
> +	};
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(privacy_control); i++)
> +		if (!memcmp(entity, privacy_control[i].entity, 16) &&
> +		    selector == privacy_control[i].selector)
> +			return true;
> +
> +	return false;
> +}
> +
>  int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  	struct uvc_xu_control_query *xqry)
>  {
> @@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  		return -ENOENT;
>  	}
>  
> +	if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
> +	    !uvc_allow_privacy_override_param) {
> +		dev_warn_once(&chain->dev->intf->dev,
> +			      "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
> +		uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
> +			entity->guid, xqry->selector);
> +		return -EACCES;
> +	}
> +
>  	if (mutex_lock_interruptible(&chain->ctrl_mutex))
>  		return -ERESTARTSYS;
>  
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index b0ca81d924b6..74c9dea29d36 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1;
>  static unsigned int uvc_quirks_param = -1;
>  unsigned int uvc_dbg_param;
>  unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
> +bool uvc_allow_privacy_override_param;
>  
>  static struct usb_driver uvc_driver;
>  
> @@ -2505,6 +2506,25 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
>  module_param_named(timeout, uvc_timeout_param, uint, 0644);
>  MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
>  
> +static int param_set_privacy(const char *val, const struct kernel_param *kp)
> +{
> +	pr_warn_once("uvcvideo: " DEPRECATED
> +		     "allow_privacy_override parameter will be eventually removed.\n");
> +	return param_set_bool(val, kp);
> +}
> +
> +static const struct kernel_param_ops param_ops_privacy = {
> +	.set = param_set_privacy,
> +	.get = param_get_bool,
> +};
> +
> +param_check_bool(allow_privacy_override, &uvc_allow_privacy_override_param);
> +module_param_cb(allow_privacy_override, &param_ops_privacy,
> +		&uvc_allow_privacy_override_param, 0644);
> +__MODULE_PARM_TYPE(allow_privacy_override, "bool");
> +MODULE_PARM_DESC(allow_privacy_override,
> +		 "Allow access to privacy related controls");
> +
>  /* ------------------------------------------------------------------------
>   * Driver initialization and cleanup
>   */
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index f9049e9c0d3a..6d4f027c8402 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
>  		return -EINVAL;
>  	}
>  
> +	if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
> +	    !uvc_allow_privacy_override_param) {
> +		dev_warn_once(&chain->dev->intf->dev,
> +			      "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
> +		return -EACCES;
> +	}
> +
>  	map = kzalloc_obj(*map);
>  	if (map == NULL)
>  		return -ENOMEM;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 8480d65ecb85..362110d58ca3 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -664,6 +664,7 @@ extern unsigned int uvc_no_drop_param;
>  extern unsigned int uvc_dbg_param;
>  extern unsigned int uvc_timeout_param;
>  extern unsigned int uvc_hw_timestamps_param;
> +extern bool uvc_allow_privacy_override_param;
>  
>  #define uvc_dbg(_dev, flag, fmt, ...)					\
>  do {									\
> @@ -794,6 +795,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  		      struct uvc_xu_control_query *xqry);
>  
>  void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
> +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
>  
>  /* Utility functions */
>  struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
> diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
> index dea23aabbad4..70c2a7d25236 100644
> --- a/include/linux/usb/uvc.h
> +++ b/include/linux/usb/uvc.h
> @@ -49,6 +49,10 @@
>  #define UVC_GUID_LOGITECH_PERIPHERAL \
>  	{0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
>  	 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
> +#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \
> +	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
> +	 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f }
> +
>  
>  /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
>  #define UVC_MSXU_CONTROL_FOCUS			0x01
>
Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
Posted by Michal Pecio 2 weeks, 5 days ago
On Mon, 16 Mar 2026 13:34:46 +0000, Ricardo Ribalda wrote:
> Some camera modules have XU controls that can configure the behaviour of
> the privacy LED.
> 
> Block mapping of those controls, unless the module is configured with
> a new parameter: allow_privacy_override.
> 
> This is just an interim solution. Based on the users feedback, we will
> either put the privacy controls behind a CONFIG option, or completely
> block them.

What feedback do you expect to get?

Users will one day see their setup broken.
They will curse you and jump through the hoops you set up.
Next year they will see their setup broken completely.
They will curse again and wish you all pain, but *after* the fact.

> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c   | 38 ++++++++++++++++++++++++++++++++++++++
>  drivers/media/usb/uvc/uvc_driver.c | 20 ++++++++++++++++++++
>  drivers/media/usb/uvc/uvc_v4l2.c   |  7 +++++++
>  drivers/media/usb/uvc/uvcvideo.h   |  2 ++
>  include/linux/usb/uvc.h            |  4 ++++
>  5 files changed, 71 insertions(+)

This doesn't seem to cover libusb, VM guests and such.

What's even the attack vector? It has to be full remote code execution.
And it's just an LED, when you see it turn on somebody already has your
mugshot, if you notice at all. And the mugshot isn't your worst worry.

> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index b6e020b41671..3ca108b83f1d 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
>  	return ret;
>  }
>  
> +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector)
> +{
> +	/*
> +	 * This list is not exhaustive, it is a best effort to block access to
> +	 * non documented controls that can affect user's privacy.
> +	 */

So it's not removal of some controversial feature, but 3KB of extra
code in everybody's kernel (I just applied this patch) and a forever
game of whack-a-mole with HW vendors? They will win...

You will blacklist features found by legitimate users and shared on
public forums, while hackers will keep their findings to themselves.
Assuming that there are any who even care.

> +	struct privacy_control {
> +		u8 entity[16];
> +		u8 selector;
> +	} privacy_control[] = {
> +		{
> +			.entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
> +			.selector = 1,
> +		},
> +		{
> +			.entity = UVC_GUID_LOGITECH_PERIPHERAL,
> +			.selector = 9,
> +		},
> +	};
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(privacy_control); i++)
> +		if (!memcmp(entity, privacy_control[i].entity, 16) &&
> +		    selector == privacy_control[i].selector)
> +			return true;
> +
> +	return false;
> +}
> +
>  int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  	struct uvc_xu_control_query *xqry)
>  {
> @@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  		return -ENOENT;
>  	}
>  
> +	if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
> +	    !uvc_allow_privacy_override_param) {
> +		dev_warn_once(&chain->dev->intf->dev,
> +			      "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
> +		uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
> +			entity->guid, xqry->selector);
> +		return -EACCES;
> +	}
> +
>  	if (mutex_lock_interruptible(&chain->ctrl_mutex))
>  		return -ERESTARTSYS;
>  
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index b0ca81d924b6..74c9dea29d36 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1;
>  static unsigned int uvc_quirks_param = -1;
>  unsigned int uvc_dbg_param;
>  unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
> +bool uvc_allow_privacy_override_param;
>  
>  static struct usb_driver uvc_driver;
>  
> @@ -2505,6 +2506,25 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
>  module_param_named(timeout, uvc_timeout_param, uint, 0644);
>  MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
>  
> +static int param_set_privacy(const char *val, const struct kernel_param *kp)
> +{
> +	pr_warn_once("uvcvideo: " DEPRECATED
> +		     "allow_privacy_override parameter will be eventually removed.\n");
> +	return param_set_bool(val, kp);
> +}
> +
> +static const struct kernel_param_ops param_ops_privacy = {
> +	.set = param_set_privacy,
> +	.get = param_get_bool,
> +};
> +
> +param_check_bool(allow_privacy_override, &uvc_allow_privacy_override_param);
> +module_param_cb(allow_privacy_override, &param_ops_privacy,
> +		&uvc_allow_privacy_override_param, 0644);
> +__MODULE_PARM_TYPE(allow_privacy_override, "bool");
> +MODULE_PARM_DESC(allow_privacy_override,
> +		 "Allow access to privacy related controls");
> +
>  /* ------------------------------------------------------------------------
>   * Driver initialization and cleanup
>   */
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index f9049e9c0d3a..6d4f027c8402 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
>  		return -EINVAL;
>  	}
>  
> +	if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
> +	    !uvc_allow_privacy_override_param) {
> +		dev_warn_once(&chain->dev->intf->dev,
> +			      "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
> +		return -EACCES;
> +	}
> +
>  	map = kzalloc_obj(*map);
>  	if (map == NULL)
>  		return -ENOMEM;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 8480d65ecb85..362110d58ca3 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -664,6 +664,7 @@ extern unsigned int uvc_no_drop_param;
>  extern unsigned int uvc_dbg_param;
>  extern unsigned int uvc_timeout_param;
>  extern unsigned int uvc_hw_timestamps_param;
> +extern bool uvc_allow_privacy_override_param;
>  
>  #define uvc_dbg(_dev, flag, fmt, ...)					\
>  do {									\
> @@ -794,6 +795,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  		      struct uvc_xu_control_query *xqry);
>  
>  void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
> +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
>  
>  /* Utility functions */
>  struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
> diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
> index dea23aabbad4..70c2a7d25236 100644
> --- a/include/linux/usb/uvc.h
> +++ b/include/linux/usb/uvc.h
> @@ -49,6 +49,10 @@
>  #define UVC_GUID_LOGITECH_PERIPHERAL \
>  	{0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
>  	 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
> +#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \
> +	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
> +	 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f }
> +
>  
>  /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
>  #define UVC_MSXU_CONTROL_FOCUS			0x01
> 
> -- 
> 2.53.0.851.ga537e3e6e9-goog
>
Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
Posted by Ricardo Ribalda 2 weeks, 4 days ago
Hi Michal

On Thu, 19 Mar 2026 at 01:37, Michal Pecio <michal.pecio@gmail.com> wrote:
>
> On Mon, 16 Mar 2026 13:34:46 +0000, Ricardo Ribalda wrote:
> > Some camera modules have XU controls that can configure the behaviour of
> > the privacy LED.
> >
> > Block mapping of those controls, unless the module is configured with
> > a new parameter: allow_privacy_override.
> >
> > This is just an interim solution. Based on the users feedback, we will
> > either put the privacy controls behind a CONFIG option, or completely
> > block them.
>
> What feedback do you expect to get?

I want to identify the valid usecases for overriding the privacy LEDs.

>
> Users will one day see their setup broken.
> They will curse you and jump through the hoops you set up.
> Next year they will see their setup broken completely.
> They will curse again and wish you all pain, but *after* the fact.

The goal of the deprecation period is exactly this: to trigger a
conversation before a permanent block. If a user relies on this, they
can report it now. We can then decide if we need a specialized API for
their use case or a Kconfig option, rather than leaving the current
"anyone can turn off the privacy LED" status quo.


>
> > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > ---
> >  drivers/media/usb/uvc/uvc_ctrl.c   | 38 ++++++++++++++++++++++++++++++++++++++
> >  drivers/media/usb/uvc/uvc_driver.c | 20 ++++++++++++++++++++
> >  drivers/media/usb/uvc/uvc_v4l2.c   |  7 +++++++
> >  drivers/media/usb/uvc/uvcvideo.h   |  2 ++
> >  include/linux/usb/uvc.h            |  4 ++++
> >  5 files changed, 71 insertions(+)
>
> This doesn't seem to cover libusb, VM guests and such.

For libusb and VM guests to work you need to unbind the uvc driver.
Only privileged users can do that.
Today, any user with camera access can disable the privacy LED.

>
> What's even the attack vector? It has to be full remote code execution.
> And it's just an LED, when you see it turn on somebody already has your
> mugshot, if you notice at all. And the mugshot isn't your worst worry.

The attack vector is that an app with camera access, like your
browser, can record you when you don't want to be recorded.
The LED will be a signal that something is happening.

Imagine that you install a Flatpak for live streaming. Assuming the
Flatpak is properly sandboxed, remote code execution is less worrisome
than the app spying on you.

>
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index b6e020b41671..3ca108b83f1d 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
> >       return ret;
> >  }
> >
> > +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector)
> > +{
> > +     /*
> > +      * This list is not exhaustive, it is a best effort to block access to
> > +      * non documented controls that can affect user's privacy.
> > +      */
>
> So it's not removal of some controversial feature, but 3KB of extra
> code in everybody's kernel (I just applied this patch) and a forever
> game of whack-a-mole with HW vendors? They will win...

Maybe I meassured it wrong. But I can only account for 1.3 KiB

$ size drivers/media/usb/uvc/uvcvideo-without.ko
   text    data     bss     dec     hex filename
 115974    3748      88  119810   1d402 drivers/media/usb/uvc/uvcvideo.ko

$ size drivers/media/usb/uvc/uvcvideo-with.ko
   text    data     bss     dec     hex filename
 117315    3767      88  121170   1d952 drivers/media/usb/uvc/uvcvideo.ko

I see no need for vendors to hide these features, they simply added
them because an OEM thought it was a nice feature to have, or because
they left them as hardware debug features.

>
> You will blacklist features found by legitimate users and shared on
> public forums, while hackers will keep their findings to themselves.
> Assuming that there are any who even care.

If a legitimate user needs a feature, this patch gives them a way to
keep using it (allow_privacy_override) while notifying us. This allows
the community to find a better, safer way to support that specific
need without leaving the door wide open for everyone else.

>
> > +     struct privacy_control {
> > +             u8 entity[16];
> > +             u8 selector;
> > +     } privacy_control[] = {
> > +             {
> > +                     .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
> > +                     .selector = 1,
> > +             },
> > +             {
> > +                     .entity = UVC_GUID_LOGITECH_PERIPHERAL,
> > +                     .selector = 9,
> > +             },
> > +     };
> > +     int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(privacy_control); i++)
> > +             if (!memcmp(entity, privacy_control[i].entity, 16) &&
> > +                 selector == privacy_control[i].selector)
> > +                     return true;
> > +
> > +     return false;
> > +}
> > +
> >  int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
> >       struct uvc_xu_control_query *xqry)
> >  {
> > @@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
> >               return -ENOENT;
> >       }
> >
> > +     if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
> > +         !uvc_allow_privacy_override_param) {
> > +             dev_warn_once(&chain->dev->intf->dev,
> > +                           "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
> > +             uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
> > +                     entity->guid, xqry->selector);
> > +             return -EACCES;
> > +     }
> > +
> >       if (mutex_lock_interruptible(&chain->ctrl_mutex))
> >               return -ERESTARTSYS;
> >
> > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > index b0ca81d924b6..74c9dea29d36 100644
> > --- a/drivers/media/usb/uvc/uvc_driver.c
> > +++ b/drivers/media/usb/uvc/uvc_driver.c
> > @@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1;
> >  static unsigned int uvc_quirks_param = -1;
> >  unsigned int uvc_dbg_param;
> >  unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
> > +bool uvc_allow_privacy_override_param;
> >
> >  static struct usb_driver uvc_driver;
> >
> > @@ -2505,6 +2506,25 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
> >  module_param_named(timeout, uvc_timeout_param, uint, 0644);
> >  MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
> >
> > +static int param_set_privacy(const char *val, const struct kernel_param *kp)
> > +{
> > +     pr_warn_once("uvcvideo: " DEPRECATED
> > +                  "allow_privacy_override parameter will be eventually removed.\n");
> > +     return param_set_bool(val, kp);
> > +}
> > +
> > +static const struct kernel_param_ops param_ops_privacy = {
> > +     .set = param_set_privacy,
> > +     .get = param_get_bool,
> > +};
> > +
> > +param_check_bool(allow_privacy_override, &uvc_allow_privacy_override_param);
> > +module_param_cb(allow_privacy_override, &param_ops_privacy,
> > +             &uvc_allow_privacy_override_param, 0644);
> > +__MODULE_PARM_TYPE(allow_privacy_override, "bool");
> > +MODULE_PARM_DESC(allow_privacy_override,
> > +              "Allow access to privacy related controls");
> > +
> >  /* ------------------------------------------------------------------------
> >   * Driver initialization and cleanup
> >   */
> > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> > index f9049e9c0d3a..6d4f027c8402 100644
> > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > @@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
> >               return -EINVAL;
> >       }
> >
> > +     if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
> > +         !uvc_allow_privacy_override_param) {
> > +             dev_warn_once(&chain->dev->intf->dev,
> > +                           "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
> > +             return -EACCES;
> > +     }
> > +
> >       map = kzalloc_obj(*map);
> >       if (map == NULL)
> >               return -ENOMEM;
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 8480d65ecb85..362110d58ca3 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -664,6 +664,7 @@ extern unsigned int uvc_no_drop_param;
> >  extern unsigned int uvc_dbg_param;
> >  extern unsigned int uvc_timeout_param;
> >  extern unsigned int uvc_hw_timestamps_param;
> > +extern bool uvc_allow_privacy_override_param;
> >
> >  #define uvc_dbg(_dev, flag, fmt, ...)                                        \
> >  do {                                                                 \
> > @@ -794,6 +795,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
> >                     struct uvc_xu_control_query *xqry);
> >
> >  void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
> > +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
> >
> >  /* Utility functions */
> >  struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
> > diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
> > index dea23aabbad4..70c2a7d25236 100644
> > --- a/include/linux/usb/uvc.h
> > +++ b/include/linux/usb/uvc.h
> > @@ -49,6 +49,10 @@
> >  #define UVC_GUID_LOGITECH_PERIPHERAL \
> >       {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
> >        0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
> > +#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \
> > +     {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
> > +      0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f }
> > +
> >
> >  /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
> >  #define UVC_MSXU_CONTROL_FOCUS                       0x01
> >
> > --
> > 2.53.0.851.ga537e3e6e9-goog
> >



--
Ricardo Ribalda
Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
Posted by Michal Pecio 2 weeks, 4 days ago
On Thu, 19 Mar 2026 10:56:59 +0100, Ricardo Ribalda wrote:
> The goal of the deprecation period is exactly this: to trigger a
> conversation before a permanent block.

Most users will just curse and edit their /etc/modprobe.conf. They may
post a rant on some distro forum. I suspect no one will monitor this.

> We can then decide if we need a specialized API for their use case or
> a Kconfig option, rather than leaving the current "anyone can turn
> off the privacy LED" status quo.

Why not just add the specialized API right away?

I believe users affected by this regression are already known,
ISTR some negative response to previous iterations of this patch.

Kconfig option sounds crazy, who would want to rebuild the kernel
for this? Depending on BROKEN is double crazy.

> The attack vector is that an app with camera access, like your
> browser, can record you when you don't want to be recorded.
> The LED will be a signal that something is happening.
> 
> Imagine that you install a Flatpak for live streaming. Assuming the
> Flatpak is properly sandboxed, remote code execution is less worrisome
> than the app spying on you.

Theoretically yes. But also nobody should rely on those LEDs.
People who care ask HW vendors for physical switches or disconnect
the camera while not in use. I have seen black tape on laptop lids.

Are there more owners of affected hardware who want this code than
those who don't? Maybe it could be a Kconfig option for them :)

Most of my USB cameras don't even have activity LEDs.

> > So it's not removal of some controversial feature, but 3KB of extra
> > code in everybody's kernel (I just applied this patch) and a forever
> > game of whack-a-mole with HW vendors? They will win...  
> 
> Maybe I meassured it wrong. But I can only account for 1.3 KiB

I simply ran stat uvcvideo.ko and calculated difference.
Could be a matter of different kernel configs.

> I see no need for vendors to hide these features, they simply added
> them because an OEM thought it was a nice feature to have, or because
> they left them as hardware debug features.

But how will the kernel know about those random debug backdoors?
It just seems that whatever is discovered by users and becomes popular
enough to reach linux-media, will be getting blacklisted and broken.
Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
Posted by Ricardo Ribalda 2 weeks, 4 days ago
Hi Michal

On Thu, 19 Mar 2026 at 12:09, Michal Pecio <michal.pecio@gmail.com> wrote:
>
> On Thu, 19 Mar 2026 10:56:59 +0100, Ricardo Ribalda wrote:
> > The goal of the deprecation period is exactly this: to trigger a
> > conversation before a permanent block.
>
> Most users will just curse and edit their /etc/modprobe.conf. They may
> post a rant on some distro forum. I suspect no one will monitor this.
>
> > We can then decide if we need a specialized API for their use case or
> > a Kconfig option, rather than leaving the current "anyone can turn
> > off the privacy LED" status quo.
>
> Why not just add the specialized API right away?

We don't know the exact use cases yet, and I do not want to design an
API without understanding the users for it.

At this moment, we have only identified these usecases:

- Disabling the LED to avoid reflections in glasses. (This is
generally a non-issue with modern hardware).
- Baby monitors. (I would argue that physical tape is the correct
solution for a sleep-disturbing light).

>
> I believe users affected by this regression are already known,
> ISTR some negative response to previous iterations of this patch.
>
> Kconfig option sounds crazy, who would want to rebuild the kernel
> for this? Depending on BROKEN is double crazy.

I am not set on the final implementation yet; it is exactly the kind
of topic we should discuss at a media summit.

>
> > The attack vector is that an app with camera access, like your
> > browser, can record you when you don't want to be recorded.
> > The LED will be a signal that something is happening.
> >
> > Imagine that you install a Flatpak for live streaming. Assuming the
> > Flatpak is properly sandboxed, remote code execution is less worrisome
> > than the app spying on you.
>
> Theoretically yes. But also nobody should rely on those LEDs.
> People who care ask HW vendors for physical switches or disconnect
> the camera while not in use. I have seen black tape on laptop lids.

I rely on my LEDs. I know they are wired to the sensor power supply,
so the LED is definitely on when the camera is in use.
I want all users to be able to trust their LEDs like I do.

>
> Are there more owners of affected hardware who want this code than
> those who don't? Maybe it could be a Kconfig option for them :)

I believe the majority of users prefer a system that is "secure by
default." Most people expect that if the LED is off, the camera is
off.

>
> Most of my USB cameras don't even have activity LEDs.
>
> > > So it's not removal of some controversial feature, but 3KB of extra
> > > code in everybody's kernel (I just applied this patch) and a forever
> > > game of whack-a-mole with HW vendors? They will win...
> >
> > Maybe I meassured it wrong. But I can only account for 1.3 KiB
>
> I simply ran stat uvcvideo.ko and calculated difference.
> Could be a matter of different kernel configs.
>
> > I see no need for vendors to hide these features, they simply added
> > them because an OEM thought it was a nice feature to have, or because
> > they left them as hardware debug features.
>
> But how will the kernel know about those random debug backdoors?
> It just seems that whatever is discovered by users and becomes popular
> enough to reach linux-media, will be getting blacklisted and broken.
>

I prefer to say "filtered" rather than "broken." It’s a matter of
perspective: we are filtering out non-standard controls that undermine
user privacy. While we might not catch every debug backdoor
immediately, setting a policy and blocking known overrides is a
significant step and also sends a strong message to vendors.

Best regards!


-- 
Ricardo Ribalda
Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
Posted by Michal Pecio 1 week, 6 days ago
On Thu, 19 Mar 2026 12:43:21 +0100, Ricardo Ribalda wrote:
> > > We can then decide if we need a specialized API for their use
> > > case or a Kconfig option, rather than leaving the current "anyone
> > > can turn off the privacy LED" status quo.  
> >
> > Why not just add the specialized API right away?  
> 
> We don't know the exact use cases yet, and I do not want to design
> an API without understanding the users for it.
> 
> At this moment, we have only identified these usecases:
> 
> - Disabling the LED to avoid reflections in glasses. (This is
>   generally a non-issue with modern hardware).
> - Baby monitors. (I would argue that physical tape is the correct
>   solution for a sleep-disturbing light).

Indeed it was a rhetorical question, I suspect this won't go anywhere
beyond the module parameter for lack of interest from users. Apparently
it's a niche thing and it already works well enough for those who care.

Kconfig could make more sense to exclude this whole "filtering" logic
for those who don't care and may not appreciate bloat, e.g. embedded.

> I rely on my LEDs. I know they are wired to the sensor power supply,
> so the LED is definitely on when the camera is in use.
> I want all users to be able to trust their LEDs like I do.

This is objectively impossible without a soldering iron, and trust in
something that's not even real is ralely a good thing.

Ultimately it's just a software controllable LED. Anyone can drive it
through USBFS. You have a point that restricting this in uvcvideo may
keep some sandboxed applications on some HW from behaving in a manner
unexpected by some users, but that's about the limit of it.

And I wish that you enjoyed the same flexibility as those Logitech
camera owners. But you wouldn't want me to try make it happen ;)

Regards,
Michal
Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
Posted by Ricardo Ribalda 1 week, 4 days ago
Hi Michal

On Tue, 24 Mar 2026 at 13:07, Michal Pecio <michal.pecio@gmail.com> wrote:
>
> On Thu, 19 Mar 2026 12:43:21 +0100, Ricardo Ribalda wrote:
> > > > We can then decide if we need a specialized API for their use
> > > > case or a Kconfig option, rather than leaving the current "anyone
> > > > can turn off the privacy LED" status quo.
> > >
> > > Why not just add the specialized API right away?
> >
> > We don't know the exact use cases yet, and I do not want to design
> > an API without understanding the users for it.
> >
> > At this moment, we have only identified these usecases:
> >
> > - Disabling the LED to avoid reflections in glasses. (This is
> >   generally a non-issue with modern hardware).
> > - Baby monitors. (I would argue that physical tape is the correct
> >   solution for a sleep-disturbing light).
>
> Indeed it was a rhetorical question, I suspect this won't go anywhere
> beyond the module parameter for lack of interest from users. Apparently
> it's a niche thing and it already works well enough for those who care.
>
> Kconfig could make more sense to exclude this whole "filtering" logic
> for those who don't care and may not appreciate bloat, e.g. embedded.
>
> > I rely on my LEDs. I know they are wired to the sensor power supply,
> > so the LED is definitely on when the camera is in use.
> > I want all users to be able to trust their LEDs like I do.
>
> This is objectively impossible without a soldering iron, and trust in
> something that's not even real is ralely a good thing.
>
> Ultimately it's just a software controllable LED. Anyone can drive it
> through USBFS. You have a point that restricting this in uvcvideo may
> keep some sandboxed applications on some HW from behaving in a manner
> unexpected by some users, but that's about the limit of it.

Just one clarification. Not every user has permission to unbind the
uvcdriver and use ubsfs. Only priviledged users can do that.

From my point of view it is similar to the filesystem. "Anyone" can
read/write files (if they have permission) but just priviledge users
can `dd of=/dev/sda`

>
> And I wish that you enjoyed the same flexibility as those Logitech
> camera owners. But you wouldn't want me to try make it happen ;)
>
> Regards,
> Michal



-- 
Ricardo Ribalda