CVE-2026-117929
Overview
Background
- `nsDOMTokenList`
- the refcounted object backing an element’s
classList;Element::ClassList()returns a borrowed pointer whose lifetime is tied to the element/content. - `ImageDocument`
- the internal document used to display a standalone image; it manipulates its image element’s
classListwhile reacting to zoom/mode changes.
Root Cause Analysis
ImageDocument::SetModeClass and CheckFullZoom stored the result of mImageContent->ClassList() in a raw nsDOMTokenList* and then called methods on it (Add, etc.). Those calls can run script/layout side effects that drop the last reference to mImageContent (and thus its class list), after which the raw pointer is dangling — a use-after-free.
The fix stores the list in a RefPtr<nsDOMTokenList>, keeping it alive across the subsequent operations.
classList pointer was held raw across operations that can free it; promoting it to RefPtr closes the use-after-free.Attack Path
- ENTRY
Navigate to or embed a standalone image so an
ImageDocumentis created. - TRIGGER
Drive a mode/zoom change (
SetModeClass/CheckFullZoom) whoseclassListmutation re-enters script or layout. - MISSING CHECK
The code keeps a raw
nsDOMTokenList*across that re-entrant operation, which can release the owning image content. - IMPACT
The now-dangling
classListpointer is dereferenced — use-after-free in the content process. (The exact re-entrant free is inferred; the patch’s raw→RefPtr change is the proof of the lifetime bug.)
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifdom/html/ImageDocument.cpp |
modified |
Files Changed
dom/html/ImageDocument.cpp
Audit Directions
- Borrowed DOM getters held rawAudit
ClassList()/similar accessors stored as raw pointers across calls that can run script or mutate the tree.
Patch
diff --git a/dom/html/ImageDocument.cpp b/dom/html/ImageDocument.cpp
index 4e2eda4d1a8..bfebbc75f40 100644
--- a/dom/html/ImageDocument.cpp
+++ b/dom/html/ImageDocument.cpp
@@ -386,7 +386,7 @@ void ImageDocument::OnHasTransparency() {
}
void ImageDocument::SetModeClass(eModeClasses mode) {
- nsDOMTokenList* classList = mImageContent->ClassList();
+ RefPtr<nsDOMTokenList> classList = mImageContent->ClassList();
if (mode == eShrinkToFit) {
classList->Add(u"shrinkToFit"_ns, IgnoreErrors());
@@ -723,7 +723,7 @@ float ImageDocument::GetZoomLevel() {
}
void ImageDocument::CheckFullZoom() {
- nsDOMTokenList* classList =
+ RefPtr<nsDOMTokenList> classList =
mImageContent ? mImageContent->ClassList() : nullptr;
if (!classList) {