Message ID | 20180724115944.31079-2-kleber.souza@canonical.com |
---|---|
State | New |
Headers | show |
Series | Fix for CVE-2017-16913 | expand |
On 24.07.2018 13:59, Kleber Sacilotto de Souza wrote: > From: Shuah Khan <shuahkh@osg.samsung.com> > > commit c6688ef9f29762e65bce325ef4acd6c675806366 upstream. > > Harden CMD_SUBMIT path to handle malicious input that could trigger > large memory allocations. Add checks to validate transfer_buffer_length > and number_of_packets to protect against bad input requesting for > unbounded memory allocations. Validate early in get_pipe() and return > failure. > > Reported-by: Secunia Research <vuln@secunia.com> > Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> > Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> > > CVE-2017-16913 > (backported from commit eebf31529012289ec20fea84e4e6fd188176be13 linux-stable) > [ klebers: adjusted the file path, the usbip is still under staging on > kernel 3.13, and some minor context adjustment. ] > Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com> Acked-by: Stefan Bader <stefan.bader@canonical.com> > --- > drivers/staging/usbip/stub_rx.c | 30 +++++++++++++++++++++++++++--- > 1 file changed, 27 insertions(+), 3 deletions(-) > > diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c > index db48a789d308..d9262ad46d42 100644 > --- a/drivers/staging/usbip/stub_rx.c > +++ b/drivers/staging/usbip/stub_rx.c > @@ -349,11 +349,13 @@ static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, > return priv; > } > > -static int get_pipe(struct stub_device *sdev, int epnum, int dir) > +static int get_pipe(struct stub_device *sdev, struct usbip_header *pdu) > { > struct usb_device *udev = sdev->udev; > struct usb_host_endpoint *ep; > struct usb_endpoint_descriptor *epd = NULL; > + int epnum = pdu->base.ep; > + int dir = pdu->base.direction; > > if (dir == USBIP_DIR_IN) > ep = udev->ep_in[epnum & 0x7f]; > @@ -366,6 +368,7 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) > } > > epd = &ep->desc; > + > if (usb_endpoint_xfer_control(epd)) { > if (dir == USBIP_DIR_OUT) > return usb_sndctrlpipe(udev, epnum); > @@ -388,6 +391,27 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) > } > > if (usb_endpoint_xfer_isoc(epd)) { > + /* validate packet size and number of packets */ > + unsigned int maxp, packets, bytes; > + > +#define USB_EP_MAXP_MULT_SHIFT 11 > +#define USB_EP_MAXP_MULT_MASK (3 << USB_EP_MAXP_MULT_SHIFT) > +#define USB_EP_MAXP_MULT(m) \ > + (((m) & USB_EP_MAXP_MULT_MASK) >> USB_EP_MAXP_MULT_SHIFT) > + > + maxp = usb_endpoint_maxp(epd); > + maxp *= (USB_EP_MAXP_MULT( > + __le16_to_cpu(epd->wMaxPacketSize)) + 1); > + bytes = pdu->u.cmd_submit.transfer_buffer_length; > + packets = DIV_ROUND_UP(bytes, maxp); > + > + if (pdu->u.cmd_submit.number_of_packets < 0 || > + pdu->u.cmd_submit.number_of_packets > packets) { > + dev_err(&sdev->udev->dev, > + "CMD_SUBMIT: isoc invalid num packets %d\n", > + pdu->u.cmd_submit.number_of_packets); > + return -1; > + } > if (dir == USBIP_DIR_OUT) > return usb_sndisocpipe(udev, epnum); > else > @@ -395,7 +419,7 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) > } > > /* NOT REACHED */ > - dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); > + dev_err(&sdev->interface->dev, "CMD_SUBMIT: invalid epnum %d\n", epnum); > return 0; > } > > @@ -460,7 +484,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, > struct stub_priv *priv; > struct usbip_device *ud = &sdev->ud; > struct usb_device *udev = sdev->udev; > - int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); > + int pipe = get_pipe(sdev, pdu); > > priv = stub_priv_alloc(sdev, pdu); > if (!priv) >
diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c index db48a789d308..d9262ad46d42 100644 --- a/drivers/staging/usbip/stub_rx.c +++ b/drivers/staging/usbip/stub_rx.c @@ -349,11 +349,13 @@ static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, return priv; } -static int get_pipe(struct stub_device *sdev, int epnum, int dir) +static int get_pipe(struct stub_device *sdev, struct usbip_header *pdu) { struct usb_device *udev = sdev->udev; struct usb_host_endpoint *ep; struct usb_endpoint_descriptor *epd = NULL; + int epnum = pdu->base.ep; + int dir = pdu->base.direction; if (dir == USBIP_DIR_IN) ep = udev->ep_in[epnum & 0x7f]; @@ -366,6 +368,7 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) } epd = &ep->desc; + if (usb_endpoint_xfer_control(epd)) { if (dir == USBIP_DIR_OUT) return usb_sndctrlpipe(udev, epnum); @@ -388,6 +391,27 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) } if (usb_endpoint_xfer_isoc(epd)) { + /* validate packet size and number of packets */ + unsigned int maxp, packets, bytes; + +#define USB_EP_MAXP_MULT_SHIFT 11 +#define USB_EP_MAXP_MULT_MASK (3 << USB_EP_MAXP_MULT_SHIFT) +#define USB_EP_MAXP_MULT(m) \ + (((m) & USB_EP_MAXP_MULT_MASK) >> USB_EP_MAXP_MULT_SHIFT) + + maxp = usb_endpoint_maxp(epd); + maxp *= (USB_EP_MAXP_MULT( + __le16_to_cpu(epd->wMaxPacketSize)) + 1); + bytes = pdu->u.cmd_submit.transfer_buffer_length; + packets = DIV_ROUND_UP(bytes, maxp); + + if (pdu->u.cmd_submit.number_of_packets < 0 || + pdu->u.cmd_submit.number_of_packets > packets) { + dev_err(&sdev->udev->dev, + "CMD_SUBMIT: isoc invalid num packets %d\n", + pdu->u.cmd_submit.number_of_packets); + return -1; + } if (dir == USBIP_DIR_OUT) return usb_sndisocpipe(udev, epnum); else @@ -395,7 +419,7 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) } /* NOT REACHED */ - dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); + dev_err(&sdev->interface->dev, "CMD_SUBMIT: invalid epnum %d\n", epnum); return 0; } @@ -460,7 +484,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, struct stub_priv *priv; struct usbip_device *ud = &sdev->ud; struct usb_device *udev = sdev->udev; - int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); + int pipe = get_pipe(sdev, pdu); priv = stub_priv_alloc(sdev, pdu); if (!priv)