Potential write beyond bounds caused by ThrottleInputStream::Read()/ReadSegments()
Categories
(Core :: Networking, defect, P1)
Tracking
()
People
(Reporter: mozillabugs, Assigned: valentin)
Details
(Keywords: csectype-bounds, reporter-external, sec-moderate, Whiteboard: [necko-triaged][necko-priority-queue][adv-main111+][adv-esr102.9+])
Attachments
(3 files)
871 bytes,
text/html
|
Details | |
48 bytes,
text/x-phabricator-request
|
dmeehan
:
approval-mozilla-beta+
dmeehan
:
approval-mozilla-esr102+
|
Details | Review |
266 bytes,
text/plain
|
Details |
ThrottleInputStream::Read()/ReadSegments()
(netwerk/base/ThrottleQueue.cpp
) ignore the caller's requested read size aCount
, potentially causing lower layers to read data into memory beyond the end of a caller's buffer.
ThrottleInputStream
is used only by ThrottleQueue
. I have found one user (the throttling control on the Developer Tools panel) which can be used to observe usage of the incorrect read size, but which doesn't suffer a WBB (write beyond bounds) because the read size is not used to buffer data, only to send data out a socket. I am not sure whether there are other users, though nsHttpTransaction::Init()
has the ability to use the class via its eventSink
argument.
The bug is that ThrottleInputStream::Read()/ReadSegments()
don't minimize the amount of data potentially available in the underlying stream (realCount
) against the requested read size aCount
. (While Available()
, line 110, takes aCount
, it doesn't use it):
102: NS_IMETHODIMP
103: ThrottleInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
104: uint32_t aCount, uint32_t* aResult) {
...
109: uint32_t realCount;
110: nsresult rv = mQueue->Available(aCount, &realCount);
111: if (NS_FAILED(rv)) {
112: return rv;
113: }
114:
115: if (realCount == 0) {
116: return NS_BASE_STREAM_WOULD_BLOCK;
117: }
118:
119: rv = mStream->ReadSegments(aWriter, aClosure, realCount, aResult);
120: if (NS_SUCCEEDED(rv) && *aResult > 0) {
121: mQueue->RecordRead(*aResult);
122: }
123: return rv;
124:
125: }
[This function doesn't use aRemaining
:]
267: NS_IMETHODIMP
268: ThrottleQueue::Available(uint32_t aRemaining, uint32_t* aAvailable) {
269: MOZ_ASSERT(OnSocketThread(), "not on socket thread");
270: TimeStamp now = TimeStamp::Now();
271: TimeStamp oneSecondAgo = now - TimeDuration::FromSeconds(1);
272: size_t i;
273:
274: // Remove all stale events.
275: for (i = 0; i < mReadEvents.Length(); ++i) {
276: if (mReadEvents[i].mTime >= oneSecondAgo) {
277: break;
278: }
279: }
280: mReadEvents.RemoveElementsAt(0, i);
281:
282: uint32_t totalBytes = 0;
283: for (i = 0; i < mReadEvents.Length(); ++i) {
284: totalBytes += mReadEvents[i].mBytesRead;
285: }
286:
287: uint32_t spread = mMaxBytesPerSecond - mMeanBytesPerSecond;
288: double prob = static_cast<double>(rand()) / RAND_MAX;
289: uint32_t thisSliceBytes =
290: mMeanBytesPerSecond - spread + static_cast<uint32_t>(2 * spread * prob);
291:
292: if (totalBytes >= thisSliceBytes) {
293: *aAvailable = 0;
294: } else {
295: *aAvailable = thisSliceBytes;
296: }
297: return NS_OK;
298: }
You can verify that ThrottleInputStream::ReadSegments()
can call the lower layer with a byte count exceeding aCount
by doing the following:
- Start FF and attach a debugger to it.
- Set a BP in
ThrottleInputStream::ReadSegments()
on the call tomStream->ReadSegments()
. - Open FF's Web Developer Tools panel, then the network tab.
- Check "Disable Cache" and select "WiFi" from the throttling control.
- Put ffbug_2080.htm on a webserver. If the server is not running on 127.0.0.1, modify ffbug_2080.htm appropriately to do the GET request to the appropriate server.
- Load ffbug_2080.htm in FF.
- When the BP fires, examine
realCount
. Notice that it far exceedsaCount
(in my test,realCount
was0x1e0000
andaCount
was0x8000
. - Step into
mStream->ReadSegments()
. Control should end up innsStringInputStream::ReadSegments()
. Proceed to the line beginning
nsresult = aWriter(...);
and notice that source
(as limited by this function's aCount
and maxCount
) contains the initial HTTP request to fetch ffbug_2080.htm. The request isn't very long, so it doesn't exceed the original aCount
(0x8000).
-
Proceed, take the BP again, step into
mStream->ReadSegments()
again, and proceed as in step 8. Notice that this function'saCount
is now0
becausemaxCount
is0
. -
Proceed and take the BP again, and step into
mStream->ReadSegments()
again, and proceed as in step 8. Notice thatsource
contains the longGET
request that the script in ffbug_2080.htm generated, and that the function'saCount
andmaxCount
are both0xc877
. This is much greater than theaCount
thatThrottleInputStream::ReadSegments()
's caller requested (0x8000
). -
You can trace all the way into
nsHttpConnection::OnReadSegment()
and find that0xc877
bytes do get written to the socket.
Updated•2 years ago
|
Assignee | ||
Comment 1•2 years ago
|
||
The fact that you can only hit this with Developer Tools throttling turned on makes this a bit less severe.
Thank you for the great bug report.
Reporter | ||
Comment 2•2 years ago
|
||
ThrottleQueue
can also be used by creating an instance of @mozilla.org/network/throttlequeue;1
or ModuleID::Anonymous169
, but it appears that only tests do this at present. Can old-style add-ons use these functions? Are there any old-style addons anymore?
Comment 3•2 years ago
|
||
No, XUL addons are long gone.
Updated•2 years ago
|
Updated•2 years ago
|
Assignee | ||
Updated•2 years ago
|
Assignee | ||
Comment 4•2 years ago
|
||
Comment 5•2 years ago
|
||
Comment 6•2 years ago
|
||
The patch landed in nightly and beta is affected.
:valentin, is this bug important enough to require an uplift?
- If yes, please nominate the patch for beta approval.
- If no, please set
status-firefox111
towontfix
.
For more information, please visit auto_nag documentation.
Assignee | ||
Comment 7•2 years ago
|
||
Comment on attachment 9318754 [details]
Bug 1811627 - ThrottleQueue::Available should return max aRemaining r=#necko
Beta/Release Uplift Approval Request
- User impact if declined: Potential out of bounds read when devtools console is open and network throttling is enabled.
- Is this code covered by automated tests?: Unknown
- Has the fix been verified in Nightly?: No
- Needs manual test from QE?: No
- If yes, steps to reproduce:
- List of other uplifts needed: None
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): This is low risk as the change only introduces a bounds check when calling ThrottleQueue::Available.
The risk of changing user behaviour is minimal - also this code is only called when the network throttling is activated from devtools. - String changes made/needed:
- Is Android affected?: No
ESR Uplift Approval Request
- If this is not a sec:{high,crit} bug, please state case for ESR consideration:
- User impact if declined: Potential out of bounds read when devtools console is open and network throttling is enabled.
- Fix Landed on Version: 112
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): This is low risk as the change only introduces a bounds check when calling ThrottleQueue::Available.
The risk of changing user behaviour is minimal - also this code is only called when the network throttling is activated from devtools.
Comment 8•2 years ago
|
||
Comment on attachment 9318754 [details]
Bug 1811627 - ThrottleQueue::Available should return max aRemaining r=#necko
Approved for 111.0b7
Comment 9•2 years ago
|
||
uplift |
Comment 10•2 years ago
|
||
Comment on attachment 9318754 [details]
Bug 1811627 - ThrottleQueue::Available should return max aRemaining r=#necko
Approved for 102.9esr.
Comment 11•2 years ago
|
||
uplift |
Updated•2 years ago
|
Updated•2 years ago
|
Updated•2 years ago
|
Updated•2 years ago
|
Comment 12•2 years ago
|
||
Updated•2 years ago
|
Updated•1 year ago
|
Updated•8 months ago
|
Description
•