All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Leon Romanovsky" <leon@kernel.org>
Cc: linux-input@vger.kernel.org, linux-usb@vger.kernel.org,
	linux-pci@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v2 19/19] USB: Authorize devices and interfaces through device trust
Date: Tue, 10 Nov 2026 07:35:26 +0000	[thread overview]
Message-ID: <sashiko-outbox-206519@kernel.org> (raw)
In-Reply-To: <20261109-device-trust-core-v2-19-3b12d9a9f861@nvidia.com>

Thank you for your contribution! Sashiko AI review found 9 potential
issues to consider:
- [High] usb: core: usb_resume_device() fails with -ENOTCONN on
  trust-disabled devices
- [High] usb: core: missing usb_notify_remove_device() on unbound
  disconnect
- [Medium] usb: core: probing deauthorized device or interface
  prematurely commits trust
- [Medium] usb: core: usb_driver_claim_interface() rejects disabled
  sibling before driver rule
- [Medium] usb: core: usb_authorize_device() drops device lock and races
  with deauthorize
- [Medium] usb: core: usb_device_authorized() true before driver
  admission
- [Medium] usb: core: usb_set_configuration() drops parent adversarial
  policy
- [Medium] usb: core: race in usb_authorize_interface() with
  deauthorization
- [Low] usb: core: missing device_trust_init() in usb_alloc_dev()
--

commit b35772e1cb354ec9df9404c1711878235a7ba8c8
Author: Leon Romanovsky <leon@kernel.org>

USB: Authorize devices and interfaces through device trust

This commit integrates USB device and interface authorization with device
trust by mapping the default authorization policies to
DEVICE_TRUST_POLICY_DISABLED and requesting DEVICE_TRUST_POLICY_FULL when
authorizing via sysfs.

> diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
> index 04b77691fbd7d..2ebd05d3855f4 100644
> --- a/drivers/usb/core/driver.c
> +++ b/drivers/usb/core/driver.c
> @@ -334,10 +334,10 @@ static int usb_probe_interface(struct device *dev)
>  	if (usb_device_is_owned(udev))
>  		return error;
>  
> -	if (udev->authorized == 0) {
> +	if (!usb_device_authorized(udev)) {
>  		dev_info(&intf->dev, "Device is not authorized for usage\n");
>  		return error;
> -	} else if (intf->authorized == 0) {
> +	} else if (!usb_interface_authorized(intf)) {
>  		dev_info(&intf->dev, "Interface %d is not authorized for usage\n",
>  				intf->altsetting->desc.bInterfaceNumber);
>  		return error;

[Severity: Medium]
Does rejecting a deauthorized device or interface inside
usb_probe_interface() (or usb_generic_driver_probe()) happen too late to
avoid committing its trust level?

When an interface starts with interface_authorized_default enabled
(trust_state == DEVICE_TRUST_SELECTABLE) and is deauthorized before any
driver binds, intf->authorized is cleared to 0 while intf->dev.trust_policy
stays DEVICE_TRUST_POLICY_DEFAULT.  Because usb_device_match() does not check
authorization, a subsequent driver load or reprobe enters really_probe() and
runs device_trust_prepare() before usb_probe_interface() is called:

drivers/base/dd.c:really_probe() {
    ...
re_probe:
	ret = device_trust_prepare(dev, drv);
	if (ret) {
    ...
}

device_trust_prepare() permanently sets dev->p->trust_state to
DEVICE_TRUST_COMMITTED before usb_probe_interface() returns -ENODEV, and
device_trust_clear() in the probe failure path does not revert trust_state.
User space can then no longer change trust_policy on the deauthorized
interface and gets -EBUSY.

> @@ -560,13 +560,9 @@ int usb_driver_claim_interface(struct usb_driver *driver,
>  		return -EBUSY;
>  
>  	/* reject claim if interface is not authorized */
> -	if (!iface->authorized)
> +	if (!usb_interface_authorized(iface))
>  		return -ENODEV;
>  
> -	if (!device_is_registered(dev) &&
> -	    dev->trust_policy == DEVICE_TRUST_POLICY_DISABLED)
> -		return -EPERM;
> -
>  	dev->driver = &driver->driver;
>  	usb_set_intfdata(iface, data);
>  	iface->needs_binding = 0;

[Severity: Medium]
Does checking usb_interface_authorized(iface) here break claiming sibling
interfaces when interfaces start disabled by default and a driver is
authorized via a device_trust.driver= boot rule?

When interface_authorized_default is 0 or device_trust.default=disabled is
set, each interface starts with iface->dev.trust_policy set to
DEVICE_TRUST_POLICY_DISABLED.  Driver rules from device_trust.driver= are
only applied when device_trust_prepare() runs inside device_bind_driver() or
device_add() later in usb_driver_claim_interface().  Testing
usb_interface_authorized(iface) up front sees DEVICE_TRUST_POLICY_DISABLED on
the sibling interface and returns -ENODEV before device_trust_prepare() can
evaluate the driver rule.

> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index 199d3f535a891..086b8db36f51a 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c

[ ... ]

> @@ -2786,7 +2786,22 @@ int usb_authorize_device(struct usb_device *usb_dev)
>  {
>  	int result = 0, c;
>  
> +	/* Authorizing a device that its trust policy holds back admits it */
> +	if (device_policy_is_disabled(&usb_dev->dev)) {
> +		result = device_trust_set_policy(&usb_dev->dev,
> +						 DEVICE_TRUST_POLICY_FULL);
> +		if (result)
> +			return result;
> +	}
> +
>  	usb_lock_device(usb_dev);
> +	if (!usb_dev->dev.driver) {
> +		/* Binding the generic driver configures the device */
> +		usb_dev->authorized = 1;
> +		usb_unlock_device(usb_dev);
> +		result = device_attach(&usb_dev->dev);
> +		return result < 0 ? result : 0;
> +	}
>  	if (usb_dev->authorized == 1)
>  		goto out_authorized;

[Severity: Medium]
Can a concurrent usb_deauthorize_device() race with usb_authorize_device()
across the unlocked windows between device_trust_set_policy(),
usb_lock_device(), and device_attach()?

Because usb_alloc_dev() initializes usb_dev->authorized to 1,
usb_device_authorized(usb_dev) becomes true as soon as
device_trust_set_policy() sets DEVICE_TRUST_POLICY_FULL and drops the device
lock:

CPU0                                  CPU1
usb_authorize_device(usb_dev)
  device_trust_set_policy(..., FULL)
    device_lock(&usb_dev->dev)
    usb_dev->dev.trust_policy = FULL
    device_unlock(&usb_dev->dev)
                                      usb_deauthorize_device(usb_dev)
                                        usb_lock_device(usb_dev)
                                        usb_dev->authorized = 0
                                        usb_set_configuration(usb_dev, -1)
                                        usb_unlock_device(usb_dev)
  usb_lock_device(usb_dev)
  if (!usb_dev->dev.driver)
    usb_dev->authorized = 1
    usb_unlock_device(usb_dev)
    device_attach(&usb_dev->dev)  <-- configures deauthorized device

A deauthorization that completed on CPU1 is silently overwritten, leaving the
device authorized and configured.  If usb_deauthorize_device() instead runs
between usb_unlock_device() and device_attach(), device_attach() still binds
usb_generic_driver and commits DEVICE_TRUST_FULL on the deauthorized device.

> diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
> index 2d75b85b2d139..ddede900dace8 100644
> --- a/drivers/usb/core/message.c
> +++ b/drivers/usb/core/message.c

[ ... ]

> @@ -1872,16 +1872,27 @@ void usb_deauthorize_interface(struct usb_interface *intf)
>   * usb_authorize_interface - authorize an USB interface
>   *
>   * @intf: USB interface structure
> + *
> + * Return: 0 on success, or the error of the trust policy request.
>   */
> -void usb_authorize_interface(struct usb_interface *intf)
> +int usb_authorize_interface(struct usb_interface *intf)
>  {
>  	struct device *dev = &intf->dev;
> +	int ret;
> +
> +	/* Authorizing an interface that its trust policy holds back admits it */
> +	if (device_policy_is_disabled(dev)) {
> +		ret = device_trust_set_policy(dev, DEVICE_TRUST_POLICY_FULL);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	if (!intf->authorized) {
>  		device_lock(dev);
>  		intf->authorized = 1; /* authorize interface */
>  		device_unlock(dev);
>  	}
> +	return 0;
>  }

[Severity: Medium]
Can usb_authorize_interface() race with a concurrent
usb_deauthorize_interface() when device_trust_set_policy() and
intf->authorized = 1 run in separate critical sections without
device_lock(dev->parent)?

CPU0                                  CPU1
usb_authorize_interface(intf)
  device_trust_set_policy(..., FULL)
    device_lock(dev)
    dev->trust_policy = FULL
    device_unlock(dev)
                                      usb_deauthorize_interface(intf)
                                        device_lock(dev->parent)
                                        device_lock(dev)
                                        intf->authorized = 0
                                        device_unlock(dev)
                                        usb_forced_unbind_intf(intf)
                                          intf->needs_binding = 1
  if (!intf->authorized)
    device_lock(dev)
    intf->authorized = 1  <-- clobbers deauthorization and races on bitfield
    device_unlock(dev)
                                        device_unlock(dev->parent)

Because intf->authorized starts at 1, usb_interface_authorized(intf) becomes
true as soon as device_trust_set_policy() drops device_lock(dev).  If
usb_deauthorize_interface() runs at that point, usb_authorize_interface()
then sees !intf->authorized and sets intf->authorized back to 1 without
holding device_lock(dev->parent), undoing the deauthorization and racing with
usb_forced_unbind_intf() writing intf->needs_binding in the same bitfield
word.

[ ... ]

> @@ -2173,7 +2184,10 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
>  		intfc = cp->intf_cache[i];
>  		intf->altsetting = intfc->altsetting;
>  		intf->num_altsetting = intfc->num_altsetting;
> -		intf->authorized = !!HCD_INTF_AUTHORIZED(hcd);
> +		/* The authorization default is the interface's initial trust policy */
> +		if (!HCD_INTF_AUTHORIZED(hcd))
> +			intf->dev.trust_policy = DEVICE_TRUST_POLICY_DISABLED;
> +		intf->authorized = 1;
>  		kref_get(&intfc->ref);

[Severity: Medium]
When a USB device is authorized with the "adversary" trust policy, does
usb_set_configuration() fail to propagate that policy to its child
interfaces?

Documentation/usb/authorization.rst explains that writing "adversary" to a
device's trust_policy before writing 1 to authorized operates the device as
an adversary, and boot rules such as device_trust=usb:1-2=adversary set the
policy on the struct usb_device as well.  However, usb_set_configuration()
leaves intf->dev.trust_policy at DEVICE_TRUST_POLICY_DEFAULT whenever
HCD_INTF_AUTHORIZED(hcd) is set, and usb_bus_type does not propagate parent
adversarial trust to child interfaces.  As a result, USB interface drivers
bind to intf->dev with DEVICE_TRUST_FULL and device_is_adversarial(&intf->dev)
returns false.

> diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
> index 2559a180fdeec..8b427209bcc02 100644
> --- a/drivers/usb/core/usb.c
> +++ b/drivers/usb/core/usb.c
> @@ -747,7 +747,10 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
>  	dev->active_duration = -jiffies;
>  #endif
>  
> -	dev->authorized = usb_dev_authorized(dev, usb_hcd);
> +	/* The authorization default is the device's initial trust policy */
> +	if (!usb_dev_authorized(dev, usb_hcd))
> +		dev->dev.trust_policy = DEVICE_TRUST_POLICY_DISABLED;
> +	dev->authorized = 1;

[Severity: High]
When a USB device is held with DEVICE_TRUST_POLICY_DISABLED so
usb_generic_driver does not bind, does usb_resume_device() fail with
-ENOTCONN after the device is suspended?

Previously, an unauthorized USB device still bound usb_generic_driver during
device_add(), so udev->dev.driver was non-NULL.  With udev->dev.driver now
NULL on a trust-disabled device, usb_suspend_device() falls back to
usb_generic_driver and suspends the port, setting udev->state to
USB_STATE_SUSPENDED, but usb_resume_device() rejects unbound devices:

drivers/usb/core/driver.c:usb_resume_device() {
    ...
	/* Can't resume it if it doesn't have a driver. */
	if (udev->dev.driver == NULL) {
		status = -ENOTCONN;
		goto done;
	}
    ...
}

System sleep resume and runtime resume therefore fail with -ENOTCONN, leaving
the upstream port suspended and udev->state stuck in USB_STATE_SUSPENDED, and
subsequent authorization via usb_authorize_device() fails when trying to
configure the device over the suspended port.

[Severity: High]
Does leaving udev->dev.driver NULL on a trust-disabled USB device also skip
usb_notify_remove_device() when the device is disconnected before being
authorized?

usb_disconnect() relies on the bound device driver's disconnect callback to
invoke the remove-device notifier chain during device_del(&udev->dev), and
usb_generic_driver_disconnect() is the only caller of
usb_notify_remove_device(udev).  While a device is held back by device trust,
its /dev/bus/usb/BBB/DDD node still exists and can be opened via
usbdev_open().  If the device is unplugged without ever binding
usb_generic_driver, usb_notify_remove_device() and usbdev_remove() are never
called, so processes blocked in poll() or usbfs ioctls on that file
descriptor are never woken up and hang indefinitely.

>  	trace_usb_alloc_dev(dev);
>  	return dev;
>  }

[Severity: Low]
Should usb_alloc_dev() call device_trust_init(&dev->dev) before
trace_usb_alloc_dev(dev)?

trace_usb_alloc_dev() and the early trace_usb_set_device_state() calls prior
to device_add() record usb_device_authorized(udev).  Because
device_trust_init(&dev->dev) has not been called yet, those tracepoints log
the HCD default policy rather than any boot policy from device_trust.default=
or device_trust=.

> diff --git a/include/linux/usb.h b/include/linux/usb.h
> index 825f03b290670..2ead3234b1301 100644
> --- a/include/linux/usb.h
> +++ b/include/linux/usb.h

[ ... ]

> @@ -747,6 +753,15 @@ struct usb_device {
>  
>  #define to_usb_device(__dev)	container_of_const(__dev, struct usb_device, dev)
>  
> +/*
> + * A device is authorized when its trust policy lets it bind and user space has
> + * not deauthorized it since.
> + */
> +static inline bool usb_device_authorized(const struct usb_device *udev)
> +{
> +	return udev->authorized && !device_policy_is_disabled(&udev->dev);
> +}

[Severity: Medium]
Does usb_device_authorized() report true as soon as trust_policy is changed
from disabled, even before the device has been authorized or bound to a
driver?

Because usb_alloc_dev() initializes udev->authorized to 1, writing
"adversary" to trust_policy (as Documentation/usb/authorization.rst instructs
before writing 1 to authorized) makes device_policy_is_disabled(&udev->dev)
false and usb_device_authorized(udev) true while udev->dev is still unbound
and unadmitted.  At that point, reading sysfs authorized already returns 1,
and writing to bConfigurationValue (or issuing USBDEVFS_SETCONFIGURATION)
passes the !usb_device_authorized(dev) check in usb_set_configuration() and
configures the device and binds interface drivers while udev->dev.driver is
still NULL and udev->dev's trust level is still DEVICE_TRUST_DISABLED.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261109-device-trust-core-v2-0-3b12d9a9f861@nvidia.com?part=19

      reply	other threads:[~2026-11-10  7:35 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-09  7:33 [PATCH v2 00/19] driver core: Authorize device trust before driver binding Leon Romanovsky
2026-11-09  7:33 ` [PATCH v2 01/19] Input: gameport - attach manual drivers through the driver core Leon Romanovsky
2026-11-09 10:06   ` driver core: Authorize device trust before driver binding bluez.test.bot
2026-11-10  7:35   ` [PATCH v2 01/19] Input: gameport - attach manual drivers through the driver core sashiko-bot
2026-11-09  7:33 ` [PATCH v2 02/19] wifi: mac80211_hwsim: lock device for manual driver binding Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 03/19] USB: Lock registered interfaces during " Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-10 14:19     ` Leon Romanovsky
2026-11-09  7:33 ` [PATCH v2 04/19] USB: Release interface claims refused during registration Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-10 14:23     ` Leon Romanovsky
2026-11-09  7:33 ` [PATCH v2 05/19] PNP: Attach card devices through the driver core Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 06/19] cdx: Propagate DMA configuration errors Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 07/19] driver core: Report deferred probes through sysfs drivers_probe Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 08/19] driver core: Add a compatibility API for manual driver attachment Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 09/19] Input: serio - use the compatibility driver attachment API Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-10 14:29     ` Leon Romanovsky
2026-11-09  7:33 ` [PATCH v2 10/19] net: phy: Use " Leon Romanovsky
2026-11-09 13:45   ` Andrew Lunn
2026-11-10  7:39     ` Leon Romanovsky
2026-11-10 14:31       ` Andrew Lunn
2026-11-10 14:58         ` Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 11/19] driver core: Introduce a generic device trust level Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-10 15:12     ` Leon Romanovsky
2026-11-09  7:33 ` [PATCH v2 12/19] driver core: Expose device trust authorization interfaces Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 13/19] driver core: Accept device trust policy on the kernel command line Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 14/19] driver core: Accept device trust policy by driver name Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 15/19] driver core: Expose committed device trust level Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 16/19] driver core: Cover trust authorization and binding with KUnit Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 17/19] driver core: Document trust admission and initrd authorization Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 18/19] PCI: Replace untrusted state with device trust Leon Romanovsky
2026-11-09 13:53   ` Robin Murphy
2026-11-10 12:22     ` Leon Romanovsky
2026-11-10 14:25       ` Jason Gunthorpe
2026-11-10 14:34         ` Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot
2026-11-09  7:33 ` [PATCH v2 19/19] USB: Authorize devices and interfaces through " Leon Romanovsky
2026-11-10  7:35   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-206519@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=leon@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.