Skip to content
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

Add a lint for static items with large type alignment #8593

Closed
wants to merge 4 commits into from

Conversation

Jaic1
Copy link
Contributor

@Jaic1 Jaic1 commented Mar 27, 2022

The goal of this PR is to warn the usage of static items which have type alignment larger than page size (typically 4KB).

Due to some known unsound issues, the type alignment may not be fulfilled.
For more information, see rust-lang/rust#70022 and rust-lang/rust#70143.

changelog: add lint [static_items_large_align]

@rust-highfive
Copy link

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @flip1995 (or someone else) soon.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Mar 27, 2022
if_chain! {
if let ItemKind::Static(hir_ty, _, _) = item.kind;
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
if let Some(adt_ref) = self.check_ty_alignment(cx.tcx, ty);
Copy link
Contributor

Choose a reason for hiding this comment

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

I did not think about references at all... that's a really good catch. I'm wondering if we'll miss anything with such an approach. I guess we could use the const validator to look at the actual allocations, but not sure we'll have other problems due to that.

Either way, your function looks good and we can add more types to recurse on later.

One thing your PR made me realize is that promoteds suffer from the same issue, but those can even be generic... not sure how to best handle them. Maybe with a mir visitor looking at the types of all promoteds. Doesn't have to be in this PR tho

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess we could use the const validator to look at the actual allocations

Could you provide more context on const validator and allocations?

One thing your PR made me realize is that promoteds suffer from the same issue, but those can even be generic...

Your mention of generic reminds me to think more about it when collecting the spans... As for promoteds, I guess I will take a peek at const eval.

Copy link
Contributor

Choose a reason for hiding this comment

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

The actual memory as we will pass it to llvm can be fetched with https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.eval_static_initializer

This gives you access to an Allocation which has an align field. It also gives you a list of relocations, which are ids of other allocations referenced by this static (essentially pointers to other memory). You can then use https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.get_global_alloc to fetch the allocations themselves and recurse. This will not give us any information about the spans or similar, but it will make sure we don't miss anything.

We could as a next step employ https://doc.rust-lang.org/nightly/nightly-rustc/rustc_const_eval/interpret/visitor/index.html but that is a bit more involved

/// 2. check if its (or its inner fields') alignment are larger than page size
/// 3. return one of them;
/// otherwise return None
fn check_ty_alignment<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<AdtDef<'tcx>> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we can do a bit better on the spans and also collect the spans of all fields of intermediate structs. Not sure how to get a struct field's span though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How about just report the intermediate types in mir but without the spans (from hir)? The difficult part in collecting the spans is dealing with the generics in hir.

A simple example:

#[repr(align(0x100000))]
struct Aligned(u8);

struct AlignedWrapper {
    f: u8,
    g: Aligned,
}

struct AlignedGeneric<T>(T);

static XG: AlignedGeneric<AlignedWrapper> = ...;

Then the intermediate types in mir would be:

AlignedGeneric<AlignedWrapper>
AlignedWrapper
Aligned

But in hir we can only get:

AlignedGeneric<T>(T)
// T needs to be resolved to AlignedWrapper
// and it is not straightforward to do it in hir
...

As we get more involved in connecting the mir and hir (the solution I have been trying), it is likely the lint/checking would become unmanageable and be unsound (i.e., having false negative).

Here is a more involved example (as a reference):

#[repr(align(0x100000))]
struct AlignedA(u8);

#[repr(align(0x100000))]
struct AlignedB(u8);

struct FnPtr<T>(fn() -> Box<T>);

struct AG<T>(T);

type AGAlias<T> = AG<T>;

struct AGWithArgs<A, B> {
    not_aligned: FnPtr<A>,
    aligned: AGAlias<B>,
}

static XG: AGWithArgs<AlignedA, AlignedB> = AGWithArgs {
    not_aligned: FnPtr(box_aligned),
    aligned: AG(AlignedB(0)),
};

fn box_aligned() -> Box<AlignedA> {
    Box::new(AlignedA(0))
}

The report in mir would be straightforward (since it is more easy to mono mir types):

AGWithArgs<AlignedA, AlignedB>
AG(AlignedB(0))
AlignedB

But it is difficult to report the relevant spans (from hir) like this:

struct AGWithArgs<A, B> {
    not_aligned: FnPtr<A>,
    aligned: AGAlias<B>,
    ^^^^^^^^^^^^^^^^^^^
}
type AGAlias<T> = AG<T>;
                  ^^^^^
struct AG<T>(T);
            ^^^
AlignedB

Or maybe there is a good way to work with (e.g., some subst mechanisms) generics in hir I am missing? And now I also start to wonder how rustc deal with generics when reporting (type) errors to user.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea, that seems reasonable. You can use tcx.def_span to get the span of the structs.

@bors
Copy link
Contributor

bors commented Mar 30, 2022

☔ The latest upstream changes (presumably #8576) made this pull request unmergeable. Please resolve the merge conflicts.

@oli-obk
Copy link
Contributor

oli-obk commented Apr 1, 2022

All my comments are for follow up improvements. IMO we can merge this as is

@Jaic1 Jaic1 force-pushed the static_items_large_align branch from 575cd03 to 69c07e0 Compare April 2, 2022 08:06
@bors
Copy link
Contributor

bors commented Apr 4, 2022

☔ The latest upstream changes (presumably #8594) made this pull request unmergeable. Please resolve the merge conflicts.

@Jarcho
Copy link
Contributor

Jarcho commented Aug 30, 2022

Ping @Jaic1. Are you willing to continue working on this? It doesn't seem like there are any major blockers for merging.

@Jaic1
Copy link
Contributor Author

Jaic1 commented Aug 30, 2022

@Jarcho Sure, I can resolve the conflicts at the moment. For the potential improvements mentioned above, maybe I can pick it up some day.

@oli-obk
Copy link
Contributor

oli-obk commented Sep 8, 2022

Please rebase instead of merge. When you rebase, the merge commit gets removed automatically without you needing to do anything

@Jaic1 Jaic1 force-pushed the static_items_large_align branch from 3476528 to 53d917b Compare September 8, 2022 07:47
Copy link
Member

@flip1995 flip1995 left a comment

Choose a reason for hiding this comment

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

There are a few changes left to do before we can merge this.

/// ```
#[clippy::version = "1.61.0"]
pub STATIC_ITEMS_LARGE_ALIGN,
pedantic,
Copy link
Member

Choose a reason for hiding this comment

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

I think this would be a good candidate for correctness or at least the suspicious group. Or do you have concerns about drastic false positives this lint might trigger?

/// println!("{:#x}", Box::into_raw(b) as usize);
/// }
/// ```
#[clippy::version = "1.61.0"]
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
#[clippy::version = "1.61.0"]
#[clippy::version = "1.66.0"]

This will most likely be merged in the 1.66 cycle.

/// }
/// ```
#[clippy::version = "1.61.0"]
pub STATIC_ITEMS_LARGE_ALIGN,
Copy link
Member

Choose a reason for hiding this comment

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

The lint name sounds a bit weird to me. Maybe static_items_with_large_alignment but then again, that's a bit wordy... Maybe the current lint name is a good compromise.

Comment on lines +74 to +76
"this static item (itself or its subfield) has a type alignment,\n\
which is larger than page size and may not be fulfilled,\n\
for more information, see <https://github.com/rust-lang/rust/issues/70022>.",
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
"this static item (itself or its subfield) has a type alignment,\n\
which is larger than page size and may not be fulfilled,\n\
for more information, see <https://github.com/rust-lang/rust/issues/70022>.",
"static item with large type alignment",

The lint message is not meant to explain the whole world :P Clippy will add a link to its documentation to the first lint emission anyway, so adding links is not necessary.

Comment on lines +170 to +171
"{} has alignment {:#x}, which is larger than {:#x},\n\
if you know what you are doing, config the default page size clippy uses in clippy.toml",
Copy link
Member

Choose a reason for hiding this comment

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

There shouldn't be any line breaks (or full stops) in a help message. The help message should just be: "{} has alignment {:#x}, which is larger than {:#x}"

The clippy.toml doesn't have to be mentioned here, because the config option will be listed in the lint documentation, which is linked to by the first lint emission.

Comment on lines +154 to +155
"{} with substitutions {:#?},\n\
contains an inner type with large alignment",
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
"{} with substitutions {:#?},\n\
contains an inner type with large alignment",
"{} with substitutions {:#?}, contains an inner type with large alignment",

no newline.

if substs_ref.is_empty() {
format!("{} contains an inner type with large alignment", ty.sort_string(cx.tcx))
} else {
// TODO - can we use :#?
Copy link
Member

Choose a reason for hiding this comment

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

Can we? Do you have a test for this? This TODO comment should be resolved before merging.

/// 4. Check if its (or its inner fields') alignment are larger than page size.
/// 5. Return one of them;
/// otherwise pop the current checked type and return `None`.
fn check_ty_alignment<'tcx>(
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't rustc have a query that just gives you the type alignment? 🤔 Implementing type alignment calculation in Clippy feels a bit wrong to me.

@flip1995 flip1995 added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Sep 8, 2022
@bors
Copy link
Contributor

bors commented Sep 27, 2022

☔ The latest upstream changes (presumably #9511) made this pull request unmergeable. Please resolve the merge conflicts.

@blyxyas
Copy link
Member

blyxyas commented Oct 19, 2023

@Jaic1 I'm closing this for inactivity. It seems like you're not active in the OSS ecosystem anymore, so, have luck in whatever else you're doing! ❤️.

If you'd like to continue with this PR, don't hesitate to comment and I'll reopen it! Thanks for trying ~(=^‥^)/.

Edit: Similar to rust-lang/rust#116520

@blyxyas blyxyas closed this Oct 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants