Skip to content

Commit b7a5910

Browse files
committed
Fix the parser to not accept invalid escapes
Only `"\/bfnrtu` are valid after a backslash.
1 parent 256cad5 commit b7a5910

File tree

7 files changed

+31
-28
lines changed

7 files changed

+31
-28
lines changed

ext/json/ext/parser/parser.c

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -639,44 +639,43 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, const char *st
639639
static VALUE json_string_unescape(JSON_ParserState *state, const char *string, const char *stringEnd, bool is_name, bool intern, bool symbolize)
640640
{
641641
size_t bufferSize = stringEnd - string;
642-
const char *p = string, *pe = string, *unescape, *bufferStart;
642+
const char *p = string, *pe = string, *bufferStart;
643643
char *buffer;
644-
int unescape_len;
645-
char buf[4];
646644

647645
VALUE result = rb_str_buf_new(bufferSize);
648646
rb_enc_associate_index(result, utf8_encindex);
649647
buffer = RSTRING_PTR(result);
650648
bufferStart = buffer;
651649

650+
#define APPEND_CHAR(chr) *buffer++ = chr; p = ++pe;
651+
652652
while (pe < stringEnd && (pe = memchr(pe, '\\', stringEnd - pe))) {
653-
unescape = (char *) "?";
654-
unescape_len = 1;
655653
if (pe > p) {
656654
MEMCPY(buffer, p, char, pe - p);
657655
buffer += pe - p;
658656
}
659657
switch (*++pe) {
658+
case '"':
659+
case '/':
660+
p = pe; // nothing to unescape just need to skip the backslash
661+
break;
662+
case '\\':
663+
APPEND_CHAR('\\');
664+
break;
660665
case 'n':
661-
unescape = (char *) "\n";
666+
APPEND_CHAR('\n');
662667
break;
663668
case 'r':
664-
unescape = (char *) "\r";
669+
APPEND_CHAR('\r');
665670
break;
666671
case 't':
667-
unescape = (char *) "\t";
668-
break;
669-
case '"':
670-
unescape = (char *) "\"";
671-
break;
672-
case '\\':
673-
unescape = (char *) "\\";
672+
APPEND_CHAR('\t');
674673
break;
675674
case 'b':
676-
unescape = (char *) "\b";
675+
APPEND_CHAR('\b');
677676
break;
678677
case 'f':
679-
unescape = (char *) "\f";
678+
APPEND_CHAR('\f');
680679
break;
681680
case 'u':
682681
if (pe > stringEnd - 5) {
@@ -714,18 +713,23 @@ static VALUE json_string_unescape(JSON_ParserState *state, const char *string, c
714713
break;
715714
}
716715
}
717-
unescape_len = convert_UTF32_to_UTF8(buf, ch);
718-
unescape = buf;
716+
717+
char buf[4];
718+
int unescape_len = convert_UTF32_to_UTF8(buf, ch);
719+
MEMCPY(buffer, buf, char, unescape_len);
720+
buffer += unescape_len;
721+
p = ++pe;
719722
}
720723
break;
721724
default:
722-
p = pe;
723-
continue;
725+
if ((unsigned char)*pe < 0x20) {
726+
raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
727+
}
728+
raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
729+
break;
724730
}
725-
MEMCPY(buffer, unescape, char, unescape_len);
726-
buffer += unescape_len;
727-
p = ++pe;
728731
}
732+
#undef APPEND_CHAR
729733

730734
if (stringEnd > p) {
731735
MEMCPY(buffer, p, char, stringEnd - p);
@@ -976,9 +980,6 @@ static inline VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig
976980
case '\\': {
977981
state->cursor++;
978982
escaped = true;
979-
if ((unsigned char)*state->cursor < 0x20) {
980-
raise_parse_error("invalid ASCII control character in string: %s", state);
981-
}
982983
break;
983984
}
984985
default:
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

test/json/json_fixtures_test.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ class JSONFixturesTest < Test::Unit::TestCase
1010
source = File.read(f)
1111
define_method("test_#{name}") do
1212
assert JSON.parse(source), "Did not pass for fixture '#{File.basename(f)}': #{source.inspect}"
13+
rescue JSON::ParserError
14+
raise "#{File.basename(f)} parsing failure"
1315
end
1416
end
1517

test/json/json_parser_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,8 @@ def test_backslash
510510
data = ['"']
511511
assert_equal data, parse(json)
512512
#
513-
json = '["\\\'"]'
514-
data = ["'"]
513+
json = '["\\/"]'
514+
data = ["/"]
515515
assert_equal data, parse(json)
516516

517517
json = '["\/"]'

0 commit comments

Comments
 (0)