From 69baf580dce71e09c1bfafa5a34c04e97ac394c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 11 Apr 2019 06:39:33 +0200 Subject: [PATCH] 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. --- c_src/decoder.c | 69 ++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/c_src/decoder.c b/c_src/decoder.c index 7f30810..1d2f44d 100644 --- a/c_src/decoder.c +++ b/c_src/decoder.c @@ -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);