diff options
author | Hitendra Prajapati <hprajapati@mvista.com> | 2022-12-09 10:38:14 +0530 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2022-12-23 23:05:44 +0000 |
commit | 893481f07a51ce9c3773459c39a59d1e8fad983c (patch) | |
tree | 6e8d3a45ce28bb366e8d7ba428fdeff4acbeb407 /meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch | |
parent | 9b1d9ad3b48524798fea6986af3a2096ab0220c0 (diff) | |
download | poky-893481f07a51ce9c3773459c39a59d1e8fad983c.tar.gz |
golang: CVE-2022-41715 regexp/syntax: limit memory used by parsing regexps
Upstream-Status: Backport from https://github.com/golang/go/commit/e9017c2416ad0ef642f5e0c2eab2dbf3cba4d997
(From OE-Core rev: 2470c52db633f206dbfcd049fcca828d1ff5f82a)
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch')
-rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch new file mode 100644 index 0000000000..fac0ebe94c --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch | |||
@@ -0,0 +1,271 @@ | |||
1 | From e9017c2416ad0ef642f5e0c2eab2dbf3cba4d997 Mon Sep 17 00:00:00 2001 | ||
2 | From: Russ Cox <rsc@golang.org> | ||
3 | Date: Wed, 28 Sep 2022 11:18:51 -0400 | ||
4 | Subject: [PATCH] [release-branch.go1.18] regexp: limit size of parsed regexps | ||
5 | |||
6 | Set a 128 MB limit on the amount of space used by []syntax.Inst | ||
7 | in the compiled form corresponding to a given regexp. | ||
8 | |||
9 | Also set a 128 MB limit on the rune storage in the *syntax.Regexp | ||
10 | tree itself. | ||
11 | |||
12 | Thanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting this issue. | ||
13 | |||
14 | Fixes CVE-2022-41715. | ||
15 | Updates #55949. | ||
16 | Fixes #55950. | ||
17 | |||
18 | Change-Id: Ia656baed81564436368cf950e1c5409752f28e1b | ||
19 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1592136 | ||
20 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
21 | Reviewed-by: Damien Neil <dneil@google.com> | ||
22 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
23 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
24 | Reviewed-on: https://go-review.googlesource.com/c/go/+/438501 | ||
25 | Run-TryBot: Carlos Amedee <carlos@golang.org> | ||
26 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
27 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
28 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
29 | Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> | ||
30 | |||
31 | Upstream-Status: Backport [https://github.com/golang/go/commit/e9017c2416ad0ef642f5e0c2eab2dbf3cba4d997] | ||
32 | CVE: CVE-2022-41715 | ||
33 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
34 | |||
35 | --- | ||
36 | src/regexp/syntax/parse.go | 145 ++++++++++++++++++++++++++++++-- | ||
37 | src/regexp/syntax/parse_test.go | 13 +-- | ||
38 | 2 files changed, 148 insertions(+), 10 deletions(-) | ||
39 | |||
40 | diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go | ||
41 | index 55bd20d..60491d5 100644 | ||
42 | --- a/src/regexp/syntax/parse.go | ||
43 | +++ b/src/regexp/syntax/parse.go | ||
44 | @@ -90,15 +90,49 @@ const ( | ||
45 | // until we've allocated at least maxHeight Regexp structures. | ||
46 | const maxHeight = 1000 | ||
47 | |||
48 | +// maxSize is the maximum size of a compiled regexp in Insts. | ||
49 | +// It too is somewhat arbitrarily chosen, but the idea is to be large enough | ||
50 | +// to allow significant regexps while at the same time small enough that | ||
51 | +// the compiled form will not take up too much memory. | ||
52 | +// 128 MB is enough for a 3.3 million Inst structures, which roughly | ||
53 | +// corresponds to a 3.3 MB regexp. | ||
54 | +const ( | ||
55 | + maxSize = 128 << 20 / instSize | ||
56 | + instSize = 5 * 8 // byte, 2 uint32, slice is 5 64-bit words | ||
57 | +) | ||
58 | + | ||
59 | +// maxRunes is the maximum number of runes allowed in a regexp tree | ||
60 | +// counting the runes in all the nodes. | ||
61 | +// Ignoring character classes p.numRunes is always less than the length of the regexp. | ||
62 | +// Character classes can make it much larger: each \pL adds 1292 runes. | ||
63 | +// 128 MB is enough for 32M runes, which is over 26k \pL instances. | ||
64 | +// Note that repetitions do not make copies of the rune slices, | ||
65 | +// so \pL{1000} is only one rune slice, not 1000. | ||
66 | +// We could keep a cache of character classes we've seen, | ||
67 | +// so that all the \pL we see use the same rune list, | ||
68 | +// but that doesn't remove the problem entirely: | ||
69 | +// consider something like [\pL01234][\pL01235][\pL01236]...[\pL^&*()]. | ||
70 | +// And because the Rune slice is exposed directly in the Regexp, | ||
71 | +// there is not an opportunity to change the representation to allow | ||
72 | +// partial sharing between different character classes. | ||
73 | +// So the limit is the best we can do. | ||
74 | +const ( | ||
75 | + maxRunes = 128 << 20 / runeSize | ||
76 | + runeSize = 4 // rune is int32 | ||
77 | +) | ||
78 | + | ||
79 | type parser struct { | ||
80 | flags Flags // parse mode flags | ||
81 | stack []*Regexp // stack of parsed expressions | ||
82 | free *Regexp | ||
83 | numCap int // number of capturing groups seen | ||
84 | wholeRegexp string | ||
85 | - tmpClass []rune // temporary char class work space | ||
86 | - numRegexp int // number of regexps allocated | ||
87 | - height map[*Regexp]int // regexp height for height limit check | ||
88 | + tmpClass []rune // temporary char class work space | ||
89 | + numRegexp int // number of regexps allocated | ||
90 | + numRunes int // number of runes in char classes | ||
91 | + repeats int64 // product of all repetitions seen | ||
92 | + height map[*Regexp]int // regexp height, for height limit check | ||
93 | + size map[*Regexp]int64 // regexp compiled size, for size limit check | ||
94 | } | ||
95 | |||
96 | func (p *parser) newRegexp(op Op) *Regexp { | ||
97 | @@ -122,6 +156,104 @@ func (p *parser) reuse(re *Regexp) { | ||
98 | p.free = re | ||
99 | } | ||
100 | |||
101 | +func (p *parser) checkLimits(re *Regexp) { | ||
102 | + if p.numRunes > maxRunes { | ||
103 | + panic(ErrInternalError) | ||
104 | + } | ||
105 | + p.checkSize(re) | ||
106 | + p.checkHeight(re) | ||
107 | +} | ||
108 | + | ||
109 | +func (p *parser) checkSize(re *Regexp) { | ||
110 | + if p.size == nil { | ||
111 | + // We haven't started tracking size yet. | ||
112 | + // Do a relatively cheap check to see if we need to start. | ||
113 | + // Maintain the product of all the repeats we've seen | ||
114 | + // and don't track if the total number of regexp nodes | ||
115 | + // we've seen times the repeat product is in budget. | ||
116 | + if p.repeats == 0 { | ||
117 | + p.repeats = 1 | ||
118 | + } | ||
119 | + if re.Op == OpRepeat { | ||
120 | + n := re.Max | ||
121 | + if n == -1 { | ||
122 | + n = re.Min | ||
123 | + } | ||
124 | + if n <= 0 { | ||
125 | + n = 1 | ||
126 | + } | ||
127 | + if int64(n) > maxSize/p.repeats { | ||
128 | + p.repeats = maxSize | ||
129 | + } else { | ||
130 | + p.repeats *= int64(n) | ||
131 | + } | ||
132 | + } | ||
133 | + if int64(p.numRegexp) < maxSize/p.repeats { | ||
134 | + return | ||
135 | + } | ||
136 | + | ||
137 | + // We need to start tracking size. | ||
138 | + // Make the map and belatedly populate it | ||
139 | + // with info about everything we've constructed so far. | ||
140 | + p.size = make(map[*Regexp]int64) | ||
141 | + for _, re := range p.stack { | ||
142 | + p.checkSize(re) | ||
143 | + } | ||
144 | + } | ||
145 | + | ||
146 | + if p.calcSize(re, true) > maxSize { | ||
147 | + panic(ErrInternalError) | ||
148 | + } | ||
149 | +} | ||
150 | + | ||
151 | +func (p *parser) calcSize(re *Regexp, force bool) int64 { | ||
152 | + if !force { | ||
153 | + if size, ok := p.size[re]; ok { | ||
154 | + return size | ||
155 | + } | ||
156 | + } | ||
157 | + | ||
158 | + var size int64 | ||
159 | + switch re.Op { | ||
160 | + case OpLiteral: | ||
161 | + size = int64(len(re.Rune)) | ||
162 | + case OpCapture, OpStar: | ||
163 | + // star can be 1+ or 2+; assume 2 pessimistically | ||
164 | + size = 2 + p.calcSize(re.Sub[0], false) | ||
165 | + case OpPlus, OpQuest: | ||
166 | + size = 1 + p.calcSize(re.Sub[0], false) | ||
167 | + case OpConcat: | ||
168 | + for _, sub := range re.Sub { | ||
169 | + size += p.calcSize(sub, false) | ||
170 | + } | ||
171 | + case OpAlternate: | ||
172 | + for _, sub := range re.Sub { | ||
173 | + size += p.calcSize(sub, false) | ||
174 | + } | ||
175 | + if len(re.Sub) > 1 { | ||
176 | + size += int64(len(re.Sub)) - 1 | ||
177 | + } | ||
178 | + case OpRepeat: | ||
179 | + sub := p.calcSize(re.Sub[0], false) | ||
180 | + if re.Max == -1 { | ||
181 | + if re.Min == 0 { | ||
182 | + size = 2 + sub // x* | ||
183 | + } else { | ||
184 | + size = 1 + int64(re.Min)*sub // xxx+ | ||
185 | + } | ||
186 | + break | ||
187 | + } | ||
188 | + // x{2,5} = xx(x(x(x)?)?)? | ||
189 | + size = int64(re.Max)*sub + int64(re.Max-re.Min) | ||
190 | + } | ||
191 | + | ||
192 | + if size < 1 { | ||
193 | + size = 1 | ||
194 | + } | ||
195 | + p.size[re] = size | ||
196 | + return size | ||
197 | +} | ||
198 | + | ||
199 | func (p *parser) checkHeight(re *Regexp) { | ||
200 | if p.numRegexp < maxHeight { | ||
201 | return | ||
202 | @@ -158,6 +290,7 @@ func (p *parser) calcHeight(re *Regexp, force bool) int { | ||
203 | |||
204 | // push pushes the regexp re onto the parse stack and returns the regexp. | ||
205 | func (p *parser) push(re *Regexp) *Regexp { | ||
206 | + p.numRunes += len(re.Rune) | ||
207 | if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] { | ||
208 | // Single rune. | ||
209 | if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) { | ||
210 | @@ -189,7 +322,7 @@ func (p *parser) push(re *Regexp) *Regexp { | ||
211 | } | ||
212 | |||
213 | p.stack = append(p.stack, re) | ||
214 | - p.checkHeight(re) | ||
215 | + p.checkLimits(re) | ||
216 | return re | ||
217 | } | ||
218 | |||
219 | @@ -305,7 +438,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) ( | ||
220 | re.Sub = re.Sub0[:1] | ||
221 | re.Sub[0] = sub | ||
222 | p.stack[n-1] = re | ||
223 | - p.checkHeight(re) | ||
224 | + p.checkLimits(re) | ||
225 | |||
226 | if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) { | ||
227 | return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]} | ||
228 | @@ -509,6 +642,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp { | ||
229 | |||
230 | for j := start; j < i; j++ { | ||
231 | sub[j] = p.removeLeadingString(sub[j], len(str)) | ||
232 | + p.checkLimits(sub[j]) | ||
233 | } | ||
234 | suffix := p.collapse(sub[start:i], OpAlternate) // recurse | ||
235 | |||
236 | @@ -566,6 +700,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp { | ||
237 | for j := start; j < i; j++ { | ||
238 | reuse := j != start // prefix came from sub[start] | ||
239 | sub[j] = p.removeLeadingRegexp(sub[j], reuse) | ||
240 | + p.checkLimits(sub[j]) | ||
241 | } | ||
242 | suffix := p.collapse(sub[start:i], OpAlternate) // recurse | ||
243 | |||
244 | diff --git a/src/regexp/syntax/parse_test.go b/src/regexp/syntax/parse_test.go | ||
245 | index 1ef6d8a..67e3c56 100644 | ||
246 | --- a/src/regexp/syntax/parse_test.go | ||
247 | +++ b/src/regexp/syntax/parse_test.go | ||
248 | @@ -484,12 +484,15 @@ var invalidRegexps = []string{ | ||
249 | `(?P<>a)`, | ||
250 | `[a-Z]`, | ||
251 | `(?i)[a-Z]`, | ||
252 | - `a{100000}`, | ||
253 | - `a{100000,}`, | ||
254 | - "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", | ||
255 | - strings.Repeat("(", 1000) + strings.Repeat(")", 1000), | ||
256 | - strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), | ||
257 | `\Q\E*`, | ||
258 | + `a{100000}`, // too much repetition | ||
259 | + `a{100000,}`, // too much repetition | ||
260 | + "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", // too much repetition | ||
261 | + strings.Repeat("(", 1000) + strings.Repeat(")", 1000), // too deep | ||
262 | + strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), // too deep | ||
263 | + "(" + strings.Repeat("(xx?)", 1000) + "){1000}", // too long | ||
264 | + strings.Repeat("(xx?){1000}", 1000), // too long | ||
265 | + strings.Repeat(`\pL`, 27000), // too many runes | ||
266 | } | ||
267 | |||
268 | var onlyPerl = []string{ | ||
269 | -- | ||
270 | 2.25.1 | ||
271 | |||