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

Lint against getting pointers from immediately dropped temporaries #128985

Merged
merged 1 commit into from
Oct 29, 2024

Conversation

GrigorenkoPV
Copy link
Contributor

@GrigorenkoPV GrigorenkoPV commented Aug 11, 2024

Fixes #123613

Changes:

  1. New lint: dangling_pointers_from_temporaries. Is a generalization of temporary_cstring_as_ptr for more types and more ways to get a temporary.
  2. temporary_cstring_as_ptr is removed and marked as renamed to dangling_pointers_from_temporaries.
  3. clippy::temporary_cstring_as_ptr is marked as renamed to dangling_pointers_from_temporaries.
  4. Fixed a false positive1 for when the pointer is not actually dangling because of lifetime extension for function/method call arguments.
  5. core::cell::Cell is now rustc_diagnostic_item = "Cell"

Questions:

Known limitations:

False negatives2:

See the comments in compiler/rustc_lint/src/dangling.rs

  1. Method calls that are not checked for:
    • temporary_unsafe_cell.get()
    • temporary_sync_unsafe_cell.get()
  2. Ways to get a temporary that are not recognized:
    • owning_temporary.field
    • owning_temporary[index]
  3. No checks for ref-to-ptr conversions:
    • &raw [mut] temporary
    • &temporary as *(const|mut) _
    • ptr::from_ref(&temporary) and friends

Footnotes

  1. lint should not be emitted, but is

  2. lint should be emitted, but is not

@rustbot
Copy link
Collaborator

rustbot commented Aug 11, 2024

r? @wesleywiser

rustbot has assigned @wesleywiser.
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. T-libs Relevant to the library team, which will review and decide on the PR/issue. A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. labels Aug 11, 2024
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@jieyouxu
Copy link
Member

FYI this probably needs T-lang approval and a crater run

@traviscross traviscross added the T-lang Relevant to the language team, which will review and decide on the PR/issue. label Aug 12, 2024
@GrigorenkoPV GrigorenkoPV marked this pull request as ready for review August 12, 2024 19:36
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@GrigorenkoPV
Copy link
Contributor Author

This probably needs a crater run. Check-only should suffice. However, the lint should probably be denied for the run, but I am not sure if crater has such an option. Maybe I will just temporary change the default level in code.

Anyways, neither do I have permissions for bors try, nor for a crater run.

compiler/rustc_lint/src/dangling.rs Outdated Show resolved Hide resolved
compiler/rustc_lint/src/dangling.rs Outdated Show resolved Hide resolved
compiler/rustc_lint/src/dangling.rs Outdated Show resolved Hide resolved

const MAX_PATH: usize = 260;
fn main() {
let str1 = String::with_capacity(MAX_PATH).as_mut_ptr();
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if it would make sense to suggest introducing a second binding in those simple let cases, aka.

    let str1 = String::with_capacity(MAX_PATH);
    let str1 = str1.as_mut_ptr();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably yes, but I would prefer to do this as a separate follow-up PR if possible

Copy link
Contributor Author

@GrigorenkoPV GrigorenkoPV Sep 9, 2024

Choose a reason for hiding this comment

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

I wonder if it would make sense to suggest introducing a second binding in those simple let cases, aka.

    let str1 = String::with_capacity(MAX_PATH);
    let str1 = str1.as_mut_ptr();

After looking at the "regressed" code from crater, I can say that a lot of the time this suggestion will not fix the problem, and will just result in code like

let vec = some_iter.collect();
let ptr = vec.as_ptr();
return ptr;

or similar where the pointer still dangles eventually, even though we binded the temporary for now.

Sometimes it would make sense to suggest things like .into_raw_parts() instead of .as_ptr(), but then the user has to remember to do ::from_raw_parts() if they want to avoid leaking...

Most of the places where this lint fires are not trivial to fix and actually require the person writing the code to be more cautious & knowledgeable about what they are doing, which can only be achieved through more experience of writing unsafe, as well as some punches from the compiler, miri, or sanitizers.

I think that not providing this suggestion in places where it would help is less bad than improperly providing it in places where it wouldn't help but would just further obscure and hide the issue.

Copy link
Member

Choose a reason for hiding this comment

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

I still feel like we should point users towards doing something, even if it's not a general solution, and not a a structured suggestion.

It's really a shame to tell users something but not how to fix it.

Opened #132283 to track this.

@rust-log-analyzer

This comment has been minimized.

@rustbot
Copy link
Collaborator

rustbot commented Aug 20, 2024

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@rust-log-analyzer

This comment has been minimized.

@GrigorenkoPV
Copy link
Contributor Author

Looks like this caught an actual mistake in miri tests. However, the test in question was added after this PR had been opened, so I will have to rebase now to be able to fix the test.

@GrigorenkoPV
Copy link
Contributor Author

Rebased

@rustbot
Copy link
Collaborator

rustbot commented Aug 21, 2024

The Miri subtree was changed

cc @rust-lang/miri

@RalfJung
Copy link
Member

Looks like this caught an actual mistake in miri tests. However, the test in question was added after this PR had been opened, so I will have to rebase now to be able to fix the test.

It did. That's a good demo for the lint being quite useful indeed. :)

@rust-log-analyzer

This comment has been minimized.

@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. to-announce Announce this issue on triage meeting and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Oct 26, 2024
@rfcbot
Copy link

rfcbot commented Oct 26, 2024

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

compiler/rustc_lint/messages.ftl Outdated Show resolved Hide resolved
compiler/rustc_lint/messages.ftl Outdated Show resolved Hide resolved
compiler/rustc_lint/src/dangling.rs Outdated Show resolved Hide resolved

fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind
&& matches!(method.ident.name, sym::as_ptr | sym::as_mut_ptr)
Copy link
Member

Choose a reason for hiding this comment

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

If you want to do it in this PR (otherwise in a follow-up), we could add an rustc attribute to those as_ptr/as_mut_ptr methods.

You can follow afcb09b and 2a930d3 (from a PR of mine) with check_applied_to_fn_or_method instead, and something similar to this to check the attribute

&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)

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 would prefer to do this as a follow-up, just not to stall this one any longer

Copy link
Member

Choose a reason for hiding this comment

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

I've created #132281 to track the use of a rustc attr for this lint.

Feel free to assign it to your-self (@rustbot assign I think).

compiler/rustc_lint/src/lib.rs Outdated Show resolved Hide resolved
.label_ptr = this pointer will immediately be invalid
.label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
.note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
.help = for more information, see <https://doc.rust-lang.org/reference/destructors.html>
Copy link
Member

Choose a reason for hiding this comment

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

It's too bad that we don't have place explaining what a "dangling pointer" is, we could have linked it here.

Opened #132286 about that.

@Urgau Urgau added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). labels Oct 28, 2024
@Urgau
Copy link
Member

Urgau commented Oct 28, 2024

It's clear that some improvements could be made (#132281, #132283, #132286), but IMO these improvements do not block merging this PR, they are all pre-existing anyway.

Therefore, @bors r+

@bors
Copy link
Contributor

bors commented Oct 28, 2024

📌 Commit c69894e has been approved by Urgau

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 Oct 28, 2024
@bors
Copy link
Contributor

bors commented Oct 29, 2024

⌛ Testing commit c69894e with merge a9d1762...

@bors
Copy link
Contributor

bors commented Oct 29, 2024

☀️ Test successful - checks-actions
Approved by: Urgau
Pushing a9d1762 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Oct 29, 2024
@bors bors merged commit a9d1762 into rust-lang:master Oct 29, 2024
7 checks passed
@rustbot rustbot added this to the 1.84.0 milestone Oct 29, 2024
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (a9d1762): comparison URL.

Overall result: ❌ regressions - no action needed

@rustbot label: -perf-regression

Instruction count

This is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.2% [0.1%, 0.2%] 3
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Max RSS (memory usage)

Results (primary 2.1%, secondary 4.0%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
2.1% [1.3%, 3.4%] 3
Regressions ❌
(secondary)
4.0% [2.3%, 5.6%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 2.1% [1.3%, 3.4%] 3

Cycles

Results (secondary -3.0%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-3.0% [-3.0%, -3.0%] 1
All ❌✅ (primary) - - 0

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 782.325s -> 783.339s (0.13%)
Artifact size: 333.64 MiB -> 333.70 MiB (0.02%)

lnicola pushed a commit to lnicola/rust-analyzer that referenced this pull request Oct 29, 2024
Lint against getting pointers from immediately dropped temporaries

Fixes #123613

## Changes:
1. New lint: `dangling_pointers_from_temporaries`. Is a generalization of `temporary_cstring_as_ptr` for more types and more ways to get a temporary.
2. `temporary_cstring_as_ptr` is removed and marked as renamed to `dangling_pointers_from_temporaries`.
3. `clippy::temporary_cstring_as_ptr` is marked as renamed to `dangling_pointers_from_temporaries`.
4. Fixed a false positive[^fp] for when the pointer is not actually dangling because of lifetime extension for function/method call arguments.
5. `core::cell::Cell` is now `rustc_diagnostic_item = "Cell"`

## Questions:
- [ ]  Instead of manually checking for a list of known methods and diagnostic items, maybe add some sort of annotation to those methods in library and check for the presence of that annotation? rust-lang/rust#128985 (comment)

## Known limitations:

### False negatives[^fn]:

See the comments in `compiler/rustc_lint/src/dangling.rs`

1. Method calls that are not checked for:
   - `temporary_unsafe_cell.get()`
   - `temporary_sync_unsafe_cell.get()`
2. Ways to get a temporary that are not recognized:
   - `owning_temporary.field`
   - `owning_temporary[index]`
3. No checks for ref-to-ptr conversions:
   - `&raw [mut] temporary`
   - `&temporary as *(const|mut) _`
    - `ptr::from_ref(&temporary)` and friends

[^fn]: lint **should** be emitted, but **is not**

[^fp]: lint **should not** be emitted, but **is**
@GrigorenkoPV GrigorenkoPV deleted the instantly-dangling-pointer branch October 29, 2024 11:00
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Oct 30, 2024
Lint against getting pointers from immediately dropped temporaries

Fixes #123613

## Changes:
1. New lint: `dangling_pointers_from_temporaries`. Is a generalization of `temporary_cstring_as_ptr` for more types and more ways to get a temporary.
2. `temporary_cstring_as_ptr` is removed and marked as renamed to `dangling_pointers_from_temporaries`.
3. `clippy::temporary_cstring_as_ptr` is marked as renamed to `dangling_pointers_from_temporaries`.
4. Fixed a false positive[^fp] for when the pointer is not actually dangling because of lifetime extension for function/method call arguments.
5. `core::cell::Cell` is now `rustc_diagnostic_item = "Cell"`

## Questions:
- [ ]  Instead of manually checking for a list of known methods and diagnostic items, maybe add some sort of annotation to those methods in library and check for the presence of that annotation? rust-lang/rust#128985 (comment)

## Known limitations:

### False negatives[^fn]:

See the comments in `compiler/rustc_lint/src/dangling.rs`

1. Method calls that are not checked for:
   - `temporary_unsafe_cell.get()`
   - `temporary_sync_unsafe_cell.get()`
2. Ways to get a temporary that are not recognized:
   - `owning_temporary.field`
   - `owning_temporary[index]`
3. No checks for ref-to-ptr conversions:
   - `&raw [mut] temporary`
   - `&temporary as *(const|mut) _`
    - `ptr::from_ref(&temporary)` and friends

[^fn]: lint **should** be emitted, but **is not**

[^fp]: lint **should not** be emitted, but **is**
flip1995 pushed a commit to flip1995/rust-clippy that referenced this pull request Oct 31, 2024
Lint against getting pointers from immediately dropped temporaries

Fixes #123613

## Changes:
1. New lint: `dangling_pointers_from_temporaries`. Is a generalization of `temporary_cstring_as_ptr` for more types and more ways to get a temporary.
2. `temporary_cstring_as_ptr` is removed and marked as renamed to `dangling_pointers_from_temporaries`.
3. `clippy::temporary_cstring_as_ptr` is marked as renamed to `dangling_pointers_from_temporaries`.
4. Fixed a false positive[^fp] for when the pointer is not actually dangling because of lifetime extension for function/method call arguments.
5. `core::cell::Cell` is now `rustc_diagnostic_item = "Cell"`

## Questions:
- [ ]  Instead of manually checking for a list of known methods and diagnostic items, maybe add some sort of annotation to those methods in library and check for the presence of that annotation? rust-lang/rust#128985 (comment)

## Known limitations:

### False negatives[^fn]:

See the comments in `compiler/rustc_lint/src/dangling.rs`

1. Method calls that are not checked for:
   - `temporary_unsafe_cell.get()`
   - `temporary_sync_unsafe_cell.get()`
2. Ways to get a temporary that are not recognized:
   - `owning_temporary.field`
   - `owning_temporary[index]`
3. No checks for ref-to-ptr conversions:
   - `&raw [mut] temporary`
   - `&temporary as *(const|mut) _`
    - `ptr::from_ref(&temporary)` and friends

[^fn]: lint **should** be emitted, but **is not**

[^fp]: lint **should not** be emitted, but **is**
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-lang Relevant to the language team, which will review and decide on the PR/issue. to-announce Announce this issue on triage meeting
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lint against instantly-dangling pointers like String::with_capacity(MAX_PATH).as_mut_ptr()