Skip to content

Commit 81a3502

Browse files
authored
Merge pull request #2973 from ksss/ksss/fix-lexer-invalid-utf8
Fix lexer infinite loop / abort on invalid UTF-8 byte
2 parents 2a11d63 + e9612b4 commit 81a3502

4 files changed

Lines changed: 26 additions & 1 deletion

File tree

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ gem "net-smtp"
4848
gem 'csv'
4949
gem 'ostruct'
5050
gem 'pstore'
51+
gem "timeout"
5152

5253
group :minitest do
5354
gem "minitest"

Gemfile.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ DEPENDENCIES
231231
steep!
232232
tempfile
233233
test-unit
234+
timeout
234235

235236
BUNDLED WITH
236237
4.0.1

src/lexstate.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ bool rbs_next_char(rbs_lexer_t *lexer, unsigned int *codepoint, size_t *byte_len
134134

135135
*byte_len = lexer->encoding->char_width((const uint8_t *) start, (ptrdiff_t) (lexer->string.end - start));
136136

137-
if (*byte_len == 1) {
137+
if (*byte_len == 0) {
138+
// Avoid infinite loop on invalid bytes.
139+
*byte_len = 1;
140+
*codepoint = (unsigned int) (unsigned char) *start;
141+
} else if (*byte_len == 1) {
138142
*codepoint = (unsigned int) *start;
139143
} else {
140144
*codepoint = 12523; // Dummy data for "ル" from "ルビー" (Ruby) in Unicode

test/rbs/parser_test.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require "test_helper"
2+
require "timeout"
23

34
class RBS::ParserTest < Test::Unit::TestCase
45
def buffer(source)
@@ -1028,4 +1029,22 @@ class Foo[T < Integer] < Bar # Comment
10281029
assert_equal [:tTRIVIA, "\n", 56...57], tokens.shift.then { |t| [t[0], t[1].source, t[1].range] }
10291030
assert_equal [:pEOF, '', 57...57], tokens.shift.then { |t| [t[0], t[1].source, t[1].range] }
10301031
end
1032+
1033+
def test_invalid_utf8_byte_in_comment_does_not_hang
1034+
# Regression: invalid UTF-8 byte in a comment used to loop forever in the lexer.
1035+
source = "# \xC2".dup.force_encoding(Encoding::UTF_8)
1036+
Timeout.timeout(5) do
1037+
RBS::Parser._parse_signature(buffer(source), 0, source.bytesize)
1038+
end
1039+
end
1040+
1041+
def test_invalid_utf8_byte_at_top_level_raises
1042+
# Regression: invalid UTF-8 byte at top level used to trip RBS_ASSERT in the C extension.
1043+
source = "\xFF".dup.force_encoding(Encoding::UTF_8)
1044+
Timeout.timeout(5) do
1045+
assert_raises(RBS::ParsingError) do
1046+
RBS::Parser._parse_signature(buffer(source), 0, source.bytesize)
1047+
end
1048+
end
1049+
end
10311050
end

0 commit comments

Comments
 (0)