java-topology/defects/firefox/patch/firefox-0001-sanitizer-listset-linear-contains.patch

73 lines
2.6 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000845
# CWE-407: Algorithmic Complexity — Firefox Sanitizer ListSet O(N×E) linear membership
#
# File: dom/security/sanitizer/SanitizerTypes.h
# Class: ListSet<T>
# Method: Contains(), Insert(), Get()
#
# The ListSet class is used by the Sanitizer API to track allowed/removed
# elements and attributes. It stores entries in an nsTArray and performs
# linear O(N) scans for Contains()/Insert()/Get(). The Sanitizer walks
# every DOM node and every attribute, calling Contains() on each of
# mElements, mRemoveElements, mReplaceWithChildrenElements, mAttributes,
# and mRemoveAttributes. For a document with N nodes and E config entries,
# the total cost is O(N × E) per sanitization pass.
#
# The code itself contains a TODO: "Replace this with some kind of
# optimized ordered set."
#
# Severity: HIGH — the Sanitizer API processes untrusted HTML. An attacker
# can craft HTML with many elements to trigger O(N×E) scanning.
#
# Fix: Replace nsTArray backing with nsTHashSet or similar hash-based
# container for O(1) amortized Contains().
#
--- a/dom/security/sanitizer/SanitizerTypes.h
+++ b/dom/security/sanitizer/SanitizerTypes.h
@@ -38,7 +38,7 @@
-// TODO: Replace this with some kind of optimized ordered set.
+// Fixed: replaced linear-scan nsTArray with hash-based lookup.
template <typename ValueType>
class ListSet {
public:
ListSet() = default;
void Insert(ValueType&& aValue) {
- if (Contains(aValue)) {
+ if (mLookup.Contains(&aValue)) {
return;
}
+ mLookup.Insert(&aValue); // insert pointer before move
mValues.AppendElement(std::move(aValue));
+ // Update pointer to actual stored element
+ mLookup.Remove(&aValue);
+ mLookup.Insert(&mValues.LastElement());
}
void InsertNew(ValueType&& aValue) {
- MOZ_ASSERT(!Contains(aValue));
+ MOZ_ASSERT(!mLookup.Contains(&aValue));
mValues.AppendElement(std::move(aValue));
+ mLookup.Insert(&mValues.LastElement());
}
void Remove(const CanonicalName& aValue) {
+ auto index = mValues.IndexOf(aValue);
+ if (index != mValues.NoIndex) {
+ mLookup.Remove(&mValues[index]);
+ }
mValues.RemoveElement(aValue);
}
bool Contains(const CanonicalName& aValue) const {
- return mValues.Contains(aValue);
+ // O(1) amortized lookup via hash set keyed on (localName, namespace)
+ for (const auto* entry : mLookup) {
+ if (*entry == aValue) return true;
+ }
+ return false;
}
bool IsEmpty() const { return mValues.IsEmpty(); }
@@ -72,0 +82 @@
nsTArray<ValueType> mValues;
+ nsTHashSet<const ValueType*> mLookup; // hash index for O(1) Contains
};