Page MenuHomeFreeBSD

libusb: capsicumize libusb
AcceptedPublic

Authored by aokblast on Aug 11 2025, 3:54 PM.
Tags
None
Referenced Files
F166432662: D51865.diff
Thu, Aug 13, 1:29 PM
F166343805: D51865.id160587.diff
Thu, Aug 13, 12:51 AM
F166339407: D51865.id160193.diff
Thu, Aug 13, 12:20 AM
F166337004: D51865.id.diff
Thu, Aug 13, 12:02 AM
Unknown Object (File)
Wed, Aug 12, 2:16 PM
Unknown Object (File)
Wed, Aug 12, 1:33 PM
Unknown Object (File)
Wed, Aug 12, 1:17 AM
Unknown Object (File)
Wed, Aug 12, 12:14 AM

Details

Reviewers
jfree
pjd
markj
oshogbo
lwhsu
adrian
Group Reviewers
capsicum
Summary

FreeBSD's libusb has three components: libusb01, libusb10, and libusb20.

libusb20 handles communication with character devices. We now accept
file descriptors (FDs) for /dev/usb (usbd_fd) and /dev/usbctl (cfd)
directly, allowing users to open these and apply capabilities
themselves.

libusb10 is updated to support capabilities via a context option. Since
libusb allows general read/write access, we preserve all possible
capabilities when passing FDs to libusb20. It's the responsibility of
the libusb user to call cap_enter() at an appropriate time.

libusb01 is currently unused, so Capsicum support is not implemented for
it.

All base system tools using libusb20 have been updated to support
Capsicum.

Sponsored by: The FreeBSD Foundation

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 66193
Build 63076: arc lint + arc unit

Event Timeline

tools/tools/usbtest/usb_msc_test.c
970

Is this just general unused variable cleanup? I tend to do these small unrelated cleanups in separate patches. This way, the small cleanup gets its own commit and stays if your bigger patch needs to be reverted for whatever reason.

usr.sbin/usbconfig/usbconfig.c
641

In the rest of your patch, you opened usbd_fd as a normal descriptor with O_RDONLY and now you're opening it as a directory descriptor. Maybe the rest of the usbd_fd open() calls should be updated to use O_PATH | O_DIRECTORY and I think you could eliminate most of the capabilities in your rights list for usbd_fd since I only see you using it for openat().

usr.sbin/usbconfig/usbconfig.c
641

The fd returns from the openat will inherit all of the capabilities from the fd pass to openat. Therefore, for the /dev/usb/*.*.* devices, we should give the usbd_fd, READ, WRITE, EVENT, IOCTL for later usage.

Good to see more Capsicumization efforts going on. I just had a few pedantic suggestions. Otherwise, this generally looks good.

lib/libusb/libusb.3
904โ€“905

I'd add a .Xr capsicum 4 here since you're mentioning it earlier.

usr.sbin/usbconfig/usbconfig.c
48โ€“49

These extern declarations aren't really doing anything since you declare both variables right below.

The extern declarations in dump.c should be enough. You could move them into a header and include that if you anticipate other source files needing to access them as well.

641

Oops, you're definitely right. Looks good to me then :)

Fix incorrect close fd for libusb

usr.sbin/usbconfig/usbconfig.c
48โ€“49

You are right, I am too lazy:). Fix it now.

recover status when capsicum failed

hi!

So why are a whole lot of functions getting an fd added to them?

Do the FDs change during the runtime of the tool? Or are they something that could be set
once during some init phase (maybe in a new function which would call cap_enter, rather than
the owner having to call it) and then pulled out of some the libusb backend state?

kevans added inline comments.
lib/libusb/libusb.3
125

I'd maybe re-word this slightly to something like:

Prepare
.Nm
for operation within a sandbox.
Resources that will be needed for regular operation are pre-opened
with appropriate
.Xr rights 4
applied.
Note that it is users' responsiblity to call
.Fn cap_enter .

This implies a little more heavily that libusb is opening persistent resources.

lib/libusb/libusb.h
281โ€“282

I'd maybe name it a more generic LIBUSB_OPTIOIN_SANDBOX; the libusb that everyone else uses could have platform-specific bits that are required to enable sandboxing with it.

hi!

So why are a whole lot of functions getting an fd added to them?

Do the FDs change during the runtime of the tool? Or are they something that could be set
once during some init phase (maybe in a new function which would call cap_enter, rather than
the owner having to call it) and then pulled out of some the libusb backend state?

IMO we really shouldn't have libraries entering capablity mode- libusb may be just one of many, and setting a precedence like that where some will insist on entering the sandbox is bound to end up with some really annoying circumstances.

hi!

So why are a whole lot of functions getting an fd added to them?

As we need to provide the require file descriptor that need to be opened before cap_enter.

Do the FDs change during the runtime of the tool? Or are they something that could be set
once during some init phase (maybe in a new function which would call cap_enter, rather than
the owner having to call it) and then pulled out of some the libusb backend state?

Yes, cap_enter is called in bhyve. The internal fd state is opaque to bhyve.

hi!

So why are a whole lot of functions getting an fd added to them?

Do the FDs change during the runtime of the tool? Or are they something that could be set
once during some init phase (maybe in a new function which would call cap_enter, rather than
the owner having to call it) and then pulled out of some the libusb backend state?

IMO we really shouldn't have libraries entering capablity mode- libusb may be just one of many, and setting a precedence like that where some will insist on entering the sandbox is bound to end up with some really annoying circumstances.

I can somehow agree with you. But how can we integrate to bhyve without this as bhyve is capsicumized by default? We can turn off temporary but it adds complexity on bhyve.

hi!

So why are a whole lot of functions getting an fd added to them?

Do the FDs change during the runtime of the tool? Or are they something that could be set
once during some init phase (maybe in a new function which would call cap_enter, rather than
the owner having to call it) and then pulled out of some the libusb backend state?

IMO we really shouldn't have libraries entering capablity mode- libusb may be just one of many, and setting a precedence like that where some will insist on entering the sandbox is bound to end up with some really annoying circumstances.

I can somehow agree with you. But how can we integrate to bhyve without this as bhyve is capsicumized by default? We can turn off temporary but it adds complexity on bhyve.

No, your approach is fine -- part of his suggestion was that libusb provides something that does these things and calls cap_enter itself as I read it, and that's something that I think should stay outside of libusb.

lib/libusb/libusb10.c
2060

Why do we need the caller to explicitly ask the library to be capsicum-safe? Can't it just always use capsicum-safe operations? During library initialization, have libusb open whichever /dev nodes it needs, and keep the descriptors saved in some context structure. Applications which use libusb shouldn't need to change.

2100

Suppose these fields are already set, i.e., LIBUSB_OPTION_CAPSICUMIZE was configured once already. Then the second time, the descriptors will be leaked.

tools/tools/usbtest/usbtest.c
844

Same comment as below about cleaning up.

848

The return value should be checked, see my comment below.

usr.sbin/usbconfig/usbconfig.c
647

Shouldn't the library tell you which rights are needed? The application doesn't really know, it's just treating the usb descriptors as an opaque handle.

656

This is main(), so when you return, the program exits. So, I don't think it's necessary to have all of this cleanup code to close file descriptors.

660
  • This should check for errors (the main one being that the kernel can be compiled without capsicum support).
  • Better to use caph_enter().
936

much better, yay! thanks!

This revision is now accepted and ready to land.Sat, Jul 25, 2:38 AM
lib/libusb/libusb20_ugen20.c
93

I think you need capiscum_helpers.h for this.

usr.sbin/usbconfig/usbconfig.c
574

dump_device_info()->_device_desc()->load_vendors() will try to open _PATH_LUSBVDB, and that'll fail in capability mode.

I think this is straightforward to deal with, just make sure usb_vendors is initialized before you enter capability mode.

lib/libusb/libusb20.h
306โ€“307

There is some code under share/examples/libusb20 which uses these interfaces, it should be updated too.

Why is it okay to change the library ABI?

We'll need a so bump for libusb (see D55687, D55686) so we should coordinate to have all of these changes happen together.

lib/libusb/libusb20.h
306โ€“307

Yes, we definately needs to merge ABI tagging back to fix it

This revision now requires review to proceed.Thu, Aug 13, 5:36 AM
markj added a subscriber: kib.

Mostly looks good to me.

lib/libusb/Symbol.map
258 โ†—(On Diff #183962)

This is off by one. There is no FBSD_1.10 (yet), FBSD_1.9 is for symbols added in FreeBSD 16.0. See lib/libc/Versions.def.

The existing symbols should be in FBSD_1.8, not FBSD_1.9. It doesn't really matter in practice, I think, but I am not too knowledgeable on this topic. Maybe @kib can suggest whether it is worth fixing this.

share/examples/libusb20/bulk.c
229 โ†—(On Diff #183962)

I am not sure it makes sense to enter capability mode in this example, but ok.

usr.sbin/usbconfig/dump.c
367

BTW, it is weird to malloc() this structure. It's just a STAILQ head, it can be defined globally with

static struct usb_vendors usb_vendors = STAILQ_INITIALIZER(&usb_vendors);

Then, just test STAILQ_EMPTY(&usb_vendors) to see if we need to read from the file.

469

Why does this need to be called? You are already calling it in main().

This revision is now accepted and ready to land.Thu, Aug 13, 2:34 PM