From nobody Sun Apr 30 06:57:57 2023 X-Original-To: dev-commits-src-branches@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 4Q8HGt33Rmz48sPH; Sun, 30 Apr 2023 06:57:58 +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 "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Q8HGt2BXKz3N0F; Sun, 30 Apr 2023 06:57:58 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1682837878; 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=dgMx89R7KsbczKy1oXDScLUmJWDT0JoVEDBgBpQNZPg=; b=N5dfzPlaBRi4tRacuwH2ilBlLb7ZfujhoQWtUPy8eE/lf9HipwdPHyqybq7ojA9URbigEH 2qu7CjU83O9OPv1aEDU7HXMwwLI7I6eZLcXCoChNvhq8NcGQwFqgUBVcFZ+nMov38w5KAD rh4X8M0Ycpc+LKdAh01kEI4w8ib3niVkLJzWhVqVAO1+yHu8MFn0XFfot7iFGNsCqVT+GR 4s0u65m6ItvDGh077Md0/2vfR48U04sUw42zSceRkthyjZr27jeI3h5QaoY6hofPwaTHSi cT5hodtvE6cUqPBJSUVvggxDPo3fSRAqLjqcU8AfN4vbMnRPIVmdk6uOxxKI/A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1682837878; 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=dgMx89R7KsbczKy1oXDScLUmJWDT0JoVEDBgBpQNZPg=; b=LN1gYJKm1187TyxJ5fiVXxi4coEdyqu47EYEw+IP0RryEVIch3lsvzGUWHmA1bArjrlfeg Xxh1ywRyWmqZ83e/1vFZkw7kg+ibfpj1t3uoHTDYzvOHwnkn0UbBTwE5bvjoYGVAEvUV6t 3VwQ0mjYLVSrSTffdcb+NuDwntPnvZzu1sJ1D5aNyzhoofD5BZYpnRax9P/yi/4XcA3LZM HmeQi0v6IxBmZm0fw7mhEPgX7q/J09Oj1V+c370js+JSwE3ExIUmgJg2lg3vyE3F6cJjjM u1/5TRS2/vafJ7fyG01aA4SEa3m7nvhh+hEjVZSgIunHYsKu97T9t875VygBXw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1682837878; a=rsa-sha256; cv=none; b=iFjSvTR3c6tWNyH7eI1iOCQMI2GzHP3ZisRhb1u50MZZwx7IKlKF36ajbAHmDCP9XWecup 7m643p2FQsebZqC+/kN0sh/A6sl/Xt/4Ihe4UKqm7aXLdRwBusoDYyZp8yGwc2+tFgX2RP +GHvY60I/PgsVk++o8E14ZOrn2rmzNG+dw1gsJ/2VVYzYSFNSM0LgOxiCVa+12nNKBMFSg NGIyO64oXfESuGAmUnWPg7xOJlh39HYbuhpZwspk21TGhD6rg1rNWW3VQwxClCHw7egBij AWhmPAu+vbvKD+WStavHlDXlsJ8UYYMNESKPmDDDqRiT4q/pPobk/4c2torWwA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (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 did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Q8HGs5g2Hz1Crg; Sun, 30 Apr 2023 06:57:57 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 33U6vvL2071619; Sun, 30 Apr 2023 06:57:57 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 33U6vval071618; Sun, 30 Apr 2023 06:57:57 GMT (envelope-from git) Date: Sun, 30 Apr 2023 06:57:57 GMT Message-Id: <202304300657.33U6vval071618@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Hans Petter Selasky Subject: git: 53e253c7e57f - stable/13 - usb(4): Separate the fast path and the slow path to avoid races and use-after-free for the USB FS interface. List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: hselasky X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: 53e253c7e57f4a0a0632fca933277e509e04543c Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by hselasky: URL: https://cgit.FreeBSD.org/src/commit/?id=53e253c7e57f4a0a0632fca933277e509e04543c commit 53e253c7e57f4a0a0632fca933277e509e04543c Author: Hans Petter Selasky AuthorDate: 2023-03-31 17:14:18 +0000 Commit: Hans Petter Selasky CommitDate: 2023-04-30 06:56:17 +0000 usb(4): Separate the fast path and the slow path to avoid races and use-after-free for the USB FS interface. Bad behaving user-space USB applicatoins may crash the kernel by issuing USB FS related ioctl(2)'s out of their expected order. By default the USB FS ioctl(2) interface is only available to the administrator, root, and driver applications like webcamd(8) needs to be hijacked in order for this to happen. The issue is the fast-path code does not always see updates made by the slow-path code, and may then work on freed memory. This is easily fixed by using an EPOCH(9) type of synchronization mechanism. A SX(9) lock will be used as a substitute for EPOCH(9), due to the need for sleepability. In addition most calls going into the fast-path originate from a single user-space process and the need for multi-thread performance is not present. Differential Revision: https://reviews.freebsd.org/D39373 Reviewed by: markj@ Reported by: C Turt admbugs: 994 Sponsored by: NVIDIA Networking (cherry picked from commit 9b077d72bcc313baea2b9283afc7f568739eaadc) --- sys/dev/usb/usb_dev.c | 4 +- sys/dev/usb/usb_dev.h | 6 ++- sys/dev/usb/usb_device.c | 6 +-- sys/dev/usb/usb_generic.c | 105 ++++++++++++++++++++++++++++++---------------- 4 files changed, 80 insertions(+), 41 deletions(-) diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c index 84446edc3ecd..5f06b1b9690e 100644 --- a/sys/dev/usb/usb_dev.c +++ b/sys/dev/usb/usb_dev.c @@ -2,7 +2,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2006-2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2006-2023 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -386,6 +386,7 @@ usb_fifo_alloc(struct mtx *mtx) f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); cv_init(&f->cv_io, "FIFO-IO"); cv_init(&f->cv_drain, "FIFO-DRAIN"); + sx_init(&f->fs_fastpath_lock, "FIFO-FP"); f->priv_mtx = mtx; f->refcount = 1; knlist_init_mtx(&f->selinfo.si_note, mtx); @@ -626,6 +627,7 @@ usb_fifo_free(struct usb_fifo *f) cv_destroy(&f->cv_io); cv_destroy(&f->cv_drain); + sx_destroy(&f->fs_fastpath_lock); knlist_clear(&f->selinfo.si_note, 0); seldrain(&f->selinfo); diff --git a/sys/dev/usb/usb_dev.h b/sys/dev/usb/usb_dev.h index 1bdfa46d064f..8df3af9990cf 100644 --- a/sys/dev/usb/usb_dev.h +++ b/sys/dev/usb/usb_dev.h @@ -2,7 +2,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008-2023 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -110,13 +110,15 @@ struct usb_fifo { struct selinfo selinfo; struct cv cv_io; struct cv cv_drain; + struct sx fs_fastpath_lock; struct usb_fifo_methods *methods; struct usb_symlink *symlink[2];/* our symlinks */ struct proc *async_p; /* process that wants SIGIO */ struct usb_fs_endpoint *fs_ep_ptr; struct usb_device *udev; +#define USB_FS_XFER_MAX 126 struct usb_xfer *xfer[2]; - struct usb_xfer **fs_xfer; + struct usb_xfer *fs_xfer[USB_FS_XFER_MAX]; struct mtx *priv_mtx; /* client data */ /* set if FIFO is opened by a FILE: */ struct usb_cdev_privdata *curr_cpd; diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c index 392d969587c0..820cb21c704d 100644 --- a/sys/dev/usb/usb_device.c +++ b/sys/dev/usb/usb_device.c @@ -2,7 +2,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2008-2020 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008-2023 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -2829,7 +2829,7 @@ usb_fifo_free_wrap(struct usb_device *udev, continue; } if ((f->dev_ep_index == 0) && - (f->fs_xfer == NULL)) { + (f->fs_ep_max == 0)) { /* no need to free this FIFO */ continue; } @@ -2837,7 +2837,7 @@ usb_fifo_free_wrap(struct usb_device *udev, if ((f->methods == &usb_ugen_methods) && (f->dev_ep_index == 0) && (!(flag & USB_UNCFG_FLAG_FREE_EP0)) && - (f->fs_xfer == NULL)) { + (f->fs_ep_max == 0)) { /* no need to free this FIFO */ continue; } diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c index ad2f23eb9bfc..640f9fdb7ad6 100644 --- a/sys/dev/usb/usb_generic.c +++ b/sys/dev/usb/usb_generic.c @@ -2,7 +2,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2008-2022 Hans Petter Selasky + * Copyright (c) 2008-2023 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -968,10 +968,10 @@ ugen_fs_init(struct usb_fifo *f, int error; /* verify input parameters */ - if (fs_ep_ptr == NULL || ep_index_max > 127) + if (fs_ep_ptr == NULL || ep_index_max > USB_FS_XFER_MAX) return (EINVAL); - if (f->fs_xfer != NULL) + if (f->fs_ep_max != 0) return (EBUSY); if (f->dev_ep_index != 0 || ep_index_max == 0) @@ -982,11 +982,11 @@ ugen_fs_init(struct usb_fifo *f, error = usb_fifo_alloc_buffer(f, 1, ep_index_max); if (error == 0) { - f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * - ep_index_max, M_USB, M_WAITOK | M_ZERO); + mtx_lock(f->priv_mtx); f->fs_ep_max = ep_index_max; f->fs_ep_ptr = fs_ep_ptr; f->fs_ep_sz = fs_ep_sz; + mtx_unlock(f->priv_mtx); } return (error); } @@ -994,15 +994,26 @@ ugen_fs_init(struct usb_fifo *f, int ugen_fs_uninit(struct usb_fifo *f) { - if (f->fs_xfer == NULL) { + if (f->fs_ep_max == 0) return (EINVAL); - } - usbd_transfer_unsetup(f->fs_xfer, f->fs_ep_max); - free(f->fs_xfer, M_USB); - f->fs_xfer = NULL; + + /* + * Prevent calls into the fast-path code, by setting fs_ep_max + * to zero: + */ + sx_xlock(&f->fs_fastpath_lock); + mtx_lock(f->priv_mtx); f->fs_ep_max = 0; + mtx_unlock(f->priv_mtx); + sx_xunlock(&f->fs_fastpath_lock); + + usbd_transfer_unsetup(f->fs_xfer, USB_FS_XFER_MAX); + + mtx_lock(f->priv_mtx); f->fs_ep_ptr = NULL; f->flag_iscomplete = 0; + mtx_unlock(f->priv_mtx); + usb_fifo_free_buffer(f); return (0); } @@ -1109,10 +1120,26 @@ usb_fs_open(struct usb_fifo *f, struct usb_fs_open *popen, static int usb_fs_close(struct usb_fifo *f, struct usb_fs_close *pclose) { + struct usb_xfer *xfer; + if (pclose->ep_index >= f->fs_ep_max) return (EINVAL); - usbd_transfer_unsetup(f->fs_xfer + pclose->ep_index, 1); + /* + * Prevent calls into the fast-path code, by setting the + * fs_xfer[] in question to NULL: + */ + sx_xlock(&f->fs_fastpath_lock); + mtx_lock(f->priv_mtx); + xfer = f->fs_xfer[pclose->ep_index]; + f->fs_xfer[pclose->ep_index] = NULL; + mtx_unlock(f->priv_mtx); + sx_xunlock(&f->fs_fastpath_lock); + + if (xfer == NULL) + return (EINVAL); + + usbd_transfer_unsetup(&xfer, 1); return (0); } @@ -1249,14 +1276,16 @@ ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index) int error; uint8_t isread; + mtx_lock(f->priv_mtx); if (ep_index >= f->fs_ep_max) { + mtx_unlock(f->priv_mtx); return (EINVAL); } xfer = f->fs_xfer[ep_index]; if (xfer == NULL) { + mtx_unlock(f->priv_mtx); return (EINVAL); } - mtx_lock(f->priv_mtx); if (usbd_transfer_pending(xfer)) { mtx_unlock(f->priv_mtx); return (EBUSY); /* should not happen */ @@ -1529,14 +1558,16 @@ ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index) int error; uint8_t isread; - if (ep_index >= f->fs_ep_max) + mtx_lock(f->priv_mtx); + if (ep_index >= f->fs_ep_max) { + mtx_unlock(f->priv_mtx); return (EINVAL); - + } xfer = f->fs_xfer[ep_index]; - if (xfer == NULL) + if (xfer == NULL) { + mtx_unlock(f->priv_mtx); return (EINVAL); - - mtx_lock(f->priv_mtx); + } if (!xfer->flags_int.transferring && !xfer->flags_int.started) { mtx_unlock(f->priv_mtx); @@ -1675,10 +1706,6 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) struct usb_fs_complete *pcomp; struct usb_fs_start *pstart; struct usb_fs_stop *pstop; - struct usb_fs_open *popen; - struct usb_fs_open_stream *popen_stream; - struct usb_fs_close *pclose; - struct usb_fs_clear_stall_sync *pstall; void *addr; } u; struct usb_xfer *xfer; @@ -1691,6 +1718,7 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) switch (cmd) { case USB_FS_COMPLETE: + sx_slock(&f->fs_fastpath_lock); mtx_lock(f->priv_mtx); error = ugen_fs_get_complete(f, &ep_index); mtx_unlock(f->priv_mtx); @@ -1701,9 +1729,11 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) u.pcomp->ep_index = ep_index; error = ugen_fs_copy_out(f, u.pcomp->ep_index); } + sx_sunlock(&f->fs_fastpath_lock); break; case USB_FS_START: + sx_slock(&f->fs_fastpath_lock); error = ugen_fs_copy_in(f, u.pstart->ep_index); if (error == 0) { mtx_lock(f->priv_mtx); @@ -1711,6 +1741,7 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) usbd_transfer_start(xfer); mtx_unlock(f->priv_mtx); } + sx_sunlock(&f->fs_fastpath_lock); break; case USB_FS_STOP: @@ -1739,20 +1770,6 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) mtx_unlock(f->priv_mtx); break; - case USB_FS_OPEN: - case USB_FS_OPEN_STREAM: - error = usb_fs_open(f, u.popen, fflags, - (cmd == USB_FS_OPEN_STREAM) ? u.popen_stream->stream_id : 0); - break; - - case USB_FS_CLOSE: - error = usb_fs_close(f, u.pclose); - break; - - case USB_FS_CLEAR_STALL_SYNC: - error = usb_fs_clear_stall_sync(f, u.pstall); - break; - default: error = ENOIOCTL; break; @@ -2212,6 +2229,10 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) struct usb_fs_init32 *pinit32; #endif struct usb_fs_uninit *puninit; + struct usb_fs_open *popen; + struct usb_fs_open_stream *popen_stream; + struct usb_fs_close *pclose; + struct usb_fs_clear_stall_sync *pstall; struct usb_device_port_path *dpp; uint32_t *ptime; void *addr; @@ -2440,6 +2461,20 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) error = ugen_fs_uninit(f); break; + case USB_FS_OPEN: + case USB_FS_OPEN_STREAM: + error = usb_fs_open(f, u.popen, fflags, + (cmd == USB_FS_OPEN_STREAM) ? u.popen_stream->stream_id : 0); + break; + + case USB_FS_CLOSE: + error = usb_fs_close(f, u.pclose); + break; + + case USB_FS_CLEAR_STALL_SYNC: + error = usb_fs_clear_stall_sync(f, u.pstall); + break; + default: mtx_lock(f->priv_mtx); error = ugen_iface_ioctl(f, cmd, addr, fflags);