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

Warn when passing pointers to asm! with nomem/readonly options #127063

Closed
wants to merge 1 commit into from

Conversation

Soveu
Copy link
Contributor

@Soveu Soveu commented Jun 27, 2024

I think these warnings would help a lot finding weird bugs caused by improper options inside asm!, while allowing existing code to compile.

@rustbot
Copy link
Collaborator

rustbot commented Jun 27, 2024

r? @BoxyUwU

rustbot has assigned @BoxyUwU.
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 Jun 27, 2024
@BoxyUwU
Copy link
Member

BoxyUwU commented Jun 27, 2024

r? @Amanieu

@rustbot rustbot assigned Amanieu and unassigned BoxyUwU Jun 27, 2024
}
if asm.options.contains(InlineAsmOptions::NOMEM) {
let msg = "Passing a pointer to asm!() with 'nomem' option";
let note = "Pointer suggest that this piece of assembly reads the underlying object, consider using usize or checking the options";
Copy link
Member

Choose a reason for hiding this comment

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

why is a usize preferable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That was just what came to my mind when writing this at midnight. The warning and note could definitely be better, for example now I'm thinking of this:

warning: passing a pointer to asm! block with `nomem` option
 --> $PATH:$LINE
  |
  | core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
  |                                  ^
  = note: `nomem` must be only used when no memory read or write happens inside the asm! block.
  = note: This is not limited to global variables, it also includes passed pointers.
  = note: If this is intentional, consider casting the pointer to an integer.
  = note: If not, change the `nomem` attribute to `readonly`

Warning is concise and note explains in more detail what is going on and how to fix the issue. Of course, depending on the pointer mutability and asm options, they will be a bit different.

Copy link
Member

@workingjubilee workingjubilee Jun 29, 2024

Choose a reason for hiding this comment

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

I think this is probably underexplained and should be omitted:

  = note: If this is intentional, consider casting the pointer to an integer.

Assembly is unsafe and we should be careful to eschew recommendations that aren't clearly applicable, because people will blindly take recommendations without thinking hard (no matter how much we would like to believe otherwise).

Further, people who work with assembly often have the interesting belief that pointers and integers are the same thing, so I can imagine someone misunderstanding this and the next line as somehow equivalent:

  = note: If this is intentional, consider casting the pointer to an integer.
  = note: If not, change the `nomem` attribute to `readonly`

So I think the suggestions should be more conservative, only including:

  • remove nomem (as this is safest)
  • replace nomem with readonly (less safe, but may be sound)
  • allow this lint (would require moving this into the linting framework, but clearly has no soundness impacts)

Copy link
Member

Choose a reason for hiding this comment

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

It is probably fine if there's a good way to explain why the cast to integer would be okay (so that people don't do it if it's not okay), but it seems sus, honestly.

@Soveu
Copy link
Contributor Author

Soveu commented Jul 11, 2024

@workingjubilee @Amanieu ?

.struct_span_warn(expr.span, "passing a mutable pointer to asm! block with 'readonly' option.")
.with_note("`readonly` means that no memory write happens inside the asm! block.")
.with_note("This is not limited to global variables, it also includes passed pointers.")
.with_note("If passing this mutable pointer is intentional, remove the `readonly` attribute.")
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this message is appropriate: if passing a mutable pointer is intentional, we should only suggest something which suppresses the warning, not something that changes the semantics of the asm!.

The problem here is that I don't see an obvious way to suppress the warning. Perhaps this lint would be better in clippy instead rather than something that generally applies to all Rust code?

Copy link
Contributor Author

@Soveu Soveu Jul 11, 2024

Choose a reason for hiding this comment

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

Hmm, now that I'm thinking about it, maybe you're right - *mut pointer should anyway implicitly cast to *const one. What do you think about the other case with *const/mut and nomem?

.struct_span_warn(expr.span, "passing a pointer to asm! block with 'nomem' option.")
.with_note("`nomem` means that no memory write or read happens inside the asm! block.")
.with_note("This is not limited to global variables, it also includes passed pointers.")
.with_note("If passing this pointer is intentional, replace the `nomem` attribute with `readonly` or remove it completely.")
Copy link
Member

Choose a reason for hiding this comment

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

Same thing here, if passing the pointer is intentional then our recommendation should not involve changing the semantics of asm!.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#127063 (comment)

The problem here is that I don't see an obvious way to suppress the warning. Perhaps this lint would be better in clippy instead rather than something that generally applies to all Rust code?

At first, I wanted to make it an error, but probably some code would break and asm! is stable (could we jump on the edition2024 bandwagon?). Yeah, this is unsafe, and we can do a lot of stuff inside the asm! block, including freestyle transmute, however transmute does one job and it is dangerous, we can't do much with it besides "being smart" and looking for surrounding context, which is what clippy does with eager_transmute for example.

Here we could impose impose a simple rule that would help avoiding bugs with unintentional nomem. I can agree that the case with *mut T and readonly could be allowed, because we could pretend that the pointer is implicitly cast to *const T (like when calling functions that take *const T) and also the difference between *mut and *const is mostly cosmetic, but I don't see the same applying to nomem + *const/mut T.

Why would someone pass a pointer and do nothing with the data? If they need just the address, maybe they also need to consider if the provenance should be exposed or not. Idk, I think you know better than me how asm! is used, so if the final decision will be to not implement it here, then I won't insist. 🤔

Copy link
Member

@workingjubilee workingjubilee Jul 11, 2024

Choose a reason for hiding this comment

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

just noting that there's like two, maybe three versions of "intended/intention" floating around in this discussion. let me see if I can name all of them:

  • "I intended to pass *mut T + nomem: I will basically ptr.addr() it or something."
  • "I intended to pass *mut T but I meant readonly: just .cast_const() it."
  • "I intended to pass *mut T, and I'm totally gonna read it, lol, GIMME DAT UB!!! (no plz don't, let me remove nomem...)"

@Amanieu
Copy link
Member

Amanieu commented Jul 27, 2024

Considering the fact that this suggestion isn't always applicable, I think this makes more sense as clippy lint than a built-in rustc lint. I would encourage you to submit it there.

@Amanieu Amanieu closed this Jul 27, 2024
bors added a commit to rust-lang/rust-clippy that referenced this pull request Sep 3, 2024
Add new check for passing pointers to an `asm!` block with `nomem` option

changelog: Add new check for passing pointers to an `asm!` block with `nomem` option

Continuing work from rust-lang/rust#127063
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants