summaryrefslogtreecommitdiffstats
path: root/execute.c
diff options
context:
space:
mode:
authorDavid Tolnay <dtolnay@gmail.com>2015-07-22 20:48:26 -0700
committerDavid Tolnay <dtolnay@gmail.com>2015-07-24 20:23:07 -0700
commit16e8d0b1abcc38e5338a330067b088b4b7d574d3 (patch)
tree9b3d077594c9848db4db2551847f98354888ab00 /execute.c
parent5856a2d2c0f611c95231c8ef84384a27394ffc0c (diff)
detect invalid path expression (fix #862)
Diffstat (limited to 'execute.c')
-rw-r--r--execute.c72
1 files changed, 65 insertions, 7 deletions
diff --git a/execute.c b/execute.c
index 7295d72d..9f3b5499 100644
--- a/execute.c
+++ b/execute.c
@@ -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;
}