Skip to content

Commit aeb8696

Browse files
romanhabibovTotktonada
authored andcommitted
yajl: skip garbadge fields
Fix bug with skipping of garbage fields when parsing json. All fields, except for "id", "method" and "params", are considered garbage fields. Closes #132
1 parent ec35427 commit aeb8696

File tree

3 files changed

+132
-34
lines changed

3 files changed

+132
-34
lines changed

src/tp_transcode.c

+42-34
Original file line numberDiff line numberDiff line change
@@ -467,23 +467,17 @@ yajl_start_map(void *ctx)
467467
{
468468
yajl_ctx_t *s_ctx = (yajl_ctx_t *)ctx;
469469

470-
if (unlikely(s_ctx->stage != PARAMS))
471-
return 1;
472-
473470
stack_grow_array(s_ctx);
474471

475-
bool r = false;
476-
if (unlikely(s_ctx->size == 0))
477-
/**/
478-
r = stack_push(s_ctx, s_ctx->tp.p, TYPE_MAP | PARAMS);
479-
else
480-
r = stack_push(s_ctx, s_ctx->tp.p, TYPE_MAP);
481-
472+
bool r = stack_push(s_ctx, s_ctx->tp.p, TYPE_MAP);
482473
if (unlikely(!r)) {
483474
say_error(s_ctx, -32603, "[BUG?] 'stack' overflow");
484475
return 0;
485476
}
486477

478+
if (unlikely(s_ctx->stage != PARAMS))
479+
return 1;
480+
487481
if (unlikely(s_ctx->tp.e < s_ctx->tp.p + 1 + sizeof(uint32_t)))
488482
say_overflow_r_2(s_ctx);
489483

@@ -498,8 +492,8 @@ yajl_end_map(void *ctx)
498492
{
499493
yajl_ctx_t *s_ctx = (yajl_ctx_t *)ctx;
500494

501-
if (s_ctx->size == 0) {
502-
495+
stack_item_t *item = stack_pop(s_ctx);
496+
if (item != NULL && s_ctx->size == 0) {
503497
if (!(s_ctx->been_stages & PARAMS)) {
504498
dd("ADDING EMPTY PARAMS\n");
505499

@@ -523,19 +517,21 @@ yajl_end_map(void *ctx)
523517
if (unlikely(s_ctx->stage != PARAMS))
524518
return 1;
525519

526-
stack_item_t *item = stack_pop(s_ctx);
527520
if (likely(item != NULL)) {
528-
529521
dd("map close, count %d '}'\n", (int)item->count);
530522

531-
if (unlikely(item->type & PARAMS)) {
532-
say_wrong_params(s_ctx);
533-
return 0;
534-
}
523+
/*
524+
* A map instead of an array as "params" value.
525+
*
526+
* {<...>, "params": {<...>}}
527+
*/
528+
if (unlikely(s_ctx->size == 1)) {
529+
say_wrong_params(s_ctx);
530+
return 0;
531+
}
535532

536533
*(item->ptr++) = 0xdf;
537534
*(uint32_t *) item->ptr = mp_bswap_u32(item->count);
538-
539535
} else {
540536
say_wrong_params(s_ctx);
541537
return 0;
@@ -550,30 +546,36 @@ yajl_start_array(void *ctx)
550546
{
551547
yajl_ctx_t *s_ctx = (yajl_ctx_t *)ctx;
552548

549+
/*
550+
* Don't store a stack item for 'batching' array. This way we
551+
* unify processing of both cases: when this array is present
552+
* and when it does not.
553+
*
554+
* 1. {"method": <...>, "params": <...>, "id": <...>, <...>}
555+
* 2. [
556+
* {"method": <...>, "params": <...>, "id": <...>, <...>},
557+
* {<...>}
558+
* ]
559+
*
560+
* All other arrays and maps are tracked in the stack.
561+
*/
553562
if (s_ctx->stage == INIT) {
554563
s_ctx->batch_mode_on = true;
555564
return 1;
556565
}
557566

558-
if (unlikely(s_ctx->stage != PARAMS))
559-
return 1;
560-
561567
dd("array open '['\n");
562-
563568
stack_grow_array(s_ctx);
564569

565-
bool push_ok = false, bind_first_argument = false;
566-
if (unlikely(s_ctx->size == 0)) {
567-
push_ok = stack_push(s_ctx, s_ctx->tp.p, TYPE_ARRAY | PARAMS);
568-
bind_first_argument = true;
569-
} else
570-
push_ok = stack_push(s_ctx, s_ctx->tp.p, TYPE_ARRAY);
571-
570+
bool push_ok = stack_push(s_ctx, s_ctx->tp.p, TYPE_ARRAY);
572571
if (unlikely(!push_ok)) {
573572
say_error(s_ctx, -32603, "[BUG?] 'stack' overflow");
574573
return 0;
575574
}
576575

576+
if (unlikely(s_ctx->stage != PARAMS))
577+
return 1;
578+
577579
if (unlikely(s_ctx->tp.e < (s_ctx->tp.p + 1 + sizeof(uint32_t))))
578580
say_overflow_r_2(s_ctx);
579581

@@ -582,7 +584,7 @@ yajl_start_array(void *ctx)
582584
// Here is bind data
583585
// e.g. http request
584586
// [
585-
if (bind_first_argument) {
587+
if (s_ctx->size == 2) {
586588
if (unlikely(!bind_data(s_ctx)))
587589
say_overflow_r_2(s_ctx);
588590
}
@@ -596,16 +598,22 @@ yajl_end_array(void *ctx)
596598
{
597599
yajl_ctx_t *s_ctx = (yajl_ctx_t *)ctx;
598600

601+
stack_item_t *item = stack_pop(s_ctx);
602+
599603
if (unlikely(s_ctx->stage != PARAMS))
600604
return 1;
601605

602-
stack_item_t *item = stack_pop(s_ctx);
603606
if (likely(item != NULL)) {
604-
605607
dd("array close, count %d ']'\n", item->count);
606608

607609
size_t item_count = item->count;
608-
if (unlikely(item->type & PARAMS)) {
610+
611+
/*
612+
* An end of "params" array.
613+
*
614+
* {<...>, "params": [<...>]}.
615+
*/
616+
if (unlikely(s_ctx->size == 1)) {
609617
dd("PARAMS END\n");
610618
s_ctx->stage = WAIT_NEXT;
611619
s_ctx->been_stages |= PARAMS;

test/test.lua

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ yaml = require('yaml')
55
os = require('os')
66
fiber = require('fiber')
77

8+
function echo(...)
9+
return ...
10+
end
11+
812
function echo_1(a)
913
return {a}
1014
end

test/v27_features.py

+86
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import sys
55
import time
6+
from collections import OrderedDict
67
sys.path.append('./t')
78
from http_utils import *
89

@@ -20,3 +21,88 @@
2021
assert(result['uri'] == '/issue_120/{}'), "expected result"
2122
print ('[+] OK')
2223

24+
print('[+] Garbage fields skipping.')
25+
preset_method_location = BASE_URL + '/echo'
26+
exp_res = {u'id': 1, u'result': [u'param']}
27+
28+
skipping_map_at_the_beginning = OrderedDict([
29+
("skipping_map_at_the_beginning", {"ttl": 123}),
30+
("id", 1),
31+
("method", "echo"),
32+
("params", ["param"])
33+
])
34+
result = post_success_pure(preset_method_location, skipping_map_at_the_beginning, {})
35+
assert (result == exp_res), "skipping_map_at_the_beginning"
36+
37+
skipping_map_in_the_middle = OrderedDict([
38+
("id", 1),
39+
("skipping_map_in_the_middle", {"ttl": 123}),
40+
("method", "echo"),
41+
("params", ["param"])
42+
])
43+
result = post_success_pure(preset_method_location, skipping_map_in_the_middle, {})
44+
assert (result == exp_res), "skipping_map_in_the_middle"
45+
46+
skipping_map_in_the_end = OrderedDict([
47+
("id", 1),
48+
("method", "echo"),
49+
("params", ["param"]),
50+
("skipping_map_in_the_end", {"ttl": 123})
51+
])
52+
result = post_success_pure(preset_method_location, skipping_map_in_the_end, {})
53+
assert (result == exp_res), "skipping_map_in_the_end"
54+
55+
skipping_array_at_the_beginning = OrderedDict([
56+
("skipping_array_at_the_beginning", ["ttl", 123]),
57+
("id", 1),
58+
("method", "echo"),
59+
("params", ["param"]),
60+
("meta", ["ttl", 123])
61+
])
62+
result = post_success_pure(preset_method_location, skipping_array_at_the_beginning, {})
63+
assert (result == exp_res), "skipping_array_at_the_beginning"
64+
65+
skipping_array_in_the_middle = OrderedDict([
66+
("id", 1),
67+
("method", "echo"),
68+
("skipping_array_in_the_middle", ["ttl", 123]),
69+
("params", ["param"])
70+
])
71+
result = post_success_pure(preset_method_location, skipping_array_in_the_middle, {})
72+
assert (result == exp_res), "skipping_array_in_the_middle"
73+
74+
skipping_array_in_the_end = OrderedDict([
75+
("id", 1),
76+
("method", "echo"),
77+
("params", ["param"]),
78+
("skipping_array_in_the_end", ["ttl", 123])
79+
])
80+
result = post_success_pure(preset_method_location, skipping_array_in_the_end, {})
81+
assert (result == exp_res), "skipping_array_in_the_end"
82+
83+
skipping_map_with_nesting_1 = OrderedDict([
84+
("skipping_map_with_nesting_1", {"ttl": {"ttl": 123, "array":[]}}),
85+
("id", 1),
86+
("method", "echo"),
87+
("params", ["param"])
88+
])
89+
result = post_success_pure(preset_method_location, skipping_map_with_nesting_1, {})
90+
assert (result == exp_res), "skipping_map_with_nesting_1"
91+
92+
skipping_array_with_nesting_2 = OrderedDict([
93+
("skipping_array_with_nesting_2", ["ttl", ["ttl", {"map": 123}]]),
94+
("id", 1),
95+
("method", "echo"),
96+
("params", ["param"])
97+
])
98+
result = post_success_pure(preset_method_location, skipping_array_with_nesting_2, {})
99+
assert (result == exp_res), "skipping_array_with_nesting_2"
100+
101+
batch = [skipping_map_at_the_beginning, skipping_map_in_the_middle,
102+
skipping_map_in_the_end, skipping_array_at_the_beginning,
103+
skipping_array_in_the_middle, skipping_array_in_the_end,
104+
skipping_map_with_nesting_1, skipping_array_with_nesting_2]
105+
result = post_success_pure(preset_method_location, batch, {})
106+
for item in result:
107+
assert (item == exp_res), "batch"
108+
print('[+] OK')

0 commit comments

Comments
 (0)