diff options
author | Roland Kovacs <roland.kovacs@est.tech> | 2025-07-07 08:38:09 +0200 |
---|---|---|
committer | Armin Kuster <akuster808@gmail.com> | 2025-07-10 20:23:11 -0400 |
commit | 3d03058fe2b2eacbd4ebe081032f85063599221c (patch) | |
tree | f8231ec16f81f14972a7c305bd419fee67ca320b | |
parent | 4a58c213346ff3dc04624328d3fad1d244047714 (diff) | |
download | meta-openembedded-3d03058fe2b2eacbd4ebe081032f85063599221c.tar.gz |
jq-1.7.1: Backport multiple CVE fixes
CVE: CVE-2024-23337
CVE: CVE-2024-53427
CVE: CVE-2025-48060
Patches CVE-2024-23337.patch and CVE-2024-53427.patch are backported from
jq-1.8.0, and CVE-2025-48060.patch is backported from jq-1.8.1.
Signed-off-by: Roland Kovacs <roland.kovacs@est.tech>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r-- | meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch | 233 | ||||
-rw-r--r-- | meta-oe/recipes-devtools/jq/jq/CVE-2024-53427.patch | 79 | ||||
-rw-r--r-- | meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch | 45 | ||||
-rw-r--r-- | meta-oe/recipes-devtools/jq/jq_1.7.1.bb | 3 |
4 files changed, 360 insertions, 0 deletions
diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch new file mode 100644 index 0000000000..93f55eecd0 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch | |||
@@ -0,0 +1,233 @@ | |||
1 | From d9237e3d607f946fe74540efa42a2eacca2a6fbd Mon Sep 17 00:00:00 2001 | ||
2 | From: itchyny <itchyny@cybozu.co.jp> | ||
3 | Date: Wed, 21 May 2025 07:45:00 +0900 | ||
4 | Subject: [PATCH] Fix signed integer overflow in jvp_array_write and | ||
5 | jvp_object_rehash | ||
6 | |||
7 | This commit fixes signed integer overflow and SEGV issues on growing | ||
8 | arrays and objects. The size of arrays and objects is now limited to | ||
9 | `536870912` (`0x20000000`). This fixes CVE-2024-23337 and fixes #3262. | ||
10 | |||
11 | (cherry picked from commit de21386681c0df0104a99d9d09db23a9b2a78b1e) | ||
12 | Signed-off-by: Roland Kovacs <roland.kovacs@est.tech> | ||
13 | --- | ||
14 | src/jv.c | 57 ++++++++++++++++++++++++++++++++++++++++----------- | ||
15 | src/jv_aux.c | 9 ++++---- | ||
16 | tests/jq.test | 4 ++++ | ||
17 | 3 files changed, 54 insertions(+), 16 deletions(-) | ||
18 | |||
19 | diff --git a/src/jv.c b/src/jv.c | ||
20 | index 34573b8..15990f1 100644 | ||
21 | --- a/src/jv.c | ||
22 | +++ b/src/jv.c | ||
23 | @@ -1001,6 +1001,11 @@ jv jv_array_set(jv j, int idx, jv val) { | ||
24 | jv_free(val); | ||
25 | return jv_invalid_with_msg(jv_string("Out of bounds negative array index")); | ||
26 | } | ||
27 | + if (idx > (INT_MAX >> 2) - jvp_array_offset(j)) { | ||
28 | + jv_free(j); | ||
29 | + jv_free(val); | ||
30 | + return jv_invalid_with_msg(jv_string("Array index too large")); | ||
31 | + } | ||
32 | // copy/free of val,j coalesced | ||
33 | jv* slot = jvp_array_write(&j, idx); | ||
34 | jv_free(*slot); | ||
35 | @@ -1020,6 +1025,7 @@ jv jv_array_concat(jv a, jv b) { | ||
36 | // FIXME: could be faster | ||
37 | jv_array_foreach(b, i, elem) { | ||
38 | a = jv_array_append(a, elem); | ||
39 | + if (!jv_is_valid(a)) break; | ||
40 | } | ||
41 | jv_free(b); | ||
42 | return a; | ||
43 | @@ -1283,15 +1289,22 @@ jv jv_string_indexes(jv j, jv k) { | ||
44 | assert(JVP_HAS_KIND(k, JV_KIND_STRING)); | ||
45 | const char *jstr = jv_string_value(j); | ||
46 | const char *idxstr = jv_string_value(k); | ||
47 | - const char *p; | ||
48 | + const char *p, *lp; | ||
49 | int jlen = jv_string_length_bytes(jv_copy(j)); | ||
50 | int idxlen = jv_string_length_bytes(jv_copy(k)); | ||
51 | jv a = jv_array(); | ||
52 | |||
53 | if (idxlen != 0) { | ||
54 | - p = jstr; | ||
55 | + int n = 0; | ||
56 | + p = lp = jstr; | ||
57 | while ((p = _jq_memmem(p, (jstr + jlen) - p, idxstr, idxlen)) != NULL) { | ||
58 | - a = jv_array_append(a, jv_number(p - jstr)); | ||
59 | + while (lp < p) { | ||
60 | + lp += jvp_utf8_decode_length(*lp); | ||
61 | + n++; | ||
62 | + } | ||
63 | + | ||
64 | + a = jv_array_append(a, jv_number(n)); | ||
65 | + if (!jv_is_valid(a)) break; | ||
66 | p++; | ||
67 | } | ||
68 | } | ||
69 | @@ -1314,14 +1327,17 @@ jv jv_string_split(jv j, jv sep) { | ||
70 | |||
71 | if (seplen == 0) { | ||
72 | int c; | ||
73 | - while ((jstr = jvp_utf8_next(jstr, jend, &c))) | ||
74 | + while ((jstr = jvp_utf8_next(jstr, jend, &c))) { | ||
75 | a = jv_array_append(a, jv_string_append_codepoint(jv_string(""), c)); | ||
76 | + if (!jv_is_valid(a)) break; | ||
77 | + } | ||
78 | } else { | ||
79 | for (p = jstr; p < jend; p = s + seplen) { | ||
80 | s = _jq_memmem(p, jend - p, sepstr, seplen); | ||
81 | if (s == NULL) | ||
82 | s = jend; | ||
83 | a = jv_array_append(a, jv_string_sized(p, s - p)); | ||
84 | + if (!jv_is_valid(a)) break; | ||
85 | // Add an empty string to denote that j ends on a sep | ||
86 | if (s + seplen == jend && seplen != 0) | ||
87 | a = jv_array_append(a, jv_string("")); | ||
88 | @@ -1339,8 +1355,10 @@ jv jv_string_explode(jv j) { | ||
89 | const char* end = i + len; | ||
90 | jv a = jv_array_sized(len); | ||
91 | int c; | ||
92 | - while ((i = jvp_utf8_next(i, end, &c))) | ||
93 | + while ((i = jvp_utf8_next(i, end, &c))) { | ||
94 | a = jv_array_append(a, jv_number(c)); | ||
95 | + if (!jv_is_valid(a)) break; | ||
96 | + } | ||
97 | jv_free(j); | ||
98 | return a; | ||
99 | } | ||
100 | @@ -1614,10 +1632,13 @@ static void jvp_object_free(jv o) { | ||
101 | } | ||
102 | } | ||
103 | |||
104 | -static jv jvp_object_rehash(jv object) { | ||
105 | +static int jvp_object_rehash(jv *objectp) { | ||
106 | + jv object = *objectp; | ||
107 | assert(JVP_HAS_KIND(object, JV_KIND_OBJECT)); | ||
108 | assert(jvp_refcnt_unshared(object.u.ptr)); | ||
109 | int size = jvp_object_size(object); | ||
110 | + if (size > INT_MAX >> 2) | ||
111 | + return 0; | ||
112 | jv new_object = jvp_object_new(size * 2); | ||
113 | for (int i=0; i<size; i++) { | ||
114 | struct object_slot* slot = jvp_object_get_slot(object, i); | ||
115 | @@ -1630,7 +1651,8 @@ static jv jvp_object_rehash(jv object) { | ||
116 | } | ||
117 | // references are transported, just drop the old table | ||
118 | jv_mem_free(jvp_object_ptr(object)); | ||
119 | - return new_object; | ||
120 | + *objectp = new_object; | ||
121 | + return 1; | ||
122 | } | ||
123 | |||
124 | static jv jvp_object_unshare(jv object) { | ||
125 | @@ -1659,27 +1681,32 @@ static jv jvp_object_unshare(jv object) { | ||
126 | return new_object; | ||
127 | } | ||
128 | |||
129 | -static jv* jvp_object_write(jv* object, jv key) { | ||
130 | +static int jvp_object_write(jv* object, jv key, jv **valpp) { | ||
131 | *object = jvp_object_unshare(*object); | ||
132 | int* bucket = jvp_object_find_bucket(*object, key); | ||
133 | struct object_slot* slot = jvp_object_find_slot(*object, key, bucket); | ||
134 | if (slot) { | ||
135 | // already has the key | ||
136 | jvp_string_free(key); | ||
137 | - return &slot->value; | ||
138 | + *valpp = &slot->value; | ||
139 | + return 1; | ||
140 | } | ||
141 | slot = jvp_object_add_slot(*object, key, bucket); | ||
142 | if (slot) { | ||
143 | slot->value = jv_invalid(); | ||
144 | } else { | ||
145 | - *object = jvp_object_rehash(*object); | ||
146 | + if (!jvp_object_rehash(object)) { | ||
147 | + *valpp = NULL; | ||
148 | + return 0; | ||
149 | + } | ||
150 | bucket = jvp_object_find_bucket(*object, key); | ||
151 | assert(!jvp_object_find_slot(*object, key, bucket)); | ||
152 | slot = jvp_object_add_slot(*object, key, bucket); | ||
153 | assert(slot); | ||
154 | slot->value = jv_invalid(); | ||
155 | } | ||
156 | - return &slot->value; | ||
157 | + *valpp = &slot->value; | ||
158 | + return 1; | ||
159 | } | ||
160 | |||
161 | static int jvp_object_delete(jv* object, jv key) { | ||
162 | @@ -1779,7 +1806,11 @@ jv jv_object_set(jv object, jv key, jv value) { | ||
163 | assert(JVP_HAS_KIND(object, JV_KIND_OBJECT)); | ||
164 | assert(JVP_HAS_KIND(key, JV_KIND_STRING)); | ||
165 | // copy/free of object, key, value coalesced | ||
166 | - jv* slot = jvp_object_write(&object, key); | ||
167 | + jv* slot; | ||
168 | + if (!jvp_object_write(&object, key, &slot)) { | ||
169 | + jv_free(object); | ||
170 | + return jv_invalid_with_msg(jv_string("Object too big")); | ||
171 | + } | ||
172 | jv_free(*slot); | ||
173 | *slot = value; | ||
174 | return object; | ||
175 | @@ -1804,6 +1835,7 @@ jv jv_object_merge(jv a, jv b) { | ||
176 | assert(JVP_HAS_KIND(a, JV_KIND_OBJECT)); | ||
177 | jv_object_foreach(b, k, v) { | ||
178 | a = jv_object_set(a, k, v); | ||
179 | + if (!jv_is_valid(a)) break; | ||
180 | } | ||
181 | jv_free(b); | ||
182 | return a; | ||
183 | @@ -1823,6 +1855,7 @@ jv jv_object_merge_recursive(jv a, jv b) { | ||
184 | jv_free(elem); | ||
185 | a = jv_object_set(a, k, v); | ||
186 | } | ||
187 | + if (!jv_is_valid(a)) break; | ||
188 | } | ||
189 | jv_free(b); | ||
190 | return a; | ||
191 | diff --git a/src/jv_aux.c b/src/jv_aux.c | ||
192 | index 6004799..bbe1c0d 100644 | ||
193 | --- a/src/jv_aux.c | ||
194 | +++ b/src/jv_aux.c | ||
195 | @@ -193,18 +193,19 @@ jv jv_set(jv t, jv k, jv v) { | ||
196 | if (slice_len < insert_len) { | ||
197 | // array is growing | ||
198 | int shift = insert_len - slice_len; | ||
199 | - for (int i = array_len - 1; i >= end; i--) { | ||
200 | + for (int i = array_len - 1; i >= end && jv_is_valid(t); i--) { | ||
201 | t = jv_array_set(t, i + shift, jv_array_get(jv_copy(t), i)); | ||
202 | } | ||
203 | } else if (slice_len > insert_len) { | ||
204 | // array is shrinking | ||
205 | int shift = slice_len - insert_len; | ||
206 | - for (int i = end; i < array_len; i++) { | ||
207 | + for (int i = end; i < array_len && jv_is_valid(t); i++) { | ||
208 | t = jv_array_set(t, i - shift, jv_array_get(jv_copy(t), i)); | ||
209 | } | ||
210 | - t = jv_array_slice(t, 0, array_len - shift); | ||
211 | + if (jv_is_valid(t)) | ||
212 | + t = jv_array_slice(t, 0, array_len - shift); | ||
213 | } | ||
214 | - for (int i=0; i < insert_len; i++) { | ||
215 | + for (int i = 0; i < insert_len && jv_is_valid(t); i++) { | ||
216 | t = jv_array_set(t, start + i, jv_array_get(jv_copy(v), i)); | ||
217 | } | ||
218 | jv_free(v); | ||
219 | diff --git a/tests/jq.test b/tests/jq.test | ||
220 | index d052b22..22bfd3a 100644 | ||
221 | --- a/tests/jq.test | ||
222 | +++ b/tests/jq.test | ||
223 | @@ -198,6 +198,10 @@ null | ||
224 | [0,1,2] | ||
225 | [0,5,2] | ||
226 | |||
227 | +try (.[999999999] = 0) catch . | ||
228 | +null | ||
229 | +"Array index too large" | ||
230 | + | ||
231 | # | ||
232 | # Multiple outputs, iteration | ||
233 | # | ||
diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427.patch new file mode 100644 index 0000000000..3e27a13036 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427.patch | |||
@@ -0,0 +1,79 @@ | |||
1 | From fa6131eb6e9d43e88e35982fa5f6049da2a77a87 Mon Sep 17 00:00:00 2001 | ||
2 | From: itchyny <itchyny@cybozu.co.jp> | ||
3 | Date: Wed, 5 Mar 2025 07:43:54 +0900 | ||
4 | Subject: [PATCH] Reject NaN with payload while parsing JSON | ||
5 | |||
6 | This commit drops support for parsing NaN with payload in JSON like | ||
7 | `NaN123` and fixes CVE-2024-53427. Other JSON extensions like `NaN` and | ||
8 | `Infinity` are still supported. Fixes #3023, fixes #3196, fixes #3246. | ||
9 | |||
10 | (cherry picked from commit a09a4dfd55e6c24d04b35062ccfe4509748b1dd3) | ||
11 | Signed-off-by: Roland Kovacs <roland.kovacs@est.tech> | ||
12 | --- | ||
13 | src/jv.c | 9 +++++++++ | ||
14 | tests/jq.test | 14 ++++++++++---- | ||
15 | tests/shtest | 5 ----- | ||
16 | 3 files changed, 19 insertions(+), 9 deletions(-) | ||
17 | |||
18 | diff --git a/src/jv.c b/src/jv.c | ||
19 | index e23d8ec..34573b8 100644 | ||
20 | --- a/src/jv.c | ||
21 | +++ b/src/jv.c | ||
22 | @@ -589,6 +589,15 @@ static jv jvp_literal_number_new(const char * literal) { | ||
23 | jv_mem_free(n); | ||
24 | return JV_INVALID; | ||
25 | } | ||
26 | + if (decNumberIsNaN(&n->num_decimal)) { | ||
27 | + // Reject NaN with payload. | ||
28 | + if (n->num_decimal.digits > 1 || *n->num_decimal.lsu != 0) { | ||
29 | + jv_mem_free(n); | ||
30 | + return JV_INVALID; | ||
31 | + } | ||
32 | + jv_mem_free(n); | ||
33 | + return jv_number(NAN); | ||
34 | + } | ||
35 | |||
36 | jv r = {JVP_FLAGS_NUMBER_LITERAL, 0, 0, JV_NUMBER_SIZE_INIT, {&n->refcnt}}; | ||
37 | return r; | ||
38 | diff --git a/tests/jq.test b/tests/jq.test | ||
39 | index 7036df2..d052b22 100644 | ||
40 | --- a/tests/jq.test | ||
41 | +++ b/tests/jq.test | ||
42 | @@ -1938,11 +1938,17 @@ tojson | fromjson | ||
43 | {"a":nan} | ||
44 | {"a":null} | ||
45 | |||
46 | -# also "nan with payload" #2985 | ||
47 | -fromjson | isnan | ||
48 | -"nan1234" | ||
49 | +# NaN with payload is not parsed | ||
50 | +.[] | try (fromjson | isnan) catch . | ||
51 | +["NaN","-NaN","NaN1","NaN10","NaN100","NaN1000","NaN10000","NaN100000"] | ||
52 | true | ||
53 | - | ||
54 | +true | ||
55 | +"Invalid numeric literal at EOF at line 1, column 4 (while parsing 'NaN1')" | ||
56 | +"Invalid numeric literal at EOF at line 1, column 5 (while parsing 'NaN10')" | ||
57 | +"Invalid numeric literal at EOF at line 1, column 6 (while parsing 'NaN100')" | ||
58 | +"Invalid numeric literal at EOF at line 1, column 7 (while parsing 'NaN1000')" | ||
59 | +"Invalid numeric literal at EOF at line 1, column 8 (while parsing 'NaN10000')" | ||
60 | +"Invalid numeric literal at EOF at line 1, column 9 (while parsing 'NaN100000')" | ||
61 | |||
62 | # calling input/0, or debug/0 in a test doesn't crash jq | ||
63 | |||
64 | diff --git a/tests/shtest b/tests/shtest | ||
65 | index 14aafbf..a471889 100755 | ||
66 | --- a/tests/shtest | ||
67 | +++ b/tests/shtest | ||
68 | @@ -594,11 +594,6 @@ if ! x=$($JQ -n "1 # foo$cr + 2") || [ "$x" != 1 ]; then | ||
69 | exit 1 | ||
70 | fi | ||
71 | |||
72 | -# CVE-2023-50268: No stack overflow comparing a nan with a large payload | ||
73 | -$VALGRIND $Q $JQ '1 != .' <<\EOF >/dev/null | ||
74 | -Nan4000 | ||
75 | -EOF | ||
76 | - | ||
77 | # Allow passing the inline jq script before -- #2919 | ||
78 | if ! r=$($JQ --args -rn -- '$ARGS.positional[0]' bar) || [ "$r" != bar ]; then | ||
79 | echo "passing the inline script after -- didn't work" | ||
diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch new file mode 100644 index 0000000000..237a50413f --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch | |||
@@ -0,0 +1,45 @@ | |||
1 | From 35c08446e4bcd89e0e87e7750c68306d6c0e9ec5 Mon Sep 17 00:00:00 2001 | ||
2 | From: itchyny <itchyny@cybozu.co.jp> | ||
3 | Date: Sat, 31 May 2025 11:46:40 +0900 | ||
4 | Subject: [PATCH] Fix heap buffer overflow when formatting an empty string | ||
5 | |||
6 | The `jv_string_empty` did not properly null-terminate the string data, | ||
7 | which could lead to a heap buffer overflow. The test case of | ||
8 | GHSA-p7rr-28xf-3m5w (`0[""*0]`) was fixed by the commit dc849e9bb74a, | ||
9 | but another case (`0[[]|implode]`) was still vulnerable. This commit | ||
10 | ensures string data is properly null-terminated, and fixes CVE-2025-48060. | ||
11 | |||
12 | (cherry picked from commit c6e041699d8cd31b97375a2596217aff2cfca85b) | ||
13 | Signed-off-by: Roland Kovacs <roland.kovacs@est.tech> | ||
14 | --- | ||
15 | src/jv.c | 1 + | ||
16 | tests/jq.test | 4 ++++ | ||
17 | 2 files changed, 5 insertions(+) | ||
18 | |||
19 | diff --git a/src/jv.c b/src/jv.c | ||
20 | index 15990f1..18dbb54 100644 | ||
21 | --- a/src/jv.c | ||
22 | +++ b/src/jv.c | ||
23 | @@ -1125,6 +1125,7 @@ static jv jvp_string_empty_new(uint32_t length) { | ||
24 | jvp_string* s = jvp_string_alloc(length); | ||
25 | s->length_hashed = 0; | ||
26 | memset(s->data, 0, length); | ||
27 | + s->data[length] = 0; | ||
28 | jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}}; | ||
29 | return r; | ||
30 | } | ||
31 | diff --git a/tests/jq.test b/tests/jq.test | ||
32 | index 22bfd3a..ecb9116 100644 | ||
33 | --- a/tests/jq.test | ||
34 | +++ b/tests/jq.test | ||
35 | @@ -2030,6 +2030,10 @@ map(try implode catch .) | ||
36 | [123,["a"],[nan]] | ||
37 | ["implode input must be an array","string (\"a\") can't be imploded, unicode codepoint needs to be numeric","number (null) can't be imploded, unicode codepoint needs to be numeric"] | ||
38 | |||
39 | +try 0[implode] catch . | ||
40 | +[] | ||
41 | +"Cannot index number with string \"\"" | ||
42 | + | ||
43 | # walk | ||
44 | walk(.) | ||
45 | {"x":0} | ||
diff --git a/meta-oe/recipes-devtools/jq/jq_1.7.1.bb b/meta-oe/recipes-devtools/jq/jq_1.7.1.bb index 6b12335513..9238474319 100644 --- a/meta-oe/recipes-devtools/jq/jq_1.7.1.bb +++ b/meta-oe/recipes-devtools/jq/jq_1.7.1.bb | |||
@@ -11,6 +11,9 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=488f4e0b04c0456337fb70d1ac1758ba" | |||
11 | GITHUB_BASE_URI = "https://github.com/jqlang/${BPN}/releases/" | 11 | GITHUB_BASE_URI = "https://github.com/jqlang/${BPN}/releases/" |
12 | SRC_URI = "${GITHUB_BASE_URI}/download/${BPN}-${PV}/${BPN}-${PV}.tar.gz \ | 12 | SRC_URI = "${GITHUB_BASE_URI}/download/${BPN}-${PV}/${BPN}-${PV}.tar.gz \ |
13 | file://run-ptest \ | 13 | file://run-ptest \ |
14 | file://CVE-2024-23337.patch \ | ||
15 | file://CVE-2024-53427.patch \ | ||
16 | file://CVE-2025-48060.patch \ | ||
14 | " | 17 | " |
15 | SRC_URI[sha256sum] = "478c9ca129fd2e3443fe27314b455e211e0d8c60bc8ff7df703873deeee580c2" | 18 | SRC_URI[sha256sum] = "478c9ca129fd2e3443fe27314b455e211e0d8c60bc8ff7df703873deeee580c2" |
16 | 19 | ||