Use-After-Free in PBackgroundIDBDatabase though [@ mozilla::dom::indexedDB::(anonymous namespace)::DatabaseFile::WriteSucceededClearBlobImpl]
Categories
(Core :: Storage: IndexedDB, defect, P2)
Tracking
()
People
(Reporter: decoder, Assigned: hsingh)
References
Details
(5 keywords, Whiteboard: [adv-esr115.34.1+r][adv-esr140.9.1+r][adv-main149.0.2+r])
Attachments
(3 files, 4 external files, 1 obsolete file)
|
4.09 KB,
text/html
|
Details | |
|
3.41 KB,
patch
|
Details | Diff | Splinter Review | |
|
19.39 KB,
text/plain
|
Details |
Summary
Heap use-after-free in the Firefox parent process (/firefox/dom/indexedDB/ActorsParent.cpp:2532) caused by a cross-thread data race on DatabaseFile::mBlobImpl. The IPDL-generated deserializer for PBackgroundIDBDatabaseFileParent* validates only the actor type — not the manager chain — allowing a compromised content process to reference a DatabaseFile actor owned by Database A inside a FileAddInfo sent to Database B. Because each database runs on its own TaskQueue backed by a shared 20-thread pool, two connection threads concurrently call WriteSucceededClearBlobImpl() on the same unsynchronized InitializedOnce<const RefPtr<BlobImpl>>, producing a double-Release() on the StreamBlobImpl.
Affected Code
File: /firefox/dom/indexedDB/ActorsParent.cpp, lines 2489–2533
class DatabaseFile final : public PBackgroundIDBDatabaseFileParent {
// mBlobImpl's ownership lifecycle:
// - Initialized on the background thread at creation time. Then
// responsibility is handed off to the connection thread. <-- SINGULAR
// - Checked and used by the connection thread to generate a stream to write
// the blob to disk by an add/put operation.
// - Cleared on the connection thread once the file has successfully been
// written to disk.
InitializedOnce<const RefPtr<BlobImpl>> mBlobImpl;
...
void WriteSucceededClearBlobImpl() {
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(*mBlobImpl); // debug-only; no-op in release
mBlobImpl.destroy(); // line 2532 — RACE POINT
}
File: /firefox/mfbt/Maybe.h, lines 856–870 (InitializedOnce::destroy → Maybe::reset)
constexpr void reset() {
if (isSome()) { // TOCTOU: both threads read mIsSome=true
...
ref().T::~T(); // both threads call ~RefPtr<BlobImpl>()
...
mIsSome = false; // written AFTER destruction
}
}
File: /firefox/obj-x86_64-pc-linux-gnu/ipc/ipdl/PBackgroundIDBDatabaseFileParent.cpp (generated)
auto ParamTraits<PBackgroundIDBDatabaseFileParent*>::Read(IPC::MessageReader* aReader)
-> IPC::ReadResult<paramType> {
auto maybe__actor = IPC::ReadParam<mozilla::ipc::IProtocol*>(aReader);
...
if (actor && actor->GetProtocolId() != PBackgroundIDBDatabaseFileMsgStart) {
aReader->FatalError("Unexpected actor type..."); // checks TYPE only
return {};
}
return static_cast<PBackgroundIDBDatabaseFileParent*>(actor); // no Manager() check
}
File: /firefox/dom/indexedDB/ActorsParent.cpp, lines 10403–10411 (VerifyRequestParams)
for (const FileAddInfo& fileAddInfo : aParams.fileAddInfos()) {
const PBackgroundIDBDatabaseFileParent* file = fileAddInfo.file().AsParent();
switch (fileAddInfo.type()) {
case StructuredCloneFileBase::eBlob:
if (NS_AUUF_OR_WARN_IF(!file)) { // null check ONLY — no manager check
return false;
}
break;
Why vulnerable: The threading invariant ("the connection thread", singular) is enforced only by convention and the assumption that a DatabaseFile is only ever referenced by its owning database's transactions. This assumption is not validated against untrusted IPC input. Maybe::reset() has no synchronization — mIsSome is a plain bool, not atomic.
Correct Pattern
// In TransactionBase::VerifyRequestParams (ActorsParent.cpp ~10403):
for (const FileAddInfo& fileAddInfo : aParams.fileAddInfos()) {
const PBackgroundIDBDatabaseFileParent* file = fileAddInfo.file().AsParent();
switch (fileAddInfo.type()) {
case StructuredCloneFileBase::eBlob:
if (NS_AUUF_OR_WARN_IF(!file)) {
return false;
}
// Reject actors managed by a different Database.
if (NS_AUUF_OR_WARN_IF(
static_cast<const Database*>(file->Manager()) != &GetDatabase())) {
return false;
}
break;
Exploit Chain
- Compromised content process opens two IndexedDB databases, A and B (distinct
PBackgroundIDBDatabaseactors → distinctTaskQueues perConnectionPool::ScheduleTransactionat/firefox/dom/indexedDB/ActorsParent.cpp:8227). - Content sends
PBackgroundIDBDatabaseFile(IPCBlob)to Database A → parent createsDatabaseFileactor X wrapping aStreamBlobImpl(/firefox/dom/indexedDB/ActorsParent.cpp:9690). - Content sends
ObjectStoreAddPutParamsto Database A withFileAddInfo.file = X(legitimate). - Content sends
ObjectStoreAddPutParamsto Database B withFileAddInfo.file = X(malicious — cross-database reference). IPDL deserialization accepts it becauseGetProtocolId() == PBackgroundIDBDatabaseFileMsgStart. TransactionBase::VerifyRequestParams(:10408) only checks!file→ passes.ObjectStoreAddOrPutRequestOp::Init(:19043–19051) casts toDatabaseFile*and stores raw pointer — no manager check.- Database A's op is dispatched to thread T_A; Database B's op to thread T_B (different
TaskQueues on the 20-threadmIOTargetpool). - Both threads enter
DoDatabaseWork(:19309) →GetInputStream()readsmBlobImpl→CreateFileFromStream()writes blob to disk →NotifyWriteSucceeded()(:3912) →WriteSucceededClearBlobImpl()(:2532). - Both threads execute
mBlobImpl.destroy()→Maybe::reset(): both readmIsSome==true, both call~RefPtr<BlobImpl>(), both callStreamBlobImpl::Release(). - First thread drops refcount 1→0 →
delete→StreamBlobImplfreed. Second thread'sRelease()reads freed vtable/refcount/members → heap-use-after-free.
IPC Path
- IPDL protocol:
PBackgroundIDBDatabase(managesPBackgroundIDBDatabaseFile) andPBackgroundIDBTransaction→PBackgroundIDBRequest(carriesObjectStoreAddPutParamswithFileAddInfo[]) - IPDL source:
/firefox/dom/indexedDB/PBackgroundIDBDatabase.ipdl,/firefox/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh(line 164:struct FileAddInfo { PBackgroundIDBDatabaseFile file; ... }) - Parent side:
Database::AllocPBackgroundIDBDatabaseFileParent(ActorsParent.cpp:9675),ObjectStoreAddOrPutRequestOp::DoDatabaseWork(ActorsParent.cpp:19076) - Child side:
IDBObjectStore::AddOrPut(IDBObjectStore.cpp:844),IDBDatabase::GetOrCreateFileActorForBlob(IDBDatabase.cpp:729) - Message flow: child→parent
PBackgroundIDBDatabaseFileconstructor, then child→parentPBackgroundIDBRequestconstructor withObjectStoreAddPutParamscontaining cross-manager actor reference
Security Impact
- Severity: Critical (sandbox escape)
- Attacker capability: A compromised content process (post-renderer-RCE) achieves heap use-after-free in the parent process. The UAF is a WRITE (atomic
fetch_subon freed refcount) and a READ (vtable dereference, string member access), both on attacker-influenced heap objects (StreamBlobImplis 184 bytes,StringBufferis 128 bytes — both groomable sizes). The race window is wide (disk I/O inCreateFileFromStream), making it highly reliable. Chainable to parent-process RCE. - Preconditions: Content process compromise (standard first-stage). No special prefs, no user interaction beyond loading a page. IndexedDB is enabled by default for all origins.
ASAN Report
==225273==ERROR: AddressSanitizer: heap-use-after-free on address 0x76c608277c80 at pc 0x7605dc180af2 bp 0x760597d8f120 sp 0x760597d8f118
WRITE of size 4 at 0x76c608277c80 thread T102
#0 0x7605dc180af1 in std::__atomic_base<unsigned int>::fetch_sub(unsigned int, std::memory_order) /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/atomic_base.h:558:16
#1 0x7605dc180af1 in mozilla::StringBuffer::Release() /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h:182:32
#2 0x7605dc180af1 in ReleaseData(void*, mozilla::detail::StringDataFlags) /firefox/xpcom/string/nsTSubstring.cpp:49:45
#3 0x7605dc180af1 in nsTSubstring<char16_t>::Finalize() /firefox/xpcom/string/nsTSubstring.cpp:284:3
#4 0x7605e478a2bd in nsTSubstring<char16_t>::~nsTSubstring() /firefox/obj-x86_64-pc-linux-gnu/dist/include/nsTSubstring.h:327:21
#5 0x7605e478a2bd in mozilla::dom::StreamBlobImpl::~StreamBlobImpl() /firefox/dom/file/StreamBlobImpl.cpp:126:1
#6 0x7605e478a42d in mozilla::dom::StreamBlobImpl::~StreamBlobImpl() /firefox/dom/file/StreamBlobImpl.cpp:120:35
#7 0x7605e4787465 in mozilla::dom::BlobImpl::Release() /firefox/dom/file/BlobImpl.cpp:98:1
#8 0x7605e4787465 in mozilla::dom::StreamBlobImpl::Release() /firefox/dom/file/StreamBlobImpl.cpp:24:1
#9 0x7605e7a0a61f in mozilla::RefPtrTraits<mozilla::dom::BlobImpl>::Release(mozilla::dom::BlobImpl*) /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h:49:40
#10 0x7605e7a0a61f in RefPtr<mozilla::dom::BlobImpl>::ConstRemovingRefPtrTraits<mozilla::dom::BlobImpl>::Release(mozilla::dom::BlobImpl*) /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h:409:36
#11 0x7605e7a0a61f in RefPtr<mozilla::dom::BlobImpl>::~RefPtr() /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h:80:7
#12 0x7605e7a0a61f in mozilla::Maybe<RefPtr<mozilla::dom::BlobImpl>>::reset() /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/Maybe.h:864:19
#13 0x7605e7a0a61f in void mozilla::detail::InitializedOnce<RefPtr<mozilla::dom::BlobImpl> const, ...>::maybeDestroy<void, void>() /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/InitializedOnce.h:125:12
#14 0x7605e7a0a61f in void mozilla::detail::InitializedOnce<RefPtr<mozilla::dom::BlobImpl> const, ...>::destroy<void, void>() /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/InitializedOnce.h:119:5
#15 0x7605e7a0a61f in mozilla::dom::indexedDB::(anonymous namespace)::DatabaseFile::WriteSucceededClearBlobImpl() /firefox/dom/indexedDB/ActorsParent.cpp:2532:15
#16 0x7605e7a0a61f in mozilla::dom::indexedDB::(anonymous namespace)::ObjectStoreAddOrPutRequestOp::StoredFileInfo::NotifyWriteSucceeded() const /firefox/dom/indexedDB/ActorsParent.cpp:3912:11
#17 0x7605e7a0a61f in mozilla::dom::indexedDB::(anonymous namespace)::ObjectStoreAddOrPutRequestOp::DoDatabaseWork(...) /firefox/dom/indexedDB/ActorsParent.cpp:19309:26
#18 0x7605e79eb1ec in mozilla::dom::indexedDB::(anonymous namespace)::TransactionDatabaseOperationBase::RunOnConnectionThread() /firefox/dom/indexedDB/ActorsParent.cpp:17309:14
#19 0x7605e79e9330 in mozilla::dom::indexedDB::(anonymous namespace)::TransactionDatabaseOperationBase::Run() /firefox/dom/indexedDB/ActorsParent.cpp:17477:7
#20 0x7605dc418b82 in mozilla::TaskQueue::Runner::Run() /firefox/xpcom/threads/TaskQueue.cpp:311:20
#21 0x7605dc468617 in nsThreadPool::Run() /firefox/xpcom/threads/nsThreadPool.cpp:446:14
0x76c608277c80 is located 0 bytes inside of 128-byte region [0x76c608277c80,0x76c608277d00)
freed by thread T43 here:
#0 0x5a381dbf36a6 in __interceptor_free _asan_rtl_:3
#1 0x7605e478a2bd in nsTSubstring<char16_t>::~nsTSubstring() /firefox/obj-x86_64-pc-linux-gnu/dist/include/nsTSubstring.h:327:21
#2 0x7605e478a2bd in mozilla::dom::StreamBlobImpl::~StreamBlobImpl() /firefox/dom/file/StreamBlobImpl.cpp:126:1
#3 0x7605e478a42d in mozilla::dom::StreamBlobImpl::~StreamBlobImpl() /firefox/dom/file/StreamBlobImpl.cpp:120:35
#4 0x7605e4787465 in mozilla::dom::BlobImpl::Release() /firefox/dom/file/BlobImpl.cpp:98:1
#5 0x7605e4787465 in mozilla::dom::StreamBlobImpl::Release() /firefox/dom/file/StreamBlobImpl.cpp:24:1
#6 0x7605e7a0a61f in mozilla::RefPtrTraits<mozilla::dom::BlobImpl>::Release(mozilla::dom::BlobImpl*) /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h:49:40
...
#12 0x7605e7a0a61f in mozilla::dom::indexedDB::(anonymous namespace)::DatabaseFile::WriteSucceededClearBlobImpl() /firefox/dom/indexedDB/ActorsParent.cpp:2532:15
#13 0x7605e7a0a61f in mozilla::dom::indexedDB::(anonymous namespace)::ObjectStoreAddOrPutRequestOp::StoredFileInfo::NotifyWriteSucceeded() const /firefox/dom/indexedDB/ActorsParent.cpp:3912:11
#14 0x7605e7a0a61f in mozilla::dom::indexedDB::(anonymous namespace)::ObjectStoreAddOrPutRequestOp::DoDatabaseWork(...) /firefox/dom/indexedDB/ActorsParent.cpp:19309:26
#15 0x7605e79eb1ec in mozilla::dom::indexedDB::(anonymous namespace)::TransactionDatabaseOperationBase::RunOnConnectionThread() /firefox/dom/indexedDB/ActorsParent.cpp:17309:14
#16 0x7605e79e9330 in mozilla::dom::indexedDB::(anonymous namespace)::TransactionDatabaseOperationBase::Run() /firefox/dom/indexedDB/ActorsParent.cpp:17477:7
#17 0x7605dc418b82 in mozilla::TaskQueue::Runner::Run() /firefox/xpcom/threads/TaskQueue.cpp:311:20
#18 0x7605dc468617 in nsThreadPool::Run() /firefox/xpcom/threads/nsThreadPool.cpp:446:14
previously allocated by thread T9 here:
#0 0x5a381dbf3944 in __interceptor_malloc _asan_rtl_:3
#1 0x7605dc17bac4 in mozilla::StringBuffer::Alloc(...) /firefox/obj-x86_64-pc-linux-gnu/dist/include/mozilla/StringBuffer.h:64:61
...
#10 0x7605e47bba66 in IPC::ParamTraits<mozilla::dom::IPCBlob>::Read(IPC::MessageReader*) /firefox/obj-x86_64-pc-linux-gnu/ipc/ipdl/IPCBlob.cpp:480:33
#12 0x7605e7ba23ae in mozilla::dom::indexedDB::PBackgroundIDBDatabaseParent::OnMessageReceived(IPC::Message const&) /firefox/obj-x86_64-pc-linux-gnu/ipc/ipdl/PBackgroundIDBDatabaseParent.cpp:535:32
Thread T102 created by T9 here:
#12 0x7605e79de361 in mozilla::dom::indexedDB::(anonymous namespace)::ConnectionPool::DatabaseInfo::Dispatch(...) /firefox/dom/indexedDB/ActorsParent.cpp:8651:24
#14 0x7605e79de361 in mozilla::dom::indexedDB::(anonymous namespace)::ConnectionPool::ScheduleTransaction(...) /firefox/dom/indexedDB/ActorsParent.cpp:8262:20
Thread T43 created by T9 here:
#12 0x7605e79de361 in mozilla::dom::indexedDB::(anonymous namespace)::ConnectionPool::DatabaseInfo::Dispatch(...) /firefox/dom/indexedDB/ActorsParent.cpp:8651:24
#14 0x7605e79de361 in mozilla::dom::indexedDB::(anonymous namespace)::ConnectionPool::ScheduleTransaction(...) /firefox/dom/indexedDB/ActorsParent.cpp:8262:20
Thread T9 created by T0 here:
#7 0x7605ddee5677 in (anonymous namespace)::ParentImpl::CreateBackgroundThread() /firefox/ipc/glue/BackgroundImpl.cpp:897:7
#11 0x7605e7d13729 in mozilla::dom::ContentParent::StartUp() /firefox/dom/ipc/ContentParent.cpp:668:3
#18 0x7605eb50d67f in XREMain::XRE_main(int, char**, ...) /firefox/toolkit/xre/nsAppRunner.cpp:6169:22
SUMMARY: AddressSanitizer: heap-use-after-free (/firefox/obj-x86_64-pc-linux-gnu/dist/bin/libxul.so+0x12b80af1)
Shadow bytes around the buggy address:
=>0x76c608277c80:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Freed heap region: fd
==225273==ABORTING
Suggested Fix
Add manager-chain validation in TransactionBase::VerifyRequestParams at /firefox/dom/indexedDB/ActorsParent.cpp:10408:
case StructuredCloneFileBase::eBlob:
if (NS_AUUF_OR_WARN_IF(!file)) {
return false;
}
// The actor must be managed by THIS transaction's database.
// Otherwise a compromised content process could race two connection
// threads on the same DatabaseFile::mBlobImpl.
if (NS_AUUF_OR_WARN_IF(file->Manager() !=
static_cast<const PBackgroundIDBDatabaseParent*>(
mDatabase.unsafeGetRawPtr()))) {
return false;
}
break;
Consider a broader fix in the IPDL compiler: generated ParamTraits::Read for managed actor types should validate that the deserialized actor's manager chain matches the receiving actor's manager chain. This class of bug (cross-manager actor smuggling) likely affects other protocols.
| Reporter | ||
Comment 1•8 months ago
|
||
| Reporter | ||
Comment 2•8 months ago
|
||
Comment 4•8 months ago
|
||
(In reply to Andrew Sutherland [:asuth] (he/him) from comment #3)
NI to Nika for the manager chain invariants
This isn't a bug in IPC. You can send any actor managed by the same toplevel actor over any other actor (in this case, the toplevel actor is PBackground). The bug here is that IndexedDB creates a separate TaskQueue for each database (searchfox.org/firefox-main/rev/7b1a35d6c70c08d2966c6c8dfc9aa9c04319f721/dom/indexedDB/ActorsParent.cpp#8227), and certain operations are performed on those queues. In this exploit path, the content process is sending an actor associated with a different Database object, but managed by the same PBackground. Normally this wouldn't cause race issues as they'd be on the same thread, but in this case the code assumes that the managed actor is managed by the same database as received the reference, and dispatches tasks to the database's TaskQueue.
TL;DR: IPDL does not enforce any sort of requirement that a send actor is under the same actor subtree as the receiving actor, it just requires that the actor is under the same toplevel actor. That has to be enforced by the receive method if it's relevant for the specific use-case as it seems to be here.
Updated•8 months ago
|
Updated•8 months ago
|
Updated•8 months ago
|
| Assignee | ||
Updated•8 months ago
|
Updated•7 months ago
|
| Assignee | ||
Comment 5•7 months ago
|
||
| Assignee | ||
Comment 6•7 months ago
|
||
[Tracking Requested - why for this release]:
Both ESRs 115 and 140 are effected. I think we should probably fix it in both or atleast in the latest 140 and the latest release should also be patched.
| Assignee | ||
Comment 7•7 months ago
|
||
[Tracking Requested - why for this release]:
150 beta is effected too.
| Assignee | ||
Comment 8•7 months ago
|
||
Comment on attachment 9552719 [details]
(secure)
Security Approval Request
- How easily could an exploit be constructed based on the patch?: Not that easy as it assumes a compromised content process.
- Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
- Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: ESR, release and beta
- If not all supported branches, which bug introduced the flaw?: None
- Do you have backports for the affected branches?: No
- If not, how different, hard to create, and risky will they be?: It should be trivial to create backports as the changes are pretty straight foward.
- How likely is this patch to cause regressions; how much testing does it need?: not likely and minimal
- Is the patch ready to land after security approval is given?: Yes
- Is Android affected?: Yes
Updated•7 months ago
|
Updated•7 months ago
|
Comment 9•7 months ago
|
||
Comment on attachment 9552719 [details]
(secure)
sec-approval+ to land and request uplifts
Comment 10•7 months ago
|
||
firefox-release Uplift Approval Request
- User impact if declined/Reason for urgency: Needs a compromised content process and the exploit could lead to parent process crash.
- Code covered by automated testing?: yes
- Fix verified in Nightly?: yes
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing:
- Risk associated with taking this patch: low
- Explanation of risk level: Risk should be low as the patch is only adding an additional check in a very localized place.
- String changes made/needed?: N/A
- Is Android affected?: yes
| Assignee | ||
Comment 11•7 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D287604
Comment 12•7 months ago
|
||
firefox-beta Uplift Approval Request
- User impact if declined/Reason for urgency: The exploit could end up crashing the parent process.
- Code covered by automated testing?: yes
- Fix verified in Nightly?: no
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: low
- Explanation of risk level: Risk of merging this change to target branches should be minimal as the patch is making a very localized change.
- String changes made/needed?: N/A
- Is Android affected?: yes
| Assignee | ||
Comment 13•7 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D287604
Comment 14•7 months ago
|
||
firefox-esr115 Uplift Approval Request
- User impact if declined/Reason for urgency: The exploit could end up crashing the parent process.
- Code covered by automated testing?: yes
- Fix verified in Nightly?: no
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: low
- Explanation of risk level: Risk of merging this change to target branches should be minimal as the patch is making a very localized change.
- String changes made/needed?: N/A
- Is Android affected?: yes
| Assignee | ||
Comment 15•7 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D287604
Comment 16•7 months ago
|
||
firefox-esr140 Uplift Approval Request
- User impact if declined/Reason for urgency: The exploit could end up crashing the parent process.
- Code covered by automated testing?: yes
- Fix verified in Nightly?: no
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: low
- Explanation of risk level: Risk of merging this change to target branches should be minimal as the patch is making a very localized change.
- String changes made/needed?: N/A
- Is Android affected?: yes
| Assignee | ||
Comment 17•7 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D287604
Comment 18•7 months ago
|
||
Updated•7 months ago
|
Comment 19•7 months ago
|
||
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Comment 20•7 months ago
|
||
| uplift | ||
Updated•7 months ago
|
Updated•7 months ago
|
Comment 21•7 months ago
|
||
| uplift | ||
Comment 22•7 months ago
|
||
Comment on attachment 9558712 [details]
(secure)
Approved for 115.35esr
Updated•7 months ago
|
Comment 23•7 months ago
|
||
| uplift | ||
Updated•7 months ago
|
Updated•7 months ago
|
Comment 25•7 months ago
|
||
| 140.9.1 uplift | ||
Comment 26•7 months ago
|
||
| 115.34.1 uplift | ||
Comment 27•7 months ago
|
||
| uplift | ||
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•6 months ago
|
Description
•