Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,10 +455,11 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {

let mut visible_parent_map: DefIdMap<DefId> = Default::default();
// This is a secondary visible_parent_map, storing the DefId of
// parents that re-export the child as `_` or module parents
// which are `#[doc(hidden)]`. Since we prefer paths that don't
// do this, merge this map at the end, only if we're missing
// keys from the former.
// parents that re-export the child as `_`, module parents
// which are `#[doc(hidden)]`, or `use` items that are themselves
// `#[doc(hidden)]`. Since we prefer paths that don't do this,
// merge this map at the end, only if we're missing keys from
// the former.
// This is a rudimentary check that does not catch all cases,
// just the easiest.
let mut fallback_map: Vec<(DefId, DefId)> = Default::default();
Expand Down Expand Up @@ -500,6 +501,18 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
return;
}

// If the re-export itself is `#[doc(hidden)]`, deprioritize it.
// See PR #99698 for the case where the *parent* is doc-hidden.
if child
.reexport_chain
.first()
.and_then(|r| r.id())
.is_some_and(|id| tcx.is_doc_hidden(id))
{
fallback_map.push((def_id, parent));
return;
}

match visible_parent_map.entry(def_id) {
Entry::Occupied(mut entry) => {
// If `child` is defined in crate `cnum`, ensure
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,10 @@ fn should_encode_attrs(def_kind: DefKind) -> bool {
| DefKind::Macro(_)
| DefKind::Field
| DefKind::Impl { .. } => true,
// Encoding attrs for `Use` items allows `#[doc(hidden)]` on re-exports
// to be read cross-crate, which is needed for diagnostic path selection
// in `visible_parent_map`. See #153477.
DefKind::Use => true,
// Tools may want to be able to detect their tool lints on
// closures from upstream crates, too. This is used by
// https://github.com/model-checking/kani and is not a performance
Expand All @@ -953,7 +957,6 @@ fn should_encode_attrs(def_kind: DefKind) -> bool {
| DefKind::ConstParam
| DefKind::Ctor(..)
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::InlineConst
Expand Down
3 changes: 2 additions & 1 deletion library/core/src/ops/control_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ impl<B, C> const ops::FromResidual<ControlFlow<B, convert::Infallible>> for Cont
}

#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
#[rustc_const_unstable(feature = "const_try_residual", issue = "91285")]
impl<B, C> const ops::Residual<C> for ControlFlow<B, convert::Infallible> {
type TryType = ControlFlow<B, C>;
}

Expand Down
24 changes: 20 additions & 4 deletions library/std/src/keyword_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2042,10 +2042,19 @@ mod type_keyword {}
/// system.
///
/// The `unsafe` keyword has two uses:
/// - to declare the existence of contracts the compiler can't check (`unsafe fn` and `unsafe
/// trait`),
/// - and to declare that a programmer has checked that these contracts have been upheld (`unsafe
/// {}` and `unsafe impl`, but also `unsafe fn` -- see below).
/// - to declare the existence of contracts the compiler can't check,
/// - and to declare that a programmer has checked that these contracts have been upheld.
///
/// Typically, each `unsafe` is either of the first or second kind: `unsafe fn` and `unsafe trait`
/// declare the existence of an unsafe contract; `unsafe {}` and `unsafe impl` declare that an
/// unsafe contract (which must have been declared elsewhere) is being upheld.
///
/// However, historically, these two are not mutually exclusive: the body of an `unsafe fn` is, on
/// old editions, treated like an unsafe block, which means that this use of `unsafe` both declares
/// the existence of a contract to call the current function, and declares that the contracts of the
/// unsafe operations inside this function are being upheld. The `unsafe_op_in_unsafe_fn` lint can
/// be enabled to change that and make `unsafe fn` only play the former role. That lint is enabled
/// by default since edition 2024.
///
/// # Unsafe abilities
///
Expand Down Expand Up @@ -2088,6 +2097,13 @@ mod type_keyword {}
/// - `unsafe impl`: the contract necessary to implement the trait has been
/// checked by the programmer and is guaranteed to be respected.
///
/// On old editions, `unsafe fn` also acts like an `unsafe {}` block around the code inside the
/// function. This means it is not just a signal to the caller, but also promises that the
/// preconditions for the operations inside the function are upheld. Mixing these two meanings can
/// be confusing, so the `unsafe_op_in_unsafe_fn` lint has been introduced and enabled by default
/// since edition 2024 to warn against that and require explicit unsafe blocks even inside `unsafe
/// fn`.
///
/// See the [Rustonomicon] and the [Reference] for more information.
///
/// # Examples
Expand Down
2 changes: 2 additions & 0 deletions src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ RUN sh /scripts/cross-apt-packages.sh
WORKDIR /build

COPY scripts/musl-toolchain.sh /build/
COPY scripts/musl-cve-2026-6042.diff /build/
COPY scripts/musl-cve-2026-40200.diff /build/
# We need to mitigate rust-lang/rust#34978 when compiling musl itself as well
RUN CFLAGS="-Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \
CXXFLAGS="-Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ ENV \

WORKDIR /build/
COPY scripts/musl.sh /build/
COPY scripts/musl-cve-2026-6042.diff /build/
COPY scripts/musl-cve-2026-40200.diff /build/
RUN CC=gcc CFLAGS="-m32 -Wa,-mrelax-relocations=no" \
CXX=g++ CXXFLAGS="-m32 -Wa,-mrelax-relocations=no" \
bash musl.sh i686 --target=i686 && \
Expand Down
2 changes: 2 additions & 0 deletions src/ci/docker/host-x86_64/dist-various-1/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ ENV PATH="/build/emsdk:/build/emsdk/upstream/emscripten:/build/emsdk/node/curren
ENV STAGING_DIR=/tmp

COPY scripts/musl.sh /build
COPY scripts/musl-cve-2026-6042.diff /build/
COPY scripts/musl-cve-2026-40200.diff /build/
RUN env \
CC=arm-linux-gnueabi-gcc CFLAGS="-march=armv5te -marm -mfloat-abi=soft" \
CXX=arm-linux-gnueabi-g++ CXXFLAGS="-march=armv5te -marm -mfloat-abi=soft" \
Expand Down
2 changes: 2 additions & 0 deletions src/ci/docker/host-x86_64/dist-various-2/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ ENV \

WORKDIR /build
COPY scripts/musl.sh /build
COPY scripts/musl-cve-2026-6042.diff /build/
COPY scripts/musl-cve-2026-40200.diff /build/
RUN env \
CC=arm-linux-gnueabi-gcc-9 CFLAGS="-march=armv7-a" \
CXX=arm-linux-gnueabi-g++-9 CXXFLAGS="-march=armv7-a" \
Expand Down
2 changes: 2 additions & 0 deletions src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
WORKDIR /build/

COPY scripts/musl-toolchain.sh /build/
COPY scripts/musl-cve-2026-6042.diff /build/
COPY scripts/musl-cve-2026-40200.diff /build/
# We need to mitigate rust-lang/rust#34978 when compiling musl itself as well
RUN CFLAGS="-Wa,-mrelax-relocations=no -Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \
CXXFLAGS="-Wa,-mrelax-relocations=no -Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \
Expand Down
2 changes: 2 additions & 0 deletions src/ci/docker/host-x86_64/test-various/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ ENV PATH="/node/bin:${PATH}"

WORKDIR /build/
COPY scripts/musl-toolchain.sh /build/
COPY scripts/musl-cve-2026-6042.diff /build/
COPY scripts/musl-cve-2026-40200.diff /build/
RUN bash musl-toolchain.sh x86_64 && rm -rf build
WORKDIR /

Expand Down
179 changes: 179 additions & 0 deletions src/ci/docker/scripts/musl-cve-2026-40200.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
>From 228da39e38c1cae13cbe637e771412c1984dba5d Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Thu, 9 Apr 2026 22:51:30 -0400
Subject: [PATCH 1/3] qsort: fix leonardo heap corruption from bug in
doubleword ctz primitive

the pntz function, implementing a "count trailing zeros" variant for a
bit vector consisting of two size_t words, erroneously returned zero
rather than the number of bits in the low word when the first bit set
was the low bit of the high word.

as a result, a loop in the trinkle function which should have a
guaranteed small bound on the number of iterations, could run
unboundedly, thereby overflowing a stack-based working-space array
which was sized for the bound.

CVE-2026-40200 has been assigned for this issue.
---
src/stdlib/qsort.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c
index ab79dc6f..13219ab3 100644
--- a/src/stdlib/qsort.c
+++ b/src/stdlib/qsort.c
@@ -34,11 +34,11 @@

typedef int (*cmpfun)(const void *, const void *, void *);

+/* returns index of first bit set, excluding the low bit assumed to always
+ * be set, starting from low bit of p[0] up through high bit of p[1] */
static inline int pntz(size_t p[2]) {
- int r = ntz(p[0] - 1);
- if(r != 0 || (r = 8*sizeof(size_t) + ntz(p[1])) != 8*sizeof(size_t)) {
- return r;
- }
+ if (p[0] != 1) return ntz(p[0] - 1);
+ if (p[1]) return 8*sizeof(size_t) + ntz(p[1]);
return 0;
}

--
2.21.0


>From b3291b9a9f77f1f993d2b4f8c68a26cf09221ae7 Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
Date: Thu, 9 Apr 2026 23:40:53 -0400
Subject: [PATCH 2/3] qsort: hard-preclude oob array writes independent of any
invariants

while the root cause of CVE-2026-40200 was a faulty ctz primitive, the
fallout of the bug would have been limited to erroneous sorting or
infinite loop if not for the stores to a stack-based array that
depended on trusting invariants in order not to go out of bounds.

increase the size of the array to a power of two so that we can mask
indices into it to force them into range. in the absence of any
further bug, the masking is a no-op, but it does not have any
measurable performance cost, and it makes spatial memory safety
trivial to prove (and for readers not familiar with the algorithms to
trust).
---
src/stdlib/qsort.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c
index 13219ab3..e4bce9f7 100644
--- a/src/stdlib/qsort.c
+++ b/src/stdlib/qsort.c
@@ -89,10 +89,16 @@ static inline void shr(size_t p[2], int n)
p[1] >>= n;
}

+/* power-of-two length for working array so that we can mask indices and
+ * not depend on any invariant of the algorithm for spatial memory safety.
+ * the original size was just 14*sizeof(size_t)+1 */
+#define AR_LEN (16 * sizeof(size_t))
+#define AR_MASK (AR_LEN - 1)
+
static void sift(unsigned char *head, size_t width, cmpfun cmp, void *arg, int pshift, size_t lp[])
{
unsigned char *rt, *lf;
- unsigned char *ar[14 * sizeof(size_t) + 1];
+ unsigned char *ar[AR_LEN];
int i = 1;

ar[0] = head;
@@ -104,16 +110,16 @@ static void sift(unsigned char *head, size_t width, cmpfun cmp, void *arg, int p
break;
}
if(cmp(lf, rt, arg) >= 0) {
- ar[i++] = lf;
+ ar[i++ & AR_MASK] = lf;
head = lf;
pshift -= 1;
} else {
- ar[i++] = rt;
+ ar[i++ & AR_MASK] = rt;
head = rt;
pshift -= 2;
}
}
- cycle(width, ar, i);
+ cycle(width, ar, i & AR_MASK);
}

static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, size_t pp[2], int pshift, int trusty, size_t lp[])
@@ -121,7 +127,7 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, si
unsigned char *stepson,
*rt, *lf;
size_t p[2];
- unsigned char *ar[14 * sizeof(size_t) + 1];
+ unsigned char *ar[AR_LEN];
int i = 1;
int trail;

@@ -142,7 +148,7 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, si
}
}

- ar[i++] = stepson;
+ ar[i++ & AR_MASK] = stepson;
head = stepson;
trail = pntz(p);
shr(p, trail);
@@ -150,7 +156,7 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, si
trusty = 0;
}
if(!trusty) {
- cycle(width, ar, i);
+ cycle(width, ar, i & AR_MASK);
sift(head, width, cmp, arg, pshift, lp);
}
}
--
2.21.0


>From 5122f9f3c99fee366167c5de98b31546312921ab Mon Sep 17 00:00:00 2001
From: Luca Kellermann <mailto.luca.kellermann@gmail.com>
Date: Fri, 10 Apr 2026 03:03:22 +0200
Subject: [PATCH 3/3] qsort: fix shift UB in shl and shr

if shl() or shr() are called with n==8*sizeof(size_t), n is adjusted
to 0. the shift by (sizeof(size_t) * 8 - n) that then follows will
consequently shift by the width of size_t, which is UB and in practice
produces an incorrect result.

return early in this case. the bitvector p was already shifted by the
required amount.
---
src/stdlib/qsort.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c
index e4bce9f7..28607450 100644
--- a/src/stdlib/qsort.c
+++ b/src/stdlib/qsort.c
@@ -71,6 +71,7 @@ static inline void shl(size_t p[2], int n)
n -= 8 * sizeof(size_t);
p[1] = p[0];
p[0] = 0;
+ if (!n) return;
}
p[1] <<= n;
p[1] |= p[0] >> (sizeof(size_t) * 8 - n);
@@ -83,6 +84,7 @@ static inline void shr(size_t p[2], int n)
n -= 8 * sizeof(size_t);
p[0] = p[1];
p[1] = 0;
+ if (!n) return;
}
p[0] >>= n;
p[0] |= p[1] << (sizeof(size_t) * 8 - n);
--
2.21.0


Loading
Loading