Limit the containment check depth
This fixes CVE-2026-40612.
This commit is contained in:
1 parent
870207323d
commit
d1a12569d9
3 files changed
+40
-14
No files matched your search
+4
-1
@@ -421,7 +421,10 @@ jv binop_greatereq(jv a, jv b) {
|
|||||||
|
|
||||||
static jv f_contains(jq_state *jq, jv a, jv b) {
|
static jv f_contains(jq_state *jq, jv a, jv b) {
|
||||||
if (jv_get_kind(a) == jv_get_kind(b)) {
|
if (jv_get_kind(a) == jv_get_kind(b)) {
|
||||||
return jv_bool(jv_contains(a, b));
|
int r = jv_contains(a, b);
|
||||||
|
if (r < 0)
|
||||||
|
return jv_invalid_with_msg(jv_string("Containment check too deep"));
|
||||||
|
return jv_bool(r);
|
||||||
} else {
|
} else {
|
||||||
return type_error2(a, b, "cannot have their containment checked");
|
return type_error2(a, b, "cannot have their containment checked");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -938,19 +938,19 @@ static void jvp_clamp_slice_params(int len, int *pstart, int *pend)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int jvp_array_contains(jv a, jv b) {
|
static int jvp_contains(jv a, jv b, int depth);
|
||||||
|
|
||||||
|
static int jvp_array_contains(jv a, jv b, int depth) {
|
||||||
int r = 1;
|
int r = 1;
|
||||||
jv_array_foreach(b, bi, belem) {
|
jv_array_foreach(b, bi, belem) {
|
||||||
int ri = 0;
|
int ri = 0;
|
||||||
jv_array_foreach(a, ai, aelem) {
|
jv_array_foreach(a, ai, aelem) {
|
||||||
if (jv_contains(aelem, jv_copy(belem))) {
|
ri = jvp_contains(aelem, jv_copy(belem), depth);
|
||||||
ri = 1;
|
if (ri) break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
jv_free(belem);
|
jv_free(belem);
|
||||||
if (!ri) {
|
if (ri <= 0) {
|
||||||
r = 0;
|
r = ri;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1844,7 +1844,7 @@ static int jvp_object_equal(jv o1, jv o2) {
|
|||||||
return len1 == len2;
|
return len1 == len2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int jvp_object_contains(jv a, jv b) {
|
static int jvp_object_contains(jv a, jv b, int depth) {
|
||||||
assert(JVP_HAS_KIND(a, JV_KIND_OBJECT));
|
assert(JVP_HAS_KIND(a, JV_KIND_OBJECT));
|
||||||
assert(JVP_HAS_KIND(b, JV_KIND_OBJECT));
|
assert(JVP_HAS_KIND(b, JV_KIND_OBJECT));
|
||||||
int r = 1;
|
int r = 1;
|
||||||
@@ -1852,9 +1852,9 @@ static int jvp_object_contains(jv a, jv b) {
|
|||||||
jv_object_foreach(b, key, b_val) {
|
jv_object_foreach(b, key, b_val) {
|
||||||
jv a_val = jv_object_get(jv_copy(a), key);
|
jv a_val = jv_object_get(jv_copy(a), key);
|
||||||
|
|
||||||
r = jv_contains(a_val, b_val);
|
r = jvp_contains(a_val, b_val, depth);
|
||||||
|
|
||||||
if (!r) break;
|
if (r <= 0) break;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -2086,14 +2086,23 @@ int jv_identical(jv a, jv b) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int jv_contains(jv a, jv b) {
|
#ifndef MAX_CONTAINS_DEPTH
|
||||||
|
#define MAX_CONTAINS_DEPTH (10000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int jvp_contains(jv a, jv b, int depth) {
|
||||||
|
if (depth > MAX_CONTAINS_DEPTH) {
|
||||||
|
jv_free(a);
|
||||||
|
jv_free(b);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
int r = 1;
|
int r = 1;
|
||||||
if (jv_get_kind(a) != jv_get_kind(b)) {
|
if (jv_get_kind(a) != jv_get_kind(b)) {
|
||||||
r = 0;
|
r = 0;
|
||||||
} else if (JVP_HAS_KIND(a, JV_KIND_OBJECT)) {
|
} else if (JVP_HAS_KIND(a, JV_KIND_OBJECT)) {
|
||||||
r = jvp_object_contains(a, b);
|
r = jvp_object_contains(a, b, depth + 1);
|
||||||
} else if (JVP_HAS_KIND(a, JV_KIND_ARRAY)) {
|
} else if (JVP_HAS_KIND(a, JV_KIND_ARRAY)) {
|
||||||
r = jvp_array_contains(a, b);
|
r = jvp_array_contains(a, b, depth + 1);
|
||||||
} else if (JVP_HAS_KIND(a, JV_KIND_STRING)) {
|
} else if (JVP_HAS_KIND(a, JV_KIND_STRING)) {
|
||||||
int b_len = jv_string_length_bytes(jv_copy(b));
|
int b_len = jv_string_length_bytes(jv_copy(b));
|
||||||
if (b_len != 0) {
|
if (b_len != 0) {
|
||||||
@@ -2109,3 +2118,8 @@ int jv_contains(jv a, jv b) {
|
|||||||
jv_free(b);
|
jv_free(b);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns 1 (contained), 0 (not contained), or -1 (too deep)
|
||||||
|
int jv_contains(jv a, jv b) {
|
||||||
|
return jvp_contains(a, b, 0);
|
||||||
|
}
|
||||||
@@ -2593,3 +2593,12 @@ null
|
|||||||
try delpaths([[range(10001) | 0]]) catch .
|
try delpaths([[range(10001) | 0]]) catch .
|
||||||
null
|
null
|
||||||
"Path too deep"
|
"Path too deep"
|
||||||
|
|
||||||
|
# regression test for CVE-2026-40612
|
||||||
|
reduce range(10000) as $_ ([]; [.]) | contains([[]])
|
||||||
|
null
|
||||||
|
true
|
||||||
|
|
||||||
|
try (reduce range(10001) as $_ ([]; [.]) as $x | $x | contains($x)) catch .
|
||||||
|
null
|
||||||
|
"Containment check too deep"
|
||||||
Reference in new issue
Block a user