Sfoglia il codice sorgente

Fix erroneous state check in the decoder

If the input contained a mismatched end-of-array/object, the stack
could become empty before a call to dec_curr, which would look
beyond the bounds of the stack. If the value at this invalid
position happened to be st_array, we would pop too much from the
stack and overwrite the data that came before it.

This commit fixes this by letting dec_pop return the previous
state or st_invalid if the stack is empty, letting us exit
gracefully if the state isn't what we expect it to be.

dec_pop_assert is identical to the old dec_pop, tearing down the
emulator on internal errors.
pull/176/head
John Högberg 6 anni fa
parent
commit
69baf580dc
1 ha cambiato i file con 40 aggiunte e 29 eliminazioni
  1. +40
    -29
      c_src/decoder.c

+ 40
- 29
c_src/decoder.c Vedi File

@ -150,7 +150,8 @@ dec_error(Decoder* d, const char* atom)
char
dec_curr(Decoder* d)
{
return d->st_data[d->st_top-1];
assert(d->st_top > 0);
return d->st_data[d->st_top - 1];
}
int
@ -178,12 +179,25 @@ dec_push(Decoder* d, char val)
d->st_data[d->st_top++] = val;
}
char
dec_pop(Decoder* d) {
char current = st_invalid;
if (d->st_top > 0) {
current = d->st_data[d->st_top - 1];
d->st_data[d->st_top - 1] = st_invalid;
d->st_top--;
}
return current;
}
void
dec_pop(Decoder* d, char val)
dec_pop_assert(Decoder* d, char val)
{
assert(d->st_data[d->st_top-1] == val && "popped invalid state.");
d->st_data[d->st_top-1] = st_invalid;
d->st_top--;
char current = dec_pop(d);
assert(current == val && "popped invalid state.");
(void)current;
}
int
@ -777,7 +791,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done;
}
val = d->null_term;
dec_pop(d, st_value);
dec_pop_assert(d, st_value);
d->i += 4;
break;
case 't':
@ -790,7 +804,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done;
}
val = d->atoms->atom_true;
dec_pop(d, st_value);
dec_pop_assert(d, st_value);
d->i += 4;
break;
case 'f':
@ -803,7 +817,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
goto done;
}
val = d->atoms->atom_false;
dec_pop(d, st_value);
dec_pop_assert(d, st_value);
d->i += 5;
break;
case '\"':
@ -811,7 +825,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ret = dec_error(d, "invalid_string");
goto done;
}
dec_pop(d, st_value);
dec_pop_assert(d, st_value);
break;
case '-':
case '0':
@ -828,7 +842,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ret = dec_error(d, "invalid_number");
goto done;
}
dec_pop(d, st_value);
dec_pop_assert(d, st_value);
break;
case '{':
dec_push(d, st_object);
@ -849,13 +863,12 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ret = dec_error(d, "invalid_json");
goto done;
}
dec_pop(d, st_value);
if(dec_curr(d) != st_array) {
dec_pop_assert(d, st_value);
if(dec_pop(d) != st_array) {
ret = dec_error(d, "invalid_json");
goto done;
}
dec_pop(d, st_array);
dec_pop(d, st_value);
dec_pop_assert(d, st_value);
val = curr; // curr is []
if(!enif_get_list_cell(env, objs, &curr, &objs)) {
ret = dec_error(d, "internal_error");
@ -888,7 +901,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ret = dec_error(d, "invalid_string");
goto done;
}
dec_pop(d, st_key);
dec_pop_assert(d, st_key);
dec_push(d, st_colon);
curr = enif_make_list_cell(env, val, curr);
break;
@ -897,9 +910,9 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ret = dec_error(d, "invalid_json");
goto done;
}
dec_pop(d, st_key);
dec_pop(d, st_object);
dec_pop(d, st_value);
dec_pop_assert(d, st_key);
dec_pop_assert(d, st_object);
dec_pop_assert(d, st_value);
val = make_empty_object(env, d->return_maps);
if(!enif_get_list_cell(env, objs, &curr, &objs)) {
ret = dec_error(d, "internal_error");
@ -928,7 +941,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
d->i++;
break;
case ':':
dec_pop(d, st_colon);
dec_pop_assert(d, st_colon);
dec_push(d, st_value);
d->i++;
break;
@ -947,7 +960,7 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
d->i++;
break;
case ',':
dec_pop(d, st_comma);
dec_pop_assert(d, st_comma);
switch(dec_curr(d)) {
case st_object:
dec_push(d, st_key);
@ -962,13 +975,12 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
d->i++;
break;
case '}':
dec_pop(d, st_comma);
if(dec_curr(d) != st_object) {
dec_pop_assert(d, st_comma);
if(dec_pop(d) != st_object) {
ret = dec_error(d, "invalid_json");
goto done;
}
dec_pop(d, st_object);
dec_pop(d, st_value);
dec_pop_assert(d, st_value);
if(!make_object(env, curr, &val,
d->return_maps, d->dedupe_keys)) {
ret = dec_error(d, "internal_object_error");
@ -987,13 +999,12 @@ decode_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
d->i++;
break;
case ']':
dec_pop(d, st_comma);
if(dec_curr(d) != st_array) {
dec_pop_assert(d, st_comma);
if(dec_pop(d) != st_array) {
ret = dec_error(d, "invalid_json");
goto done;
}
dec_pop(d, st_array);
dec_pop(d, st_value);
dec_pop_assert(d, st_value);
val = make_array(env, curr);
if(!enif_get_list_cell(env, objs, &curr, &objs)) {
ret = dec_error(d, "internal_error");
@ -1042,7 +1053,7 @@ decode_done:
goto done;
}
if(dec_curr(d) != st_done) {
if(dec_pop(d) != st_done) {
ret = dec_error(d, "truncated_json");
} else if(d->is_partial) {
ret = enif_make_tuple2(env, d->atoms->atom_partial, val);

Caricamento…
Annulla
Salva