-
Notifications
You must be signed in to change notification settings - Fork 13k
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 instantly-dangling pointers like String::with_capacity(MAX_PATH).as_mut_ptr()
#123613
Comments
@FlorentinoJink The problem is not truly More unfortunately, a raw pointer does not have a lifetime. This is a fundamental reality of Rust. Code that requires eschewing lifetimes needs that. And you have chosen to eschew lifetimes, and then ignore the invariants of It is indeed an undesirable API but I do not see how we can realistically require it to not compile anymore, as this code, for instance, is perfectly reasonable, and also has no lifetime bound, for the same reason: fn main() {
let str1_len = String::from("does not live to the next line").len();
} And you seem to claim this code does not compile? Or perhaps that was a "should not"? fn main() {
let str1 = String::from("x").as_mut_ptr();
} Unfortunately, it does. Ultimately, you have not fully explained how the safe functions in question are at fault, you have only specified how irresponsible use of |
To be clear, it sure as hell does allocate valid memory, the problem is that the string doesn't last past the end of that expression so you created a pointer to some valid memory, and then it becomes invalid just as quickly. |
We should have a lint against this pattern of creating an instantly-dangling pointer. I've seen this pattern mistakenly used in unit tests; it's a funny sort of canary for people who write unsafe code but didn't use ASan before publishing their work. |
String::with_capacity(MAX_PATH).as_mut_ptr()
can lead to undefined behaviorString::with_capacity(MAX_PATH).as_mut_ptr()
Thank you for your patient explanation. I will be more careful when using raw pointers in the future. |
@saethlin Hmm. It would be worthwhile if possible but how would we do this without also linting on e.g. hand-implementations of I suppose we could have the Rust wrapper of the system allocator contain debug assertions (e.g. this particular example winds up deallocating the same pointer twice for a double-free, the second time after the |
What I'm suggesting is just a lint against the specific pattern of calling a constructor function then a function called |
I do not think that the scenario as described in the initial issue description is a reasonable thing to detect and warn about in the frontend. We have Miri and ASan for that. |
We already lint |
@rustbot claim One question though, there is no simple way for a lint to check that a value is a temporary? Something like The current implementation of |
...do we have a concept of borrow-checker lints...? because it seems it would be really easy to tell using MIR "oh, this is a pointer to a dead thing." |
That doesn't help for |
…ter, r=<try> Lint against getting pointers from immediately dropped temporaries Fixes rust-lang#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 marked as renamed to `dangling_pointers_from_temporaries`. 3. `clippy::temporary_cstring_as_ptr` is marked as renamed to `dangling_pointers_from_temporaries`. 4. `core::cell::Cell` is now `rustc_diagnostic_item = "Cell"` ## TODO: - [x] ~Add tests for different types~ - [x] ~Add tests for different ways of getting a temporary~ - [x] ~Regroup tests for this and `temporary_cstring_as_ptr`~ - [x] ~Fix a strange ICE when the lint is `allow`ed.~ - [x] ~Check what happens when you `break` with a bound variable from `loop`/`match`/`if`/`block`.~ - [x] ~Document the lint~ - [x] ~Fix tests rust-lang#128985 (comment) - [x] ~Fix clippy~ - [x] ~Fix miri rust-lang#128985 (review) - [ ] Crater run? ## Questions: - [ ] Should we make something like `is_temporary_rvalue` (but not bogus) available in compiler? - [x] ~Should `temporary_cstring_as_ptr` be deprecated? Across an edition boundary?~ - [ ] Instead of manually checking for a list of known methods, maybe add some sort of annotation to those methods in library and check for the presence of that annotation? - [ ] Maybe even introduce some form of primitive lifetimes for pointers and check those in borrow-checker? ## Future improvements: - [ ] Fix false negatives[^fn] - [ ] Add suggestions rust-lang#128985 (comment) ## Known limitations: ### False negatives[^fn]: [^fn]: lint **should** be emitted, but **is not** - `temporary_unsafe_cell.get()` - `temporary.field.as_ptr()` - `temporary[index].as_ptr()` - Converting `&temporary` to pointer with `as` or stuff like `ptr::from_ref`. - The `&raw [mut] temporary` ### False positives[^fp]: [^fp]: lint **should not** be emitted, but **is** Both this lint and the already existing `temporary_cstring_as_ptr` will fire on code like this: ```rust foo(CString::new("hello").unwrap().as_ptr()) ``` even though using the resulting pointer inside of `foo` is completely fine (at least according to miri), probably due to argument promotion logic.
…ter, r=<try> Lint against getting pointers from immediately dropped temporaries Fixes rust-lang#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 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"` ## TODO: - [x] ~Add tests for different types~ - [x] ~Add tests for different ways of getting a temporary~ - [x] ~Regroup tests for this and `temporary_cstring_as_ptr`~ - [x] ~Fix a strange ICE when the lint is `allow`ed.~ - [x] ~Check what happens when you `break` with a bound variable from `loop`/`match`/`if`/`block`.~ - [x] ~Document the lint~ - [x] ~Fix tests rust-lang#128985 (comment) - [x] ~Fix clippy~ - [x] ~Fix miri rust-lang#128985 (review) - [x] [Crater run](https://crater.rust-lang.org/ex/pr-128985) - [x] Put a comprehensive list of known false negatives[^fn] into comments. - [ ] Instead of diagnostic items, maybe use lang items or some special attributes? rust-lang#128985 (comment) ## Questions: - [ ] Should we make something like `is_temporary_rvalue` (but not bogus) available in compiler? - [x] ~Should `temporary_cstring_as_ptr` be deprecated? Across an edition boundary?~ - [ ] Instead of manually checking for a list of known methods, maybe add some sort of annotation to those methods in library and check for the presence of that annotation? - [ ] Maybe even introduce some form of primitive lifetimes for pointers and check those in borrow-checker? ## Future improvements: - [ ] Fix false negatives[^fn] - [ ] Add suggestions rust-lang#128985 (comment) ## Known limitations: ### False negatives[^fn]: See the comments in `compiler/rustc_lint/src/dangling.rs` [^fn]: lint **should** be emitted, but **is not** [^fp]: lint **should not** be emitted, but **is**
…ter, r=Urgau Lint against getting pointers from immediately dropped temporaries Fixes rust-lang#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#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**
Issue Description
The following Rust code:
uses the
String::with_capacity
function to create a string with a capacity of 260 characters. It then uses theas_mut_ptr
method to get a raw pointer to the string.However, this approach can lead to undefined behavior because:
The
String::with_capacity
function only guarantees to allocate enough memory to store the specified number of characters. It does not guarantee that the allocated memory is valid.The
as_mut_ptr
method returns a raw pointer to the internal data of the string. This pointer may point to uninitialized memory or memory that has been invalidated by other operations.Therefore, using the
String::with_capacity(MAX_PATH).as_mut_ptr()
method to create a raw pointer can lead to the following problems-Program crashes
-Data corruption
-Security vulnerabilities
Expected Behavior
The
String::with_capacity
function should not allow the creation of a raw pointer to uninitialized memory.Similar to String::from("x").as_mut_ptr(), such raw pointers should not be compiled successfully.
The text was updated successfully, but these errors were encountered: