html: improve Noah's Ark clause performance Instead of iterating over each element in the stack, and checking each attribute against each other attribute in a ~cubic fashion, sort the attributes and just use slices.Equal. Thanks to IPC Labs for reporting this issue. Fixes CVE-2026-25680 Change-Id: Iec3513ba0b5da4f28f1359d24846401b9ab76ee3 Reviewed-on: https://go-review.googlesource.com/c/net/+/781702 TryBot-Bypass: Roland Shoemaker <roland@golang.org> Reviewed-by: Nicholas Husin <nsh@golang.org> Reviewed-by: Neal Patel <nealpatel@google.com> Reviewed-by: Nicholas Husin <husin@google.com> Auto-Submit: Gopher Robot <gobot@golang.org>
diff --git a/html/parse.go b/html/parse.go index 70a6335..b3d2a25 100644 --- a/html/parse.go +++ b/html/parse.go
@@ -5,9 +5,11 @@ package html import ( + "cmp" "errors" "fmt" "io" + "slices" "strings" a "golang.org/x/net/html/atom" @@ -328,6 +330,14 @@ }) } +func attrCompare(a, b Attribute) int { + return cmp.Or( + cmp.Compare(a.Namespace, b.Namespace), + cmp.Compare(a.Key, b.Key), + cmp.Compare(a.Val, b.Val), + ) +} + // addElement adds a child element based on the current token. func (p *parser) addElement() { p.addChild(&Node{ @@ -343,6 +353,10 @@ tagAtom, attr := p.tok.DataAtom, p.tok.Attr p.addElement() + // In order to optimize the search, we need the attributes to be sorted, so we + // can just use slices.Equal. + slices.SortFunc(attr, attrCompare) + // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0 findIdenticalElements: @@ -360,19 +374,7 @@ if n.DataAtom != tagAtom { continue } - if len(n.Attr) != len(attr) { - continue - } - compareAttributes: - for _, t0 := range n.Attr { - for _, t1 := range attr { - if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { - // Found a match for this attribute, continue with the next attribute. - continue compareAttributes - } - } - // If we get here, there is no attribute that matches a. - // Therefore the element is not identical to the new one. + if !slices.Equal(n.Attr, attr) { continue findIdenticalElements } @@ -382,7 +384,11 @@ } } - p.afe = append(p.afe, p.top()) + // Sort the attributes to optimize future identical-element searches. + top := p.top() + slices.SortFunc(top.Attr, attrCompare) + + p.afe = append(p.afe, top) } // Section 12.2.4.3.