From nobody Fri Aug 14 15:09:22 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hM5H63NF7z6nyx4 for ; Fri, 14 Aug 2026 15:09:22 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hM5H62YwLz3qNW for ; Fri, 14 Aug 2026 15:09:22 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1786720162; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=yMy8XcP3fXV/kKDw7WAo9ixJ4kVmdXa5fxK4gytjsmY=; b=iY2vK+8d26VyTSPD2MvXS8oTBrM96X06Rl32xAnni1lcjuAQeTSqFlieNQje9IaX6obFmq anPjN31UpQ+ngmZVzbNsfSSxkpKkGVTxu7j5VHywmZtbxQW5unqsDHQOXL+D3CNsPBlBj0 cMbM6+QJm78JtaDQFvm3Cigjw7B2Cs/HhmuJeN3+je7kzI5NkZ+9SR0XmMqvzhw/wB3A/g oF/GVtFey+WxhoqCU7W4xVBfEzWGMNd3OoHMpDVHMgX+NWj24yBGXaYGlrNbJlQn4kXXF4 Y/mcfzmQkp8Eai5PI4aHCS7/VPhzSYz77oDfdodAU5O/scZR+SW0slkUjFLO4A== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1786720162; a=rsa-sha256; cv=none; b=Jw8/DRg4sDZWWT69JB5eCFlToEfZWxf9y/dPoKop4lepMh5jjw+VQf+T/jEgHCQsRM/EAJ CJYDPLUm+qE0yq6jtugbh4jpg3QEvKgh8xZWGEOfUz8I6brRlHE5HE8iT9hFAv6e95iAdO dvTB8OhybwHcOgEhniIjoxScU+sEOLrIGIVkTwv3Fva1uTU6uhyCgIJdM0nZuzVGz8Fc0D jMM0OeH6DKJhVuTSRgcmX8h2B8Tj+NrwOGkBmVcJ3BKEUCbYYAKl92dNVPewdMe6BslYpg sbOYsoMrnrM0VbW8b9elcfCde6gozJMjiQt9pZsmvsq5kEkqfodfOtjgk2GMnw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1786720162; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=yMy8XcP3fXV/kKDw7WAo9ixJ4kVmdXa5fxK4gytjsmY=; b=QSOI3Mck2QjiThCiKaBLQC4mO6iHc0Z9gRDMvXVBv5YMLQNcOzs8pK7BHE6xsM9139fEky Vc7s4sO+rTKCBMTG7XJe1XfeSEafkdUz+K+kjhY6mh3gX+tk39mUAEOyzfxakLK92UawBl 4OGCsyL//utpiDBrSErAqyFpPpkb4gcvfbXoypddEd6l6HdM4DoaZjR4XK8zUATZSUFW2z PhP2uUoQD1t26yFNVFOoOv73Dy5bJAnpcgPmanEAT9SkIfTHDDztf0rFlu6uRHql5ft85X m1Xs/Jxl8Gk72S09k3BFmKq7/7tOia+6A59e14OzMzhlScf+A9iC0yZ2N1zK6w== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hM5H61MDYz137G for ; Fri, 14 Aug 2026 15:09:22 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 41138 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Fri, 14 Aug 2026 15:09:22 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: ShengYi Hung Subject: git: 749318f2ae56 - main - libusb: capsicumize libusb List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: aokblast X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 749318f2ae56127abdffee405f8f4658efa1c807 Auto-Submitted: auto-generated Date: Fri, 14 Aug 2026 15:09:22 +0000 Message-Id: <6a7f2fa2.41138.6d86e1c7@gitrepo.freebsd.org> The branch main has been updated by aokblast: URL: https://cgit.FreeBSD.org/src/commit/?id=749318f2ae56127abdffee405f8f4658efa1c807 commit 749318f2ae56127abdffee405f8f4658efa1c807 Author: ShengYi Hung AuthorDate: 2025-08-11 15:05:24 +0000 Commit: ShengYi Hung CommitDate: 2026-08-14 15:09:08 +0000 libusb: capsicumize libusb FreeBSD's libusb has three components: libusb01, libusb10, and libusb20. libusb20 handles communication with character devices. We now requires a backend context for libusb20. The backend context contains contains the capsicumized usbctrl fd and usb directory (/dev/usb) fd so that the library user can enter the capiblity mode safely while using libusb. libusb10 is updated to support capabilities via a context option. Since libusb allows general read/write access, we preserve all possible capabilities when passing backend context to libusb20. It is the responsibility of the libusb user to call cap_enter() at an appropriate time. All base system tools using libusb and libusb20 have been updated to support Capsicum. Reviewed by: adrian, markj Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D51865 --- lib/libusb/Symbol.map | 13 ++-- lib/libusb/libusb.3 | 17 +++++ lib/libusb/libusb01.c | 15 +++- lib/libusb/libusb10.c | 18 ++++- lib/libusb/libusb10.h | 3 + lib/libusb/libusb20.3 | 65 ++++++++++++++++- lib/libusb/libusb20.c | 29 ++++++-- lib/libusb/libusb20.h | 13 ++-- lib/libusb/libusb20_be_device_foreach.3 | 2 +- lib/libusb/libusb20_dev_open.3 | 2 +- lib/libusb/libusb20_int.h | 22 ++++++ lib/libusb/libusb20_ugen20.c | 124 ++++++++++++++++++++++++++------ share/examples/libusb20/bulk.c | 9 ++- share/examples/libusb20/control.c | 9 ++- tools/tools/usbtest/usb_msc_test.c | 4 +- tools/tools/usbtest/usbtest.c | 19 +++++ tools/tools/usbtest/usbtest.h | 3 + usr.sbin/usbconfig/dump.c | 64 ++++++----------- usr.sbin/usbconfig/dump.h | 1 + usr.sbin/usbconfig/usbconfig.c | 13 +++- 20 files changed, 354 insertions(+), 91 deletions(-) diff --git a/lib/libusb/Symbol.map b/lib/libusb/Symbol.map index 46bd1d8aeace..310bb4c35a21 100644 --- a/lib/libusb/Symbol.map +++ b/lib/libusb/Symbol.map @@ -120,10 +120,6 @@ FBSD_1.8 { libusb_wait_for_event; libusb_wrap_sys_device; libusb20_be_add_dev_quirk; - libusb20_be_alloc; - libusb20_be_alloc_default; - libusb20_be_alloc_linux; - libusb20_be_alloc_ugen20; libusb20_be_dequeue_device; libusb20_be_device_foreach; libusb20_be_enqueue_device; @@ -258,3 +254,12 @@ FBSD_1.8 { usb_set_debug; usb_strerror; }; + +FBSD_1.9 { + libusb20_be_alloc; + libusb20_be_alloc_default; + libusb20_be_alloc_linux; + libusb20_be_alloc_ugen20; + libusb20_be_ctx_alloc; + libusb20_be_ctx_free; +}; diff --git a/lib/libusb/libusb.3 b/lib/libusb/libusb.3 index 42b2e90f148e..a946e998df9e 100644 --- a/lib/libusb/libusb.3 +++ b/lib/libusb/libusb.3 @@ -945,8 +945,25 @@ The library is also compliant with LibUSB version 0.1.12. .Fn usb_get_driver_np .Fn usb_detach_kernel_driver_np .Fn usb_attach_kernel_driver_np +.Sh CAPABILITY MODE +Every +.Nm +context opens the file descriptors needed to reach the USB devices at +.Fn libusb_init +time and opens device nodes relative to a per-context directory +descriptor using +.Xr openat 2 . +All operations on the context, including enumeration of hotplugged +devices, therefore keep working after entering capability mode, see +.Xr capsicum 4 . +The file descriptors are limited with +.Xr cap_rights_limit 2 . +Initialise the context before calling +.Xr cap_enter 2 . .Sh SEE ALSO +.Xr cap_enter 2 , .Xr libusb20 3 , +.Xr capsicum 4 , .Xr usb 4 , .Xr usbconfig 8 , .Xr usbdump 8 diff --git a/lib/libusb/libusb01.c b/lib/libusb/libusb01.c index 84367765815a..865c115efd68 100644 --- a/lib/libusb/libusb01.c +++ b/lib/libusb/libusb01.c @@ -85,6 +85,13 @@ static struct usb_bus usb_global_bus = { static struct libusb20_backend *usb_backend = NULL; +/* + * The backend context lives for the lifetime of the process, because + * opened devices are dequeued from the backend and survive backend + * rescans. The libusb v0.1 API has no exit function anyway. + */ +static struct libusb20_be_ctx *usb_be_ctx = NULL; + struct usb_parse_state { struct { @@ -901,8 +908,14 @@ usb_find_devices(void) libusb20_be_free(usb_backend); + if (usb_be_ctx == NULL) { + usb_be_ctx = libusb20_be_ctx_alloc(); + if (usb_be_ctx == NULL) { + return (-1); + } + } /* do a new backend device search */ - usb_backend = libusb20_be_alloc_default(); + usb_backend = libusb20_be_alloc_default(usb_be_ctx); if (usb_backend == NULL) { return (-1); } diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c index f47b301ea302..2fc1dfe71f25 100644 --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -302,6 +302,21 @@ libusb_init_context(libusb_context **context, return (LIBUSB_ERROR_OTHER); } + /* + * The backend context acquires the file descriptors needed to + * reach the USB devices, so that the context keeps working + * after cap_enter(2) has been called. + */ + ctx->be_ctx = libusb20_be_ctx_alloc(); + if (ctx->be_ctx == NULL) { + close(ctx->event); + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_mutex_destroy(&ctx->hotplug_lock); + pthread_cond_destroy(&ctx->ctx_cond); + free(ctx); + return (LIBUSB_ERROR_NO_MEM); + } + libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->event, POLLIN); pthread_mutex_lock(&default_context_lock); @@ -353,6 +368,7 @@ libusb_exit(libusb_context *ctx) libusb10_remove_pollfd(ctx, &ctx->ctx_poll); close(ctx->event); + libusb20_be_ctx_free(ctx->be_ctx); pthread_mutex_destroy(&ctx->ctx_lock); pthread_mutex_destroy(&ctx->hotplug_lock); pthread_cond_destroy(&ctx->ctx_cond); @@ -384,7 +400,7 @@ libusb_get_device_list(libusb_context *ctx, libusb_device ***list) if (list == NULL) return (LIBUSB_ERROR_INVALID_PARAM); - usb_backend = libusb20_be_alloc_default(); + usb_backend = libusb20_be_alloc_default(ctx->be_ctx); if (usb_backend == NULL) return (LIBUSB_ERROR_NO_MEM); diff --git a/lib/libusb/libusb10.h b/lib/libusb/libusb10.h index cd90531d1a7b..1036270da00b 100644 --- a/lib/libusb/libusb10.h +++ b/lib/libusb/libusb10.h @@ -120,6 +120,9 @@ struct libusb_context { void *fd_cb_user_data; libusb_log_cb log_cb; int no_discovery; + + /* backend context holding the USB file descriptors */ + struct libusb20_be_ctx *be_ctx; }; struct libusb_device { diff --git a/lib/libusb/libusb20.3 b/lib/libusb/libusb20.3 index f2405e90ce7a..67ed44515a27 100644 --- a/lib/libusb/libusb20.3 +++ b/lib/libusb/libusb20.3 @@ -198,11 +198,15 @@ USB access library (libusb -lusb) .Ft int .Fn libusb20_be_remove_dev_quirk "struct libusb20_backend *pbe" "struct libusb20_quirk *pq" .Ft struct libusb20_backend * -.Fn libusb20_be_alloc_default "void" +.Fn libusb20_be_alloc_default "struct libusb20_be_ctx *pctx" .Ft struct libusb20_backend * -.Fn libusb20_be_alloc_freebsd "void" +.Fn libusb20_be_alloc_freebsd "struct libusb20_be_ctx *pctx" .Ft struct libusb20_backend * -.Fn libusb20_be_alloc_linux "void" +.Fn libusb20_be_alloc_linux "struct libusb20_be_ctx *pctx" +.Ft struct libusb20_be_ctx * +.Fn libusb20_be_ctx_alloc "void" +.Ft void +.Fn libusb20_be_ctx_free "struct libusb20_be_ctx *pctx" .Ft struct libusb20_device * .Fn libusb20_be_device_foreach "struct libusb20_backend *pbe" "struct libusb20_device *pdev" .Ft void @@ -994,6 +998,33 @@ returned. These functions are used to allocate a specific USB backend or the operating system default USB backend. Allocating a backend is a way to scan for currently present USB devices. +The backend and the USB devices it enumerates borrow the given +backend context, which holds the file descriptors used to reach the +USB devices and must stay alive for as long as they are in use. +If the +.Fa pctx +argument is +.Dv NULL, +the backend allocates its own context and releases +it when the backend is freed. +USB devices which have been dequeued from such a backend must not be +used after the backend has been freed. +.Pp +. +.Fn libusb20_be_ctx_alloc +allocates a backend context, which opens and holds the file +descriptors used to reach the USB devices. +The context is immutable after allocation and may therefore be shared +between threads, backends and devices without any locking. +Sharing one context across multiple backend allocations allows +devices to be enumerated and opened after the process has entered +capability mode. +. +.Fn libusb20_be_ctx_free +frees the given backend context. +No backend or device allocated from the context may be used +afterwards. +This function is NULL safe. .Pp . .Fn libusb20_be_device_foreach @@ -1086,8 +1117,36 @@ This function does not return NULL. .Bl -tag -width Pa .It Pa /dev/usb .El +.Sh CAPABILITY MODE +The backend context holds file descriptors for +.Pa /dev/usbctl +and the +.Pa /dev/usb +directory, and device nodes are opened relative to the directory +descriptor using +.Xr openat 2 . +All backend operations, including enumeration of hotplugged devices, +therefore keep working after entering capability mode, see +.Xr capsicum 4 , +as long as the backend context was allocated before +.Xr cap_enter 2 +was called. +Applications which allocate more than one USB backend from capability +mode must allocate a backend context with +.Fn libusb20_be_ctx_alloc +up front and pass it to every backend allocation. +The file descriptors are limited with +.Xr cap_rights_limit 2 , +and USB device file descriptors derived from the directory descriptor +inherit the +.Dv CAP_PREAD , CAP_PWRITE , CAP_EVENT +and +.Dv CAP_IOCTL +rights. .Sh SEE ALSO +.Xr cap_enter 2 , .Xr libusb 3 , +.Xr capsicum 4 , .Xr usb 4 , .Xr usbconfig 8 , .Xr usbdump 8 diff --git a/lib/libusb/libusb20.c b/lib/libusb/libusb20.c index 2cd8a6ea7037..4fe3d3068905 100644 --- a/lib/libusb/libusb20.c +++ b/lib/libusb/libusb20.c @@ -30,6 +30,7 @@ #else #include #include +#include #include #include #include @@ -1273,7 +1274,8 @@ libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device } struct libusb20_backend * -libusb20_be_alloc(const struct libusb20_backend_methods *methods) +libusb20_be_alloc(const struct libusb20_backend_methods *methods, + struct libusb20_be_ctx *pctx) { struct libusb20_backend *pbe; @@ -1283,6 +1285,17 @@ libusb20_be_alloc(const struct libusb20_backend_methods *methods) } memset(pbe, 0, sizeof(*pbe)); + if (pctx == NULL) { + /* the backend owns its own context */ + pctx = libusb20_be_ctx_alloc(); + if (pctx == NULL) { + free(pbe); + return (NULL); + } + pbe->be_ctx_owner = 1; + } + pbe->be_ctx = pctx; + TAILQ_INIT(&(pbe->usb_devs)); pbe->methods = methods; /* set backend methods */ @@ -1295,29 +1308,29 @@ libusb20_be_alloc(const struct libusb20_backend_methods *methods) } struct libusb20_backend * -libusb20_be_alloc_linux(void) +libusb20_be_alloc_linux(struct libusb20_be_ctx *pctx) { return (NULL); } struct libusb20_backend * -libusb20_be_alloc_ugen20(void) +libusb20_be_alloc_ugen20(struct libusb20_be_ctx *pctx) { - return (libusb20_be_alloc(&libusb20_ugen20_backend)); + return (libusb20_be_alloc(&libusb20_ugen20_backend, pctx)); } struct libusb20_backend * -libusb20_be_alloc_default(void) +libusb20_be_alloc_default(struct libusb20_be_ctx *pctx) { struct libusb20_backend *pbe; #ifdef __linux__ - pbe = libusb20_be_alloc_linux(); + pbe = libusb20_be_alloc_linux(pctx); if (pbe) { return (pbe); } #endif - pbe = libusb20_be_alloc_ugen20(); + pbe = libusb20_be_alloc_ugen20(pctx); if (pbe) { return (pbe); } @@ -1340,6 +1353,8 @@ libusb20_be_free(struct libusb20_backend *pbe) if (pbe->methods->exit_backend) { pbe->methods->exit_backend(pbe); } + if (pbe->be_ctx_owner) + libusb20_be_ctx_free(pbe->be_ctx); /* free backend */ free(pbe); } diff --git a/lib/libusb/libusb20.h b/lib/libusb/libusb20.h index 6a95042eda44..6fa08b2b485e 100644 --- a/lib/libusb/libusb20.h +++ b/lib/libusb/libusb20.h @@ -177,6 +177,7 @@ struct usb_device_info; struct libusb20_transfer; struct libusb20_backend; struct libusb20_backend_methods; +struct libusb20_be_ctx; struct libusb20_device; struct libusb20_device_methods; struct libusb20_config; @@ -294,11 +295,13 @@ int libusb20_be_set_template(struct libusb20_backend *pbe, int temp); /* USB backend operations */ -struct libusb20_backend *libusb20_be_alloc(const struct libusb20_backend_methods *methods); -struct libusb20_backend *libusb20_be_alloc_default(void); -struct libusb20_backend *libusb20_be_alloc_freebsd(void); -struct libusb20_backend *libusb20_be_alloc_linux(void); -struct libusb20_backend *libusb20_be_alloc_ugen20(void); +struct libusb20_backend *libusb20_be_alloc(const struct libusb20_backend_methods *methods, struct libusb20_be_ctx *pctx); +struct libusb20_backend *libusb20_be_alloc_default(struct libusb20_be_ctx *pctx); +struct libusb20_backend *libusb20_be_alloc_freebsd(struct libusb20_be_ctx *pctx); +struct libusb20_backend *libusb20_be_alloc_linux(struct libusb20_be_ctx *pctx); +struct libusb20_backend *libusb20_be_alloc_ugen20(struct libusb20_be_ctx *pctx); +struct libusb20_be_ctx *libusb20_be_ctx_alloc(void); +void libusb20_be_ctx_free(struct libusb20_be_ctx *pctx); struct libusb20_device *libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev); void libusb20_be_dequeue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev); void libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev); diff --git a/lib/libusb/libusb20_be_device_foreach.3 b/lib/libusb/libusb20_be_device_foreach.3 index bc741813ddb0..672e2a488d31 100644 --- a/lib/libusb/libusb20_be_device_foreach.3 +++ b/lib/libusb/libusb20_be_device_foreach.3 @@ -39,7 +39,7 @@ Otherwise this is a pointer to the next device. .Sh EXAMPLES .Bd -literal #include - struct libusb20_backend *be = libusb20_be_alloc_default(); + struct libusb20_backend *be = libusb20_be_alloc_default(NULL); struct libusb20_device *device = NULL; while ( (device = libusb20_be_device_foreach(be, device)) != NULL ) { if (libusb20_dev_open(device, 0) == LIBUSB20_SUCCESS) { diff --git a/lib/libusb/libusb20_dev_open.3 b/lib/libusb/libusb20_dev_open.3 index fa5d1746ff23..2b494771e547 100644 --- a/lib/libusb/libusb20_dev_open.3 +++ b/lib/libusb/libusb20_dev_open.3 @@ -53,7 +53,7 @@ and .Sh EXAMPLES .Bd -literal #include - struct libusb20_backend *be = libusb20_be_alloc_default(); + struct libusb20_backend *be = libusb20_be_alloc_default(NULL); struct libusb20_device *device = NULL; while ( (device = libusb20_be_device_foreach(be, device)) != NULL ) { if (libusb20_dev_open(device, 0) == LIBUSB20_SUCCESS) { diff --git a/lib/libusb/libusb20_int.h b/lib/libusb/libusb20_int.h index b9d614ffd8ff..238cd8d71df4 100644 --- a/lib/libusb/libusb20_int.h +++ b/lib/libusb/libusb20_int.h @@ -43,6 +43,21 @@ union libusb20_session_data { uint32_t plugtime; }; +/* + * The backend context holds the file descriptors needed to reach the + * USB devices. It is immutable after allocation, so it can be shared + * between threads, backends and devices without any locking. Sharing + * it between backend allocations keeps enumeration working after the + * process has entered capability mode, see capsicum(4). + * + * The context must stay alive for as long as any backend or device + * allocated from it is in use. + */ +struct libusb20_be_ctx { + int ctrl_fd; /* /dev/usbctl */ + int usb_dfd; /* /dev/usb directory */ +}; + /* USB backend specific */ typedef const char *(libusb20_get_backend_name_t)(void); typedef int (libusb20_root_get_dev_quirk_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); @@ -144,6 +159,10 @@ struct libusb20_device_methods { struct libusb20_backend { TAILQ_HEAD(, libusb20_device) usb_devs; const struct libusb20_backend_methods *methods; + + /* borrowed backend context, owned when "be_ctx_owner" is set */ + struct libusb20_be_ctx *be_ctx; + uint8_t be_ctx_owner; }; struct libusb20_transfer { @@ -195,6 +214,9 @@ struct libusb20_device { /* backend methods */ const struct libusb20_backend_methods *beMethods; + /* borrowed backend context */ + struct libusb20_be_ctx *be_ctx; + /* list of USB transfers */ struct libusb20_transfer *pTransfer; diff --git a/lib/libusb/libusb20_ugen20.c b/lib/libusb/libusb20_ugen20.c index 43d870fbe112..7d5aa439197a 100644 --- a/lib/libusb/libusb20_ugen20.c +++ b/lib/libusb/libusb20_ugen20.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #endif @@ -69,6 +70,67 @@ const struct libusb20_backend_methods libusb20_ugen20_backend = { LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20) }; +struct libusb20_be_ctx * +libusb20_be_ctx_alloc(void) +{ + cap_rights_t rights; + struct libusb20_be_ctx *pctx; + int fd; + + pctx = malloc(sizeof(*pctx)); + if (pctx == NULL) + return (NULL); + + /* + * The context is immutable once this function returns, which + * makes it safe to share between threads without any locking. + * Open failures are tolerated, so that allocation also + * succeeds on systems without USB support. + */ + fd = open("/dev/" USB_DEVICE_NAME, O_RDONLY | O_CLOEXEC); + if (fd > -1) { + cap_rights_init(&rights, CAP_READ, CAP_EVENT, CAP_IOCTL); + if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) { + close(fd); + fd = -1; + } + } + pctx->ctrl_fd = fd; + + fd = open("/dev/" USB_DEVICE_DIR, O_DIRECTORY | O_PATH | O_CLOEXEC); + if (fd > -1) { + /* + * Device descriptors derived through openat(2) + * inherit these rights, so they must cover all + * operations performed on open devices, including by + * applications using libusb20_dev_get_fd(). + */ + cap_rights_init(&rights, CAP_LOOKUP, CAP_PREAD, CAP_PWRITE, + CAP_EVENT, CAP_IOCTL); + if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) { + close(fd); + fd = -1; + } + } + pctx->usb_dfd = fd; + + return (pctx); +} + +void +libusb20_be_ctx_free(struct libusb20_be_ctx *pctx) +{ + if (pctx == NULL) { + /* be NULL safe */ + return; + } + if (pctx->ctrl_fd > -1) + close(pctx->ctrl_fd); + if (pctx->usb_dfd > -1) + close(pctx->usb_dfd); + free(pctx); +} + /* USB device specific */ static libusb20_get_config_desc_full_t ugen20_get_config_desc_full; static libusb20_get_config_index_t ugen20_get_config_index; @@ -173,6 +235,25 @@ ugen20_path_convert_one(const char **pp) return (temp); } +static int +ugen20_open_dev(struct libusb20_be_ctx *pctx, int bus_num, int dev_addr, + int flag) +{ + char path[48]; + int usb_dfd; + + usb_dfd = pctx->usb_dfd; + if (usb_dfd > -1) { + snprintf(path, sizeof(path), "%u.%u.0", bus_num, dev_addr); + return (openat(usb_dfd, path, flag)); + } + + /* fall back when the /dev/usb directory is not available */ + snprintf(path, sizeof(path), "/dev/" USB_GENERIC_NAME "%u.%u", + bus_num, dev_addr); + return (open(path, flag)); +} + static int ugen20_enumerate(struct libusb20_device *pdev, const char *id) { @@ -181,17 +262,14 @@ ugen20_enumerate(struct libusb20_device *pdev, const char *id) struct usb_device_info devinfo; struct usb_device_port_path udpp; uint32_t plugtime; - char buf[64]; int f; int error; pdev->bus_number = ugen20_path_convert_one(&tmp); pdev->device_address = ugen20_path_convert_one(&tmp); - snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", - pdev->bus_number, pdev->device_address); - - f = open(buf, O_RDWR); + f = ugen20_open_dev(pdev->be_ctx, pdev->bus_number, + pdev->device_address, O_RDWR); if (f < 0) { return (LIBUSB20_ERROR_OTHER); } @@ -332,7 +410,7 @@ ugen20_init_backend(struct libusb20_backend *pbe) memset(&state, 0, sizeof(state)); - state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY); + state.f = pbe->be_ctx->ctrl_fd; if (state.f < 0) return (LIBUSB20_ERROR_OTHER); @@ -348,6 +426,12 @@ ugen20_init_backend(struct libusb20_backend *pbe) if (pdev == NULL) { continue; } + /* + * The device borrows the backend context, which must + * stay alive for as long as the device is in use. + */ + pdev->be_ctx = pbe->be_ctx; + if (ugen20_enumerate(pdev, state.src + 4)) { libusb20_dev_free(pdev); continue; @@ -355,7 +439,6 @@ ugen20_init_backend(struct libusb20_backend *pbe) /* put the device on the backend list */ libusb20_be_enqueue_device(pbe, pdev); } - close(state.f); return (0); /* success */ } @@ -422,24 +505,22 @@ static int ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer) { uint32_t plugtime; - char buf[64]; int f; int g; int error; - snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", - pdev->bus_number, pdev->device_address); - /* * We need two file handles, one for the control endpoint and one * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised * kernel locking. */ - g = open(buf, O_RDWR); + g = ugen20_open_dev(pdev->be_ctx, pdev->bus_number, + pdev->device_address, O_RDWR); if (g < 0) { return (LIBUSB20_ERROR_NO_DEVICE); } - f = open(buf, O_RDWR); + f = ugen20_open_dev(pdev->be_ctx, pdev->bus_number, + pdev->device_address, O_RDWR); if (f < 0) { close(g); return (LIBUSB20_ERROR_NO_DEVICE); @@ -991,12 +1072,12 @@ ugen20_tr_cancel_async(struct libusb20_transfer *xfer) } static int -ugen20_be_ioctl(uint32_t cmd, void *data) +ugen20_be_ioctl(struct libusb20_be_ctx *pctx, uint32_t cmd, void *data) { int f; int error; - f = open("/dev/" USB_DEVICE_NAME, O_RDONLY); + f = pctx->ctrl_fd; if (f < 0) return (LIBUSB20_ERROR_OTHER); error = ioctl(f, cmd, data); @@ -1007,7 +1088,6 @@ ugen20_be_ioctl(uint32_t cmd, void *data) error = LIBUSB20_ERROR_OTHER; } } - close(f); return (error); } @@ -1050,7 +1130,7 @@ ugen20_root_get_dev_quirk(struct libusb20_backend *pbe, q.index = quirk_index; - error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q); + error = ugen20_be_ioctl(pbe->be_ctx, IOUSB(USB_DEV_QUIRK_GET), &q); if (error) { if (errno == EINVAL) { @@ -1077,7 +1157,7 @@ ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index, q.index = quirk_index; - error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q); + error = ugen20_be_ioctl(pbe->be_ctx, IOUSB(USB_QUIRK_NAME_GET), &q); if (error) { if (errno == EINVAL) { @@ -1104,7 +1184,7 @@ ugen20_root_add_dev_quirk(struct libusb20_backend *pbe, q.bcdDeviceHigh = pq->bcdDeviceHigh; strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); - error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q); + error = ugen20_be_ioctl(pbe->be_ctx, IOUSB(USB_DEV_QUIRK_ADD), &q); if (error) { if (errno == ENOMEM) { return (LIBUSB20_ERROR_NO_MEM); @@ -1128,7 +1208,7 @@ ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe, q.bcdDeviceHigh = pq->bcdDeviceHigh; strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); - error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q); + error = ugen20_be_ioctl(pbe->be_ctx, IOUSB(USB_DEV_QUIRK_REMOVE), &q); if (error) { if (errno == EINVAL) { return (LIBUSB20_ERROR_NOT_FOUND); @@ -1140,11 +1220,11 @@ ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe, static int ugen20_root_set_template(struct libusb20_backend *pbe, int temp) { - return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp)); + return (ugen20_be_ioctl(pbe->be_ctx, IOUSB(USB_SET_TEMPLATE), &temp)); } static int ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp) { - return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp)); + return (ugen20_be_ioctl(pbe->be_ctx, IOUSB(USB_GET_TEMPLATE), ptemp)); } diff --git a/share/examples/libusb20/bulk.c b/share/examples/libusb20/bulk.c index 2f7588bd592f..f61175c1545b 100644 --- a/share/examples/libusb20/bulk.c +++ b/share/examples/libusb20/bulk.c @@ -32,6 +32,7 @@ */ +#include #include #include #include @@ -219,12 +220,18 @@ main(int argc, char **argv) struct libusb20_backend *be; struct libusb20_device *dev; - if ((be = libusb20_be_alloc_default()) == NULL) + if ((be = libusb20_be_alloc_default(NULL)) == NULL) { perror("libusb20_be_alloc()"); return 1; } + if (caph_enter() < 0) + { + perror("caph_enter()"); + return 1; + } + dev = NULL; while ((dev = libusb20_be_device_foreach(be, dev)) != NULL) { diff --git a/share/examples/libusb20/control.c b/share/examples/libusb20/control.c index e2ae8f74cc32..fa044925325b 100644 --- a/share/examples/libusb20/control.c +++ b/share/examples/libusb20/control.c @@ -30,6 +30,7 @@ */ +#include #include #include #include @@ -391,12 +392,18 @@ main(int argc, char **argv) struct libusb20_backend *be; struct libusb20_device *dev; - if ((be = libusb20_be_alloc_default()) == NULL) + if ((be = libusb20_be_alloc_default(NULL)) == NULL) { perror("libusb20_be_alloc()"); return 1; } + if (caph_enter() < 0) + { + perror("caph_enter()"); + return 1; + } + dev = NULL; while ((dev = libusb20_be_device_foreach(be, dev)) != NULL) { diff --git a/tools/tools/usbtest/usb_msc_test.c b/tools/tools/usbtest/usb_msc_test.c index 1b9c3192a472..733fc6ce21b0 100644 --- a/tools/tools/usbtest/usb_msc_test.c +++ b/tools/tools/usbtest/usb_msc_test.c @@ -870,7 +870,7 @@ show_host_device_selection(uint8_t level, struct uaddr *puaddr) const char *ptr; top: - pbe = libusb20_be_alloc_default(); + pbe = libusb20_be_alloc_default(usb_be_ctx); pdev = NULL; index = 0; @@ -916,7 +916,7 @@ top: struct libusb20_device * find_usb_device(struct uaddr uaddr) { - struct libusb20_backend *pbe = libusb20_be_alloc_default(); + struct libusb20_backend *pbe = libusb20_be_alloc_default(usb_be_ctx); struct libusb20_device *pdev = NULL; struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; diff --git a/tools/tools/usbtest/usbtest.c b/tools/tools/usbtest/usbtest.c index adb46ef44b34..e04573da037d 100644 --- a/tools/tools/usbtest/usbtest.c +++ b/tools/tools/usbtest/usbtest.c @@ -31,9 +31,13 @@ #include #include +#include + #include #include +#include + #include #include "usbtest.h" @@ -45,6 +49,8 @@ static uint8_t usb_ts_select[USB_TS_MAX_LEVELS]; +struct libusb20_be_ctx *usb_be_ctx; + const char *indent[USB_TS_MAX_LEVELS] = { " ", " ", @@ -805,7 +811,20 @@ show_mode_select(uint8_t level) int main(int argc, char **argv) { + /* + * The backend context acquires the descriptors needed to + * reach the USB devices before entering capability mode. + */ + usb_be_ctx = libusb20_be_ctx_alloc(); + if (usb_be_ctx == NULL) + err(1, "could not allocate USB backend context"); + + if (caph_enter() < 0) + err(1, "caph_enter() failed"); + show_mode_select(1); + libusb20_be_ctx_free(usb_be_ctx); + return (0); } diff --git a/tools/tools/usbtest/usbtest.h b/tools/tools/usbtest/usbtest.h index efe484bc7d37..94157047473d 100644 --- a/tools/tools/usbtest/usbtest.h +++ b/tools/tools/usbtest/usbtest.h @@ -65,4 +65,7 @@ extern int get_integer(void); extern uint8_t usb_ts_show_menu(uint8_t, const char *, const char *,...); extern int32_t usb_ts_rand_noise(void); +struct libusb20_be_ctx; +extern struct libusb20_be_ctx *usb_be_ctx; + #endif /* _USBTEST_H_ */ diff --git a/usr.sbin/usbconfig/dump.c b/usr.sbin/usbconfig/dump.c index 10ff2125853e..b474ece22017 100644 --- a/usr.sbin/usbconfig/dump.c +++ b/usr.sbin/usbconfig/dump.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include @@ -52,10 +51,6 @@ #include "dump.h" #include "pathnames.h" -#ifndef IOUSB -#define IOUSB(a) a -#endif - #define DUMP0(n,type,field,...) dump_field(pdev, " ", #field, n->field); #define DUMP0L(n,type,field,...) dump_fieldl(pdev, " ", #field, n->field); #define DUMP1(n,type,field,...) dump_field(pdev, " ", #field, n->field); @@ -77,6 +72,8 @@ struct usb_vendor_info { STAILQ_HEAD(usb_vendors, usb_vendor_info); +static struct usb_vendors *usb_vendors = NULL; + const char * dump_mode(uint8_t value) { @@ -352,18 +349,24 @@ dump_iface(struct libusb20_device *pdev, } } -static struct usb_vendors * -load_vendors(void) +/* + * Read the USB vendor database. This has to happen before entering + * capability mode, because the database is looked up by pathname. + */ +void +dump_vendors_init(void) { const char *dbf; FILE *db = NULL; struct usb_vendor_info *cv; struct usb_product_info *cd; - struct usb_vendors *usb_vendors; char buf[1024], str[1024]; char *ch; int id; + if (usb_vendors != NULL) + return; + usb_vendors = malloc(sizeof(*usb_vendors)); if (usb_vendors == NULL) err(1, "out of memory"); @@ -375,7 +378,7 @@ load_vendors(void) if ((db = fopen(dbf, "r")) == NULL) { dbf = _PATH_USBVDB; if ((db = fopen(dbf, "r")) == NULL) - return (usb_vendors); + return; } } cv = NULL; @@ -433,7 +436,6 @@ load_vendors(void) fclose(db); /* cleanup */ - return (usb_vendors); } enum _device_descr_list_type { @@ -446,7 +448,6 @@ static char * _device_desc(struct libusb20_device *pdev, enum _device_descr_list_type list_type) { - static struct usb_vendors *usb_vendors = NULL; char *desc = NULL; const char *vendor = NULL, *product = NULL; uint16_t vid; @@ -465,9 +466,6 @@ _device_desc(struct libusb20_device *pdev, vid = libusb20_dev_get_device_desc(pdev)->idVendor; pid = libusb20_dev_get_device_desc(pdev)->idProduct; - if (usb_vendors == NULL) - usb_vendors = load_vendors(); - STAILQ_FOREACH(vi, usb_vendors, link) { if (vi->id == vid) { vendor = vi->desc; @@ -483,34 +481,18 @@ _device_desc(struct libusb20_device *pdev, } *** 90 LINES SKIPPED ***