From: Alan Stern <stern@rowland.harvard.edu>
To: Sergei Shtylyov <sergei.shtylyov@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
linux-usb@vger.kernel.org
Subject: Re: Buffer overflow in drivers/usb/host/ehci-sched.c?
Date: Sun, 3 Apr 2022 13:57:21 -0400 [thread overview]
Message-ID: <YkngAZgW0vvoQW3s@rowland.harvard.edu> (raw)
In-Reply-To: <58902d03-c789-c13a-b8af-39249bf0fd0f@gmail.com>
On Sun, Apr 03, 2022 at 08:22:44PM +0300, Sergei Shtylyov wrote:
> On 4/3/22 6:17 PM, Alan Stern wrote:
>
> >> The following function in the EHCI scheduling code causes the SVACE static analyzer to
> >> report possible buffer overflow (see the last assignment below), e.g.:
> >>
> >> Buffer 'ehci->bandwidth' of size 64 accessed at ehci-sched.c:240 can overflow, since its
> >> index 'i + j' can have value 66 that is out of range, as indicated by preceding conditional
> >> expression at ehci-sched.c:240.
> >
> > Not sure what you mean.
>
> What SVACE means, in this case. :-)
>
> > In my copy of the file, line 240 is the assignment.
>
> For SVACE as well. This is not a full report -- some details did follow but I failed
> to copy/paste them...
>
> > There is a conditional in line 239 and in line 238 (the "for" condition),
> > but I don't see how either one indicates that i+j could be as large as 66.
>
> SVACE doesn't know what qh->ps.bw_uperiod could be...
>
> >> I tried hard to analyze this code but couldn't quite figure out whether an overflow could
> >> actually happen... Maybe Alan (or Greg?) could please help me out?
> >
> > All right. Hold on to your hat...
> >
>
> :-)
>
> >> static void reserve_release_intr_bandwidth(struct ehci_hcd *ehci,
> >> struct ehci_qh *qh, int sign)
> >> {
> >> unsigned start_uf;
> >> unsigned i, j, m;
> >> int usecs = qh->ps.usecs;
> >> int c_usecs = qh->ps.c_usecs;
> >> int tt_usecs = qh->ps.tt_usecs;
> >> struct ehci_tt *tt;
> >>
> >> if (qh->ps.phase == NO_FRAME) /* Bandwidth wasn't reserved */
> >> return;
> >> start_uf = qh->ps.bw_phase << 3;
> >
> > This guarantees that start_uf is a multiple of 8.
>
> I figured. :-)
>
> >> bandwidth_dbg(ehci, sign, "intr", &qh->ps);
> >>
> >> if (sign < 0) { /* Release bandwidth */
> >> usecs = -usecs;
> >> c_usecs = -c_usecs;
> >> tt_usecs = -tt_usecs;
> >> }
> >>
> >> /* Entire transaction (high speed) or start-split (full/low speed) */
> >> for (i = start_uf + qh->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE;
> >> i += qh->ps.bw_uperiod)
> >> ehci->bandwidth[i] += usecs;
> >>
> >> /* Complete-split (full/low speed) */
> >> if (qh->ps.c_usecs) {
> >
> > The fact that c_usecs is nonzero guarantees that we are dealing with a
> > full/low-speed endpoint.
>
> Ah! That's what I missed...
>
> > High-speed transfers don't use split
> > transactions, so they don't reserve any bandwidth for complete-splits.
>
> Aha! I still lack the detailed enough knowledge of the USB 2.0 spec...
>
> >> /* NOTE: adjustments needed for FSTN */
> >> for (i = start_uf; i < EHCI_BANDWIDTH_SIZE;
> >> i += qh->ps.bw_uperiod) {
> >
> > It takes a little checking to make sure, but in fact bw_uperiod is always a
> > multiple of 8 for full/low-speed endpoints. (Such endpoints don't make use
> > of microframes; everything is in multiples of frames, i.e., multiples of 8
> > microframes.)
> >
> > Therefore i is always a multiple of 8 between 0 and 56 (inclusive), since
> > EHCI_BANDWIDTH_SIZE is 64.
>
> Aha!
>
> >> for ((j = 2, m = 1 << (j+8)); j < 8; (++j, m <<= 1)) {
> >
> > We always have 2 <= j < 8.
>
> BTW, why don't we start with 0?
That's another detail from the USB 2.0 spec. Here's how full/low-speed
split interrupt transactions work, in brief:
The host controller sends a Start Split packet in uframe n on the
HS bus.
The intervening hub carries out the interrupt transaction on the
FS/LS bus at some time in uframe n+1 or shortly thereafter. The
transaction completes at some point during uframe n+1, n+2, or n+3,
depending on how much data was transferred and how much other stuff
is happening on the FS/LS bus.
The hub doesn't make the data it received from the device available
until the uframe after the FS/LS transfer completes. So it won't
send data back to the host until uframe n+2, n+3, or n+4.
Since n >= 0, there's no point scheduling a Complete Split packet
until uframe 2 at the earliest because the data won't be ready yet.
This ignores one significant complication: If n is large enough, the hub
might want to send the interrupt data back to the host in uframe 0 or 1 of
the _next_ frame. The design of the EHCI controller makes handling these
cases unreasonably difficult -- they require special FSTN (Frame Span
Traversal Node) data structures -- and therefore ehci-hcd doesn't implement
them.
As a result, ehci-hcd is unable to schedule some loads on full- or low-speed
buses that in theory could be handled: those involving relatively high
bandwidth. This tends to show up more with full-speed isochronous rather
than interrupt transfers, however; things like video applications.
Alan Stern
> >> if (qh->ps.cs_mask & m)
> >> ehci->bandwidth[i+j] += c_usecs;
> >
> > Therefore 2 <= i+j < 56+8 = 64.
> >
> >> }
> >> }
> >> }
> >> [...]
> >>
> >> There shouldn't be a buffer overflow iff qh->ps.bw_uperiod is a
> >> multiple of 8, right?
> >
> > Correct; see above. To probe that qh->ps.bw_uperiod is always a multiple of
> > 8, search the driver for assignments to qh->ps.bw_uperiod. (The only such
> > assignments occur in ehci-q.c:qh_make() --
>
> Yes, seeing them (again)...
>
> > the assignments in ehci-sched.c
> > are all to stream->ps.bw_uperiod, and a stream is different from a qh.) The
> > first assigment is for high-speed endpoints and the second is for
> > full/low-speed endpoints. That second assignment does:
> >
> > qh->ps.bw_uperiod = qh->ps.bw_period << 3;
> >
> > which is always a multiple of 8.
>
> Yes.
>
> Thank you for the prompt reply! :-)
>
> > Alan Stern
>
> MBR, Sergey
prev parent reply other threads:[~2022-04-03 17:57 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-04-02 21:12 Buffer overflow in drivers/usb/host/ehci-sched.c? Sergei Shtylyov
2022-04-03 15:17 ` Alan Stern
2022-04-03 17:22 ` Sergei Shtylyov
2022-04-03 17:57 ` Alan Stern [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YkngAZgW0vvoQW3s@rowland.harvard.edu \
--to=stern@rowland.harvard.edu \
--cc=gregkh@linuxfoundation.org \
--cc=linux-usb@vger.kernel.org \
--cc=sergei.shtylyov@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox