json 2.19.5 → 2.19.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +12 -0
- data/ext/json/ext/fbuffer/fbuffer.h +10 -1
- data/ext/json/ext/generator/generator.c +11 -6
- data/ext/json/ext/parser/parser.c +55 -27
- data/lib/json/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 579bc938143b2fc703d90623fb985af883a7b46f0641d874cc44a39a4ffae2dd
|
|
4
|
+
data.tar.gz: f69a8d12a9f83378e0e0e6b0abdda34cf23d7ebf026dcc114dc70089dd159190
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3bb0a35502ac84a22a71f076a68feb5ea525ff802491e2532bd863653d1b6808de4715108886e6bb00cc5ef378ec298bea81e2d932a748886f1f4ec8b0637b03
|
|
7
|
+
data.tar.gz: e50b9b31f846e105a725807db54fe86f8176331d975ea772a1f9f666ed91e4555497f79008a4b01b08de3c2605cb1867671f87b86f196632b05d1f5a21872143
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-05-28 (2.19.7)
|
|
6
|
+
|
|
7
|
+
* Fix some more edge cases with out of range floats.
|
|
8
|
+
* Ensure the string provided to `JSON.parse` can't be mutated during parsing.
|
|
9
|
+
* Add missing write barriers in `State#dup`.
|
|
10
|
+
* Further validate generator `depth` config.
|
|
11
|
+
|
|
12
|
+
### 2026-05-28 (2.19.6)
|
|
13
|
+
|
|
14
|
+
* Cleanly handle overly large `depth` generator argument.
|
|
15
|
+
* Add missing write barrier in `ParserConfig`.
|
|
16
|
+
|
|
5
17
|
### 2026-05-04 (2.19.5)
|
|
6
18
|
|
|
7
19
|
* Cap the parser to emit a maximum of 5 deprecation warnings per document. Emitting more is not helpful.
|
|
@@ -131,6 +131,15 @@ static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
static inline size_t fbuffer_size_mul_or_raise(size_t a, size_t b)
|
|
135
|
+
{
|
|
136
|
+
size_t result = a * b;
|
|
137
|
+
if (RB_UNLIKELY(a != 0 && (result / a) != b)) {
|
|
138
|
+
rb_raise(rb_eArgError, "Buffer overflow, the resulting document is too large to be generated");
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
134
143
|
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
|
|
135
144
|
{
|
|
136
145
|
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
@@ -175,7 +184,7 @@ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
|
175
184
|
size_t len;
|
|
176
185
|
RSTRING_GETMEM(str, ptr, len);
|
|
177
186
|
|
|
178
|
-
fbuffer_inc_capa(fb, repeat
|
|
187
|
+
fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
|
|
179
188
|
while (repeat) {
|
|
180
189
|
#if JSON_DEBUG
|
|
181
190
|
fb->requested = len;
|
|
@@ -1367,12 +1367,14 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
|
|
|
1367
1367
|
if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
|
|
1368
1368
|
|
|
1369
1369
|
MEMCPY(objState, origState, JSON_Generator_State, 1);
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1370
|
+
|
|
1371
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
|
|
1372
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space);
|
|
1373
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
|
|
1374
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
|
|
1375
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
|
|
1376
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
|
|
1377
|
+
|
|
1376
1378
|
return obj;
|
|
1377
1379
|
}
|
|
1378
1380
|
|
|
@@ -1590,6 +1592,9 @@ static long depth_config(VALUE num)
|
|
|
1590
1592
|
if (RB_UNLIKELY(d < 0)) {
|
|
1591
1593
|
rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
|
|
1592
1594
|
}
|
|
1595
|
+
if (RB_UNLIKELY(d > INT_MAX)) {
|
|
1596
|
+
rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
|
|
1597
|
+
}
|
|
1593
1598
|
return d;
|
|
1594
1599
|
}
|
|
1595
1600
|
|
|
@@ -1202,7 +1202,11 @@ static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig
|
|
|
1202
1202
|
raise_parse_error_at("invalid number: %s", state, start);
|
|
1203
1203
|
}
|
|
1204
1204
|
|
|
1205
|
-
|
|
1205
|
+
if (RB_UNLIKELY(exponent_digits >= 20 || abs_exponent > (uint64_t)INT64_MAX)) {
|
|
1206
|
+
exponent = negative_exponent ? INT64_MIN : INT64_MAX;
|
|
1207
|
+
} else {
|
|
1208
|
+
exponent = negative_exponent ? -(int64_t)abs_exponent : (int64_t)abs_exponent;
|
|
1209
|
+
}
|
|
1206
1210
|
}
|
|
1207
1211
|
|
|
1208
1212
|
if (integer) {
|
|
@@ -1457,23 +1461,39 @@ static void json_ensure_eof(JSON_ParserState *state)
|
|
|
1457
1461
|
|
|
1458
1462
|
static VALUE convert_encoding(VALUE source)
|
|
1459
1463
|
{
|
|
1460
|
-
|
|
1464
|
+
StringValue(source);
|
|
1465
|
+
int encindex = RB_ENCODING_GET(source);
|
|
1461
1466
|
|
|
1462
|
-
|
|
1467
|
+
if (RB_LIKELY(encindex == utf8_encindex)) {
|
|
1468
|
+
return source;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
if (encindex == binary_encindex) {
|
|
1472
|
+
// For historical reason, we silently reinterpret binary strings as UTF-8
|
|
1473
|
+
return rb_enc_associate_index(rb_str_dup(source), utf8_encindex);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
source = rb_funcall(source, i_encode, 1, Encoding_UTF_8);
|
|
1477
|
+
StringValue(source);
|
|
1463
1478
|
return source;
|
|
1464
|
-
|
|
1479
|
+
}
|
|
1465
1480
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1481
|
+
struct parser_config_init_args {
|
|
1482
|
+
JSON_ParserConfig *config;
|
|
1483
|
+
VALUE self;
|
|
1484
|
+
};
|
|
1470
1485
|
|
|
1471
|
-
|
|
1486
|
+
static void parser_config_wb_write(VALUE self, VALUE *dest, VALUE val)
|
|
1487
|
+
{
|
|
1488
|
+
*dest = val;
|
|
1489
|
+
if (self) RB_OBJ_WRITTEN(self, Qundef, val);
|
|
1472
1490
|
}
|
|
1473
1491
|
|
|
1474
1492
|
static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
1475
1493
|
{
|
|
1476
|
-
|
|
1494
|
+
struct parser_config_init_args *args = (struct parser_config_init_args *)data;
|
|
1495
|
+
JSON_ParserConfig *config = args->config;
|
|
1496
|
+
VALUE self = args->self;
|
|
1477
1497
|
|
|
1478
1498
|
if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
|
|
1479
1499
|
else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
|
|
@@ -1482,15 +1502,15 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1482
1502
|
else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
|
|
1483
1503
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
1484
1504
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
1485
|
-
else if (key == sym_on_load) { config->on_load_proc
|
|
1505
|
+
else if (key == sym_on_load) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); }
|
|
1486
1506
|
else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
|
1487
1507
|
else if (key == sym_decimal_class) {
|
|
1488
1508
|
if (RTEST(val)) {
|
|
1489
1509
|
if (rb_respond_to(val, i_try_convert)) {
|
|
1490
|
-
config->decimal_class
|
|
1510
|
+
parser_config_wb_write(self, &config->decimal_class, val);
|
|
1491
1511
|
config->decimal_method_id = i_try_convert;
|
|
1492
1512
|
} else if (rb_respond_to(val, i_new)) {
|
|
1493
|
-
config->decimal_class
|
|
1513
|
+
parser_config_wb_write(self, &config->decimal_class, val);
|
|
1494
1514
|
config->decimal_method_id = i_new;
|
|
1495
1515
|
} else if (RB_TYPE_P(val, T_CLASS)) {
|
|
1496
1516
|
VALUE name = rb_class_name(val);
|
|
@@ -1499,7 +1519,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1499
1519
|
if (last_colon) {
|
|
1500
1520
|
const char *mod_path_end = last_colon - 1;
|
|
1501
1521
|
VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
|
|
1502
|
-
config->decimal_class
|
|
1522
|
+
parser_config_wb_write(self, &config->decimal_class, rb_path_to_class(mod_path));
|
|
1503
1523
|
|
|
1504
1524
|
const char *method_name_beg = last_colon + 1;
|
|
1505
1525
|
long before_len = method_name_beg - name_cstr;
|
|
@@ -1507,7 +1527,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1507
1527
|
VALUE method_name = rb_str_substr(name, before_len, len);
|
|
1508
1528
|
config->decimal_method_id = SYM2ID(rb_str_intern(method_name));
|
|
1509
1529
|
} else {
|
|
1510
|
-
config->decimal_class
|
|
1530
|
+
parser_config_wb_write(self, &config->decimal_class, rb_mKernel);
|
|
1511
1531
|
config->decimal_method_id = SYM2ID(rb_str_intern(name));
|
|
1512
1532
|
}
|
|
1513
1533
|
}
|
|
@@ -1517,16 +1537,21 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1517
1537
|
return ST_CONTINUE;
|
|
1518
1538
|
}
|
|
1519
1539
|
|
|
1520
|
-
static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
|
|
1540
|
+
static void parser_config_init(JSON_ParserConfig *config, VALUE opts, VALUE self)
|
|
1521
1541
|
{
|
|
1522
1542
|
config->max_nesting = 100;
|
|
1523
1543
|
|
|
1544
|
+
struct parser_config_init_args args = {
|
|
1545
|
+
.config = config,
|
|
1546
|
+
.self = self,
|
|
1547
|
+
};
|
|
1548
|
+
|
|
1524
1549
|
if (!NIL_P(opts)) {
|
|
1525
1550
|
Check_Type(opts, T_HASH);
|
|
1526
1551
|
if (RHASH_SIZE(opts) > 0) {
|
|
1527
1552
|
// We assume in most cases few keys are set so it's faster to go over
|
|
1528
1553
|
// the provided keys than to check all possible keys.
|
|
1529
|
-
rb_hash_foreach(opts, parser_config_init_i, (VALUE)
|
|
1554
|
+
rb_hash_foreach(opts, parser_config_init_i, (VALUE)&args);
|
|
1530
1555
|
}
|
|
1531
1556
|
|
|
1532
1557
|
}
|
|
@@ -1560,17 +1585,21 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
|
|
|
1560
1585
|
rb_check_frozen(self);
|
|
1561
1586
|
GET_PARSER_CONFIG;
|
|
1562
1587
|
|
|
1563
|
-
parser_config_init(config, opts);
|
|
1564
|
-
|
|
1565
|
-
RB_OBJ_WRITTEN(self, Qundef, config->decimal_class);
|
|
1588
|
+
parser_config_init(config, opts, self);
|
|
1566
1589
|
|
|
1567
1590
|
return self;
|
|
1568
1591
|
}
|
|
1569
1592
|
|
|
1570
|
-
static VALUE cParser_parse(JSON_ParserConfig *config, VALUE
|
|
1593
|
+
static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
1571
1594
|
{
|
|
1572
|
-
Vsource = convert_encoding(
|
|
1573
|
-
|
|
1595
|
+
VALUE Vsource = convert_encoding(src);
|
|
1596
|
+
|
|
1597
|
+
// Ensure the string isn't mutated under us.
|
|
1598
|
+
// The classic API to use is `rb_str_locktmp`, but then we'd
|
|
1599
|
+
// need to use `rb_protect` to make sure we always unlock.
|
|
1600
|
+
if (Vsource == src) {
|
|
1601
|
+
Vsource = rb_str_new_frozen(Vsource);
|
|
1602
|
+
}
|
|
1574
1603
|
|
|
1575
1604
|
VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA];
|
|
1576
1605
|
rvalue_stack stack = {
|
|
@@ -1581,6 +1610,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
|
|
|
1581
1610
|
|
|
1582
1611
|
long len;
|
|
1583
1612
|
const char *start;
|
|
1613
|
+
|
|
1584
1614
|
RSTRING_GETMEM(Vsource, start, len);
|
|
1585
1615
|
|
|
1586
1616
|
VALUE stack_handle = 0;
|
|
@@ -1599,6 +1629,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
|
|
|
1599
1629
|
// it won't cause a leak.
|
|
1600
1630
|
rvalue_stack_eagerly_release(stack_handle);
|
|
1601
1631
|
RB_GC_GUARD(stack_handle);
|
|
1632
|
+
RB_GC_GUARD(Vsource);
|
|
1602
1633
|
json_ensure_eof(state);
|
|
1603
1634
|
|
|
1604
1635
|
return result;
|
|
@@ -1619,12 +1650,9 @@ static VALUE cParserConfig_parse(VALUE self, VALUE Vsource)
|
|
|
1619
1650
|
|
|
1620
1651
|
static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
|
|
1621
1652
|
{
|
|
1622
|
-
Vsource = convert_encoding(StringValue(Vsource));
|
|
1623
|
-
StringValue(Vsource);
|
|
1624
|
-
|
|
1625
1653
|
JSON_ParserConfig _config = {0};
|
|
1626
1654
|
JSON_ParserConfig *config = &_config;
|
|
1627
|
-
parser_config_init(config, opts);
|
|
1655
|
+
parser_config_init(config, opts, false);
|
|
1628
1656
|
|
|
1629
1657
|
return cParser_parse(config, Vsource);
|
|
1630
1658
|
}
|
data/lib/json/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: json
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.19.
|
|
4
|
+
version: 2.19.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Frank
|
|
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
84
84
|
- !ruby/object:Gem::Version
|
|
85
85
|
version: '0'
|
|
86
86
|
requirements: []
|
|
87
|
-
rubygems_version: 4.0.
|
|
87
|
+
rubygems_version: 4.0.12
|
|
88
88
|
specification_version: 4
|
|
89
89
|
summary: JSON Implementation for Ruby
|
|
90
90
|
test_files: []
|