Skip to content

Taint the type of ill-formed (unsized) statics #144226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 27, 2025

Conversation

cjgillot
Copy link
Contributor

@cjgillot cjgillot commented Jul 20, 2025

Fixes #121176
Fixes #129109
Fixes #130970
Fixes #131347
Fixes #139872
Fixes #140332

@rustbot
Copy link
Collaborator

rustbot commented Jul 20, 2025

r? @davidtwco

rustbot has assigned @davidtwco.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 20, 2025
@rustbot
Copy link
Collaborator

rustbot commented Jul 20, 2025

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

This PR changes a file inside tests/crashes. If a crash was fixed, please move into the corresponding ui subdir and add 'Fixes #' to the PR description to autoclose the issue upon merge.

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

@@ -243,6 +243,17 @@ impl<'tcx, Prov: Provenance> std::ops::Deref for ImmTy<'tcx, Prov> {
}

impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
#[inline(always)]
pub fn try_from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Option<Self> {
Copy link
Member

@RalfJung RalfJung Jul 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of these functions. The point of the assertions is that they are a last line of defense to detect defective callers. They are not exhaustive checks. If the caller can't ensure that the value has the right type, that can only be fixed in the caller.

IOW, matches_abi here really is more of a maybe_matches_abi. It is necessary, but not sufficient. And trying to make it sufficient is the wrong approach; the right approach is figuring out why someone is feeding bogus data into these functions.


use std::fmt::Debug;

static STATIC_1: dyn Debug + Sync = *();
Copy link
Member

@RalfJung RalfJung Jul 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For instance, this is a clearly ill-formed static. Nothing should ever look at its MIR. Trying to make the MIR interpreter APIs resistant against bogus MIR is a pointless game of whack-a-mole.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea those cases are straight forward to prevent within type_of, we don't even need to compute the layout, just doing a sizedness check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't quite work since we allow extern statics that have extern types, which are unsized.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, we can check the tail manually for slices and dyn trait

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that requires unfolding the type which will trigger the same cycle error, won't it?

Copy link
Member

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These all seem to be standard cases of "ill-formed MIR gets too far into the pipeline". We should never run any MIR passes on ill-formed MIR; it is a waste of effort to try and fix this inside every individual pass.

@cjgillot
Copy link
Contributor Author

cjgillot commented Jul 20, 2025

These all seem to be standard cases of "ill-formed MIR gets too far into the pipeline". We should never run any MIR passes on ill-formed MIR; it is a waste of effort to try and fix this inside every individual pass.

I agree, but fixing this is general is hard too. We have an ImpossiblePredicates pass to skip this kind of nonsense, but it is quite limited to avoid query cycles. And it cannot handle nonsense that spans multiple items.

A "cleaner" way would be to ensure we never produce a constant with mismatching ABI, be it as a ConstValue or as an OpTy. But that also clashes with the notion that miri should run on sensible code.

@RalfJung
Copy link
Member

RalfJung commented Jul 20, 2025

These are statics failing the "it must be sized" check. It's not some impossible where bound. I don't see what is so hard about marking them as tainted so nothing else ever touches them again. It's the exact same thing as a static whose initializer body is plainly ill-typed.

@cjgillot cjgillot force-pushed the known-panics-panics branch 2 times, most recently from 7e54047 to a825a96 Compare July 20, 2025 21:44
@cjgillot
Copy link
Contributor Author

@RalfJung this latest version prevents creating the erroneous constants altogether. Do you agree with this version?

@rust-log-analyzer

This comment has been minimized.

@cjgillot cjgillot force-pushed the known-panics-panics branch from a825a96 to a250112 Compare July 20, 2025 22:33
@rust-log-analyzer

This comment has been minimized.

@RalfJung
Copy link
Member

Yeah, that looks much better, thanks.

I'm not familiar with MIR building so I can't approve this on my own, however.

@cjgillot
Copy link
Contributor Author

It's not ready yet. Just checking sizedness is not enough, as some non-sized types are OK too, like extern types. And using layout causes cycles.

@RalfJung
Copy link
Member

Extern types can only work with extern statics, right?

And even then, that seems kind of cursed. Did we intentionally allow that or just forget to disallow it?

@cjgillot
Copy link
Contributor Author

Extern types can only work with extern statics, right?

Or inside pointers. And pointers to extern types are scalars.

And even then, that seems kind of cursed. Did we intentionally allow that or just forget to disallow it?

It's intentional. We have a run-make test to verify this works.

@RalfJung
Copy link
Member

RalfJung commented Jul 22, 2025 via email

@cjgillot
Copy link
Contributor Author

The failing test is this one:

extern "C" {
type Foo;
static FOO: Foo;
fn bar(foo: *const Foo) -> u8;
}

As a foreign type Foo is not sized, but &Foo and *mut Foo are still scalar. This is a special case in layout computation.

Checking whether &T is scalar in full generality requires either to call layout_of, or to open-code it. Internally, it normalizes <T as Pointee>::Metadata and compute that type's layout. The normalization process may try to normalize opaque types. This breaks https://github.com/rust-lang/rust/blob/master/tests/ui/coroutine/layout-error.rs

@RalfJung
Copy link
Member

The failing test is this one:

Yeah, that is a very odd test, I didn't think we'd allow extern statics with unknown size. Interestingly, we reject static FOO2: [i32]; (as we have to).

What is not entirely clear to me is why we'd care about whether &Foo has metadata or not. Is that for constructing the pointer that represents the extern static access in MIR? As far as I can see, those pointers are always scalar, since statics with unsized types that require metadata are forbidden (and that makes sense; where would the metadata come from anyway).

@RalfJung
Copy link
Member

RalfJung commented Jul 23, 2025

All the issues referenced here seem to be about statics that are unsized-with-metadata. You can check for this without knowing the layout; just call ptr_metadata_ty on the type of the static. Statics can't be generic so this should always give you a definite answer.

This still has to normalize some things though, so that coroutine case could become interesting. But it's okay for that code to error, it just can't ICE.

// Still, producing a single scalar constant would be inconsistent, as pointers to
// non-`Sized` types are scalar pairs. Avoid an ICE by producing an error constant.
let guar =
tcx.dcx().span_delayed_bug(span, format!("static's type `{ty}` is not Sized"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tcx.dcx().span_delayed_bug(span, format!("static's type `{ty}` is not Sized"));
tcx.dcx().span_delayed_bug(span, format!("static's type `{pointee}` is not Sized"));

@RalfJung
Copy link
Member

Wouldn't an alternative be to change the type of the static itself to TyKind::Error after we determined that it's not a valid type for a static? Then we don't need patches like this all over the rest of the compiler.

Cc @oli-obk

@cjgillot cjgillot force-pushed the known-panics-panics branch from a250112 to 1a23d74 Compare July 25, 2025 03:00
@cjgillot cjgillot force-pushed the known-panics-panics branch from 1a23d74 to 8095c2d Compare July 25, 2025 23:07
@rustbot
Copy link
Collaborator

rustbot commented Jul 25, 2025

This PR modifies tests/ui/issues/. If this PR is adding new tests to tests/ui/issues/,
please refrain from doing so, and instead add it to more descriptive subdirectories.

@cjgillot
Copy link
Contributor Author

Wouldn't an alternative be to change the type of the static itself to TyKind::Error after we determined that it's not a valid type for a static? Then we don't need patches like this all over the rest of the compiler.

that would mean moving these checks to type_of, which should be fine for static items, but are very cycle prone for other things

The latest commit tries that. I think it's worse because it breaks passing tests.

@rust-log-analyzer

This comment was marked as outdated.

@cjgillot cjgillot force-pushed the known-panics-panics branch from 8095c2d to 7c64961 Compare July 25, 2025 23:39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... I don't think I understand why this fails, but I guess that's how type_alias_impl_trait works...?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, if we do use the "reveal opaque type to check the auto trait" logic, then this must cycle if we do it as a part of type_of. But if you are using a TAIT as the type of a static then it's not unreasonable to mark it as Sync, too.

Of course we could keep the Sync check out of the type_of code path and check it only in wfcheck like we did before this PR, but that'll be up to the lang team I guess?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see. Yeah Sync can't influence layout so it's much less likely to ICE later parts of the compiler... but I'm fine either way.

@@ -17,6 +17,7 @@ impl<F: Future> Task<F> {
}

pub type F = impl Future;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub type F = impl Future;
pub type F = impl Future + Sync;

Needing an extra annotation for an opaque type in a static item seems fine to me

@@ -23,5 +22,6 @@ mod helper {

// Static queries the layout of the coroutine.
static A: Option<helper::F> = None;
//~^ ERROR cycle detected when computing type of `A`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here. Open an issue that this cycle diagnostic should propose adding the bound to the opaque. We'll fix that for TAITs but that shouldn't stop your PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, if we do use the "reveal opaque type to check the auto trait" logic, then this must cycle if we do it as a part of type_of. But if you are using a TAIT as the type of a static then it's not unreasonable to mark it as Sync, too.

Of course we could keep the Sync check out of the type_of code path and check it only in wfcheck like we did before this PR, but that'll be up to the lang team I guess?

@oli-obk
Copy link
Contributor

oli-obk commented Jul 27, 2025

r? @oli-obk

@bors r+

@bors
Copy link
Collaborator

bors commented Jul 27, 2025

📌 Commit 8817572 has been approved by oli-obk

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 27, 2025
Copy link
Member

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some minor comment nits, I can imagine this being quite confusing for someone who doesn't have the context we do right now.

@bors r-
(But we already got rolled up so this may have to be resolved in a follow-up PR.)

Comment on lines +223 to +224
// Verify that here to avoid ill-formed MIR.
match check_static_item(tcx, def_id, ty, false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Verify that here to avoid ill-formed MIR.
match check_static_item(tcx, def_id, ty, false) {
// Verify that here to avoid ill-formed MIR.
// We skip the `Sync` check to avoid cycles for type-alias-impl-trait,
// relying on the fact that non-Sync statics don't ICE the rest of the compiler.
match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) {

Comment on lines +288 to +289
// Verify that here to avoid ill-formed MIR.
match check_static_item(tcx, def_id, ty, false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Verify that here to avoid ill-formed MIR.
match check_static_item(tcx, def_id, ty, false) {
// Verify that here to avoid ill-formed MIR.
// We skip the `Sync` check to avoid cycles for type-alias-impl-trait,
// relying on the fact that non-Sync statics don't ICE the rest of the compiler.
match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) {

@@ -767,7 +767,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
DefKind::Static { .. } => {
check_static_inhabited(tcx, def_id);
check_static_linkage(tcx, def_id);
res = res.and(wfcheck::check_static_item(tcx, def_id));
let ty = tcx.type_of(def_id).instantiate_identity();
res = res.and(wfcheck::check_static_item(tcx, def_id, ty, true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
res = res.and(wfcheck::check_static_item(tcx, def_id, ty, true));
res = res.and(wfcheck::check_static_item(tcx, def_id, ty, /* should_check_for_sync */ true));

bors added a commit that referenced this pull request Jul 27, 2025
Rollup of 4 pull requests

Successful merges:

 - #144226 (Do not assert layout in KnownPanicsLint.)
 - #144385 (Optimize performance by inline in macro hygiene system)
 - #144454 (move uefi test to run-make)
 - #144455 (Unify LLVM ctlz/cttz intrinsic generation)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 58f1037 into rust-lang:master Jul 27, 2025
10 checks passed
@rustbot rustbot added this to the 1.90.0 milestone Jul 27, 2025
rust-timer added a commit that referenced this pull request Jul 27, 2025
Rollup merge of #144226 - cjgillot:known-panics-panics, r=oli-obk

Do not assert layout in KnownPanicsLint.

Fixes #121176
Fixes #129109
Fixes #130970
Fixes #131347
Fixes #139872
Fixes #140332
@RalfJung
Copy link
Member

I'll submit a PR with the comments.

@RalfJung RalfJung changed the title Do not assert layout in KnownPanicsLint. Taint the type of ill-formed (unsized) statics Jul 27, 2025
@cjgillot cjgillot deleted the known-panics-panics branch July 27, 2025 13:46
jhpratt added a commit to jhpratt/rust that referenced this pull request Jul 27, 2025
…compiler-errors

check_static_item: explain should_check_for_sync choices

Follow-up to rust-lang#144226.

r? `@oli-obk`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 27, 2025
…compiler-errors

check_static_item: explain should_check_for_sync choices

Follow-up to rust-lang#144226.

r? ``@oli-obk``
rust-timer added a commit that referenced this pull request Jul 28, 2025
Rollup merge of #144534 - RalfJung:should_check_for_sync, r=compiler-errors

check_static_item: explain should_check_for_sync choices

Follow-up to #144226.

r? ``@oli-obk``
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
7 participants