Browse Source

Full json validation with max_levels option

pull/195/head
Jose M Perez 5 years ago
parent
commit
89ac61b777
2 changed files with 38 additions and 36 deletions
  1. +1
    -2
      README.md
  2. +37
    -34
      c_src/decoder.c

+ 1
- 2
README.md View File

@ -61,8 +61,7 @@ The options for decode are:
the decode result is still in use. the decode result is still in use.
* `{max_levels, N}` where N >= 0 - This controls when to stop decoding * `{max_levels, N}` where N >= 0 - This controls when to stop decoding
by depth, after N levels are decoded, the rest is returned as a by depth, after N levels are decoded, the rest is returned as a
`{json, binary()}`. Note that json validation is relaxed in levels deeper
than N.
`{json, binary()}`.
* `{bytes_per_red, N}` where N >= 0 - This controls the number of * `{bytes_per_red, N}` where N >= 0 - This controls the number of
bytes that Jiffy will process as an equivalent to a reduction. Each bytes that Jiffy will process as an equivalent to a reduction. Each
20 reductions we consume 1% of our allocated time slice for the current 20 reductions we consume 1% of our allocated time slice for the current

+ 37
- 34
c_src/decoder.c View File

@ -65,9 +65,10 @@ typedef struct {
int st_size; int st_size;
int st_top; int st_top;
unsigned int current_level;
unsigned int current_depth;
unsigned int max_levels; unsigned int max_levels;
unsigned int level_start; unsigned int level_start;
unsigned int empty_element;
} Decoder; } Decoder;
Decoder* Decoder*
@ -103,9 +104,10 @@ dec_new(ErlNifEnv* env)
d->st_data[i] = st_invalid; d->st_data[i] = st_invalid;
} }
d->current_level = 0;
d->current_depth = 0;
d->max_levels = 0; d->max_levels = 0;
d->level_start = 0; d->level_start = 0;
d->empty_element = 1;
d->st_data[0] = st_value; d->st_data[0] = st_value;
d->st_top++; d->st_top++;
@ -197,14 +199,15 @@ dec_pop_assert(Decoder* d, char val)
static void inline static void inline
level_increase(Decoder* d) { level_increase(Decoder* d) {
if(d->max_levels && (d->max_levels == d->current_level++)) {
if(d->max_levels && (d->max_levels == d->current_depth++)) {
d->level_start = d->i; d->level_start = d->i;
} }
} }
static int inline static int inline
level_decrease(Decoder* d, ERL_NIF_TERM* value) { level_decrease(Decoder* d, ERL_NIF_TERM* value) {
if (d->max_levels && d->max_levels == --d->current_level) {
if (d->max_levels && d->max_levels == --d->current_depth) {
// Only builds term in threshold
ERL_NIF_TERM bin; ERL_NIF_TERM bin;
if(!d->copy_strings) { if(!d->copy_strings) {
bin = enif_make_sub_binary(d->env, d->arg, d->level_start, (d->i - d->level_start + 1)); bin = enif_make_sub_binary(d->env, d->arg, d->level_start, (d->i - d->level_start + 1));
@ -221,7 +224,7 @@ level_decrease(Decoder* d, ERL_NIF_TERM* value) {
static int inline static int inline
level_allows_terms(Decoder* d) { level_allows_terms(Decoder* d) {
return (!d->max_levels) || (d->max_levels >= d->current_level);
return (!d->max_levels) || (d->max_levels >= d->current_depth);
} }
int int
@ -234,7 +237,7 @@ dec_string(Decoder* d, ERL_NIF_TERM* value)
int ui; int ui;
int hi; int hi;
int lo; int lo;
char* chrbuf;
char* chrbuf = NULL;
int chrpos; int chrpos;
if(d->p[d->i] != '\"') { if(d->p[d->i] != '\"') {
@ -328,7 +331,9 @@ dec_string(Decoder* d, ERL_NIF_TERM* value)
return 0; return 0;
parse: parse:
if(!level_allows_terms(d)) {
if(!has_escape && !level_allows_terms(d)) {
// If has_escape, the binary is still constructed as a side effect of
// the escape validation, although it's ignored by the caller
return 1; return 1;
} else if(!has_escape && !d->copy_strings) { } else if(!has_escape && !d->copy_strings) {
*value = enif_make_sub_binary(d->env, d->arg, st, (d->i - st - 1)); *value = enif_make_sub_binary(d->env, d->arg, st, (d->i - st - 1));
@ -611,10 +616,6 @@ dec_number(Decoder* d, ERL_NIF_TERM* value)
} }
parse: parse:
if(!level_allows_terms(d)) {
return 1;
}
switch(state) { switch(state) {
case nst_init: case nst_init:
case nst_sign: case nst_sign:
@ -625,6 +626,10 @@ parse:
break; break;
} }
if(!level_allows_terms(d)) {
return 1;
}
errno = 0; errno = 0;
if(d->i - st < NUM_BUF_LEN) { if(d->i - st < NUM_BUF_LEN) {
@ -868,6 +873,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
val = d->null_term; val = d->null_term;
dec_pop_assert(d, st_value); dec_pop_assert(d, st_value);
d->i += 4; d->i += 4;
d->empty_element = 0;
break; break;
case 't': case 't':
if(d->i + 3 >= d->len) { if(d->i + 3 >= d->len) {
@ -881,6 +887,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
val = d->atoms->atom_true; val = d->atoms->atom_true;
dec_pop_assert(d, st_value); dec_pop_assert(d, st_value);
d->i += 4; d->i += 4;
d->empty_element = 0;
break; break;
case 'f': case 'f':
if(d->i + 4 >= bin.size) { if(d->i + 4 >= bin.size) {
@ -894,6 +901,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
val = d->atoms->atom_false; val = d->atoms->atom_false;
dec_pop_assert(d, st_value); dec_pop_assert(d, st_value);
d->i += 5; d->i += 5;
d->empty_element = 0;
break; break;
case '\"': case '\"':
if(!dec_string(d, &val)) { if(!dec_string(d, &val)) {
@ -901,6 +909,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done; goto done;
} }
dec_pop_assert(d, st_value); dec_pop_assert(d, st_value);
d->empty_element = 0;
break; break;
case '-': case '-':
case '0': case '0':
@ -918,6 +927,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done; goto done;
} }
dec_pop_assert(d, st_value); dec_pop_assert(d, st_value);
d->empty_element = 0;
break; break;
case '{': case '{':
dec_push(d, st_object); dec_push(d, st_object);
@ -929,6 +939,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
curr = enif_make_list(env, 0); curr = enif_make_list(env, 0);
} }
d->i++; d->i++;
d->empty_element = 1;
break; break;
case '[': case '[':
dec_push(d, st_array); dec_push(d, st_array);
@ -940,13 +951,12 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
curr = enif_make_list(env, 0); curr = enif_make_list(env, 0);
} }
d->i++; d->i++;
d->empty_element = 1;
break; break;
case ']': case ']':
if(level_allows_terms(d)) {
if(!enif_is_empty_list(env, curr)) {
ret = dec_error(d, "invalid_json");
goto done;
}
if(!d->empty_element) {
ret = dec_error(d, "invalid_json");
goto done;
} }
dec_pop_assert(d, st_value); dec_pop_assert(d, st_value);
if(dec_pop(d) != st_array) { if(dec_pop(d) != st_array) {
@ -961,10 +971,10 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done; goto done;
} }
} }
level_decrease(d, &val); level_decrease(d, &val);
d->i++; d->i++;
d->empty_element = 0;
break; break;
default: default:
ret = dec_error(d, "invalid_json"); ret = dec_error(d, "invalid_json");
@ -1000,11 +1010,9 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
} }
break; break;
case '}': case '}':
if(level_allows_terms(d)) {
if(!enif_is_empty_list(env, curr)) {
ret = dec_error(d, "invalid_json");
goto done;
}
if(!d->empty_element) {
ret = dec_error(d, "invalid_json");
goto done;
} }
dec_pop_assert(d, st_key); dec_pop_assert(d, st_key);
dec_pop_assert(d, st_object); dec_pop_assert(d, st_object);
@ -1016,6 +1024,8 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done; goto done;
} }
} }
level_decrease(d, &val);
if(dec_top(d) == 0) { if(dec_top(d) == 0) {
dec_push(d, st_done); dec_push(d, st_done);
} else { } else {
@ -1025,11 +1035,8 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
} }
} }
if(level_decrease(d, &val)) {
curr = enif_make_list_cell(env, val, curr);
}
d->i++; d->i++;
d->empty_element = 0;
break; break;
default: default:
ret = dec_error(d, "invalid_json"); ret = dec_error(d, "invalid_json");
@ -1097,6 +1104,8 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done; goto done;
} }
} }
level_decrease(d, &val);
if(dec_top(d) > 0) { if(dec_top(d) > 0) {
dec_push(d, st_comma); dec_push(d, st_comma);
if(level_allows_terms(d)) { if(level_allows_terms(d)) {
@ -1106,10 +1115,6 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
dec_push(d, st_done); dec_push(d, st_done);
} }
if(level_decrease(d, &val)) {
curr = enif_make_list_cell(env, val, curr);
}
d->i++; d->i++;
break; break;
case ']': case ']':
@ -1126,6 +1131,8 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done; goto done;
} }
} }
level_decrease(d, &val);
if(dec_top(d) > 0) { if(dec_top(d) > 0) {
dec_push(d, st_comma); dec_push(d, st_comma);
if(level_allows_terms(d)) { if(level_allows_terms(d)) {
@ -1135,10 +1142,6 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
dec_push(d, st_done); dec_push(d, st_done);
} }
if(level_decrease(d, &val)) {
curr = enif_make_list_cell(env, val, curr);
}
d->i++; d->i++;
break; break;
default: default:

Loading…
Cancel
Save