git: 749318f2ae56 - main - libusb: capsicumize libusb

ShengYi Hung <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a7f2fa2.41138.6d86e1c7__21257.4556980812$1786720194$gmane$org@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 <[email protected]>
AuthorDate: 2025-08-11 15:05:24 +0000
Commit:     ShengYi Hung <[email protected]>
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 <ctype.h>
 #include <poll.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -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 <libusb20.h>
-    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 <libusb20.h>
-    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 <string.h>
 #include <unistd.h>
 #include <time.h>
+#include <sys/capsicum.h>
 #include <sys/queue.h>
 #include <sys/types.h>
 #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 <capsicum_helpers.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -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 <capsicum_helpers.h>
 #include <limits.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -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 <stdarg.h>
 #include <stdlib.h>
 
+#include <capsicum_helpers.h>
+
 #include <sys/types.h>
 #include <sys/sysctl.h>
 
+#include <libusb20.h>
+
 #include <dev/usb/usb_ioctl.h>
 
 #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 <pwd.h>
 #include <grp.h>
 #include <ctype.h>
-#include <fcntl.h>
 
 #include <libusb20.h>
 #include <libusb20_desc.h>
@@ -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 ***
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.