ruby: fix CVE-2024-41123

REXML is an XML toolkit for Ruby. The REXML gem before 3.3.2 has some DoS
vulnerabilities when it parses an XML that has many specific characters
such as whitespace character, `>]` and `]>`. The REXML gem 3.3.3 or later
include the patches to fix these vulnerabilities.

Reference:
https://nvd.nist.gov/vuln/detail/CVE-2024-41123

Upstream-patches:
2c39c91a65
4444a04ece
ebc3e85bfa
6cac15d458
e2546e6eca

(From OE-Core rev: 6b2a2e689a69deef6098f6c266542234e46fb24b)

Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
This commit is contained in:
Divya Chellam 2025-11-20 15:07:22 +05:30 committed by Steve Sakoman
parent 7c4bd642e4
commit 6639c7b295
6 changed files with 415 additions and 0 deletions

View File

@ -0,0 +1,44 @@
From 2c39c91a65d69357cfbc35dd8079b3606d86bb70 Mon Sep 17 00:00:00 2001
From: Watson <watson1978@gmail.com>
Date: Fri, 19 Jul 2024 17:15:15 +0900
Subject: [PATCH] Fix method scope in test in order to invoke the tests
properly and fix exception message (#182)
This PR includes following two fixes.
1. The `test_empty` and `test_linear_performance_gt` were defined as
private method. Seems that test-unit runner does not invoke private
methods even if the methods have `test_` prefix.
2. When parse malformed entity declaration, the exception might have the
message about `NoMethodError`. The proper exception message will be
contained by this fix.
CVE: CVE-2024-41123
Upstream-Status: Backport [https://github.com/ruby/rexml/commit/2c39c91a65d69357cfbc35dd8079b3606d86bb70]
Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
---
.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --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
index 4864ba1..451fbf8 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
@@ -308,7 +308,11 @@ module REXML
raise REXML::ParseException.new( "Bad ELEMENT declaration!", @source ) if md.nil?
return [ :elementdecl, "<!ELEMENT" + md[1] ]
elsif @source.match("ENTITY", true)
- match = [:entitydecl, *@source.match(Private::ENTITYDECL_PATTERN, true, term: Private::ENTITY_TERM).captures.compact]
+ match_data = @source.match(Private::ENTITYDECL_PATTERN, true, term: Private::ENTITY_TERM)
+ unless match_data
+ raise REXML::ParseException.new("Malformed entity declaration", @source)
+ end
+ match = [:entitydecl, *match_data.captures.compact]
ref = false
if match[1] == '%'
ref = true
--
2.40.0

View File

@ -0,0 +1,37 @@
From 4444a04ece4c02a7bd51e8c75623f22dc12d882b Mon Sep 17 00:00:00 2001
From: Sutou Kouhei <kou@clear-code.com>
Date: Sun, 2 Jun 2024 16:59:16 +0900
Subject: [PATCH] Add missing encode for custom term
CVE: CVE-2024-41123
Upstream-Status: Backport [https://github.com/ruby/rexml/commit/4444a04ece4c02a7bd51e8c75623f22dc12d882b]
Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
---
.bundle/gems/rexml-3.2.5/lib/rexml/source.rb | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
index 08a035c..7be430a 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
@@ -160,6 +160,7 @@ module REXML
end
def read(term = nil)
+ term = encode(term) if term
begin
@scanner << readline(term)
true
@@ -171,6 +172,7 @@ module REXML
def read_until(term)
pattern = Regexp.union(term)
+ term = encode(term)
data = []
begin
until str = @scanner.scan_until(pattern)
--
2.40.0

View File

@ -0,0 +1,55 @@
From ebc3e85bfa2796fb4922c1932760bec8390ff87c Mon Sep 17 00:00:00 2001
From: NAITOH Jun <naitoh@gmail.com>
Date: Mon, 8 Jul 2024 05:54:06 +0900
Subject: [PATCH] Add position check for XML declaration (#162)
XML declaration must be the first item.
https://www.w3.org/TR/2006/REC-xml11-20060816/#document
```
[1] document ::= ( prolog element Misc* ) - ( Char* RestrictedChar Char* )
```
https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-prolog
```
[22] prolog ::= XMLDecl Misc* (doctypedecl Misc*)?
```
https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-XMLDecl
```
[23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
```
See: https://github.com/ruby/rexml/pull/161#discussion_r1666118193
CVE: CVE-2024-41123
Upstream-Status: Backport [https://github.com/ruby/rexml/commit/ebc3e85bfa2796fb4922c1932760bec8390ff87c]
Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
---
.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --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
index 451fbf8..71fce99 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
@@ -670,7 +670,10 @@ module REXML
@source.position = start_position
raise REXML::ParseException.new(message, @source)
end
- if @document_status.nil? and match_data[1] == "xml"
+ if match_data[1] == "xml"
+ if @document_status
+ raise ParseException.new("Malformed XML: XML declaration is not at the start", @source)
+ end
content = match_data[2]
version = VERSION.match(content)
version = version[1] unless version.nil?
--
2.40.0

View File

@ -0,0 +1,163 @@
From 6cac15d45864c8d70904baa5cbfcc97181000960 Mon Sep 17 00:00:00 2001
From: tomoya ishida <tomoyapenguin@gmail.com>
Date: Thu, 1 Aug 2024 09:21:19 +0900
Subject: [PATCH] Fix source.match performance without specifying term string
(#186)
Performance problem of `source.match(regexp)` was recently fixed by
specifying terminator string. However, I think maintaining appropriate
terminator string for a regexp is hard.
I propose solving this performance issue by increasing bytes to read in
each iteration.
CVE: CVE-2024-41123
Upstream-Status: Backport [https://github.com/ruby/rexml/commit/6cac15d45864c8d70904baa5cbfcc97181000960]
Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
---
.../lib/rexml/parsers/baseparser.rb | 22 ++++++------------
.bundle/gems/rexml-3.2.5/lib/rexml/source.rb | 23 +++++++++++++++----
2 files changed, 25 insertions(+), 20 deletions(-)
diff --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
index 71fce99..c1a22b8 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
@@ -124,14 +124,6 @@ module REXML
}
module Private
- # Terminal requires two or more letters.
- INSTRUCTION_TERM = "?>"
- COMMENT_TERM = "-->"
- CDATA_TERM = "]]>"
- DOCTYPE_TERM = "]>"
- # Read to the end of DOCTYPE because there is no proper ENTITY termination
- ENTITY_TERM = DOCTYPE_TERM
-
INSTRUCTION_END = /#{NAME}(\s+.*?)?\?>/um
TAG_PATTERN = /((?>#{QNAME_STR}))\s*/um
CLOSE_PATTERN = /(#{QNAME_STR})\s*>/um
@@ -244,7 +236,7 @@ module REXML
return process_instruction(start_position)
elsif @source.match("<!", true)
if @source.match("--", true)
- md = @source.match(/(.*?)-->/um, true, term: Private::COMMENT_TERM)
+ md = @source.match(/(.*?)-->/um, true)
if md.nil?
raise REXML::ParseException.new("Unclosed comment", @source)
end
@@ -308,7 +300,7 @@ module REXML
raise REXML::ParseException.new( "Bad ELEMENT declaration!", @source ) if md.nil?
return [ :elementdecl, "<!ELEMENT" + md[1] ]
elsif @source.match("ENTITY", true)
- match_data = @source.match(Private::ENTITYDECL_PATTERN, true, term: Private::ENTITY_TERM)
+ match_data = @source.match(Private::ENTITYDECL_PATTERN, true)
unless match_data
raise REXML::ParseException.new("Malformed entity declaration", @source)
end
@@ -377,14 +369,14 @@ module REXML
raise REXML::ParseException.new(message, @source)
end
return [:notationdecl, name, *id]
- elsif md = @source.match(/--(.*?)-->/um, true, term: Private::COMMENT_TERM)
+ elsif md = @source.match(/--(.*?)-->/um, true)
case md[1]
when /--/, /-\z/
raise REXML::ParseException.new("Malformed comment", @source)
end
return [ :comment, md[1] ] if md
end
- elsif match = @source.match(/(%.*?;)\s*/um, true, term: Private::DOCTYPE_TERM)
+ elsif match = @source.match(/(%.*?;)\s*/um, true)
return [ :externalentity, match[1] ]
elsif @source.match(/\]\s*>/um, true)
@document_status = :after_doctype
@@ -417,7 +409,7 @@ module REXML
#STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
raise REXML::ParseException.new("Malformed node", @source) unless md
if md[0][0] == ?-
- md = @source.match(/--(.*?)-->/um, true, term: Private::COMMENT_TERM)
+ md = @source.match(/--(.*?)-->/um, true)
case md[1]
when /--/, /-\z/
@@ -426,7 +418,7 @@ module REXML
return [ :comment, md[1] ] if md
else
- md = @source.match(/\[CDATA\[(.*?)\]\]>/um, true, term: Private::CDATA_TERM)
+ md = @source.match(/\[CDATA\[(.*?)\]\]>/um, true)
return [ :cdata, md[1] ] if md
end
raise REXML::ParseException.new( "Declarations can only occur "+
@@ -664,7 +656,7 @@ module REXML
end
def process_instruction(start_position)
- match_data = @source.match(Private::INSTRUCTION_END, true, term: Private::INSTRUCTION_TERM)
+ match_data = @source.match(Private::INSTRUCTION_END, true)
unless match_data
message = "Invalid processing instruction node"
@source.position = start_position
diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
index 7be430a..7c05cb5 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
@@ -72,7 +72,7 @@ module REXML
@scanner.scan_until(Regexp.union(term)) or @scanner.rest
end
- def match(pattern, cons=false, term: nil)
+ def match(pattern, cons=false)
if cons
@scanner.scan(pattern).nil? ? nil : @scanner
else
@@ -159,10 +159,20 @@ module REXML
end
end
- def read(term = nil)
+ def read(term = nil, min_bytes = 1)
term = encode(term) if term
begin
- @scanner << readline(term)
+ str = readline(term)
+ @scanner << str
+ read_bytes = str.bytesize
+ begin
+ while read_bytes < min_bytes
+ str = readline(term)
+ @scanner << str
+ read_bytes += str.bytesize
+ end
+ rescue IOError
+ end
true
rescue Exception, NameError
@source = nil
@@ -186,7 +196,9 @@ module REXML
end
end
- def match( pattern, cons=false, term: nil )
+ def match( pattern, cons=false )
+ # To avoid performance issue, we need to increase bytes to read per scan
+ min_bytes = 1
read if @scanner.eos? && @source
while true
if cons
@@ -197,7 +209,8 @@ module REXML
break if md
return nil if pattern.is_a?(String) && pattern.bytesize <= @scanner.rest_size
return nil if @source.nil?
- return nil unless read(term)
+ return nil unless read(nil, min_bytes)
+ min_bytes *= 2
end
md.nil? ? nil : @scanner
--
2.40.0

View File

@ -0,0 +1,111 @@
From e2546e6ecade16b04c9ee528e5be8509fe16c2d6 Mon Sep 17 00:00:00 2001
From: Sutou Kouhei <kou@clear-code.com>
Date: Thu, 1 Aug 2024 11:23:43 +0900
Subject: [PATCH] parse pi: improve invalid case detection
CVE: CVE-2024-41123
Upstream-Status: Backport [https://github.com/ruby/rexml/commit/e2546e6ecade16b04c9ee528e5be8509fe16c2d6]
Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
---
.../lib/rexml/parsers/baseparser.rb | 35 +++++++++++--------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --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
index c1a22b8..0ece9b5 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
@@ -124,11 +124,10 @@ module REXML
}
module Private
- INSTRUCTION_END = /#{NAME}(\s+.*?)?\?>/um
TAG_PATTERN = /((?>#{QNAME_STR}))\s*/um
CLOSE_PATTERN = /(#{QNAME_STR})\s*>/um
ATTLISTDECL_END = /\s+#{NAME}(?:#{ATTDEF})*\s*>/um
- NAME_PATTERN = /\s*#{NAME}/um
+ NAME_PATTERN = /#{NAME}/um
GEDECL_PATTERN = "\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
PEDECL_PATTERN = "\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
ENTITYDECL_PATTERN = /(?:#{GEDECL_PATTERN})|(?:#{PEDECL_PATTERN})/um
@@ -233,7 +232,7 @@ module REXML
if @document_status == nil
start_position = @source.position
if @source.match("<?", true)
- return process_instruction(start_position)
+ return process_instruction
elsif @source.match("<!", true)
if @source.match("--", true)
md = @source.match(/(.*?)-->/um, true)
@@ -424,7 +423,7 @@ module REXML
raise REXML::ParseException.new( "Declarations can only occur "+
"in the doctype declaration.", @source)
elsif @source.match("?", true)
- return process_instruction(start_position)
+ return process_instruction
else
# Get the next tag
md = @source.match(TAG_PATTERN, true)
@@ -579,14 +578,14 @@ module REXML
def parse_name(base_error_message)
md = @source.match(NAME_PATTERN, true)
unless md
- if @source.match(/\s*\S/um)
+ if @source.match(/\S/um)
message = "#{base_error_message}: invalid name"
else
message = "#{base_error_message}: name is missing"
end
raise REXML::ParseException.new(message, @source)
end
- md[1]
+ md[0]
end
def parse_id(base_error_message,
@@ -655,18 +654,24 @@ module REXML
end
end
- def process_instruction(start_position)
- match_data = @source.match(Private::INSTRUCTION_END, true)
- unless match_data
- message = "Invalid processing instruction node"
- @source.position = start_position
- raise REXML::ParseException.new(message, @source)
+ def process_instruction
+ name = parse_name("Malformed XML: Invalid processing instruction node")
+ if @source.match(/\s+/um, true)
+ match_data = @source.match(/(.*?)\?>/um, true)
+ unless match_data
+ raise ParseException.new("Malformed XML: Unclosed processing instruction", @source)
+ end
+ content = match_data[1]
+ else
+ content = nil
+ unless @source.match("?>", true)
+ raise ParseException.new("Malformed XML: Unclosed processing instruction", @source)
+ end
end
- if match_data[1] == "xml"
+ if name == "xml"
if @document_status
raise ParseException.new("Malformed XML: XML declaration is not at the start", @source)
end
- content = match_data[2]
version = VERSION.match(content)
version = version[1] unless version.nil?
encoding = ENCODING.match(content)
@@ -681,7 +686,7 @@ module REXML
standalone = standalone[1] unless standalone.nil?
return [ :xmldecl, version, encoding, standalone ]
end
- [:processing_instruction, match_data[1], match_data[2]]
+ [:processing_instruction, name, content]
end
def parse_attributes(prefixes)
--
2.40.0

View File

@ -66,6 +66,11 @@ SRC_URI = "http://cache.ruby-lang.org/pub/ruby/${SHRT_VER}/ruby-${PV}.tar.gz \
file://CVE-2024-39908-0010.patch \
file://CVE-2024-39908-0011.patch \
file://CVE-2024-39908-0012.patch \
file://CVE-2024-41123-0001.patch \
file://CVE-2024-41123-0002.patch \
file://CVE-2024-41123-0003.patch \
file://CVE-2024-41123-0004.patch \
file://CVE-2024-41123-0005.patch \
"
UPSTREAM_CHECK_URI = "https://www.ruby-lang.org/en/downloads/"