-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Added From<Vec<NonZeroU8>> for CString #64069
Added From<Vec<NonZeroU8>> for CString #64069
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
Perhaps there could be a method to return the nonzero bytes as a slice as well? |
Sounds reasonable to me. That should be its own PR though, as that can be feature-gated for some time while trait implementations are instantly stable. |
Ping from triage Thanks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly just some nits...
src/libstd/ffi/c_str.rs
Outdated
/// - for any `cap: usize`, `Layout<[T; cap]>` needs to be equal to | ||
/// `Layout<[U; cap]>` (for the allocator) | ||
#[inline] | ||
unsafe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's with the line break here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woops, my own Rust style leaked here (Out of topic: I see pub
and unsafe
, among others, as markers, hence the logic of them being on an extra line rather than on the same line, in the same vein as other #[meta]
attributes)
src/libstd/ffi/c_str.rs
Outdated
#[inline] | ||
unsafe | ||
fn transmute_vec<T, U>(v: Vec<T>) -> Vec<U> { | ||
// necessary conditions for `Layout<[T; N]> == Layout<[U; N]>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not check Layout::array::<T / U>().unwrap()
against each other?
src/libstd/ffi/c_str.rs
Outdated
/// `[T; length]` and `[U; length]`. | ||
/// | ||
/// - for any `cap: usize`, `Layout<[T; cap]>` needs to be equal to | ||
/// `Layout<[U; cap]>` (for the allocator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// `Layout<[U; cap]>` (for the allocator) | |
/// `Layout<[U; cap]>` (for the allocator). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Soundness argument seems fine to me, just some nits.
src/libstd/ffi/c_str.rs
Outdated
// | ||
// - `v` cannot contain null bytes, given the type-level | ||
// invariant of `NonZeroU8` (this would still apply even if | ||
// this invariant was a safety invariant and not a validity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: strike the hypothetical. I am reasonably sure you do need the safety invariant here. Most operations on vectors don't assert validity of any elements except perhaps the ones they move into or out of the vec. Of course, a Vec<T>
is only safe if elements 0..len
are safe at T
, so the soundness argument can rest on that plus the safety/validity (same thing) of the NonZeroU8
.
(Insert standard disclaimer about all of these details being not yet ratified here.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, I will remove the part between brackets then (I'm still glad I put it, it is good to clearly think about these things before merging).
src/libstd/ffi/c_str.rs
Outdated
Vec::<U>::from_raw_parts(ptr as *mut U, length, capacity) | ||
} | ||
|
||
let v = unsafe { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A type annotation won't hurt here IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
src/libstd/ffi/c_str.rs
Outdated
crate::alloc::Layout::new::<U>(), | ||
); | ||
// The previous assert should imply the following one: | ||
debug_assert_eq!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first check is an assert since it is expected to be easily optimized out (only const / compile-time information here).
On the other hand, the check involving Layout::array
cannot be const
, since it involves the runtime value of the vector capacity.
That being said, I expect Layout<T> == Layout<U>
to imply that for all N: usize
, Layout<[T; N]> == Layout<[U; N]>
.
If this were not to be correct / guaranteed by the language, this debug assertion could be upgraded to a normal assert!
; in this case however, the function is only used with the monomorphisation to T = NonZeroU8
and V = u8
, which does not require such check at runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be a serious bug in Layout::array
if this is not given... its comparison test would fail to take something into account, or so.
Not sure if this check is worth the clutter it introduces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! I will remove the debug_assert!
involving the Layout::array
check
755fe39
to
b32aec7
Compare
I'm afraid my Rust inbox is swamped, I won't be able to review this -- I can take a glance, but don't have time to do a full review. Any takers? (Also is there some way to tell bors "sorry, not me", if I don't know whom else to assign?) |
src/libstd/ffi/c_str.rs
Outdated
// End-of-scope `mem::forget` but without aliasing problems. | ||
let mut v = mem::ManuallyDrop::<Vec<T>>::new(v); | ||
let v: &mut Vec<T> = &mut *v; | ||
v.as_mut_ptr() // Cannot be aliased. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ManuallyDrop
implements Deref
; why is the intermediate let v: &mut Vec<T> = ...
needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just me being (overly?) cautious with type inference / method resolution within unsafe
code , so I wanted to have that Deref
be very explicit.
Going to re-assign to @rkruppe as they appear to have done some review here but feel free to reassign to someone else if you don't feel confident reviewing this. |
I'm fine with reviewing this. Will take another close look later for due digilence but from what I recall this should be in good shape. I think some @rust-lang/libs sign-off is also required since this is a new insta-stable API addition. |
I've nominated to get someone from T-libs to kick off an FCP merge here |
I checked Kimundi's box per rust-lang/team@bfbf48f. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
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. The RFC will be merged soon. |
Did we come to a decision on the multiple But thanks for working on this @danielhenrymantilla! Would you like to squash some of your commits down before merging? |
Updated tracking issue number Added safeguards for transmute_vec potentially being factored out elsewhere Clarified comment about avoiding mem::forget Removed unneeded unstable guard Added back a stability annotation for CI Minor documentation improvements Thanks to @Centril's code review Co-Authored-By: Mazdak Farrokhzad <twingoow@gmail.com> Improved layout checks, type annotations and removed unaccurate comment Removed unnecessary check on array layout Adapt the stability annotation to the new 1.41 milestone Co-Authored-By: Mazdak Farrokhzad <twingoow@gmail.com> Simplify the implementation. Use `Vec::into_raw_parts` instead of a manual implementation of `Vec::transmute`. If `Vec::into_raw_parts` uses `NonNull` instead, then the code here will need to be adjusted to take it into account (issue rust-lang#65816) Reduce the whitespace of safety comments
8a9725a
to
60274a9
Compare
I did have a preference for the split blocks, but I seem to be at a minority here, so I have merged the There should be nothing blocking the merge, now 🙂 |
r? @KodrAus |
@bors r+ |
📌 Commit 60274a9 has been approved by |
…_from_vec_of_nonzerou8, r=KodrAus Added From<Vec<NonZeroU8>> for CString Added a `From<Vec<NonZeroU8>>` `impl` for `CString` # Rationale - `CString::from_vec_unchecked` is a subtle function, that makes `unsafe` code harder to audit when the generated `Vec`'s creation is non-trivial. This `impl` allows to write safer `unsafe` code thanks to the very explicit semantics of the `Vec<NonZeroU8>` type. - One such situation is when trying to `.read()` a `CString`, see issue rust-lang#59229. - this lead to a PR: rust-lang#59314, that was closed for being too specific / narrow (it only targetted being able to `.read()` a `CString`, when this pattern could have been generalized). - the issue suggested another route, based on `From<Vec<NonZeroU8>>`, which is indeed a less general and more concise code pattern. - quoting @Shnatsel: - > For me the main thing about making this safe is simplifying auditing - people have spent like an hour looking at just this one unsafe block in libflate because it's not clear what exactly is unchecked, so you have to look it up when auditing anyway. This has distracted us from much more serious memory safety issues the library had. Having this trivial impl in stdlib would turn this into safe code with compiler more or less guaranteeing that it's fine, and save anyone auditing the code a whole lot of time.
Rollup of 6 pull requests Successful merges: - #64069 (Added From<Vec<NonZeroU8>> for CString) - #66721 (implement LowerExp and UpperExp for integers) - #69106 (Fix std::fs::copy on WASI target) - #69154 (Avoid calling `fn_sig` on closures) - #69166 (Check `has_typeck_tables` before calling `typeck_tables_of`) - #69180 (Suggest a comma if a struct initializer field fails to parse) Failed merges: r? @ghost
Added a
From<Vec<NonZeroU8>>
impl
forCString
Rationale
CString::from_vec_unchecked
is a subtle function, that makesunsafe
code harder to audit when the generatedVec
's creation is non-trivial. Thisimpl
allows to write saferunsafe
code thanks to the very explicit semantics of theVec<NonZeroU8>
type.One such situation is when trying to
.read()
aCString
, see issue Reading a CString safely without overhead from Read #59229.this lead to a PR: Implement CString::from_reader #59314, that was closed for being too specific / narrow (it only targetted being able to
.read()
aCString
, when this pattern could have been generalized).the issue suggested another route, based on
From<Vec<NonZeroU8>>
, which is indeed a less general and more concise code pattern.quoting @Shnatsel: