summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Woolley <rob.woolley@windriver.com>2025-07-24 13:12:08 -0700
committerSteve Sakoman <steve@sakoman.com>2025-07-30 07:47:48 -0700
commit8fa7ff501e27a0ccd9bfc3f3e58a26949cdfc1f0 (patch)
treeb8b535cbd53667c82f9dc103cbe59e814c2dae60
parenta485d82c25b97f4b7b9f656d60849136fbbde40a (diff)
downloadpoky-8fa7ff501e27a0ccd9bfc3f3e58a26949cdfc1f0.tar.gz
ruby: correct fix for CVE-2024-43398
The previous fix for CVE-2024-43398 did not include patches to provide context for the changes it made. This caused an exception at run-time when ruby parsed rexml/parsers/baseparser.rb. This was first observed when using ruby-native to build the sdformat recipe. With these additional backports, the sdformat build proceeds successfully. The REXML library was also tested manually on-target with a script that used REXML::Document.new file to parse an XML file. (From OE-Core rev: 6bf00fde2d4043c6b558733a33041ce5694342d3) Signed-off-by: Rob Woolley <rob.woolley@windriver.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0001.patch212
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0002.patch130
-rw-r--r--meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0003.patch (renamed from meta/recipes-devtools/ruby/ruby/CVE-2024-43398.patch)23
-rw-r--r--meta/recipes-devtools/ruby/ruby_3.1.3.bb4
4 files changed, 355 insertions, 14 deletions
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0001.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0001.patch
new file mode 100644
index 0000000000..6c4869bc8c
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0001.patch
@@ -0,0 +1,212 @@
1From 0496940d5998ccbc50d16fb734993ab50fc60c2d Mon Sep 17 00:00:00 2001
2From: NAITOH Jun <naitoh@gmail.com>
3Date: Mon, 18 Mar 2024 23:30:47 +0900
4Subject: [PATCH] Optimize the parse_attributes method to use `Source#match`
5 to parse XML. (#119)
6
7## Why?
8
9Improve maintainability by consolidating processing into `Source#match`.
10
11## Benchmark
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.891 10.622 16.356 17.403 i/s - 100.000 times in 9.182130s 9.414177s 6.113806s 5.746133s
18 sax 30.335 29.845 49.749 54.877 i/s - 100.000 times in 3.296483s 3.350595s 2.010071s 1.822259s
19 pull 35.514 34.801 61.123 66.908 i/s - 100.000 times in 2.815793s 2.873484s 1.636041s 1.494591s
20 stream 35.141 34.475 52.110 56.836 i/s - 100.000 times in 2.845646s 2.900638s 1.919017s 1.759456s
21
22Comparison:
23 dom
24 after(YJIT): 17.4 i/s
25 before(YJIT): 16.4 i/s - 1.06x slower
26 before: 10.9 i/s - 1.60x slower
27 after: 10.6 i/s - 1.64x slower
28
29 sax
30 after(YJIT): 54.9 i/s
31 before(YJIT): 49.7 i/s - 1.10x slower
32 before: 30.3 i/s - 1.81x slower
33 after: 29.8 i/s - 1.84x slower
34
35 pull
36 after(YJIT): 66.9 i/s
37 before(YJIT): 61.1 i/s - 1.09x slower
38 before: 35.5 i/s - 1.88x slower
39 after: 34.8 i/s - 1.92x slower
40
41 stream
42 after(YJIT): 56.8 i/s
43 before(YJIT): 52.1 i/s - 1.09x slower
44 before: 35.1 i/s - 1.62x slower
45 after: 34.5 i/s - 1.65x slower
46
47```
48
49- YJIT=ON : 1.06x - 1.10x faster
50- YJIT=OFF : 0.97x - 0.98x faster
51
52CVE: CVE-2024-43398
53
54Upstream-Status: Backport [https://github.com/ruby/rexml/commit/0496940d5998ccbc50d16fb734993ab50fc60c2d]
55
56Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
57---
58 lib/rexml/parsers/baseparser.rb | 116 ++++++++++++--------------------
59 test/parse/test_element.rb | 4 +-
60 test/test_core.rb | 20 +++++-
61 3 files changed, 64 insertions(+), 76 deletions(-)
62
63Index: ruby-3.1.3/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
64===================================================================
65--- ruby-3.1.3.orig/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
66+++ ruby-3.1.3/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
67@@ -114,7 +114,7 @@ module REXML
68
69 module Private
70 INSTRUCTION_END = /#{NAME}(\s+.*?)?\?>/um
71- TAG_PATTERN = /((?>#{QNAME_STR}))/um
72+ TAG_PATTERN = /((?>#{QNAME_STR}))\s*/um
73 CLOSE_PATTERN = /(#{QNAME_STR})\s*>/um
74 ATTLISTDECL_END = /\s+#{NAME}(?:#{ATTDEF})*\s*>/um
75 NAME_PATTERN = /\s*#{NAME}/um
76@@ -136,7 +136,6 @@ module REXML
77 self.stream = source
78 @listeners = []
79 @entity_expansion_count = 0
80- @attributes_scanner = StringScanner.new('')
81 end
82
83 def add_listener( listener )
84@@ -635,86 +634,60 @@ module REXML
85 def parse_attributes(prefixes, curr_ns)
86 attributes = {}
87 closed = false
88- match_data = @source.match(/^(.*?)(\/)?>/um, true)
89- if match_data.nil?
90- message = "Start tag isn't ended"
91- raise REXML::ParseException.new(message, @source)
92- end
93+ while true
94+ if @source.match(">", true)
95+ return attributes, closed
96+ elsif @source.match("/>", true)
97+ closed = true
98+ return attributes, closed
99+ elsif match = @source.match(QNAME, true)
100+ name = match[1]
101+ prefix = match[2]
102+ local_part = match[3]
103
104- raw_attributes = match_data[1]
105- closed = !match_data[2].nil?
106- return attributes, closed if raw_attributes.nil?
107- return attributes, closed if raw_attributes.empty?
108-
109- @attributes_scanner.string = raw_attributes
110- scanner = @attributes_scanner
111- until scanner.eos?
112- if scanner.scan(/\s+/)
113- break if scanner.eos?
114- end
115-
116- pos = scanner.pos
117- while true
118- break if scanner.scan(ATTRIBUTE_PATTERN)
119- unless scanner.scan(QNAME)
120- message = "Invalid attribute name: <#{scanner.rest}>"
121- raise REXML::ParseException.new(message, @source)
122- end
123- name = scanner[0]
124- unless scanner.scan(/\s*=\s*/um)
125+ unless @source.match(/\s*=\s*/um, true)
126 message = "Missing attribute equal: <#{name}>"
127 raise REXML::ParseException.new(message, @source)
128 end
129- quote = scanner.scan(/['"]/)
130- unless quote
131- message = "Missing attribute value start quote: <#{name}>"
132- raise REXML::ParseException.new(message, @source)
133- end
134- unless scanner.scan(/.*#{Regexp.escape(quote)}/um)
135- match_data = @source.match(/^(.*?)(\/)?>/um, true)
136- if match_data
137- scanner << "/" if closed
138- scanner << ">"
139- scanner << match_data[1]
140- scanner.pos = pos
141- closed = !match_data[2].nil?
142- next
143+ unless match = @source.match(/(['"])(.*?)\1\s*/um, true)
144+ if match = @source.match(/(['"])/, true)
145+ message =
146+ "Missing attribute value end quote: <#{name}>: <#{match[1]}>"
147+ raise REXML::ParseException.new(message, @source)
148+ else
149+ message = "Missing attribute value start quote: <#{name}>"
150+ raise REXML::ParseException.new(message, @source)
151 end
152- message =
153- "Missing attribute value end quote: <#{name}>: <#{quote}>"
154- raise REXML::ParseException.new(message, @source)
155 end
156- end
157- name = scanner[1]
158- prefix = scanner[2]
159- local_part = scanner[3]
160- # quote = scanner[4]
161- value = scanner[5]
162- if prefix == "xmlns"
163- if local_part == "xml"
164- if value != "http://www.w3.org/XML/1998/namespace"
165- msg = "The 'xml' prefix must not be bound to any other namespace "+
166+ value = match[2]
167+ if prefix == "xmlns"
168+ if local_part == "xml"
169+ if value != "http://www.w3.org/XML/1998/namespace"
170+ msg = "The 'xml' prefix must not be bound to any other namespace "+
171+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
172+ raise REXML::ParseException.new( msg, @source, self )
173+ end
174+ elsif local_part == "xmlns"
175+ msg = "The 'xmlns' prefix must not be declared "+
176 "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
177- raise REXML::ParseException.new( msg, @source, self )
178+ raise REXML::ParseException.new( msg, @source, self)
179 end
180- elsif local_part == "xmlns"
181- msg = "The 'xmlns' prefix must not be declared "+
182- "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
183- raise REXML::ParseException.new( msg, @source, self)
184+ curr_ns << local_part
185+ elsif prefix
186+ prefixes << prefix unless prefix == "xml"
187 end
188- curr_ns << local_part
189- elsif prefix
190- prefixes << prefix unless prefix == "xml"
191- end
192
193- if attributes.has_key?(name)
194- msg = "Duplicate attribute #{name.inspect}"
195- raise REXML::ParseException.new(msg, @source, self)
196- end
197+ if attributes.has_key?(name)
198+ msg = "Duplicate attribute #{name.inspect}"
199+ raise REXML::ParseException.new(msg, @source, self)
200+ end
201
202- attributes[name] = value
203+ attributes[name] = value
204+ else
205+ message = "Invalid attribute name: <#{@source.buffer.split(%r{[/>\s]}).first}>"
206+ raise REXML::ParseException.new(message, @source)
207+ end
208 end
209- return attributes, closed
210 end
211 end
212 end
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0002.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0002.patch
new file mode 100644
index 0000000000..e091aa7e79
--- /dev/null
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0002.patch
@@ -0,0 +1,130 @@
1From cb158582f18cebb3bf7b3f21f230e2fb17d435aa Mon Sep 17 00:00:00 2001
2From: Sutou Kouhei <kou@clear-code.com>
3Date: Sat, 17 Aug 2024 17:39:14 +0900
4Subject: [PATCH] parser: keep the current namespaces instead of stack of Set
5
6It improves namespace resolution performance for deep element.
7
8CVE: CVE-2024-43398
9
10Upstream-Status: Backport [https://github.com/ruby/rexml/commit/cb158582f18cebb3bf7b3f21f230e2fb17d435aa]
11
12Signed-off-by: Rob Woolley <rob.woolley@windriver.com>
13
14---
15 lib/rexml/parsers/baseparser.rb | 45 +++++++++++++++++++++++++--------
16 1 file changed, 35 insertions(+), 10 deletions(-)
17
18Index: ruby-3.1.3/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
19===================================================================
20--- ruby-3.1.3.orig/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
21+++ ruby-3.1.3/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
22@@ -152,7 +152,8 @@ module REXML
23 @tags = []
24 @stack = []
25 @entities = []
26- @nsstack = []
27+ @namespaces = {}
28+ @namespaces_restore_stack = []
29 end
30
31 def position
32@@ -235,7 +236,6 @@ module REXML
33 @source.string = "<!DOCTYPE" + @source.buffer
34 raise REXML::ParseException.new(message, @source)
35 end
36- @nsstack.unshift(curr_ns=Set.new)
37 name = parse_name(base_error_message)
38 if @source.match(/\s*\[/um, true)
39 id = [nil, nil, nil]
40@@ -320,7 +320,7 @@ module REXML
41 val = attdef[4] if val == "#FIXED "
42 pairs[attdef[0]] = val
43 if attdef[0] =~ /^xmlns:(.*)/
44- @nsstack[0] << $1
45+ @namespaces[$1] = val
46 end
47 end
48 end
49@@ -365,7 +365,7 @@ module REXML
50 begin
51 if @source.match("<", true)
52 if @source.match("/", true)
53- @nsstack.shift
54+ @namespaces_restore_stack.pop
55 last_tag = @tags.pop
56 md = @source.match(CLOSE_PATTERN, true)
57 if md and !last_tag
58@@ -411,18 +411,18 @@ module REXML
59 @document_status = :in_element
60 prefixes = Set.new
61 prefixes << md[2] if md[2]
62- @nsstack.unshift(curr_ns=Set.new)
63- attributes, closed = parse_attributes(prefixes, curr_ns)
64+ push_namespaces_restore
65+ attributes, closed = parse_attributes(@prefixes)
66 # Verify that all of the prefixes have been defined
67 for prefix in prefixes
68- unless @nsstack.find{|k| k.member?(prefix)}
69+ unless @namespaces.key?(prefix)
70 raise UndefinedNamespaceException.new(prefix,@source,self)
71 end
72 end
73
74 if closed
75 @closed = tag
76- @nsstack.shift
77+ pop_namespaces_restore
78 else
79 @tags.push( tag )
80 end
81@@ -512,6 +512,31 @@ module REXML
82 end
83
84 private
85+ def add_namespace(prefix, uri)
86+ @namespaces_restore_stack.last[prefix] = @namespaces[prefix]
87+ if uri.nil?
88+ @namespaces.delete(prefix)
89+ else
90+ @namespaces[prefix] = uri
91+ end
92+ end
93+
94+ def push_namespaces_restore
95+ namespaces_restore = {}
96+ @namespaces_restore_stack.push(namespaces_restore)
97+ namespaces_restore
98+ end
99+
100+ def pop_namespaces_restore
101+ namespaces_restore = @namespaces_restore_stack.pop
102+ namespaces_restore.each do |prefix, uri|
103+ if uri.nil?
104+ @namespaces.delete(prefix)
105+ else
106+ @namespaces[prefix] = uri
107+ end
108+ end
109+ end
110
111 def record_entity_expansion
112 @entity_expansion_count += 1
113@@ -631,7 +656,7 @@ module REXML
114 [:processing_instruction, match_data[1], match_data[2]]
115 end
116
117- def parse_attributes(prefixes, curr_ns)
118+ def parse_attributes(prefixes)
119 attributes = {}
120 closed = false
121 while true
122@@ -672,7 +697,7 @@ module REXML
123 "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
124 raise REXML::ParseException.new( msg, @source, self)
125 end
126- curr_ns << local_part
127+ add_namespace(local_part, value)
128 elsif prefix
129 prefixes << prefix unless prefix == "xml"
130 end
diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2024-43398.patch b/meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0003.patch
index 02dc0a20be..2a48aabbc5 100644
--- a/meta/recipes-devtools/ruby/ruby/CVE-2024-43398.patch
+++ b/meta/recipes-devtools/ruby/ruby/CVE-2024-43398-0003.patch
@@ -47,17 +47,17 @@ diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/
47index e32c7f4..154f2ac 100644 47index e32c7f4..154f2ac 100644
48--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb 48--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
49+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb 49+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
50@@ -634,6 +634,7 @@ module REXML 50@@ -658,6 +658,7 @@ module REXML
51 51
52 def parse_attributes(prefixes, curr_ns) 52 def parse_attributes(prefixes)
53 attributes = {} 53 attributes = {}
54+ expanded_names = {} 54+ expanded_names = {}
55 closed = false 55 closed = false
56 match_data = @source.match(/^(.*?)(\/)?>/um, true) 56 while true
57 if match_data.nil? 57 if @source.match(">", true)
58@@ -641,6 +642,20 @@ module REXML 58@@ -707,6 +708,20 @@ module REXML
59 raise REXML::ParseException.new(message, @source) 59 raise REXML::ParseException.new(msg, @source, self)
60 end 60 end
61 61
62+ unless prefix == "xmlns" 62+ unless prefix == "xmlns"
63+ uri = @namespaces[prefix] 63+ uri = @namespaces[prefix]
@@ -73,9 +73,6 @@ index e32c7f4..154f2ac 100644
73+ expanded_names[expanded_name] = prefix 73+ expanded_names[expanded_name] = prefix
74+ end 74+ end
75+ 75+
76 raw_attributes = match_data[1] 76 attributes[name] = value
77 closed = !match_data[2].nil? 77 else
78 return attributes, closed if raw_attributes.nil? 78 message = "Invalid attribute name: <#{@source.buffer.split(%r{[/>\s]}).first}>"
79--
802.40.0
81
diff --git a/meta/recipes-devtools/ruby/ruby_3.1.3.bb b/meta/recipes-devtools/ruby/ruby_3.1.3.bb
index 65d62002ec..19641e5a51 100644
--- a/meta/recipes-devtools/ruby/ruby_3.1.3.bb
+++ b/meta/recipes-devtools/ruby/ruby_3.1.3.bb
@@ -48,7 +48,9 @@ SRC_URI = "http://cache.ruby-lang.org/pub/ruby/${SHRT_VER}/ruby-${PV}.tar.gz \
48 file://CVE-2024-41946.patch \ 48 file://CVE-2024-41946.patch \
49 file://CVE-2025-27220.patch \ 49 file://CVE-2025-27220.patch \
50 file://CVE-2025-27219.patch \ 50 file://CVE-2025-27219.patch \
51 file://CVE-2024-43398.patch \ 51 file://CVE-2024-43398-0001.patch \
52 file://CVE-2024-43398-0002.patch \
53 file://CVE-2024-43398-0003.patch \
52 file://CVE-2025-27221-0001.patch \ 54 file://CVE-2025-27221-0001.patch \
53 file://CVE-2025-27221-0002.patch \ 55 file://CVE-2025-27221-0002.patch \
54 " 56 "