Skip to content

Commit 084cdac

Browse files
haukerobimarko
authored andcommitted
toolchain: musl: backport patches with CVE fixes
This fixes: * CVE-2026-6042: Algorithmic Complexity DoS in musl libc iconv * CVE-2026-40200: musl libc: stack corruption in qsort with sufficiently large inputs Link: openwrt/openwrt#23329 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> (cherry picked from commit 6865d48) Link: openwrt/openwrt#23332 Signed-off-by: Robert Marko <robimarko@gmail.com>
1 parent e4c15eb commit 084cdac

5 files changed

Lines changed: 481 additions & 9 deletions
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
From 67219f0130ec7c876ac0b299046460fad31caabf Mon Sep 17 00:00:00 2001
2+
From: Rich Felker <dalias@aerifal.cx>
3+
Date: Mon, 30 Mar 2026 16:00:50 -0400
4+
Subject: fix pathological slowness & incorrect mappings in iconv gb18030
5+
decoder
6+
7+
in order to implement the "UTF" aspect of gb18030 (ability to
8+
represent arbitrary unicode characters not present in the 2-byte
9+
mapping), we have to apply the index obtained from the encoded 4-byte
10+
sequence into the set of unmapped characters. this was done by
11+
scanning repeatedly over the table of mapped characters and counting
12+
off mapped characters below a running index by which to adjust the
13+
running index by on each iteration. this iterative process eventually
14+
leaves us with the value of the Nth unmapped character replacing the
15+
index, but depending on which particular character that is, the number
16+
of iterations needed to find it can be in the tens of thousands, and
17+
each iteration traverses the whole 126x190 table in the inner loop.
18+
this can lead to run times exceeding an entire second per character on
19+
moderate-speed machines.
20+
21+
on top of that, the transformation logic produced wrong results for
22+
BMP characters above the the surrogate range, as a result of not
23+
correctly accounting for it being excluded, and for characters outside
24+
the BMP, as a result of a misunderstanding of how gb18030 encodes
25+
them.
26+
27+
this patch replaces the unmapped character lookup with a single linear
28+
search of a list of unmapped ranges. there are only 206 such ranges,
29+
and these are permanently assigned and unchangeable as a consequence
30+
of the character encoding having to be stable, so a simple array of
31+
16-bit start/length values for each range consumes only 824 bytes, a
32+
very reasonable size cost here.
33+
34+
this new table accounts for the previously-incorrect surrogate
35+
handling, and non-BMP characters are handled correctly by a single
36+
offset, without the need for any unmapped-range search.
37+
38+
there are still a small number of mappings that are incorrect due to
39+
late changes made in the definition of gb18030, swapping PUA
40+
codepoints with proper Unicode characters. correcting these requires a
41+
postprocessing step that will be added later.
42+
---
43+
src/locale/gb18030utf.h | 206 ++++++++++++++++++++++++++++++++++++++++
44+
src/locale/iconv.c | 33 +++++--
45+
2 files changed, 230 insertions(+), 9 deletions(-)
46+
create mode 100644 src/locale/gb18030utf.h
47+
48+
--- /dev/null
49+
+++ b/src/locale/gb18030utf.h
50+
@@ -0,0 +1,206 @@
51+
+{ 0x80, 36 },
52+
+{ 0xa5, 2 },
53+
+{ 0xa9, 7 },
54+
+{ 0xb2, 5 },
55+
+{ 0xb8, 31 },
56+
+{ 0xd8, 8 },
57+
+{ 0xe2, 6 },
58+
+{ 0xeb, 1 },
59+
+{ 0xee, 4 },
60+
+{ 0xf4, 3 },
61+
+{ 0xf8, 1 },
62+
+{ 0xfb, 1 },
63+
+{ 0xfd, 4 },
64+
+{ 0x102, 17 },
65+
+{ 0x114, 7 },
66+
+{ 0x11c, 15 },
67+
+{ 0x12c, 24 },
68+
+{ 0x145, 3 },
69+
+{ 0x149, 4 },
70+
+{ 0x14e, 29 },
71+
+{ 0x16c, 98 },
72+
+{ 0x1cf, 1 },
73+
+{ 0x1d1, 1 },
74+
+{ 0x1d3, 1 },
75+
+{ 0x1d5, 1 },
76+
+{ 0x1d7, 1 },
77+
+{ 0x1d9, 1 },
78+
+{ 0x1db, 1 },
79+
+{ 0x1dd, 28 },
80+
+{ 0x1fa, 87 },
81+
+{ 0x252, 15 },
82+
+{ 0x262, 101 },
83+
+{ 0x2c8, 1 },
84+
+{ 0x2cc, 13 },
85+
+{ 0x2da, 183 },
86+
+{ 0x3a2, 1 },
87+
+{ 0x3aa, 7 },
88+
+{ 0x3c2, 1 },
89+
+{ 0x3ca, 55 },
90+
+{ 0x402, 14 },
91+
+{ 0x450, 1 },
92+
+{ 0x452, 7102 },
93+
+{ 0x2011, 2 },
94+
+{ 0x2017, 1 },
95+
+{ 0x201a, 2 },
96+
+{ 0x201e, 7 },
97+
+{ 0x2027, 9 },
98+
+{ 0x2031, 1 },
99+
+{ 0x2034, 1 },
100+
+{ 0x2036, 5 },
101+
+{ 0x203c, 112 },
102+
+{ 0x20ad, 86 },
103+
+{ 0x2104, 1 },
104+
+{ 0x2106, 3 },
105+
+{ 0x210a, 12 },
106+
+{ 0x2117, 10 },
107+
+{ 0x2122, 62 },
108+
+{ 0x216c, 4 },
109+
+{ 0x217a, 22 },
110+
+{ 0x2194, 2 },
111+
+{ 0x219a, 110 },
112+
+{ 0x2209, 6 },
113+
+{ 0x2210, 1 },
114+
+{ 0x2212, 3 },
115+
+{ 0x2216, 4 },
116+
+{ 0x221b, 2 },
117+
+{ 0x2221, 2 },
118+
+{ 0x2224, 1 },
119+
+{ 0x2226, 1 },
120+
+{ 0x222c, 2 },
121+
+{ 0x222f, 5 },
122+
+{ 0x2238, 5 },
123+
+{ 0x223e, 10 },
124+
+{ 0x2249, 3 },
125+
+{ 0x224d, 5 },
126+
+{ 0x2253, 13 },
127+
+{ 0x2262, 2 },
128+
+{ 0x2268, 6 },
129+
+{ 0x2270, 37 },
130+
+{ 0x2296, 3 },
131+
+{ 0x229a, 11 },
132+
+{ 0x22a6, 25 },
133+
+{ 0x22c0, 82 },
134+
+{ 0x2313, 333 },
135+
+{ 0x246a, 10 },
136+
+{ 0x249c, 100 },
137+
+{ 0x254c, 4 },
138+
+{ 0x2574, 13 },
139+
+{ 0x2590, 3 },
140+
+{ 0x2596, 10 },
141+
+{ 0x25a2, 16 },
142+
+{ 0x25b4, 8 },
143+
+{ 0x25be, 8 },
144+
+{ 0x25c8, 3 },
145+
+{ 0x25cc, 2 },
146+
+{ 0x25d0, 18 },
147+
+{ 0x25e6, 31 },
148+
+{ 0x2607, 2 },
149+
+{ 0x260a, 54 },
150+
+{ 0x2641, 1 },
151+
+{ 0x2643, 2110 },
152+
+{ 0x2e82, 2 },
153+
+{ 0x2e85, 3 },
154+
+{ 0x2e89, 2 },
155+
+{ 0x2e8d, 10 },
156+
+{ 0x2e98, 15 },
157+
+{ 0x2ea8, 2 },
158+
+{ 0x2eab, 3 },
159+
+{ 0x2eaf, 4 },
160+
+{ 0x2eb4, 2 },
161+
+{ 0x2eb8, 3 },
162+
+{ 0x2ebc, 14 },
163+
+{ 0x2ecb, 293 },
164+
+{ 0x2ffc, 4 },
165+
+{ 0x3004, 1 },
166+
+{ 0x3018, 5 },
167+
+{ 0x301f, 2 },
168+
+{ 0x302a, 20 },
169+
+{ 0x303f, 2 },
170+
+{ 0x3094, 7 },
171+
+{ 0x309f, 2 },
172+
+{ 0x30f7, 5 },
173+
+{ 0x30ff, 6 },
174+
+{ 0x312a, 246 },
175+
+{ 0x322a, 7 },
176+
+{ 0x3232, 113 },
177+
+{ 0x32a4, 234 },
178+
+{ 0x3390, 12 },
179+
+{ 0x339f, 2 },
180+
+{ 0x33a2, 34 },
181+
+{ 0x33c5, 9 },
182+
+{ 0x33cf, 2 },
183+
+{ 0x33d3, 2 },
184+
+{ 0x33d6, 113 },
185+
+{ 0x3448, 43 },
186+
+{ 0x3474, 298 },
187+
+{ 0x359f, 111 },
188+
+{ 0x360f, 11 },
189+
+{ 0x361b, 765 },
190+
+{ 0x3919, 85 },
191+
+{ 0x396f, 96 },
192+
+{ 0x39d1, 14 },
193+
+{ 0x39e0, 147 },
194+
+{ 0x3a74, 218 },
195+
+{ 0x3b4f, 287 },
196+
+{ 0x3c6f, 113 },
197+
+{ 0x3ce1, 885 },
198+
+{ 0x4057, 264 },
199+
+{ 0x4160, 471 },
200+
+{ 0x4338, 116 },
201+
+{ 0x43ad, 4 },
202+
+{ 0x43b2, 43 },
203+
+{ 0x43de, 248 },
204+
+{ 0x44d7, 373 },
205+
+{ 0x464d, 20 },
206+
+{ 0x4662, 193 },
207+
+{ 0x4724, 5 },
208+
+{ 0x472a, 82 },
209+
+{ 0x477d, 16 },
210+
+{ 0x478e, 441 },
211+
+{ 0x4948, 50 },
212+
+{ 0x497b, 2 },
213+
+{ 0x497e, 4 },
214+
+{ 0x4984, 1 },
215+
+{ 0x4987, 20 },
216+
+{ 0x499c, 3 },
217+
+{ 0x49a0, 22 },
218+
+{ 0x49b8, 703 },
219+
+{ 0x4c78, 39 },
220+
+{ 0x4ca4, 111 },
221+
+{ 0x4d1a, 148 },
222+
+{ 0x4daf, 81 },
223+
+{ 0x9fa6, 14426 },
224+
+{ 0xe76c, 1 },
225+
+{ 0xe7c8, 1 },
226+
+{ 0xe7e7, 13 },
227+
+{ 0xe815, 1 },
228+
+{ 0xe819, 5 },
229+
+{ 0xe81f, 7 },
230+
+{ 0xe827, 4 },
231+
+{ 0xe82d, 4 },
232+
+{ 0xe833, 8 },
233+
+{ 0xe83c, 7 },
234+
+{ 0xe844, 16 },
235+
+{ 0xe856, 14 },
236+
+{ 0xe865, 4295 },
237+
+{ 0xf92d, 76 },
238+
+{ 0xf97a, 27 },
239+
+{ 0xf996, 81 },
240+
+{ 0xf9e8, 9 },
241+
+{ 0xf9f2, 26 },
242+
+{ 0xfa10, 1 },
243+
+{ 0xfa12, 1 },
244+
+{ 0xfa15, 3 },
245+
+{ 0xfa19, 6 },
246+
+{ 0xfa22, 1 },
247+
+{ 0xfa25, 2 },
248+
+{ 0xfa2a, 1030 },
249+
+{ 0xfe32, 1 },
250+
+{ 0xfe45, 4 },
251+
+{ 0xfe53, 1 },
252+
+{ 0xfe58, 1 },
253+
+{ 0xfe67, 1 },
254+
+{ 0xfe6c, 149 },
255+
+{ 0xff5f, 129 },
256+
+{ 0xffe6, 26 },
257+
--- a/src/locale/iconv.c
258+
+++ b/src/locale/iconv.c
259+
@@ -74,6 +74,10 @@ static const unsigned short gb18030[126]
260+
#include "gb18030.h"
261+
};
262+
263+
+static const unsigned short gb18030utf[][2] = {
264+
+#include "gb18030utf.h"
265+
+};
266+
+
267+
static const unsigned short big5[89][157] = {
268+
#include "big5.h"
269+
};
270+
@@ -224,6 +228,8 @@ static unsigned uni_to_jis(unsigned c)
271+
}
272+
}
273+
274+
+#define countof(a) (sizeof (a) / sizeof *(a))
275+
+
276+
size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restrict out, size_t *restrict outb)
277+
{
278+
size_t x=0;
279+
@@ -423,15 +429,24 @@ size_t iconv(iconv_t cd, char **restrict
280+
d = *((unsigned char *)*in + 3);
281+
if (d-'0'>9) goto ilseq;
282+
c += d-'0';
283+
- c += 128;
284+
- for (d=0; d<=c; ) {
285+
- k = 0;
286+
- for (int i=0; i<126; i++)
287+
- for (int j=0; j<190; j++)
288+
- if (gb18030[i][j]-d <= c-d)
289+
- k++;
290+
- d = c+1;
291+
- c += k;
292+
+ /* Starting at 90 30 81 30 (189000), mapping is
293+
+ * linear without gaps, to U+10000 and up. */
294+
+ if (c >= 189000) {
295+
+ c -= 189000;
296+
+ c += 0x10000;
297+
+ if (c >= 0x110000) goto ilseq;
298+
+ break;
299+
+ }
300+
+ /* Otherwise we must process an index into set
301+
+ * of characters unmapped by 2-byte table. */
302+
+ for (int i=0; ; i++) {
303+
+ if (i==countof(gb18030utf))
304+
+ goto ilseq;
305+
+ if (c<gb18030utf[i][1]) {
306+
+ c += gb18030utf[i][0];
307+
+ break;
308+
+ }
309+
+ c -= gb18030utf[i][1];
310+
}
311+
break;
312+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
From 228da39e38c1cae13cbe637e771412c1984dba5d Mon Sep 17 00:00:00 2001
2+
From: Rich Felker <dalias@aerifal.cx>
3+
Date: Thu, 9 Apr 2026 22:51:30 -0400
4+
Subject: qsort: fix leonardo heap corruption from bug in doubleword ctz
5+
primitive
6+
7+
the pntz function, implementing a "count trailing zeros" variant for a
8+
bit vector consisting of two size_t words, erroneously returned zero
9+
rather than the number of bits in the low word when the first bit set
10+
was the low bit of the high word.
11+
12+
as a result, a loop in the trinkle function which should have a
13+
guaranteed small bound on the number of iterations, could run
14+
unboundedly, thereby overflowing a stack-based working-space array
15+
which was sized for the bound.
16+
17+
CVE-2026-40200 has been assigned for this issue.
18+
---
19+
src/stdlib/qsort.c | 8 ++++----
20+
1 file changed, 4 insertions(+), 4 deletions(-)
21+
22+
--- a/src/stdlib/qsort.c
23+
+++ b/src/stdlib/qsort.c
24+
@@ -34,11 +34,11 @@
25+
26+
typedef int (*cmpfun)(const void *, const void *, void *);
27+
28+
+/* returns index of first bit set, excluding the low bit assumed to always
29+
+ * be set, starting from low bit of p[0] up through high bit of p[1] */
30+
static inline int pntz(size_t p[2]) {
31+
- int r = ntz(p[0] - 1);
32+
- if(r != 0 || (r = 8*sizeof(size_t) + ntz(p[1])) != 8*sizeof(size_t)) {
33+
- return r;
34+
- }
35+
+ if (p[0] != 1) return ntz(p[0] - 1);
36+
+ if (p[1]) return 8*sizeof(size_t) + ntz(p[1]);
37+
return 0;
38+
}
39+

0 commit comments

Comments
 (0)