net/mail: fix quadratic consumePhrase behavior

Updates #78987
Fixes CVE-2026-42499

Change-Id: I8438e5dee7e6433573d4161baf8fb2151e7fbc2f
Reviewed-on: https://go-review.googlesource.com/c/go/+/771520
Reviewed-by: Nicholas Husin <husin@google.com>
Reviewed-by: Nicholas Husin <nsh@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Neal Patel 2026-04-28 12:10:24 -04:00
parent a3f569adee
commit 2c59389fcc
2 changed files with 28 additions and 6 deletions

View file

@ -575,8 +575,10 @@ func (p *addrParser) consumeAddrSpec() (spec string, err error) {
func (p *addrParser) consumePhrase() (phrase string, err error) {
debug.Printf("consumePhrase: [%s]", p.s)
// phrase = 1*word
var words []string
var isPrevEncoded bool
var (
words []string
sb strings.Builder
)
for {
// obs-phrase allows CFWS after one word
if len(words) > 0 {
@ -608,13 +610,22 @@ func (p *addrParser) consumePhrase() (phrase string, err error) {
break
}
debug.Printf("consumePhrase: consumed %q", word)
if isPrevEncoded && isEncoded {
words[len(words)-1] += word
} else {
switch {
case isEncoded:
sb.WriteString(word)
case !isEncoded && sb.Len() > 0:
words = append(words, sb.String())
sb.Reset()
words = append(words, word)
default:
words = append(words, word)
}
isPrevEncoded = isEncoded
}
if sb.Len() > 0 {
words = append(words, sb.String())
}
// Ignore any error if we got at least one word.
if err != nil && len(words) == 0 {
debug.Printf("consumePhrase: hit err: %v", err)

View file

@ -1262,6 +1262,17 @@ func TestEmptyAddress(t *testing.T) {
}
}
func BenchmarkConsumePhrase(b *testing.B) {
for _, n := range []int{10, 100, 1000, 10000} {
b.Run(fmt.Sprintf("words-%d", n), func(b *testing.B) {
input := strings.Repeat("=?utf-8?q?hello?= ", n) + "<user@example.com>"
for b.Loop() {
(&addrParser{s: input}).consumePhrase()
}
})
}
}
func BenchmarkConsumeComment(b *testing.B) {
for _, n := range []int{10, 100, 1000, 10000} {
b.Run(fmt.Sprintf("depth-%d", n), func(b *testing.B) {