Skip to content

Commit f927414

Browse files
Co-authored-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
1 parent f5a024d commit f927414

9 files changed

Lines changed: 604 additions & 85 deletions
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
From cd4e238269e60d142079910d5aa94d9545c8a503 Mon Sep 17 00:00:00 2001
2+
From: Roland Shoemaker <roland@golang.org>
3+
Date: Tue, 12 May 2026 15:36:39 -0400
4+
Subject: [PATCH] html: improve Noah's Ark clause performance
5+
6+
Instead of iterating over each element in the stack, and checking each
7+
attribute against each other attribute in a ~cubic fashion, sort the
8+
attributes and just use slices.Equal.
9+
10+
Thanks to IPC Labs for reporting this issue.
11+
12+
Fixes CVE-2026-25680
13+
14+
Change-Id: Iec3513ba0b5da4f28f1359d24846401b9ab76ee3
15+
Reviewed-on: https://go-review.googlesource.com/c/net/+/781702
16+
TryBot-Bypass: Roland Shoemaker <roland@golang.org>
17+
Reviewed-by: Nicholas Husin <nsh@golang.org>
18+
Reviewed-by: Neal Patel <nealpatel@google.com>
19+
Reviewed-by: Nicholas Husin <husin@google.com>
20+
Auto-Submit: Gopher Robot <gobot@golang.org>
21+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
22+
Upstream-reference: https://github.com/golang/net/commit/08be507abce89191d78cd49da60f4501fc910472.patch
23+
---
24+
vendor/golang.org/x/net/html/parse.go | 34 ++++++++++++++++-----------
25+
1 file changed, 20 insertions(+), 14 deletions(-)
26+
27+
diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go
28+
index 3392845..4bd5e6d 100644
29+
--- a/vendor/golang.org/x/net/html/parse.go
30+
+++ b/vendor/golang.org/x/net/html/parse.go
31+
@@ -5,9 +5,11 @@
32+
package html
33+
34+
import (
35+
+ "cmp"
36+
"errors"
37+
"fmt"
38+
"io"
39+
+ "slices"
40+
"strings"
41+
42+
a "golang.org/x/net/html/atom"
43+
@@ -328,6 +330,14 @@ func (p *parser) addText(text string) {
44+
})
45+
}
46+
47+
+func attrCompare(a, b Attribute) int {
48+
+ return cmp.Or(
49+
+ cmp.Compare(a.Namespace, b.Namespace),
50+
+ cmp.Compare(a.Key, b.Key),
51+
+ cmp.Compare(a.Val, b.Val),
52+
+ )
53+
+}
54+
+
55+
// addElement adds a child element based on the current token.
56+
func (p *parser) addElement() {
57+
p.addChild(&Node{
58+
@@ -343,6 +353,10 @@ func (p *parser) addFormattingElement() {
59+
tagAtom, attr := p.tok.DataAtom, p.tok.Attr
60+
p.addElement()
61+
62+
+ // In order to optimize the search, we need the attributes to be sorted, so we
63+
+ // can just use slices.Equal.
64+
+ slices.SortFunc(attr, attrCompare)
65+
+
66+
// Implement the Noah's Ark clause, but with three per family instead of two.
67+
identicalElements := 0
68+
findIdenticalElements:
69+
@@ -360,19 +374,7 @@ findIdenticalElements:
70+
if n.DataAtom != tagAtom {
71+
continue
72+
}
73+
- if len(n.Attr) != len(attr) {
74+
- continue
75+
- }
76+
- compareAttributes:
77+
- for _, t0 := range n.Attr {
78+
- for _, t1 := range attr {
79+
- if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val {
80+
- // Found a match for this attribute, continue with the next attribute.
81+
- continue compareAttributes
82+
- }
83+
- }
84+
- // If we get here, there is no attribute that matches a.
85+
- // Therefore the element is not identical to the new one.
86+
+ if !slices.Equal(n.Attr, attr) {
87+
continue findIdenticalElements
88+
}
89+
90+
@@ -382,7 +384,11 @@ findIdenticalElements:
91+
}
92+
}
93+
94+
- p.afe = append(p.afe, p.top())
95+
+ // Sort the attributes to optimize future identical-element searches.
96+
+ top := p.top()
97+
+ slices.SortFunc(top.Attr, attrCompare)
98+
+
99+
+ p.afe = append(p.afe, top)
100+
}
101+
102+
// Section 12.2.4.3.
103+
--
104+
2.45.4
105+
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
From a2341f917becd64a2b652b4200e4371e5d80f235 Mon Sep 17 00:00:00 2001
2+
From: Roland Shoemaker <roland@golang.org>
3+
Date: Mon, 4 May 2026 11:47:15 -0700
4+
Subject: [PATCH] html: escape greater-than symbol in doctype identifiers
5+
6+
During parsing, we unescape character references. When rendering, we
7+
re-escape certain characters in certain scenarios in order to avoid
8+
token content causing unexpected parser behavior.
9+
10+
We appear to have not taken this into account when rendering DOCTYPE
11+
tokens, allowing ">" in PUBLIC/SYSTEM identifier strings, which trigger
12+
a abrupt-doctype-system-identifier parse error which immediately emits
13+
the current DOCTYPE token and then continues parsing in the data state.
14+
15+
This may cause bypass in HTML santizers which use the html package for
16+
parsing.
17+
18+
Thanks to ensy for reporting this issue.
19+
20+
Fixes CVE-2026-25681
21+
22+
Change-Id: I1d5be92129d17bfbf0917148db2672d57c224a18
23+
Reviewed-on: https://go-review.googlesource.com/c/net/+/781703
24+
Reviewed-by: Neal Patel <nealpatel@google.com>
25+
Reviewed-by: Nicholas Husin <nsh@golang.org>
26+
TryBot-Bypass: Roland Shoemaker <roland@golang.org>
27+
Auto-Submit: Gopher Robot <gobot@golang.org>
28+
Reviewed-by: Nicholas Husin <husin@google.com>
29+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
30+
Upstream-reference: https://github.com/golang/net/commit/4ece7b612ad44ad6c4d5e0d5d4df9c18cc211905.patch
31+
---
32+
vendor/golang.org/x/net/html/render.go | 19 +++++++++++++------
33+
.../html/testdata/go/doctype_named_entity.dat | 8 ++++++++
34+
2 files changed, 21 insertions(+), 6 deletions(-)
35+
create mode 100644 vendor/golang.org/x/net/html/testdata/go/doctype_named_entity.dat
36+
37+
diff --git a/vendor/golang.org/x/net/html/render.go b/vendor/golang.org/x/net/html/render.go
38+
index e8c1233..f3740cc 100644
39+
--- a/vendor/golang.org/x/net/html/render.go
40+
+++ b/vendor/golang.org/x/net/html/render.go
41+
@@ -113,14 +113,14 @@ func render1(w writer, n *Node) error {
42+
if _, err := w.WriteString(" PUBLIC "); err != nil {
43+
return err
44+
}
45+
- if err := writeQuoted(w, p); err != nil {
46+
+ if err := writeDoctypeQuoted(w, p); err != nil {
47+
return err
48+
}
49+
if s != "" {
50+
if err := w.WriteByte(' '); err != nil {
51+
return err
52+
}
53+
- if err := writeQuoted(w, s); err != nil {
54+
+ if err := writeDoctypeQuoted(w, s); err != nil {
55+
return err
56+
}
57+
}
58+
@@ -128,7 +128,7 @@ func render1(w writer, n *Node) error {
59+
if _, err := w.WriteString(" SYSTEM "); err != nil {
60+
return err
61+
}
62+
- if err := writeQuoted(w, s); err != nil {
63+
+ if err := writeDoctypeQuoted(w, s); err != nil {
64+
return err
65+
}
66+
}
67+
@@ -251,19 +251,26 @@ func childTextNodesAreLiteral(n *Node) bool {
68+
}
69+
}
70+
71+
-// writeQuoted writes s to w surrounded by quotes. Normally it will use double
72+
+// writeDoctypeQuoted writes s to w surrounded by quotes. Normally it will use double
73+
// quotes, but if s contains a double quote, it will use single quotes.
74+
+// If s contains any '>' characters, they are replaced with &gt; in order
75+
+// to prevent triggering an abrupt-doctype-system-identifier parse error.
76+
// It is used for writing the identifiers in a doctype declaration.
77+
// In valid HTML, they can't contain both types of quotes.
78+
-func writeQuoted(w writer, s string) error {
79+
+func writeDoctypeQuoted(w writer, s string) error {
80+
var q byte = '"'
81+
if strings.Contains(s, `"`) {
82+
+ // parseDoctype will never produce a Node with both quote types, but a user
83+
+ // can construct their own Node that violates this assumption.
84+
+ if strings.Contains(s, `'`) {
85+
+ return errors.New("doctype contains both quote types, cannot be safely rendered")
86+
+ }
87+
q = '\''
88+
}
89+
if err := w.WriteByte(q); err != nil {
90+
return err
91+
}
92+
- if _, err := w.WriteString(s); err != nil {
93+
+ if _, err := w.WriteString(strings.ReplaceAll(s, ">", "&gt;")); err != nil {
94+
return err
95+
}
96+
if err := w.WriteByte(q); err != nil {
97+
diff --git a/vendor/golang.org/x/net/html/testdata/go/doctype_named_entity.dat b/vendor/golang.org/x/net/html/testdata/go/doctype_named_entity.dat
98+
new file mode 100644
99+
index 0000000..a8bd963
100+
--- /dev/null
101+
+++ b/vendor/golang.org/x/net/html/testdata/go/doctype_named_entity.dat
102+
@@ -0,0 +1,8 @@
103+
+#data
104+
+<!DOCTYPE &gt; PUBLIC "&gt;" "&gt;">
105+
+#errors
106+
+#document
107+
+| <!DOCTYPE > ">" ">">
108+
+| <html>
109+
+| <head>
110+
+| <body>
111+
--
112+
2.45.4
113+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
From d69603f14c9a7baa50ae855aed9cc4b18301e3db Mon Sep 17 00:00:00 2001
2+
From: Nicola Murino <nicola.murino@gmail.com>
3+
Date: Sun, 1 Mar 2026 11:49:28 +0100
4+
Subject: [PATCH] ssh: prevent memory leak when rejecting channels
5+
6+
When a server rejects an incoming channel request via
7+
NewChannel.Reject, the channel is left in the multiplexer's
8+
channel list. Because the channel is never explicitly removed or
9+
closed, its internal buffers and sync primitives remain allocated
10+
for the lifetime of the SSH connection.
11+
12+
A malicious client could exploit this behavior by repeatedly
13+
requesting to open channels that are destined to be rejected,
14+
causing unbounded memory growth and potentially leading to a
15+
Denial of Service (DoS) via resource exhaustion.
16+
17+
This change fixes the leak by calling ch.mux.chanList.remove
18+
within the Reject method, removing the channel from the list and allowing the
19+
garbage collector to reclaim the associated memory immediately.
20+
21+
Fixes golang/go#35127
22+
Fixes CVE-2026-3982
23+
24+
Change-Id: Iaa177f5dfd151812dd404e528a4a1c77527a0e29
25+
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/781320
26+
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
27+
Reviewed-by: Roland Shoemaker <roland@golang.org>
28+
Reviewed-by: Nicholas Husin <nsh@golang.org>
29+
Reviewed-by: Nicholas Husin <husin@google.com>
30+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
31+
Upstream-reference: https://github.com/golang/crypto/commit/6c195c8a97ae3d91a366ebdd7787d5faa64bf42a.patch
32+
---
33+
vendor/golang.org/x/crypto/ssh/channel.go | 12 +++++++++++-
34+
1 file changed, 11 insertions(+), 1 deletion(-)
35+
36+
diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go
37+
index 3967b65..77bac19 100644
38+
--- a/vendor/golang.org/x/crypto/ssh/channel.go
39+
+++ b/vendor/golang.org/x/crypto/ssh/channel.go
40+
@@ -536,7 +536,17 @@ func (ch *channel) Reject(reason RejectionReason, message string) error {
41+
Language: "en",
42+
}
43+
ch.decided = true
44+
- return ch.sendMessage(reject)
45+
+ err := ch.sendMessage(reject)
46+
+
47+
+ // Remove the channel from the mux to prevent memory leaks.
48+
+ // Do not call ch.close() here: no goroutine holds a reference to a
49+
+ // rejected channel's internal channels (msg, incomingRequests), so
50+
+ // removing it from chanList is sufficient for GC. Calling close()
51+
+ // would race with the mux loop goroutine (handlePacket or dropAll),
52+
+ // causing a panic from closing an already-closed channel.
53+
+ ch.mux.chanList.remove(ch.localId)
54+
+
55+
+ return err
56+
}
57+
58+
func (ch *channel) Read(data []byte) (int, error) {
59+
--
60+
2.45.4
61+

0 commit comments

Comments
 (0)