Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian....@packages.debian.org
Usertags: pu
X-Debbugs-Cc: z...@debian.org, t...@security.debian.org

[ Reason ]

Backport patch for CVE-2022-24921.

[ Impact ]

CVE-2022-24921:
regexp: stack exhaustion compiling deeply nested expressions

This is bug in standard library.

[ Tests ]

The backported patch has new regression tests in it.

[ Risks ]

Low, change is small. Backport from 1.16 release without modification.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [ ] the issue is verified as fixed in unstable
      golang-1.15 has been removed from unstable.

[ Changes ]

See attachment.

[ Other info ]

I don't think the severity needs to rebuild other Go packages.
diff -Nru golang-1.15-1.15.15/debian/changelog 
golang-1.15-1.15.15/debian/changelog
--- golang-1.15-1.15.15/debian/changelog        2022-02-11 23:45:44.000000000 
+0800
+++ golang-1.15-1.15.15/debian/changelog        2022-03-04 21:48:18.000000000 
+0800
@@ -1,3 +1,10 @@
+golang-1.15 (1.15.15-1~deb11u4) bullseye; urgency=medium
+
+  * Backport patch for CVE-2022-24921:
+    regexp: stack exhaustion compiling deeply nested expressions
+
+ -- Shengjing Zhu <z...@debian.org>  Fri, 04 Mar 2022 21:48:18 +0800
+
 golang-1.15 (1.15.15-1~deb11u3) bullseye; urgency=medium
 
   * Backport patches for CVE-2022-23806 CVE-2022-23772 CVE-2022-23773
diff -Nru golang-1.15-1.15.15/debian/patches/0015-CVE-2022-24921.patch 
golang-1.15-1.15.15/debian/patches/0015-CVE-2022-24921.patch
--- golang-1.15-1.15.15/debian/patches/0015-CVE-2022-24921.patch        
1970-01-01 08:00:00.000000000 +0800
+++ golang-1.15-1.15.15/debian/patches/0015-CVE-2022-24921.patch        
2022-03-04 21:48:18.000000000 +0800
@@ -0,0 +1,169 @@
+From: Russ Cox <r...@golang.org>
+Date: Wed, 2 Feb 2022 16:41:32 -0500
+Subject: CVE-2022-24921
+
+Origin: backport, https://github.com/golang/go/commit/2b65cde5
+---
+ src/regexp/syntax/parse.go      | 72 +++++++++++++++++++++++++++++++++++++++--
+ src/regexp/syntax/parse_test.go |  7 ++++
+ 2 files changed, 77 insertions(+), 2 deletions(-)
+
+diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go
+index 7b40309..d7cf2af 100644
+--- a/src/regexp/syntax/parse.go
++++ b/src/regexp/syntax/parse.go
+@@ -76,13 +76,29 @@ const (
+       opVerticalBar
+ )
+ 
++// maxHeight is the maximum height of a regexp parse tree.
++// It is somewhat arbitrarily chosen, but the idea is to be large enough
++// that no one will actually hit in real use but at the same time small enough
++// that recursion on the Regexp tree will not hit the 1GB Go stack limit.
++// The maximum amount of stack for a single recursive frame is probably
++// closer to 1kB, so this could potentially be raised, but it seems unlikely
++// that people have regexps nested even this deeply.
++// We ran a test on Google's C++ code base and turned up only
++// a single use case with depth > 100; it had depth 128.
++// Using depth 1000 should be plenty of margin.
++// As an optimization, we don't even bother calculating heights
++// until we've allocated at least maxHeight Regexp structures.
++const maxHeight = 1000
++
+ type parser struct {
+       flags       Flags     // parse mode flags
+       stack       []*Regexp // stack of parsed expressions
+       free        *Regexp
+       numCap      int // number of capturing groups seen
+       wholeRegexp string
+-      tmpClass    []rune // temporary char class work space
++      tmpClass    []rune          // temporary char class work space
++      numRegexp   int             // number of regexps allocated
++      height      map[*Regexp]int // regexp height for height limit check
+ }
+ 
+ func (p *parser) newRegexp(op Op) *Regexp {
+@@ -92,16 +108,52 @@ func (p *parser) newRegexp(op Op) *Regexp {
+               *re = Regexp{}
+       } else {
+               re = new(Regexp)
++              p.numRegexp++
+       }
+       re.Op = op
+       return re
+ }
+ 
+ func (p *parser) reuse(re *Regexp) {
++      if p.height != nil {
++              delete(p.height, re)
++      }
+       re.Sub0[0] = p.free
+       p.free = re
+ }
+ 
++func (p *parser) checkHeight(re *Regexp) {
++      if p.numRegexp < maxHeight {
++              return
++      }
++      if p.height == nil {
++              p.height = make(map[*Regexp]int)
++              for _, re := range p.stack {
++                      p.checkHeight(re)
++              }
++      }
++      if p.calcHeight(re, true) > maxHeight {
++              panic(ErrInternalError)
++      }
++}
++
++func (p *parser) calcHeight(re *Regexp, force bool) int {
++      if !force {
++              if h, ok := p.height[re]; ok {
++                      return h
++              }
++      }
++      h := 1
++      for _, sub := range re.Sub {
++              hsub := p.calcHeight(sub, false)
++              if h < 1+hsub {
++                      h = 1 + hsub
++              }
++      }
++      p.height[re] = h
++      return h
++}
++
+ // Parse stack manipulation.
+ 
+ // push pushes the regexp re onto the parse stack and returns the regexp.
+@@ -137,6 +189,7 @@ func (p *parser) push(re *Regexp) *Regexp {
+       }
+ 
+       p.stack = append(p.stack, re)
++      p.checkHeight(re)
+       return re
+ }
+ 
+@@ -246,6 +299,7 @@ func (p *parser) repeat(op Op, min, max int, before, 
after, lastRepeat string) (
+       re.Sub = re.Sub0[:1]
+       re.Sub[0] = sub
+       p.stack[n-1] = re
++      p.checkHeight(re)
+ 
+       if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) 
{
+               return "", &Error{ErrInvalidRepeatSize, 
before[:len(before)-len(after)]}
+@@ -693,6 +747,21 @@ func literalRegexp(s string, flags Flags) *Regexp {
+ // Flags, and returns a regular expression parse tree. The syntax is
+ // described in the top-level comment.
+ func Parse(s string, flags Flags) (*Regexp, error) {
++      return parse(s, flags)
++}
++
++func parse(s string, flags Flags) (_ *Regexp, err error) {
++      defer func() {
++              switch r := recover(); r {
++              default:
++                      panic(r)
++              case nil:
++                      // ok
++              case ErrInternalError:
++                      err = &Error{Code: ErrInternalError, Expr: s}
++              }
++      }()
++
+       if flags&Literal != 0 {
+               // Trivial parser for literal string.
+               if err := checkUTF8(s); err != nil {
+@@ -704,7 +773,6 @@ func Parse(s string, flags Flags) (*Regexp, error) {
+       // Otherwise, must do real work.
+       var (
+               p          parser
+-              err        error
+               c          rune
+               op         Op
+               lastRepeat string
+diff --git a/src/regexp/syntax/parse_test.go b/src/regexp/syntax/parse_test.go
+index 5581ba1..1ef6d8a 100644
+--- a/src/regexp/syntax/parse_test.go
++++ b/src/regexp/syntax/parse_test.go
+@@ -207,6 +207,11 @@ var parseTests = []parseTest{
+       // Valid repetitions.
+       {`((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}))`, ``},
+       {`((((((((((x{1}){2}){2}){2}){2}){2}){2}){2}){2}){2})`, ``},
++
++      // Valid nesting.
++      {strings.Repeat("(", 999) + strings.Repeat(")", 999), ``},
++      {strings.Repeat("(?:", 999) + strings.Repeat(")*", 999), ``},
++      {"(" + strings.Repeat("|", 12345) + ")", ``}, // not nested at all
+ }
+ 
+ const testFlags = MatchNL | PerlX | UnicodeGroups
+@@ -482,6 +487,8 @@ var invalidRegexps = []string{
+       `a{100000}`,
+       `a{100000,}`,
+       "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})",
++      strings.Repeat("(", 1000) + strings.Repeat(")", 1000),
++      strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000),
+       `\Q\E*`,
+ }
+ 
diff -Nru golang-1.15-1.15.15/debian/patches/series 
golang-1.15-1.15.15/debian/patches/series
--- golang-1.15-1.15.15/debian/patches/series   2022-02-11 23:45:44.000000000 
+0800
+++ golang-1.15-1.15.15/debian/patches/series   2022-03-04 21:48:18.000000000 
+0800
@@ -12,3 +12,4 @@
 0012-CVE-2022-23806.patch
 0013-CVE-2022-23772.patch
 0014-CVE-2022-23773.patch
+0015-CVE-2022-24921.patch

Reply via email to