diff options
author | David Tolnay <dtolnay@gmail.com> | 2015-07-22 20:48:26 -0700 |
---|---|---|
committer | David Tolnay <dtolnay@gmail.com> | 2015-07-24 20:23:07 -0700 |
commit | 16e8d0b1abcc38e5338a330067b088b4b7d574d3 (patch) | |
tree | 9b3d077594c9848db4db2551847f98354888ab00 /execute.c | |
parent | 5856a2d2c0f611c95231c8ef84384a27394ffc0c (diff) |
detect invalid path expression (fix #862)
Diffstat (limited to 'execute.c')
-rw-r--r-- | execute.c | 72 |
1 files changed, 65 insertions, 7 deletions
@@ -34,6 +34,7 @@ struct jq_state { stack_ptr fork_top; jv path; + jv value_at_path; int subexp_nest; int debug_trace_enabled; int initial_execution; @@ -191,6 +192,7 @@ struct forkpoint { stack_ptr saved_data_stack; stack_ptr saved_curr_frame; int path_len, subexp_nest; + jv value_at_path; uint16_t* return_address; }; @@ -210,20 +212,33 @@ void stack_save(jq_state *jq, uint16_t* retaddr, struct stack_pos sp){ fork->saved_curr_frame = jq->curr_frame; fork->path_len = jv_get_kind(jq->path) == JV_KIND_ARRAY ? jv_array_length(jv_copy(jq->path)) : 0; + fork->value_at_path = jv_copy(jq->value_at_path); fork->subexp_nest = jq->subexp_nest; fork->return_address = retaddr; jq->stk_top = sp.saved_data_stack; jq->curr_frame = sp.saved_curr_frame; } -void path_append(jq_state* jq, jv component) { +static int path_intact(jq_state *jq, jv curr) { + if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) { + return jv_identical(curr, jv_copy(jq->value_at_path)); + } else { + jv_free(curr); + return 1; + } +} + +static void path_append(jq_state* jq, jv component, jv value_at_path) { if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) { int n1 = jv_array_length(jv_copy(jq->path)); jq->path = jv_array_append(jq->path, component); int n2 = jv_array_length(jv_copy(jq->path)); assert(n2 == n1 + 1); + jv_free(jq->value_at_path); + jq->value_at_path = value_at_path; } else { jv_free(component); + jv_free(value_at_path); } } @@ -253,6 +268,8 @@ uint16_t* stack_restore(jq_state *jq){ } else { assert(path_len == 0); } + jv_free(jq->value_at_path); + jq->value_at_path = fork->value_at_path; jq->subexp_nest = fork->subexp_nest; jq->fork_top = stack_pop_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint)); return retaddr; @@ -271,6 +288,8 @@ static void jq_reset(jq_state *jq) { if (jv_get_kind(jq->path) != JV_KIND_INVALID) jv_free(jq->path); jq->path = jv_null(); + jv_free(jq->value_at_path); + jq->value_at_path = jv_null(); jq->subexp_nest = 0; } @@ -536,17 +555,29 @@ jv jq_next(jq_state *jq) { stack_save(jq, pc - 1, stack_get_pos(jq)); stack_push(jq, jv_number(jq->subexp_nest)); - stack_push(jq, v); + stack_push(jq, jq->value_at_path); + stack_push(jq, jv_copy(v)); jq->path = jv_array(); + jq->value_at_path = v; // next INDEX operation must index into v jq->subexp_nest = 0; break; } case PATH_END: { jv v = stack_pop(jq); + // detect invalid path expression like path(.a | reverse) + if (!path_intact(jq, jv_copy(v))) { + char errbuf[30]; + jv msg = jv_string_fmt( + "Invalid path expression with result %s", + jv_dump_string_trunc(v, errbuf, sizeof(errbuf))); + set_error(jq, jv_invalid_with_msg(msg)); + goto do_backtrack; + } jv_free(v); // discard value, only keep path + jv old_value_at_path = stack_pop(jq); int old_subexp_nest = (int)jv_number_value(stack_pop(jq)); jv path = jq->path; @@ -558,6 +589,8 @@ jv jq_next(jq_state *jq) { stack_push(jq, path); jq->subexp_nest = old_subexp_nest; + jv_free(jq->value_at_path); + jq->value_at_path = old_value_at_path; break; } @@ -572,11 +605,23 @@ jv jq_next(jq_state *jq) { case INDEX_OPT: { jv t = stack_pop(jq); jv k = stack_pop(jq); - path_append(jq, jv_copy(k)); - jv v = jv_get(t, k); + // detect invalid path expression like path(reverse | .a) + if (!path_intact(jq, jv_copy(t))) { + char keybuf[15]; + char objbuf[30]; + jv msg = jv_string_fmt( + "Invalid path expression near attempt to access element %s of %s", + jv_dump_string_trunc(k, keybuf, sizeof(keybuf)), + jv_dump_string_trunc(t, objbuf, sizeof(objbuf))); + set_error(jq, jv_invalid_with_msg(msg)); + goto do_backtrack; + } + jv v = jv_get(t, jv_copy(k)); if (jv_is_valid(v)) { + path_append(jq, k, jv_copy(v)); stack_push(jq, v); } else { + jv_free(k); if (opcode == INDEX) set_error(jq, v); else @@ -605,9 +650,21 @@ jv jq_next(jq_state *jq) { } case EACH: - case EACH_OPT: + case EACH_OPT: { + jv container = stack_pop(jq); + // detect invalid path expression like path(reverse | .[]) + if (!path_intact(jq, jv_copy(container))) { + char errbuf[30]; + jv msg = jv_string_fmt( + "Invalid path expression near attempt to iterate through %s", + jv_dump_string_trunc(container, errbuf, sizeof(errbuf))); + set_error(jq, jv_invalid_with_msg(msg)); + goto do_backtrack; + } + stack_push(jq, container); stack_push(jq, jv_number(-1)); // fallthrough + } case ON_BACKTRACK(EACH): case ON_BACKTRACK(EACH_OPT): { int idx = jv_number_value(stack_pop(jq)); @@ -653,14 +710,14 @@ jv jq_next(jq_state *jq) { } else if (is_last) { // we don't need to make a backtrack point jv_free(container); - path_append(jq, key); + path_append(jq, key, jv_copy(value)); stack_push(jq, value); } else { struct stack_pos spos = stack_get_pos(jq); stack_push(jq, container); stack_push(jq, jv_number(idx)); stack_save(jq, pc - 1, spos); - path_append(jq, key); + path_append(jq, key, jv_copy(value)); stack_push(jq, value); } break; @@ -880,6 +937,7 @@ jq_state *jq_init(void) { jq->attrs = jv_object(); jq->path = jv_null(); + jq->value_at_path = jv_null(); return jq; } |