summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/ruby
diff options
context:
space:
mode:
authorDivya Chellam <divya.chellam@windriver.com>2025-01-10 12:27:56 +0000
committerSteve Sakoman <steve@sakoman.com>2025-01-18 06:21:02 -0800
commit61c55b9e300785a953a62eca15f41c61973ce012 (patch)
treec886e09863506ebd693c9a008e5d80b4e3ec0fb4 /meta/recipes-devtools/ruby
parent4f959ce14c7693c6df713bb99d6ef15337d37d98 (diff)
downloadpoky-61c55b9e300785a953a62eca15f41c61973ce012.tar.gz
ruby: fix CVE-2024-49761
REXML is an XML toolkit for Ruby. The REXML gem before 3.3.9 has a ReDoS vulnerability when it parses an XML that has many digits between &# and x...; in a hex numeric character reference (&#x.... This does not happen with Ruby 3.2 or later. Ruby 3.1 is the only affected maintained Ruby. The REXML gem 3.3.9 or later include the patch to fix the vulnerability. CVE-2024-49761-0009.patch is the CVE fix and rest are dependent commits. Reference: https://nvd.nist.gov/vuln/detail/CVE-2024-49761 Upstream-patch: https://github.com/ruby/rexml/commit/810d2285235d5501a0a124f300832e6e9515da3c https://github.com/ruby/rexml/commit/83ca5c4b0f76cf7b307dd1be1dc934e1e8199863 https://github.com/ruby/rexml/commit/51217dbcc64ecc34aa70f126b103bedf07e153fc https://github.com/ruby/rexml/commit/7e4049f6a68c99c4efec2df117057ee080680c9f https://github.com/ruby/rexml/commit/fc6cad570b849692a28f26a963ceb58edc282bbc https://github.com/ruby/rexml/commit/77128555476cb0db798e2912fb3a07d6411dc320 https://github.com/ruby/rexml/commit/370666e314816b57ecd5878e757224c3b6bc93f5 https://github.com/ruby/rexml/commit/a579730f25ec7443796495541ec57c071b91805d https://github.com/ruby/rexml/commit/ce59f2eb1aeb371fe1643414f06618dbe031979f (From OE-Core rev: 5b453400e9dd878b81b1447d14b3f518809de17e) Signed-off-by: Divya Chellam <divya.chellam@windriver.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
Diffstat (limited to 'meta/recipes-devtools/ruby')
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0001.patch391
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0002.patch104
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0003.patch85
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0004.patch71
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0005.patch51
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0006.patch79
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0007.patch561
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0008.patch107
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0009.patch46
-rw-r--r--meta/recipes-devtools/ruby/ruby_3.1.3.bb9
10 files changed, 1504 insertions, 0 deletions
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0001.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0001.patch
new file mode 100644
index 0000000000..3caf389923
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0001.patch
@@ -0,0 +1,391 @@
1From 810d2285235d5501a0a124f300832e6e9515da3c Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Wed, 17 Jan 2024 15:32:57 +0900
4Subject: [PATCH] Use string scanner with baseparser (#105)
5
6Using StringScanner reduces the string copying process and speeds up the
7process.
8
9And I removed unnecessary methods.
10
11https://github.com/ruby/rexml/actions/runs/7549990000/job/20554906140?pr=105
12
13```
14ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux]
15Calculating -------------------------------------
16 rexml 3.2.6 master 3.2.6(YJIT) master(YJIT)
17 dom 4.868 5.077 8.137 8.303 i/s - 100.000 times in 20.540529s 19.696590s 12.288900s 12.043666s
18 sax 13.597 13.953 19.206 20.948 i/s - 100.000 times in 7.354343s 7.167142s 5.206745s 4.773765s
19 pull 15.641 16.918 22.266 25.378 i/s - 100.000 times in 6.393424s 5.910955s 4.491201s 3.940471s
20 stream 14.339 15.844 19.810 22.206 i/s - 100.000 times in 6.973856s 6.311350s 5.047957s 4.503244s
21
22Comparison:
23 dom
24 master(YJIT): 8.3 i/s
25 3.2.6(YJIT): 8.1 i/s - 1.02x slower
26 master: 5.1 i/s - 1.64x slower
27 rexml 3.2.6: 4.9 i/s - 1.71x slower
28
29 sax
30 master(YJIT): 20.9 i/s
31 3.2.6(YJIT): 19.2 i/s - 1.09x slower
32 master: 14.0 i/s - 1.50x slower
33 rexml 3.2.6: 13.6 i/s - 1.54x slower
34
35 pull
36 master(YJIT): 25.4 i/s
37 3.2.6(YJIT): 22.3 i/s - 1.14x slower
38 master: 16.9 i/s - 1.50x slower
39 rexml 3.2.6: 15.6 i/s - 1.62x slower
40
41 stream
42 master(YJIT): 22.2 i/s
43 3.2.6(YJIT): 19.8 i/s - 1.12x slower
44 master: 15.8 i/s - 1.40x slower
45 rexml 3.2.6: 14.3 i/s - 1.55x slower
46```
47
48- YJIT=ON : 1.02x - 1.14x faster
49- YJIT=OFF : 1.02x - 1.10x faster
50
51---------
52
53Co-authored-by: Sutou Kouhei <kou@cozmixng.org>
54
55CVE: CVE-2024-49761
56
57Upstream-Status: Backport [https://github.com/ruby/rexml/commit/810d2285235d5501a0a124f300832e6e9515da3c]
58
59Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
60---
61 .../lib/rexml/parsers/baseparser.rb | 21 ++-
62 .bundle/gems/rexml-3.2.5/lib/rexml/source.rb | 149 ++++++------------
63 2 files changed, 56 insertions(+), 114 deletions(-)
64
65diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
66index 305b120..65bad26 100644
67--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
68+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
69@@ -96,7 +96,7 @@ module REXML
70 ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
71 PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
72 GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
73- ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
74+ ENTITYDECL = /\s*(?:#{GEDECL})|\s*(?:#{PEDECL})/um
75
76 NOTATIONDECL_START = /\A\s*<!NOTATION/um
77 EXTERNAL_ID_PUBLIC = /\A\s*PUBLIC\s+#{PUBIDLITERAL}\s+#{SYSTEMLITERAL}\s*/um
78@@ -259,7 +259,7 @@ module REXML
79 else
80 @document_status = :after_doctype
81 if @source.encoding == "UTF-8"
82- @source.buffer.force_encoding(::Encoding::UTF_8)
83+ @source.buffer_encoding = ::Encoding::UTF_8
84 end
85 end
86 end
87@@ -274,8 +274,7 @@ module REXML
88 return [ :elementdecl, @source.match( ELEMENTDECL_PATTERN, true )[1] ]
89
90 when ENTITY_START
91- match = @source.match( ENTITYDECL, true ).to_a.compact
92- match[0] = :entitydecl
93+ match = [:entitydecl, *@source.match( ENTITYDECL, true ).captures.compact]
94 ref = false
95 if match[1] == '%'
96 ref = true
97@@ -392,6 +391,7 @@ module REXML
98 unless md
99 raise REXML::ParseException.new("malformed XML: missing tag start", @source)
100 end
101+ tag = md[1]
102 @document_status = :in_element
103 prefixes = Set.new
104 prefixes << md[2] if md[2]
105@@ -405,23 +405,20 @@ module REXML
106 end
107
108 if closed
109- @closed = md[1]
110+ @closed = tag
111 @nsstack.shift
112 else
113- @tags.push( md[1] )
114+ @tags.push( tag )
115 end
116- return [ :start_element, md[1], attributes ]
117+ return [ :start_element, tag, attributes ]
118 end
119 else
120 md = @source.match( TEXT_PATTERN, true )
121+ text = md[1]
122 if md[0].length == 0
123 @source.match( /(\s+)/, true )
124 end
125- #STDERR.puts "GOT #{md[1].inspect}" unless md[0].length == 0
126- #return [ :text, "" ] if md[0].length == 0
127- # unnormalized = Text::unnormalize( md[1], self )
128- # return PullEvent.new( :text, md[1], unnormalized )
129- return [ :text, md[1] ]
130+ return [ :text, text ]
131 end
132 rescue REXML::UndefinedNamespaceException
133 raise
134diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
135index 90b370b..71b08f9 100644
136--- a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
137+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
138@@ -30,8 +30,6 @@ module REXML
139 # objects and provides consumption of text
140 class Source
141 include Encoding
142- # The current buffer (what we're going to read next)
143- attr_reader :buffer
144 # The line number of the last consumed text
145 attr_reader :line
146 attr_reader :encoding
147@@ -41,7 +39,8 @@ module REXML
148 # @param encoding if non-null, sets the encoding of the source to this
149 # value, overriding all encoding detection
150 def initialize(arg, encoding=nil)
151- @orig = @buffer = arg
152+ @orig = arg
153+ @scanner = StringScanner.new(@orig)
154 if encoding
155 self.encoding = encoding
156 else
157@@ -50,6 +49,14 @@ module REXML
158 @line = 0
159 end
160
161+ # The current buffer (what we're going to read next)
162+ def buffer
163+ @scanner.rest
164+ end
165+
166+ def buffer_encoding=(encoding)
167+ @scanner.string.force_encoding(encoding)
168+ end
169
170 # Inherited from Encoding
171 # Overridden to support optimized en/decoding
172@@ -58,98 +65,57 @@ module REXML
173 encoding_updated
174 end
175
176- # Scans the source for a given pattern. Note, that this is not your
177- # usual scan() method. For one thing, the pattern argument has some
178- # requirements; for another, the source can be consumed. You can easily
179- # confuse this method. Originally, the patterns were easier
180- # to construct and this method more robust, because this method
181- # generated search regexps on the fly; however, this was
182- # computationally expensive and slowed down the entire REXML package
183- # considerably, since this is by far the most commonly called method.
184- # @param pattern must be a Regexp, and must be in the form of
185- # /^\s*(#{your pattern, with no groups})(.*)/. The first group
186- # will be returned; the second group is used if the consume flag is
187- # set.
188- # @param consume if true, the pattern returned will be consumed, leaving
189- # everything after it in the Source.
190- # @return the pattern, if found, or nil if the Source is empty or the
191- # pattern is not found.
192- def scan(pattern, cons=false)
193- return nil if @buffer.nil?
194- rv = @buffer.scan(pattern)
195- @buffer = $' if cons and rv.size>0
196- rv
197- end
198-
199 def read
200 end
201
202- def consume( pattern )
203- @buffer = $' if pattern.match( @buffer )
204- end
205-
206- def match_to( char, pattern )
207- return pattern.match(@buffer)
208- end
209-
210- def match_to_consume( char, pattern )
211- md = pattern.match(@buffer)
212- @buffer = $'
213- return md
214- end
215-
216 def match(pattern, cons=false)
217- md = pattern.match(@buffer)
218- @buffer = $' if cons and md
219- return md
220+ if cons
221+ @scanner.scan(pattern).nil? ? nil : @scanner
222+ else
223+ @scanner.check(pattern).nil? ? nil : @scanner
224+ end
225 end
226
227 # @return true if the Source is exhausted
228 def empty?
229- @buffer == ""
230- end
231-
232- def position
233- @orig.index( @buffer )
234+ @scanner.eos?
235 end
236
237 # @return the current line in the source
238 def current_line
239 lines = @orig.split
240- res = lines.grep @buffer[0..30]
241+ res = lines.grep @scanner.rest[0..30]
242 res = res[-1] if res.kind_of? Array
243 lines.index( res ) if res
244 end
245
246 private
247+
248 def detect_encoding
249- buffer_encoding = @buffer.encoding
250+ scanner_encoding = @scanner.rest.encoding
251 detected_encoding = "UTF-8"
252 begin
253- @buffer.force_encoding("ASCII-8BIT")
254- if @buffer[0, 2] == "\xfe\xff"
255- @buffer[0, 2] = ""
256+ @scanner.string.force_encoding("ASCII-8BIT")
257+ if @scanner.scan(/\xfe\xff/n)
258 detected_encoding = "UTF-16BE"
259- elsif @buffer[0, 2] == "\xff\xfe"
260- @buffer[0, 2] = ""
261+ elsif @scanner.scan(/\xff\xfe/n)
262 detected_encoding = "UTF-16LE"
263- elsif @buffer[0, 3] == "\xef\xbb\xbf"
264- @buffer[0, 3] = ""
265+ elsif @scanner.scan(/\xef\xbb\xbf/n)
266 detected_encoding = "UTF-8"
267 end
268 ensure
269- @buffer.force_encoding(buffer_encoding)
270+ @scanner.string.force_encoding(scanner_encoding)
271 end
272 self.encoding = detected_encoding
273 end
274
275 def encoding_updated
276 if @encoding != 'UTF-8'
277- @buffer = decode(@buffer)
278+ @scanner.string = decode(@scanner.rest)
279 @to_utf = true
280 else
281 @to_utf = false
282- @buffer.force_encoding ::Encoding::UTF_8
283+ @scanner.string.force_encoding(::Encoding::UTF_8)
284 end
285 end
286 end
287@@ -172,7 +138,7 @@ module REXML
288 end
289
290 if !@to_utf and
291- @buffer.respond_to?(:force_encoding) and
292+ @orig.respond_to?(:force_encoding) and
293 @source.respond_to?(:external_encoding) and
294 @source.external_encoding != ::Encoding::UTF_8
295 @force_utf8 = true
296@@ -181,65 +147,44 @@ module REXML
297 end
298 end
299
300- def scan(pattern, cons=false)
301- rv = super
302- # You'll notice that this next section is very similar to the same
303- # section in match(), but just a liiittle different. This is
304- # because it is a touch faster to do it this way with scan()
305- # than the way match() does it; enough faster to warrant duplicating
306- # some code
307- if rv.size == 0
308- until @buffer =~ pattern or @source.nil?
309- begin
310- @buffer << readline
311- rescue Iconv::IllegalSequence
312- raise
313- rescue
314- @source = nil
315- end
316- end
317- rv = super
318- end
319- rv.taint if RUBY_VERSION < '2.7'
320- rv
321- end
322-
323 def read
324 begin
325- @buffer << readline
326+ # NOTE: `@scanner << readline` does not free memory, so when parsing huge XML in JRuby's DOM,
327+ # out-of-memory error `Java::JavaLang::OutOfMemoryError: Java heap space` occurs.
328+ # `@scanner.string = @scanner.rest + readline` frees memory that is already consumed
329+ # and avoids this problem.
330+ @scanner.string = @scanner.rest + readline
331 rescue Exception, NameError
332 @source = nil
333 end
334 end
335
336- def consume( pattern )
337- match( pattern, true )
338- end
339-
340 def match( pattern, cons=false )
341- rv = pattern.match(@buffer)
342- @buffer = $' if cons and rv
343- while !rv and @source
344+ if cons
345+ md = @scanner.scan(pattern)
346+ else
347+ md = @scanner.check(pattern)
348+ end
349+ while md.nil? and @source
350 begin
351- @buffer << readline
352- rv = pattern.match(@buffer)
353- @buffer = $' if cons and rv
354+ @scanner << readline
355+ if cons
356+ md = @scanner.scan(pattern)
357+ else
358+ md = @scanner.check(pattern)
359+ end
360 rescue
361 @source = nil
362 end
363 end
364- rv.taint if RUBY_VERSION < '2.7'
365- rv
366+
367+ md.nil? ? nil : @scanner
368 end
369
370 def empty?
371 super and ( @source.nil? || @source.eof? )
372 end
373
374- def position
375- @er_source.pos rescue 0
376- end
377-
378 # @return the current line in the source
379 def current_line
380 begin
381@@ -290,7 +235,7 @@ module REXML
382 @source.set_encoding(@encoding, @encoding)
383 end
384 @line_break = encode(">")
385- @pending_buffer, @buffer = @buffer, ""
386+ @pending_buffer, @scanner.string = @scanner.rest, ""
387 @pending_buffer.force_encoding(@encoding)
388 super
389 end
390--
3912.40.0
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0002.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0002.patch
new file mode 100644
index 0000000000..35e90b632b
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0002.patch
@@ -0,0 +1,104 @@
1From 83ca5c4b0f76cf7b307dd1be1dc934e1e8199863 Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Sun, 21 Jan 2024 06:11:42 +0900
4Subject: [PATCH] Reduce calls to `Source#buffer`(`StringScanner#rest`) (#106)
5
6Reduce calls to `Source#buffer`(`StringScanner#rest`)
7
8## Why
9`Source#buffer` calling `StringScanner#rest`.
10`StringScanner#rest` is slow.
11Reduce calls to `Source#buffer`.
12
13## Benchmark
14
15```
16RUBYLIB= BUNDLER_ORIG_RUBYLIB= /Users/naitoh/.rbenv/versions/3.3.0/bin/ruby -v -S benchmark-driver /Users/naitoh/ghq/github.com/naitoh/rexml/benchmark/parse.yaml
17ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22]
18Calculating -------------------------------------
19 before after before(YJIT) after(YJIT)
20 dom 10.639 10.985 16.213 16.221 i/s - 100.000 times in 9.399033s 9.103461s 6.167962s 6.164794s
21 sax 28.357 29.440 42.900 44.375 i/s - 100.000 times in 3.526479s 3.396688s 2.331024s 2.253511s
22 pull 32.852 34.210 48.976 51.273 i/s - 100.000 times in 3.043965s 2.923140s 2.041816s 1.950344s
23 stream 30.821 31.908 43.953 44.697 i/s - 100.000 times in 3.244539s 3.134020s 2.275172s 2.237310s
24
25Comparison:
26 dom
27 after(YJIT): 16.2 i/s
28 before(YJIT): 16.2 i/s - 1.00x slower
29 after: 11.0 i/s - 1.48x slower
30 before: 10.6 i/s - 1.52x slower
31
32 sax
33 after(YJIT): 44.4 i/s
34 before(YJIT): 42.9 i/s - 1.03x slower
35 after: 29.4 i/s - 1.51x slower
36 before: 28.4 i/s - 1.56x slower
37
38 pull
39 after(YJIT): 51.3 i/s
40 before(YJIT): 49.0 i/s - 1.05x slower
41 after: 34.2 i/s - 1.50x slower
42 before: 32.9 i/s - 1.56x slower
43
44 stream
45 after(YJIT): 44.7 i/s
46 before(YJIT): 44.0 i/s - 1.02x slower
47 after: 31.9 i/s - 1.40x slower
48 before: 30.8 i/s - 1.45x slower
49
50```
51
52- YJIT=ON : 1.00x - 1.05x faster
53- YJIT=OFF : 1.03x - 1.04x faster
54
55CVE: CVE-2024-49761
56
57Upstream-Status: Backport [https://github.com/ruby/rexml/commit/83ca5c4b0f76cf7b307dd1be1dc934e1e8199863]
58
59Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
60---
61 .../rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 14 +++++++++-----
62 1 file changed, 9 insertions(+), 5 deletions(-)
63
64diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
65index 65bad26..7126a12 100644
66--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
67+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
68@@ -348,9 +348,13 @@ module REXML
69 @source.match(/\A\s*/um, true)
70 end
71 begin
72- @source.read if @source.buffer.size<2
73- if @source.buffer[0] == ?<
74- if @source.buffer[1] == ?/
75+ next_data = @source.buffer
76+ if next_data.size < 2
77+ @source.read
78+ next_data = @source.buffer
79+ end
80+ if next_data[0] == ?<
81+ if next_data[1] == ?/
82 @nsstack.shift
83 last_tag = @tags.pop
84 md = @source.match( CLOSE_MATCH, true )
85@@ -364,7 +368,7 @@ module REXML
86 raise REXML::ParseException.new(message, @source)
87 end
88 return [ :end_element, last_tag ]
89- elsif @source.buffer[1] == ?!
90+ elsif next_data[1] == ?!
91 md = @source.match(/\A(\s*[^>]*>)/um)
92 #STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
93 raise REXML::ParseException.new("Malformed node", @source) unless md
94@@ -383,7 +387,7 @@ module REXML
95 end
96 raise REXML::ParseException.new( "Declarations can only occur "+
97 "in the doctype declaration.", @source)
98- elsif @source.buffer[1] == ??
99+ elsif next_data[1] == ??
100 return process_instruction
101 else
102 # Get the next tag
103--
1042.40.0
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0003.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0003.patch
new file mode 100644
index 0000000000..9d3515e7a6
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0003.patch
@@ -0,0 +1,85 @@
1From 51217dbcc64ecc34aa70f126b103bedf07e153fc Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Wed, 31 Jan 2024 16:35:55 +0900
4Subject: [PATCH] Reduce calls to StringScanner.new() (#108)
5
6## Why
7
8`StringScanner.new()` instances can be reused within parse_attributes,
9reducing initialization costs.
10
11## Benchmark
12
13```
14RUBYLIB= BUNDLER_ORIG_RUBYLIB= /Users/naitoh/.rbenv/versions/3.3.0/bin/ruby -v -S benchmark-driver /Users/naitoh/ghq/github.com/naitoh/rexml/benchmark/parse.yaml
15ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22]
16Calculating -------------------------------------
17 before after before(YJIT) after(YJIT)
18 dom 11.018 11.207 17.059 16.660 i/s - 100.000 times in 9.075992s 8.923280s 5.861969s 6.002555s
19 sax 29.843 30.821 45.518 47.505 i/s - 100.000 times in 3.350909s 3.244524s 2.196940s 2.105037s
20 pull 34.480 35.937 52.816 57.098 i/s - 100.000 times in 2.900205s 2.782632s 1.893370s 1.751378s
21 stream 32.430 33.516 46.247 48.412 i/s - 100.000 times in 3.083536s 2.983607s 2.162288s 2.065584s
22
23Comparison:
24 dom
25 before(YJIT): 17.1 i/s
26 after(YJIT): 16.7 i/s - 1.02x slower
27 after: 11.2 i/s - 1.52x slower
28 before: 11.0 i/s - 1.55x slower
29
30 sax
31 after(YJIT): 47.5 i/s
32 before(YJIT): 45.5 i/s - 1.04x slower
33 after: 30.8 i/s - 1.54x slower
34 before: 29.8 i/s - 1.59x slower
35
36 pull
37 after(YJIT): 57.1 i/s
38 before(YJIT): 52.8 i/s - 1.08x slower
39 after: 35.9 i/s - 1.59x slower
40 before: 34.5 i/s - 1.66x slower
41
42 stream
43 after(YJIT): 48.4 i/s
44 before(YJIT): 46.2 i/s - 1.05x slower
45 after: 33.5 i/s - 1.44x slower
46 before: 32.4 i/s - 1.49x slower
47
48```
49
50- YJIT=ON : 1.02x - 1.08x faster
51- YJIT=OFF : 1.01x - 1.04x faster
52
53CVE: CVE-2024-49761
54
55Upstream-Status: Backport [https://github.com/ruby/rexml/commit/51217dbcc64ecc34aa70f126b103bedf07e153fc]
56
57Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
58---
59 .bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 4 +++-
60 1 file changed, 3 insertions(+), 1 deletion(-)
61
62diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
63index 7126a12..b66b0ed 100644
64--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
65+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
66@@ -115,6 +115,7 @@ module REXML
67 def initialize( source )
68 self.stream = source
69 @listeners = []
70+ @attributes_scanner = StringScanner.new('')
71 end
72
73 def add_listener( listener )
74@@ -601,7 +602,8 @@ module REXML
75 return attributes, closed if raw_attributes.nil?
76 return attributes, closed if raw_attributes.empty?
77
78- scanner = StringScanner.new(raw_attributes)
79+ @attributes_scanner.string = raw_attributes
80+ scanner = @attributes_scanner
81 until scanner.eos?
82 if scanner.scan(/\s+/)
83 break if scanner.eos?
84--
852.40.0
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0004.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0004.patch
new file mode 100644
index 0000000000..f2bbbd76f7
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0004.patch
@@ -0,0 +1,71 @@
1From 7e4049f6a68c99c4efec2df117057ee080680c9f Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Wed, 31 Jan 2024 17:17:51 +0900
4Subject: [PATCH] Change loop in parse_attributes to `while true`. (#109)
5
6loop is slower than `while true`.
7
8```
9RUBYLIB= BUNDLER_ORIG_RUBYLIB= /Users/naitoh/.rbenv/versions/3.3.0/bin/ruby -v -S benchmark-driver /Users/naitoh/ghq/github.com/naitoh/rexml/benchmark/parse.yaml
10ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22]
11Calculating -------------------------------------
12 before after before(YJIT) after(YJIT)
13 dom 11.186 11.304 17.395 17.450 i/s - 100.000 times in 8.940144s 8.846590s 5.748718s 5.730793s
14 sax 30.811 31.629 47.352 48.040 i/s - 100.000 times in 3.245601s 3.161619s 2.111854s 2.081594s
15 pull 35.793 36.621 56.924 57.313 i/s - 100.000 times in 2.793829s 2.730693s 1.756732s 1.744812s
16 stream 33.157 34.757 46.792 50.536 i/s - 100.000 times in 3.015940s 2.877088s 2.137106s 1.978787s
17
18Comparison:
19 dom
20 after(YJIT): 17.4 i/s
21 before(YJIT): 17.4 i/s - 1.00x slower
22 after: 11.3 i/s - 1.54x slower
23 before: 11.2 i/s - 1.56x slower
24
25 sax
26 after(YJIT): 48.0 i/s
27 before(YJIT): 47.4 i/s - 1.01x slower
28 after: 31.6 i/s - 1.52x slower
29 before: 30.8 i/s - 1.56x slower
30
31 pull
32 after(YJIT): 57.3 i/s
33 before(YJIT): 56.9 i/s - 1.01x slower
34 after: 36.6 i/s - 1.57x slower
35 before: 35.8 i/s - 1.60x slower
36
37 stream
38 after(YJIT): 50.5 i/s
39 before(YJIT): 46.8 i/s - 1.08x slower
40 after: 34.8 i/s - 1.45x slower
41 before: 33.2 i/s - 1.52x slower
42
43```
44
45- YJIT=ON : 1.00x - 1.08x faster
46- YJIT=OFF : 1.01x - 1.04x faster
47
48CVE: CVE-2024-49761
49
50Upstream-Status: Backport [https://github.com/ruby/rexml/commit/7e4049f6a68c99c4efec2df117057ee080680c9f]
51
52Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
53---
54 .bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 2 +-
55 1 file changed, 1 insertion(+), 1 deletion(-)
56
57diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
58index b66b0ed..3fe5c29 100644
59--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
60+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
61@@ -610,7 +610,7 @@ module REXML
62 end
63
64 pos = scanner.pos
65- loop do
66+ while true
67 break if scanner.scan(ATTRIBUTE_PATTERN)
68 unless scanner.scan(QNAME)
69 message = "Invalid attribute name: <#{scanner.rest}>"
70--
712.40.0
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0005.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0005.patch
new file mode 100644
index 0000000000..304270092e
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0005.patch
@@ -0,0 +1,51 @@
1From fc6cad570b849692a28f26a963ceb58edc282bbc Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Fri, 16 Feb 2024 04:51:16 +0900
4Subject: [PATCH] Remove unnecessary checks in baseparser (#112)
5
6https://github.com/ruby/rexml/blob/444c9ce7449d3c5a75ae50087555ec73ae1963a8/lib/rexml/parsers/baseparser.rb#L352-L425
7```
8 next_data = @source.buffer
9 if next_data.size < 2
10 @source.read
11 next_data = @source.buffer
12 end
13 if next_data[0] == ?<
14 :
15 (omit)
16 :
17 else # next_data is a string of one or more characters other than '<'.
18 md = @source.match( TEXT_PATTERN, true ) # TEXT_PATTERN = /\A([^<]*)/um
19 text = md[1]
20 if md[0].length == 0 # md[0].length is greater than or equal to 1.
21 @source.match( /(\s+)/, true )
22 end
23```
24This is an unnecessary check because md[0].length is greater than or
25equal to 1.
26
27CVE: CVE-2024-49761
28
29Upstream-Status: Backport [https://github.com/ruby/rexml/commit/fc6cad570b849692a28f26a963ceb58edc282bbc]
30
31Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
32---
33 .bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 3 ---
34 1 file changed, 3 deletions(-)
35
36diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
37index 3fe5c29..595669c 100644
38--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
39+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
40@@ -420,9 +420,6 @@ module REXML
41 else
42 md = @source.match( TEXT_PATTERN, true )
43 text = md[1]
44- if md[0].length == 0
45- @source.match( /(\s+)/, true )
46- end
47 return [ :text, text ]
48 end
49 rescue REXML::UndefinedNamespaceException
50--
512.40.0
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0006.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0006.patch
new file mode 100644
index 0000000000..7d3f547089
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0006.patch
@@ -0,0 +1,79 @@
1From 77128555476cb0db798e2912fb3a07d6411dc320 Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Sun, 21 Jan 2024 20:02:00 +0900
4Subject: [PATCH] Use `@scanner << readline` instead of `@scanner.string =
5 @scanner.rest + readline` (#107)
6
7JRuby's `StringScanner#<<` and `StringScanner#scan` OutOfMemoryError has
8been resolved in strscan gem 3.0.9.
9
10https://github.com/ruby/strscan/issues/83
11
12```
13RUBYLIB= BUNDLER_ORIG_RUBYLIB= /Users/naitoh/.rbenv/versions/3.3.0/bin/ruby -v -S benchmark-driver /Users/naitoh/ghq/github.com/naitoh/rexml/benchmark/parse.yaml
14ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22]
15Calculating -------------------------------------
16 before after before(YJIT) after(YJIT)
17 dom 10.958 11.044 16.615 16.783 i/s - 100.000 times in 9.126104s 9.055023s 6.018799s 5.958437s
18 sax 29.624 29.609 44.390 45.370 i/s - 100.000 times in 3.375641s 3.377372s 2.252774s 2.204080s
19 pull 33.868 34.695 51.173 53.492 i/s - 100.000 times in 2.952679s 2.882229s 1.954138s 1.869422s
20 stream 31.719 32.351 43.604 45.403 i/s - 100.000 times in 3.152713s 3.091052s 2.293356s 2.202514s
21
22Comparison:
23 dom
24 after(YJIT): 16.8 i/s
25 before(YJIT): 16.6 i/s - 1.01x slower
26 after: 11.0 i/s - 1.52x slower
27 before: 11.0 i/s - 1.53x slower
28
29 sax
30 after(YJIT): 45.4 i/s
31 before(YJIT): 44.4 i/s - 1.02x slower
32 before: 29.6 i/s - 1.53x slower
33 after: 29.6 i/s - 1.53x slower
34
35 pull
36 after(YJIT): 53.5 i/s
37 before(YJIT): 51.2 i/s - 1.05x slower
38 after: 34.7 i/s - 1.54x slower
39 before: 33.9 i/s - 1.58x slower
40
41 stream
42 after(YJIT): 45.4 i/s
43 before(YJIT): 43.6 i/s - 1.04x slower
44 after: 32.4 i/s - 1.40x slower
45 before: 31.7 i/s - 1.43x slower
46
47```
48
49- YJIT=ON : 1.01x - 1.05x faster
50- YJIT=OFF : 1.00x - 1.02x faster
51
52CVE: CVE-2024-49761
53
54Upstream-Status: Backport [https://github.com/ruby/rexml/commit/77128555476cb0db798e2912fb3a07d6411dc320]
55
56Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
57---
58 .bundle/gems/rexml-3.2.5/lib/rexml/source.rb | 6 +-----
59 1 file changed, 1 insertion(+), 5 deletions(-)
60
61diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
62index 71b08f9..db78a12 100644
63--- a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
64+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
65@@ -149,11 +149,7 @@ module REXML
66
67 def read
68 begin
69- # NOTE: `@scanner << readline` does not free memory, so when parsing huge XML in JRuby's DOM,
70- # out-of-memory error `Java::JavaLang::OutOfMemoryError: Java heap space` occurs.
71- # `@scanner.string = @scanner.rest + readline` frees memory that is already consumed
72- # and avoids this problem.
73- @scanner.string = @scanner.rest + readline
74+ @scanner << readline
75 rescue Exception, NameError
76 @source = nil
77 end
78--
792.40.0
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0007.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0007.patch
new file mode 100644
index 0000000000..4ba60823ab
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0007.patch
@@ -0,0 +1,561 @@
1From 370666e314816b57ecd5878e757224c3b6bc93f5 Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Tue, 27 Feb 2024 09:48:35 +0900
4Subject: [PATCH] Use more StringScanner based API to parse XML (#114)
5
6## Why?
7
8Improve maintainability by optimizing the process so that the parsing
9process proceeds using StringScanner#scan.
10
11## Changed
12- Change `REXML::Parsers::BaseParser` from `frozen_string_literal:
13false` to `frozen_string_literal: true`.
14- Added `Source#string=` method for error message output.
15- Added TestParseDocumentTypeDeclaration#test_no_name test case.
16- Of the `intSubset` of DOCTYPE, "<!" added consideration for processing
17`Comments` that begin with "<!".
18
19## [Benchmark]
20
21```
22RUBYLIB= BUNDLER_ORIG_RUBYLIB= /Users/naitoh/.rbenv/versions/3.3.0/bin/ruby -v -S benchmark-driver /Users/naitoh/ghq/github.com/naitoh/rexml/benchmark/parse.yaml
23ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22]
24Calculating -------------------------------------
25 before after before(YJIT) after(YJIT)
26 dom 11.240 10.569 17.173 18.219 i/s - 100.000 times in 8.896882s 9.461267s 5.823007s 5.488884s
27 sax 31.812 30.716 48.383 52.532 i/s - 100.000 times in 3.143500s 3.255655s 2.066861s 1.903600s
28 pull 36.855 36.354 56.718 61.443 i/s - 100.000 times in 2.713300s 2.750693s 1.763099s 1.627523s
29 stream 34.176 34.758 49.801 54.622 i/s - 100.000 times in 2.925991s 2.877065s 2.008003s 1.830779s
30
31Comparison:
32 dom
33 after(YJIT): 18.2 i/s
34 before(YJIT): 17.2 i/s - 1.06x slower
35 before: 11.2 i/s - 1.62x slower
36 after: 10.6 i/s - 1.72x slower
37
38 sax
39 after(YJIT): 52.5 i/s
40 before(YJIT): 48.4 i/s - 1.09x slower
41 before: 31.8 i/s - 1.65x slower
42 after: 30.7 i/s - 1.71x slower
43
44 pull
45 after(YJIT): 61.4 i/s
46 before(YJIT): 56.7 i/s - 1.08x slower
47 before: 36.9 i/s - 1.67x slower
48 after: 36.4 i/s - 1.69x slower
49
50 stream
51 after(YJIT): 54.6 i/s
52 before(YJIT): 49.8 i/s - 1.10x slower
53 after: 34.8 i/s - 1.57x slower
54 before: 34.2 i/s - 1.60x slower
55
56```
57
58- YJIT=ON : 1.06x - 1.10x faster
59- YJIT=OFF : 0.94x - 1.01x faster
60
61---------
62
63Co-authored-by: Sutou Kouhei <kou@clear-code.com>
64
65CVE: CVE-2024-49761
66
67Upstream-Status: Backport [https://github.com/ruby/rexml/commit/370666e314816b57ecd5878e757224c3b6bc93f5]
68
69Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
70---
71 .../lib/rexml/parsers/baseparser.rb | 325 +++++++++---------
72 .bundle/gems/rexml-3.2.5/lib/rexml/source.rb | 31 +-
73 2 files changed, 188 insertions(+), 168 deletions(-)
74
75diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
76index 595669c..bc59bcd 100644
77--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
78+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
79@@ -1,4 +1,4 @@
80-# frozen_string_literal: false
81+# frozen_string_literal: true
82 require_relative '../parseexception'
83 require_relative '../undefinednamespaceexception'
84 require_relative '../source'
85@@ -112,6 +112,19 @@ module REXML
86 "apos" => [/&apos;/, "&apos;", "'", /'/]
87 }
88
89+ module Private
90+ INSTRUCTION_END = /#{NAME}(\s+.*?)?\?>/um
91+ TAG_PATTERN = /((?>#{QNAME_STR}))/um
92+ CLOSE_PATTERN = /(#{QNAME_STR})\s*>/um
93+ ATTLISTDECL_END = /\s+#{NAME}(?:#{ATTDEF})*\s*>/um
94+ NAME_PATTERN = /\s*#{NAME}/um
95+ GEDECL_PATTERN = "\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
96+ PEDECL_PATTERN = "\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
97+ ENTITYDECL_PATTERN = /(?:#{GEDECL_PATTERN})|(?:#{PEDECL_PATTERN})/um
98+ end
99+ private_constant :Private
100+ include Private
101+
102 def initialize( source )
103 self.stream = source
104 @listeners = []
105@@ -198,183 +211,172 @@ module REXML
106 #STDERR.puts @source.encoding
107 #STDERR.puts "BUFFER = #{@source.buffer.inspect}"
108 if @document_status == nil
109- word = @source.match( /\A((?:\s+)|(?:<[^>]*>))/um )
110- word = word[1] unless word.nil?
111- #STDERR.puts "WORD = #{word.inspect}"
112- case word
113- when COMMENT_START
114- return [ :comment, @source.match( COMMENT_PATTERN, true )[1] ]
115- when XMLDECL_START
116- #STDERR.puts "XMLDECL"
117- results = @source.match( XMLDECL_PATTERN, true )[1]
118- version = VERSION.match( results )
119- version = version[1] unless version.nil?
120- encoding = ENCODING.match(results)
121- encoding = encoding[1] unless encoding.nil?
122- if need_source_encoding_update?(encoding)
123- @source.encoding = encoding
124- end
125- if encoding.nil? and /\AUTF-16(?:BE|LE)\z/i =~ @source.encoding
126- encoding = "UTF-16"
127- end
128- standalone = STANDALONE.match(results)
129- standalone = standalone[1] unless standalone.nil?
130- return [ :xmldecl, version, encoding, standalone ]
131- when INSTRUCTION_START
132+ if @source.match("<?", true)
133 return process_instruction
134- when DOCTYPE_START
135- base_error_message = "Malformed DOCTYPE"
136- @source.match(DOCTYPE_START, true)
137- @nsstack.unshift(curr_ns=Set.new)
138- name = parse_name(base_error_message)
139- if @source.match(/\A\s*\[/um, true)
140- id = [nil, nil, nil]
141- @document_status = :in_doctype
142- elsif @source.match(/\A\s*>/um, true)
143- id = [nil, nil, nil]
144- @document_status = :after_doctype
145- else
146- id = parse_id(base_error_message,
147- accept_external_id: true,
148- accept_public_id: false)
149- if id[0] == "SYSTEM"
150- # For backward compatibility
151- id[1], id[2] = id[2], nil
152+ elsif @source.match("<!", true)
153+ if @source.match("--", true)
154+ return [ :comment, @source.match(/(.*?)-->/um, true)[1] ]
155+ elsif @source.match("DOCTYPE", true)
156+ base_error_message = "Malformed DOCTYPE"
157+ unless @source.match(/\s+/um, true)
158+ if @source.match(">")
159+ message = "#{base_error_message}: name is missing"
160+ else
161+ message = "#{base_error_message}: invalid name"
162+ end
163+ @source.string = "<!DOCTYPE" + @source.buffer
164+ raise REXML::ParseException.new(message, @source)
165 end
166- if @source.match(/\A\s*\[/um, true)
167+ @nsstack.unshift(curr_ns=Set.new)
168+ name = parse_name(base_error_message)
169+ if @source.match(/\s*\[/um, true)
170+ id = [nil, nil, nil]
171 @document_status = :in_doctype
172- elsif @source.match(/\A\s*>/um, true)
173+ elsif @source.match(/\s*>/um, true)
174+ id = [nil, nil, nil]
175 @document_status = :after_doctype
176 else
177- message = "#{base_error_message}: garbage after external ID"
178- raise REXML::ParseException.new(message, @source)
179+ id = parse_id(base_error_message,
180+ accept_external_id: true,
181+ accept_public_id: false)
182+ if id[0] == "SYSTEM"
183+ # For backward compatibility
184+ id[1], id[2] = id[2], nil
185+ end
186+ if @source.match(/\s*\[/um, true)
187+ @document_status = :in_doctype
188+ elsif @source.match(/\s*>/um, true)
189+ @document_status = :after_doctype
190+ else
191+ message = "#{base_error_message}: garbage after external ID"
192+ raise REXML::ParseException.new(message, @source)
193+ end
194 end
195- end
196- args = [:start_doctype, name, *id]
197- if @document_status == :after_doctype
198- @source.match(/\A\s*/um, true)
199- @stack << [ :end_doctype ]
200- end
201- return args
202- when /\A\s+/
203- else
204- @document_status = :after_doctype
205- if @source.encoding == "UTF-8"
206- @source.buffer_encoding = ::Encoding::UTF_8
207+ args = [:start_doctype, name, *id]
208+ if @document_status == :after_doctype
209+ @source.match(/\s*/um, true)
210+ @stack << [ :end_doctype ]
211+ end
212+ return args
213+ else
214+ message = "Invalid XML"
215+ raise REXML::ParseException.new(message, @source)
216 end
217 end
218 end
219 if @document_status == :in_doctype
220- md = @source.match(/\A\s*(.*?>)/um)
221- case md[1]
222- when SYSTEMENTITY
223- match = @source.match( SYSTEMENTITY, true )[1]
224- return [ :externalentity, match ]
225-
226- when ELEMENTDECL_START
227- return [ :elementdecl, @source.match( ELEMENTDECL_PATTERN, true )[1] ]
228-
229- when ENTITY_START
230- match = [:entitydecl, *@source.match( ENTITYDECL, true ).captures.compact]
231- ref = false
232- if match[1] == '%'
233- ref = true
234- match.delete_at 1
235- end
236- # Now we have to sort out what kind of entity reference this is
237- if match[2] == 'SYSTEM'
238- # External reference
239- match[3] = match[3][1..-2] # PUBID
240- match.delete_at(4) if match.size > 4 # Chop out NDATA decl
241- # match is [ :entity, name, SYSTEM, pubid(, ndata)? ]
242- elsif match[2] == 'PUBLIC'
243- # External reference
244- match[3] = match[3][1..-2] # PUBID
245- match[4] = match[4][1..-2] # HREF
246- match.delete_at(5) if match.size > 5 # Chop out NDATA decl
247- # match is [ :entity, name, PUBLIC, pubid, href(, ndata)? ]
248- else
249- match[2] = match[2][1..-2]
250- match.pop if match.size == 4
251- # match is [ :entity, name, value ]
252- end
253- match << '%' if ref
254- return match
255- when ATTLISTDECL_START
256- md = @source.match( ATTLISTDECL_PATTERN, true )
257- raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil?
258- element = md[1]
259- contents = md[0]
260-
261- pairs = {}
262- values = md[0].scan( ATTDEF_RE )
263- values.each do |attdef|
264- unless attdef[3] == "#IMPLIED"
265- attdef.compact!
266- val = attdef[3]
267- val = attdef[4] if val == "#FIXED "
268- pairs[attdef[0]] = val
269- if attdef[0] =~ /^xmlns:(.*)/
270- @nsstack[0] << $1
271- end
272+ @source.match(/\s*/um, true) # skip spaces
273+ if @source.match("<!", true)
274+ if @source.match("ELEMENT", true)
275+ md = @source.match(/(.*?)>/um, true)
276+ raise REXML::ParseException.new( "Bad ELEMENT declaration!", @source ) if md.nil?
277+ return [ :elementdecl, "<!ELEMENT" + md[1] ]
278+ elsif @source.match("ENTITY", true)
279+ match = [:entitydecl, *@source.match(ENTITYDECL_PATTERN, true).captures.compact]
280+ ref = false
281+ if match[1] == '%'
282+ ref = true
283+ match.delete_at 1
284 end
285- end
286- return [ :attlistdecl, element, pairs, contents ]
287- when NOTATIONDECL_START
288- base_error_message = "Malformed notation declaration"
289- unless @source.match(/\A\s*<!NOTATION\s+/um, true)
290- if @source.match(/\A\s*<!NOTATION\s*>/um)
291- message = "#{base_error_message}: name is missing"
292+ # Now we have to sort out what kind of entity reference this is
293+ if match[2] == 'SYSTEM'
294+ # External reference
295+ match[3] = match[3][1..-2] # PUBID
296+ match.delete_at(4) if match.size > 4 # Chop out NDATA decl
297+ # match is [ :entity, name, SYSTEM, pubid(, ndata)? ]
298+ elsif match[2] == 'PUBLIC'
299+ # External reference
300+ match[3] = match[3][1..-2] # PUBID
301+ match[4] = match[4][1..-2] # HREF
302+ match.delete_at(5) if match.size > 5 # Chop out NDATA decl
303+ # match is [ :entity, name, PUBLIC, pubid, href(, ndata)? ]
304 else
305- message = "#{base_error_message}: invalid declaration name"
306+ match[2] = match[2][1..-2]
307+ match.pop if match.size == 4
308+ # match is [ :entity, name, value ]
309 end
310- raise REXML::ParseException.new(message, @source)
311- end
312- name = parse_name(base_error_message)
313- id = parse_id(base_error_message,
314- accept_external_id: true,
315- accept_public_id: true)
316- unless @source.match(/\A\s*>/um, true)
317- message = "#{base_error_message}: garbage before end >"
318- raise REXML::ParseException.new(message, @source)
319+ match << '%' if ref
320+ return match
321+ elsif @source.match("ATTLIST", true)
322+ md = @source.match(ATTLISTDECL_END, true)
323+ raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil?
324+ element = md[1]
325+ contents = md[0]
326+
327+ pairs = {}
328+ values = md[0].scan( ATTDEF_RE )
329+ values.each do |attdef|
330+ unless attdef[3] == "#IMPLIED"
331+ attdef.compact!
332+ val = attdef[3]
333+ val = attdef[4] if val == "#FIXED "
334+ pairs[attdef[0]] = val
335+ if attdef[0] =~ /^xmlns:(.*)/
336+ @nsstack[0] << $1
337+ end
338+ end
339+ end
340+ return [ :attlistdecl, element, pairs, contents ]
341+ elsif @source.match("NOTATION", true)
342+ base_error_message = "Malformed notation declaration"
343+ unless @source.match(/\s+/um, true)
344+ if @source.match(">")
345+ message = "#{base_error_message}: name is missing"
346+ else
347+ message = "#{base_error_message}: invalid name"
348+ end
349+ @source.string = " <!NOTATION" + @source.buffer
350+ raise REXML::ParseException.new(message, @source)
351+ end
352+ name = parse_name(base_error_message)
353+ id = parse_id(base_error_message,
354+ accept_external_id: true,
355+ accept_public_id: true)
356+ unless @source.match(/\s*>/um, true)
357+ message = "#{base_error_message}: garbage before end >"
358+ raise REXML::ParseException.new(message, @source)
359+ end
360+ return [:notationdecl, name, *id]
361+ elsif md = @source.match(/--(.*?)-->/um, true)
362+ case md[1]
363+ when /--/, /-\z/
364+ raise REXML::ParseException.new("Malformed comment", @source)
365+ end
366+ return [ :comment, md[1] ] if md
367 end
368- return [:notationdecl, name, *id]
369- when DOCTYPE_END
370+ elsif match = @source.match(/(%.*?;)\s*/um, true)
371+ return [ :externalentity, match[1] ]
372+ elsif @source.match(/\]\s*>/um, true)
373 @document_status = :after_doctype
374- @source.match( DOCTYPE_END, true )
375 return [ :end_doctype ]
376 end
377 end
378 if @document_status == :after_doctype
379- @source.match(/\A\s*/um, true)
380+ @source.match(/\s*/um, true)
381 end
382 begin
383- next_data = @source.buffer
384- if next_data.size < 2
385- @source.read
386- next_data = @source.buffer
387- end
388- if next_data[0] == ?<
389- if next_data[1] == ?/
390+ if @source.match("<", true)
391+ if @source.match("/", true)
392 @nsstack.shift
393 last_tag = @tags.pop
394- md = @source.match( CLOSE_MATCH, true )
395+ md = @source.match(CLOSE_PATTERN, true)
396 if md and !last_tag
397 message = "Unexpected top-level end tag (got '#{md[1]}')"
398 raise REXML::ParseException.new(message, @source)
399 end
400 if md.nil? or last_tag != md[1]
401 message = "Missing end tag for '#{last_tag}'"
402- message << " (got '#{md[1]}')" if md
403+ message += " (got '#{md[1]}')" if md
404+ @source.string = "</" + @source.buffer if md.nil?
405 raise REXML::ParseException.new(message, @source)
406 end
407 return [ :end_element, last_tag ]
408- elsif next_data[1] == ?!
409- md = @source.match(/\A(\s*[^>]*>)/um)
410+ elsif @source.match("!", true)
411+ md = @source.match(/([^>]*>)/um)
412 #STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
413 raise REXML::ParseException.new("Malformed node", @source) unless md
414- if md[0][2] == ?-
415- md = @source.match( COMMENT_PATTERN, true )
416+ if md[0][0] == ?-
417+ md = @source.match(/--(.*?)-->/um, true)
418
419 case md[1]
420 when /--/, /-\z/
421@@ -383,17 +385,18 @@ module REXML
422
423 return [ :comment, md[1] ] if md
424 else
425- md = @source.match( CDATA_PATTERN, true )
426+ md = @source.match(/\[CDATA\[(.*?)\]\]>/um, true)
427 return [ :cdata, md[1] ] if md
428 end
429 raise REXML::ParseException.new( "Declarations can only occur "+
430 "in the doctype declaration.", @source)
431- elsif next_data[1] == ??
432+ elsif @source.match("?", true)
433 return process_instruction
434 else
435 # Get the next tag
436- md = @source.match(TAG_MATCH, true)
437+ md = @source.match(TAG_PATTERN, true)
438 unless md
439+ @source.string = "<" + @source.buffer
440 raise REXML::ParseException.new("malformed XML: missing tag start", @source)
441 end
442 tag = md[1]
443@@ -418,7 +421,7 @@ module REXML
444 return [ :start_element, tag, attributes ]
445 end
446 else
447- md = @source.match( TEXT_PATTERN, true )
448+ md = @source.match(/([^<]*)/um, true)
449 text = md[1]
450 return [ :text, text ]
451 end
452@@ -462,8 +465,7 @@ module REXML
453
454 # Unescapes all possible entities
455 def unnormalize( string, entities=nil, filter=nil )
456- rv = string.clone
457- rv.gsub!( /\r\n?/, "\n" )
458+ rv = string.gsub( /\r\n?/, "\n" )
459 matches = rv.scan( REFERENCE_RE )
460 return rv if matches.size == 0
461 rv.gsub!( /&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/ ) {
462@@ -498,9 +500,9 @@ module REXML
463 end
464
465 def parse_name(base_error_message)
466- md = @source.match(/\A\s*#{NAME}/um, true)
467+ md = @source.match(NAME_PATTERN, true)
468 unless md
469- if @source.match(/\A\s*\S/um)
470+ if @source.match(/\s*\S/um)
471 message = "#{base_error_message}: invalid name"
472 else
473 message = "#{base_error_message}: name is missing"
474@@ -577,11 +579,28 @@ module REXML
475 end
476
477 def process_instruction
478- match_data = @source.match(INSTRUCTION_PATTERN, true)
479+ match_data = @source.match(INSTRUCTION_END, true)
480 unless match_data
481 message = "Invalid processing instruction node"
482+ @source.string = "<?" + @source.buffer
483 raise REXML::ParseException.new(message, @source)
484 end
485+ if @document_status.nil? and match_data[1] == "xml"
486+ content = match_data[2]
487+ version = VERSION.match(content)
488+ version = version[1] unless version.nil?
489+ encoding = ENCODING.match(content)
490+ encoding = encoding[1] unless encoding.nil?
491+ if need_source_encoding_update?(encoding)
492+ @source.encoding = encoding
493+ end
494+ if encoding.nil? and /\AUTF-16(?:BE|LE)\z/i =~ @source.encoding
495+ encoding = "UTF-16"
496+ end
497+ standalone = STANDALONE.match(content)
498+ standalone = standalone[1] unless standalone.nil?
499+ return [ :xmldecl, version, encoding, standalone ]
500+ end
501 [:processing_instruction, match_data[1], match_data[2]]
502 end
503
504diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
505index db78a12..4111d1d 100644
506--- a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
507+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
508@@ -76,6 +76,10 @@ module REXML
509 end
510 end
511
512+ def string=(string)
513+ @scanner.string = string
514+ end
515+
516 # @return true if the Source is exhausted
517 def empty?
518 @scanner.eos?
519@@ -150,28 +154,25 @@ module REXML
520 def read
521 begin
522 @scanner << readline
523+ true
524 rescue Exception, NameError
525 @source = nil
526+ false
527 end
528 end
529
530 def match( pattern, cons=false )
531- if cons
532- md = @scanner.scan(pattern)
533- else
534- md = @scanner.check(pattern)
535- end
536- while md.nil? and @source
537- begin
538- @scanner << readline
539- if cons
540- md = @scanner.scan(pattern)
541- else
542- md = @scanner.check(pattern)
543- end
544- rescue
545- @source = nil
546+ read if @scanner.eos? && @source
547+ while true
548+ if cons
549+ md = @scanner.scan(pattern)
550+ else
551+ md = @scanner.check(pattern)
552 end
553+ break if md
554+ return nil if pattern.is_a?(String) && pattern.bytesize <= @scanner.rest_size
555+ return nil if @source.nil?
556+ return nil unless read
557 end
558
559 md.nil? ? nil : @scanner
560--
5612.40.0
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0008.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0008.patch
new file mode 100644
index 0000000000..959f8a0794
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0008.patch
@@ -0,0 +1,107 @@
1From a579730f25ec7443796495541ec57c071b91805d Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Tue, 25 Jun 2024 09:07:11 +0900
4Subject: [PATCH] Optimize BaseParser#unnormalize method (#158)
5
6## Benchmark
7```
8RUBYLIB= BUNDLER_ORIG_RUBYLIB= /Users/naitoh/.rbenv/versions/3.3.3/bin/ruby -v -S benchmark-driver /Users/naitoh/ghq/github.com/naitoh/rexml/benchmark/parse.yaml
9ruby 3.3.3 (2024-06-12 revision f1c7b6f435) [arm64-darwin22]
10Calculating -------------------------------------
11 before after before(YJIT) after(YJIT)
12 dom 17.704 18.106 34.215 33.806 i/s - 100.000 times in 5.648398s 5.523110s 2.922698s 2.958036s
13 sax 25.664 25.302 48.429 48.602 i/s - 100.000 times in 3.896488s 3.952289s 2.064859s 2.057537s
14 pull 28.966 29.215 61.710 62.068 i/s - 100.000 times in 3.452275s 3.422901s 1.620480s 1.611129s
15 stream 28.291 28.426 53.860 55.548 i/s - 100.000 times in 3.534716s 3.517884s 1.856667s 1.800247s
16
17Comparison:
18 dom
19 before(YJIT): 34.2 i/s
20 after(YJIT): 33.8 i/s - 1.01x slower
21 after: 18.1 i/s - 1.89x slower
22 before: 17.7 i/s - 1.93x slower
23
24 sax
25 after(YJIT): 48.6 i/s
26 before(YJIT): 48.4 i/s - 1.00x slower
27 before: 25.7 i/s - 1.89x slower
28 after: 25.3 i/s - 1.92x slower
29
30 pull
31 after(YJIT): 62.1 i/s
32 before(YJIT): 61.7 i/s - 1.01x slower
33 after: 29.2 i/s - 2.12x slower
34 before: 29.0 i/s - 2.14x slower
35
36 stream
37 after(YJIT): 55.5 i/s
38 before(YJIT): 53.9 i/s - 1.03x slower
39 after: 28.4 i/s - 1.95x slower
40 before: 28.3 i/s - 1.96x slower
41
42```
43
44- YJIT=ON : 1.00x - 1.03x faster
45- YJIT=OFF : 0.98x - 1.02x faster
46
47CVE: CVE-2024-49761
48
49Upstream-Status: Backport [https://github.com/ruby/rexml/commit/a579730f25ec7443796495541ec57c071b91805d]
50
51Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
52---
53 .../rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 15 +++++++++++----
54 1 file changed, 11 insertions(+), 4 deletions(-)
55
56diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
57index bc59bcd..9983d51 100644
58--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
59+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
60@@ -121,6 +121,13 @@ module REXML
61 GEDECL_PATTERN = "\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
62 PEDECL_PATTERN = "\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
63 ENTITYDECL_PATTERN = /(?:#{GEDECL_PATTERN})|(?:#{PEDECL_PATTERN})/um
64+ CARRIAGE_RETURN_NEWLINE_PATTERN = /\r\n?/
65+ CHARACTER_REFERENCES = /&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/
66+ DEFAULT_ENTITIES_PATTERNS = {}
67+ default_entities = ['gt', 'lt', 'quot', 'apos', 'amp']
68+ default_entities.each do |term|
69+ DEFAULT_ENTITIES_PATTERNS[term] = /&#{term};/
70+ end
71 end
72 private_constant :Private
73 include Private
74@@ -465,10 +472,10 @@ module REXML
75
76 # Unescapes all possible entities
77 def unnormalize( string, entities=nil, filter=nil )
78- rv = string.gsub( /\r\n?/, "\n" )
79+ rv = string.gsub( Private::CARRIAGE_RETURN_NEWLINE_PATTERN, "\n" )
80 matches = rv.scan( REFERENCE_RE )
81 return rv if matches.size == 0
82- rv.gsub!( /&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/ ) {
83+ rv.gsub!( Private::CHARACTER_REFERENCES ) {
84 m=$1
85 m = "0#{m}" if m[0] == ?x
86 [Integer(m)].pack('U*')
87@@ -479,7 +486,7 @@ module REXML
88 unless filter and filter.include?(entity_reference)
89 entity_value = entity( entity_reference, entities )
90 if entity_value
91- re = /&#{entity_reference};/
92+ re = Private::DEFAULT_ENTITIES_PATTERNS[entity_reference] || /&#{entity_reference};/
93 rv.gsub!( re, entity_value )
94 else
95 er = DEFAULT_ENTITIES[entity_reference]
96@@ -487,7 +494,7 @@ module REXML
97 end
98 end
99 end
100- rv.gsub!( /&amp;/, '&' )
101+ rv.gsub!( Private::DEFAULT_ENTITIES_PATTERNS['amp'], '&' )
102 end
103 rv
104 end
105--
1062.40.0
107
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0009.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0009.patch
new file mode 100644
index 0000000000..58a0894b09
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-49761-0009.patch
@@ -0,0 +1,46 @@
1From ce59f2eb1aeb371fe1643414f06618dbe031979f Mon Sep 17 00:00:00 2001
2From: Sutou Kouhei <kou@clear-code.com>
3Date: Thu, 24 Oct 2024 14:45:31 +0900
4Subject: [PATCH] parser: fix a bug that &#0x...; is accepted as a character
5 reference
6
7CVE: CVE-2024-49761
8
9Upstream-Status: Backport [https://github.com/ruby/rexml/commit/ce59f2eb1aeb371fe1643414f06618dbe031979f]
10
11Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
12---
13 .../gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 10 +++++++---
14 1 file changed, 7 insertions(+), 3 deletions(-)
15
16diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
17index 9983d51..661f0e2 100644
18--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
19+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
20@@ -122,7 +122,7 @@ module REXML
21 PEDECL_PATTERN = "\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
22 ENTITYDECL_PATTERN = /(?:#{GEDECL_PATTERN})|(?:#{PEDECL_PATTERN})/um
23 CARRIAGE_RETURN_NEWLINE_PATTERN = /\r\n?/
24- CHARACTER_REFERENCES = /&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/
25+ CHARACTER_REFERENCES = /&#((?:\d+)|(?:x[a-fA-F0-9]+));/
26 DEFAULT_ENTITIES_PATTERNS = {}
27 default_entities = ['gt', 'lt', 'quot', 'apos', 'amp']
28 default_entities.each do |term|
29@@ -477,8 +477,12 @@ module REXML
30 return rv if matches.size == 0
31 rv.gsub!( Private::CHARACTER_REFERENCES ) {
32 m=$1
33- m = "0#{m}" if m[0] == ?x
34- [Integer(m)].pack('U*')
35+ if m.start_with?("x")
36+ code_point = Integer(m[1..-1], 16)
37+ else
38+ code_point = Integer(m, 10)
39+ end
40+ [code_point].pack('U*')
41 }
42 matches.collect!{|x|x[0]}.compact!
43 if matches.size > 0
44--
452.40.0
46
diff --git a/meta/recipes-devtools/ruby/ruby_3.1.3.bb b/meta/recipes-devtools/ruby/ruby_3.1.3.bb
index a00df5d191..eec7e4684c 100644
--- a/meta/recipes-devtools/ruby/ruby_3.1.3.bb
+++ b/meta/recipes-devtools/ruby/ruby_3.1.3.bb
@@ -36,6 +36,15 @@ SRC_URI = "http://cache.ruby-lang.org/pub/ruby/${SHRT_VER}/ruby-${PV}.tar.gz \
36 file://CVE-2024-27281.patch \ 36 file://CVE-2024-27281.patch \
37 file://CVE-2024-27280.patch \ 37 file://CVE-2024-27280.patch \
38 file://CVE-2024-27282.patch \ 38 file://CVE-2024-27282.patch \
39 file://CVE-2024-49761-0001.patch \
40 file://CVE-2024-49761-0002.patch \
41 file://CVE-2024-49761-0003.patch \
42 file://CVE-2024-49761-0004.patch \
43 file://CVE-2024-49761-0005.patch \
44 file://CVE-2024-49761-0006.patch \
45 file://CVE-2024-49761-0007.patch \
46 file://CVE-2024-49761-0008.patch \
47 file://CVE-2024-49761-0009.patch \
39 " 48 "
40UPSTREAM_CHECK_URI = "https://www.ruby-lang.org/en/downloads/" 49UPSTREAM_CHECK_URI = "https://www.ruby-lang.org/en/downloads/"
41 50