Skip to content

Commit c072ac5

Browse files
committed
Auto merge of #155171 - cuviper:musl-cves, r=<try>
Patch musl's CVE-2026-6042 and CVE-2025-26519 try-job: dist-arm-linux-musl try-job: dist-i586-gnu-i586-i686-musl try-job: dist-various-1 try-job: dist-various-2 try-job: dist-x86_64-musl try-job: test-various
2 parents bf4fbfb + e7f91d9 commit c072ac5

10 files changed

Lines changed: 531 additions & 0 deletions

File tree

src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ RUN sh /scripts/cross-apt-packages.sh
66
WORKDIR /build
77

88
COPY scripts/musl-toolchain.sh /build/
9+
COPY scripts/musl-cve-2026-6042.diff /build/
10+
COPY scripts/musl-cve-2026-40200.diff /build/
911
# We need to mitigate rust-lang/rust#34978 when compiling musl itself as well
1012
RUN CFLAGS="-Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \
1113
CXXFLAGS="-Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \

src/ci/docker/host-x86_64/dist-i586-gnu-i586-i686-musl/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ ENV \
3939

4040
WORKDIR /build/
4141
COPY scripts/musl.sh /build/
42+
COPY scripts/musl-cve-2026-6042.diff /build/
43+
COPY scripts/musl-cve-2026-40200.diff /build/
4244
RUN CC=gcc CFLAGS="-m32 -Wa,-mrelax-relocations=no" \
4345
CXX=g++ CXXFLAGS="-m32 -Wa,-mrelax-relocations=no" \
4446
bash musl.sh i686 --target=i686 && \

src/ci/docker/host-x86_64/dist-various-1/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ ENV PATH="/build/emsdk:/build/emsdk/upstream/emscripten:/build/emsdk/node/curren
6565
ENV STAGING_DIR=/tmp
6666

6767
COPY scripts/musl.sh /build
68+
COPY scripts/musl-cve-2026-6042.diff /build/
69+
COPY scripts/musl-cve-2026-40200.diff /build/
6870
RUN env \
6971
CC=arm-linux-gnueabi-gcc CFLAGS="-march=armv5te -marm -mfloat-abi=soft" \
7072
CXX=arm-linux-gnueabi-g++ CXXFLAGS="-march=armv5te -marm -mfloat-abi=soft" \

src/ci/docker/host-x86_64/dist-various-2/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ ENV \
6868

6969
WORKDIR /build
7070
COPY scripts/musl.sh /build
71+
COPY scripts/musl-cve-2026-6042.diff /build/
72+
COPY scripts/musl-cve-2026-40200.diff /build/
7173
RUN env \
7274
CC=arm-linux-gnueabi-gcc-9 CFLAGS="-march=armv7-a" \
7375
CXX=arm-linux-gnueabi-g++-9 CXXFLAGS="-march=armv7-a" \

src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
2525
WORKDIR /build/
2626

2727
COPY scripts/musl-toolchain.sh /build/
28+
COPY scripts/musl-cve-2026-6042.diff /build/
29+
COPY scripts/musl-cve-2026-40200.diff /build/
2830
# We need to mitigate rust-lang/rust#34978 when compiling musl itself as well
2931
RUN CFLAGS="-Wa,-mrelax-relocations=no -Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \
3032
CXXFLAGS="-Wa,-mrelax-relocations=no -Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \

src/ci/docker/host-x86_64/test-various/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ ENV PATH="/node/bin:${PATH}"
3535

3636
WORKDIR /build/
3737
COPY scripts/musl-toolchain.sh /build/
38+
COPY scripts/musl-cve-2026-6042.diff /build/
39+
COPY scripts/musl-cve-2026-40200.diff /build/
3840
RUN bash musl-toolchain.sh x86_64 && rm -rf build
3941
WORKDIR /
4042

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
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: [PATCH 1/3] qsort: fix leonardo heap corruption from bug in
5+
doubleword ctz 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+
diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c
23+
index ab79dc6f..13219ab3 100644
24+
--- a/src/stdlib/qsort.c
25+
+++ b/src/stdlib/qsort.c
26+
@@ -34,11 +34,11 @@
27+
28+
typedef int (*cmpfun)(const void *, const void *, void *);
29+
30+
+/* returns index of first bit set, excluding the low bit assumed to always
31+
+ * be set, starting from low bit of p[0] up through high bit of p[1] */
32+
static inline int pntz(size_t p[2]) {
33+
- int r = ntz(p[0] - 1);
34+
- if(r != 0 || (r = 8*sizeof(size_t) + ntz(p[1])) != 8*sizeof(size_t)) {
35+
- return r;
36+
- }
37+
+ if (p[0] != 1) return ntz(p[0] - 1);
38+
+ if (p[1]) return 8*sizeof(size_t) + ntz(p[1]);
39+
return 0;
40+
}
41+
42+
--
43+
2.21.0
44+
45+
46+
>From b3291b9a9f77f1f993d2b4f8c68a26cf09221ae7 Mon Sep 17 00:00:00 2001
47+
From: Rich Felker <dalias@aerifal.cx>
48+
Date: Thu, 9 Apr 2026 23:40:53 -0400
49+
Subject: [PATCH 2/3] qsort: hard-preclude oob array writes independent of any
50+
invariants
51+
52+
while the root cause of CVE-2026-40200 was a faulty ctz primitive, the
53+
fallout of the bug would have been limited to erroneous sorting or
54+
infinite loop if not for the stores to a stack-based array that
55+
depended on trusting invariants in order not to go out of bounds.
56+
57+
increase the size of the array to a power of two so that we can mask
58+
indices into it to force them into range. in the absence of any
59+
further bug, the masking is a no-op, but it does not have any
60+
measurable performance cost, and it makes spatial memory safety
61+
trivial to prove (and for readers not familiar with the algorithms to
62+
trust).
63+
---
64+
src/stdlib/qsort.c | 20 +++++++++++++-------
65+
1 file changed, 13 insertions(+), 7 deletions(-)
66+
67+
diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c
68+
index 13219ab3..e4bce9f7 100644
69+
--- a/src/stdlib/qsort.c
70+
+++ b/src/stdlib/qsort.c
71+
@@ -89,10 +89,16 @@ static inline void shr(size_t p[2], int n)
72+
p[1] >>= n;
73+
}
74+
75+
+/* power-of-two length for working array so that we can mask indices and
76+
+ * not depend on any invariant of the algorithm for spatial memory safety.
77+
+ * the original size was just 14*sizeof(size_t)+1 */
78+
+#define AR_LEN (16 * sizeof(size_t))
79+
+#define AR_MASK (AR_LEN - 1)
80+
+
81+
static void sift(unsigned char *head, size_t width, cmpfun cmp, void *arg, int pshift, size_t lp[])
82+
{
83+
unsigned char *rt, *lf;
84+
- unsigned char *ar[14 * sizeof(size_t) + 1];
85+
+ unsigned char *ar[AR_LEN];
86+
int i = 1;
87+
88+
ar[0] = head;
89+
@@ -104,16 +110,16 @@ static void sift(unsigned char *head, size_t width, cmpfun cmp, void *arg, int p
90+
break;
91+
}
92+
if(cmp(lf, rt, arg) >= 0) {
93+
- ar[i++] = lf;
94+
+ ar[i++ & AR_MASK] = lf;
95+
head = lf;
96+
pshift -= 1;
97+
} else {
98+
- ar[i++] = rt;
99+
+ ar[i++ & AR_MASK] = rt;
100+
head = rt;
101+
pshift -= 2;
102+
}
103+
}
104+
- cycle(width, ar, i);
105+
+ cycle(width, ar, i & AR_MASK);
106+
}
107+
108+
static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, size_t pp[2], int pshift, int trusty, size_t lp[])
109+
@@ -121,7 +127,7 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, si
110+
unsigned char *stepson,
111+
*rt, *lf;
112+
size_t p[2];
113+
- unsigned char *ar[14 * sizeof(size_t) + 1];
114+
+ unsigned char *ar[AR_LEN];
115+
int i = 1;
116+
int trail;
117+
118+
@@ -142,7 +148,7 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, si
119+
}
120+
}
121+
122+
- ar[i++] = stepson;
123+
+ ar[i++ & AR_MASK] = stepson;
124+
head = stepson;
125+
trail = pntz(p);
126+
shr(p, trail);
127+
@@ -150,7 +156,7 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, si
128+
trusty = 0;
129+
}
130+
if(!trusty) {
131+
- cycle(width, ar, i);
132+
+ cycle(width, ar, i & AR_MASK);
133+
sift(head, width, cmp, arg, pshift, lp);
134+
}
135+
}
136+
--
137+
2.21.0
138+
139+
140+
>From 5122f9f3c99fee366167c5de98b31546312921ab Mon Sep 17 00:00:00 2001
141+
From: Luca Kellermann <mailto.luca.kellermann@gmail.com>
142+
Date: Fri, 10 Apr 2026 03:03:22 +0200
143+
Subject: [PATCH 3/3] qsort: fix shift UB in shl and shr
144+
145+
if shl() or shr() are called with n==8*sizeof(size_t), n is adjusted
146+
to 0. the shift by (sizeof(size_t) * 8 - n) that then follows will
147+
consequently shift by the width of size_t, which is UB and in practice
148+
produces an incorrect result.
149+
150+
return early in this case. the bitvector p was already shifted by the
151+
required amount.
152+
---
153+
src/stdlib/qsort.c | 2 ++
154+
1 file changed, 2 insertions(+)
155+
156+
diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c
157+
index e4bce9f7..28607450 100644
158+
--- a/src/stdlib/qsort.c
159+
+++ b/src/stdlib/qsort.c
160+
@@ -71,6 +71,7 @@ static inline void shl(size_t p[2], int n)
161+
n -= 8 * sizeof(size_t);
162+
p[1] = p[0];
163+
p[0] = 0;
164+
+ if (!n) return;
165+
}
166+
p[1] <<= n;
167+
p[1] |= p[0] >> (sizeof(size_t) * 8 - n);
168+
@@ -83,6 +84,7 @@ static inline void shr(size_t p[2], int n)
169+
n -= 8 * sizeof(size_t);
170+
p[0] = p[1];
171+
p[1] = 0;
172+
+ if (!n) return;
173+
}
174+
p[0] >>= n;
175+
p[0] |= p[1] << (sizeof(size_t) * 8 - n);
176+
--
177+
2.21.0
178+
179+

0 commit comments

Comments
 (0)