ruby/SOURCES/ruby-4.0.3-Fix-a-format-string-injection-vulnerability.patch
2026-05-26 07:41:35 -04:00

87 lines
3.1 KiB
Diff

From 83dbd3f615bd8e7a31571124059eb3264f73c559 Mon Sep 17 00:00:00 2001
From: Jean Boussier <jean.boussier@gmail.com>
Date: Wed, 18 Mar 2026 08:56:17 +0100
Subject: [PATCH] Fix a format string injection vulnerability
In `JSON.parse(doc, allow_duplicate_key: false)`.
---
ext/json/parser/parser.c | 26 +++++++++++++++++++-------
test/json/json_parser_test.rb | 7 +++++++
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index 8f9729ef28..a05c5e9657 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -400,14 +400,9 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
#define PARSE_ERROR_FRAGMENT_LEN 32
-#ifdef RBIMPL_ATTR_NORETURN
-RBIMPL_ATTR_NORETURN()
-#endif
-static void raise_parse_error(const char *format, JSON_ParserState *state)
+static VALUE build_parse_error_message(const char *format, JSON_ParserState *state, long line, long column)
{
unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3];
- long line, column;
- cursor_position(state, &line, &column);
const char *ptr = "EOF";
if (state->cursor && state->cursor < state->end) {
@@ -442,11 +437,23 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
VALUE msg = rb_sprintf(format, ptr);
VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column);
RB_GC_GUARD(msg);
+ return message;
+}
+static VALUE parse_error_new(VALUE message, long line, long column)
+{
VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message);
rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line));
rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column));
- rb_exc_raise(exc);
+ return exc;
+}
+
+NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state)
+{
+ long line, column;
+ cursor_position(state, &line, &column);
+ VALUE message = build_parse_error_message(format, state, line, column);
+ rb_exc_raise(parse_error_new(message, line, column));
}
#ifdef RBIMPL_ATTR_NORETURN
@@ -896,6 +903,11 @@ static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_k
rb_inspect(duplicate_key)
);
+ long line, column;
+ cursor_position(state, &line, &column);
+ rb_str_concat(message, build_parse_error_message("", state, line, column)) ;
+ rb_exc_raise(parse_error_new(message, line, column));
+
raise_parse_error(RSTRING_PTR(message), state);
RB_GC_GUARD(message);
}
diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb
index ec9391909d..61ea35d1f9 100644
--- a/test/json/json_parser_test.rb
+++ b/test/json/json_parser_test.rb
@@ -411,6 +411,13 @@ def test_parse_duplicate_key
end
end
+ def test_parse_duplicate_key_escape
+ error = assert_raise(ParserError) do
+ JSON.parse('{"%s%s%s%s":1,"%s%s%s%s":2}', allow_duplicate_key: false)
+ end
+ assert_match "%s%s%s%s", error.message
+ end
+
def test_some_wrong_inputs
assert_raise(ParserError) { parse('[] bla') }
assert_raise(ParserError) { parse('[] 1') }