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
prev parent reply other threads:[~2026-11-10 7:35 UTC|newest]
Thread overview: 41+ 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 7:33 ` [PATCH v2 02/19] wifi: mac80211_hwsim: lock device for manual driver binding Leon Romanovsky
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-09 7:33 ` [PATCH v2 06/19] cdx: Propagate DMA configuration errors Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 07/19] driver core: Report deferred probes through sysfs drivers_probe Leon Romanovsky
2026-11-09 7:33 ` [PATCH v2 08/19] driver core: Add a compatibility API for manual driver attachment Leon Romanovsky
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-09 7:33 ` [PATCH v2 13/19] driver core: Accept device trust policy on the kernel command line Leon Romanovsky
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-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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox