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

Document Rust's stance on /proc/self/mem #97837

Merged
merged 9 commits into from
Jun 20, 2022

Conversation

sunfishcode
Copy link
Member

Add documentation to std::os::unix::io describing Rust's stance on
/proc/self/mem, treating it as an external entity which is outside
the scope of Rust's safety guarantees.

Add documentation to `std::os::unix::io` describing Rust's stance on
`/proc/self/mem`, treating it as an external entity which is outside
the scope of Rust's safety guarantees.
@rust-highfive
Copy link
Collaborator

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with r? rust-lang/libs-api @rustbot label +T-libs-api -T-libs to request review from a libs-api team reviewer. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@rustbot rustbot added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Jun 7, 2022
@rust-highfive
Copy link
Collaborator

r? @Mark-Simulacrum

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 7, 2022
sunfishcode and others added 2 commits June 7, 2022 09:53
Co-authored-by: Sean Stangl <sean.stangl@gmail.com>
Co-authored-by: Sean Stangl <sean.stangl@gmail.com>
Co-authored-by: Josh Triplett <josh@joshtriplett.org>
@joshtriplett
Copy link
Member

@rfcbot merge

@rfcbot
Copy link

rfcbot commented Jun 7, 2022

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jun 7, 2022
Co-authored-by: Josh Triplett <josh@joshtriplett.org>
@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Jun 8, 2022
@rfcbot
Copy link

rfcbot commented Jun 8, 2022

🔔 This is now entering its final comment period, as per the review above. 🔔

@JakobDegen
Copy link
Contributor

@sunfishcode that seems like a reasonable set of rules. It's a little unclear to me how dlclose fits into that, but if you can argue that it does, I'd be happy.

But is that set of rules what this FCP is approving? Seems unlikely, since it wasn't even written down before now. Maybe it was just unclear to me though.

@sunfishcode
Copy link
Member Author

@sunfishcode that seems like a reasonable set of rules. It's a little unclear to me how dlclose fits into that, but if you can argue that it does, I'd be happy.

Are you asking why we say that resources accessed through the filesystem namespace are safe, when resources accessed through the dynamic linking namespace are unsafe?

In the filesystem case, it's because filesystem APIs are widely used, and most of the things that most people do with the filesystem namespace don't cause UB most of the time. Making it unsafe would be coherent, but on a practical level, it would likely do more harm than good. Making it safe is also coherent, using of this internal/external dichotomy, and much more practical, so we do that.

In the dynamic linking case, the things the dynamic linker produces are raw pointers. The thing most people do with them most of the time is call them, and if calling them could invoke UB, we have to treat them as unsafe. They're internal in the dichotomy—they're loaded into the program. Encapsulating this unsafety requires knowing the conditions under which they'll invoke UB, and the only way to know that is to know and trust the library they're loaded from, and have a commitment from that library that they'll behave. With that framework in mind: dlclose is loaded from this library called "libc" which we probably know and trust, but libc documents that dlclose in particular does multiple things which are quite unsafe, in a Rust sense. Consequently, dlclose is unsafe.

But is that set of rules what this FCP is approving? Seems unlikely, since it wasn't even written down before now. Maybe it was just unclear to me though.

The proposed change contains a lot fewer words, but I believe it's saying essentially the same thing.

Rust isn't alone here; lots of programming languages have effectively the exact same rules, yet as far as I know, none of them document the rules as such. I think there's a widespread assumption that this overall framework is just how computers are understood to work and it doesn't need to be stated.

That may mean we have a curse-of-knowledge phenomenon going on. Once one has the intuition here, there seems to be a sense of inevitability about the rules. /proc/self/mem is a little surprising at first, but once one discovers the solution of classifying it as external, it lines up with the intuition and feels similarly inevitable.

So perhaps what we're seeing here and in other places where people keep asking questions about this, is that not everyone makes the same assumptions or thinks about this the same way. In that case, perhaps the path forward looks like:

  • Assuming this FCP completes here, we merge this PR, establishing that this is T-libs' official position, at least in the eyes of people who share the overall intuition.
  • Then, someone could write up the rules above in a form that could be added to the language-level documentation, perhaps in the Nomicon, because I think this relates to your questions about why this isn't a T-lang/UCG question.
  • Then, we could update the library documentation here to reference that language documentation.

How does that sound?

@ChrisDenton
Copy link
Contributor

ChrisDenton commented Jun 18, 2022

Making it unsafe would be coherent, but on a practical level, it would likely do more harm than good.

This I very much agree with. Rust's stance can be fully justified on pragmatic grounds.

What I'm less clear is on is the proposed "internal" and "external" dichotomy. If I understand you right, a hypothetical libc::corrupt_my_memory function would be internal but a File::open("/special/corrupt_my_memory") would be external? Even if they used the same underlying mechanism?

Also, if Rust could encapsulate "person with a soldering iron" then surely it should do so? The problem with that is a practical one, not a philosophical objection to doing so.

@sunfishcode
Copy link
Member Author

What I'm less clear is on is the proposed "internal" and "external" dichotomy. If I understand you right, a hypothetical libc::corrupt_my_memory function would be internal but a File::open("/special/corrupt_my_memory") would be external? Even if they used the same underlying mechanism?

The underlying mechanism matters. If it's doing File::open, users have the option to control what it can do by running it in a sandbox.

Also, if Rust could encapsulate "person with a soldering iron" then surely it should do so? The problem with that is a practical one, not a philosophical objection to doing so.

"Is there a boundary?" is philosophical while "where is the boundary?" is practical.

@cuviper
Copy link
Member

cuviper commented Jun 18, 2022

a hypothetical libc::corrupt_my_memory function

There is libc::process_vm_writev, but at least that's unsafe, like any FFI. However, nix has a safe wrapper, which is probably a mistake.

@sunfishcode
Copy link
Member Author

sunfishcode commented Jun 18, 2022

In theory, process_vm_writev could go either way. On one hand, users could stop it with seccomp or by withholding PTRACE_MODE_ATTACH_REALCREDS, so one could reasonably classify the access as "external" and call it safe. On the other hand, it's not widely used, and it's quite easy to get it to invoke UB in the calling process, so it seems practical and more desirable to classify it as unsafe.

Edit: Also, unlike /proc/self/mem, it appears process_vm_writev bypasses prctl(PR_SET_DUMPABLE, SUID_DUMP_DISABLE), which is a pretty strong signal that it doesn't behave like a "debugger" feature, and thus is very desirable to classify as unsafe.

Edit: It also bypasses /proc/sys/kernel/yama/ptrace_scope even in its most restrictive setting 3, no attach.

Edit: I was testing calling process_vm_writev on the same process. Linux's ptrace access rules start with "If the calling thread and the target thread are in the same thread group, access is always allowed."

@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 Jun 18, 2022
@rfcbot
Copy link

rfcbot commented Jun 18, 2022

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.

@m-ou-se
Copy link
Member

m-ou-se commented Jun 20, 2022

@bors r+

@bors
Copy link
Contributor

bors commented Jun 20, 2022

📌 Commit 6959441 has been approved by m-ou-se

@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 Jun 20, 2022
@JakobDegen
Copy link
Contributor

JakobDegen commented Jun 20, 2022

I unfortunately didn't get a chance to respond before, and not sure if it's worth doing it now, but I guess I will anyway.

Rust isn't alone here; lots of programming languages have effectively the exact same rules, yet as far as I know, none of them document the rules as such. I think there's a widespread assumption that this overall framework is just how computers are understood to work and it doesn't need to be stated.

Not at all. There's no other language I know of that attempts to make a formal distinction between "sound" and "unsound," and so no other language has to deal with this issue. I think I've actually made a mistake in not emphasizing this point enough in this conversation. "Sound" and "unsound" are not (supposed to be) vague ideas that represent some philosophical goals, they are concrete concepts that we hope to tie to a formal definition. I don't mean to be rude here, but it frankly seems like this is being hand-waved away both in this conversation and in the PR itself, and I think that is a distinctly dangerous road to go down.

I went back and re-read your proposed rules, and honestly I'm not quite sure why I was happy with them the first time around - I don't agree with the distinctions, and I especially don't believe that arguing these kinds of points from a list of examples is a way forward. I have some alternative ideas in mind, and would be happy to discuss more on Zulip instead of here.

So perhaps what we're seeing here and in other places where people keep asking questions about this, is that not everyone makes the same assumptions or thinks about this the same way. In that case, perhaps the path forward looks like:

  • Assuming this FCP completes here, we merge this PR, establishing that this is T-libs' official position, at least in the eyes of people who share the overall intuition.
  • Then, someone could write up the rules above in a form that could be added to the language-level documentation, perhaps in the Nomicon, because I think this relates to your questions about why this isn't a T-lang/UCG question.
  • Then, we could update the library documentation here to reference that language documentation.

How does that sound?

Honestly, about as backwards as could be. "Stabilize that this API is sound first and figure out some rules under which that makes sense later" is not at all how these things should work. When we add the language level documentation, what choice will be left for T-Lang and T-types? What if they (like me) don't agree that we can just call this sound? It's not like they'll be able to say no anymore.

I suppose it's on me that I failed to ask for this to be nominated for T-Types earlier. I guess it's too late now?

@ChrisDenton
Copy link
Contributor

I mean, it's not set in stone until it reaches stable? I think you can still discuss this on Zulip and ask t-libs if you have reasons they might want to reconsider.

I personally don't want to take issue with /proc/self/mem being safe (even if it's far from ideal). I am however uncomfortable with the broad reasoning given in this PR, particularly how it might be applied to other cases that can be encapsulated without the same practical problems of making File unsafe to use.

@JakobDegen
Copy link
Contributor

Yeah, agreed. If this was just documenting that std doesn't care about protecting you from /proc/self/mem we'd be fine.

That's a good point about us having until this reaches stable. I might bring this up with T-types on zulip then (as they are the proper team to be making this decision). Later though, it is bed time for me right now

bors added a commit to rust-lang-ci/rust that referenced this pull request Jun 20, 2022
Rollup of 8 pull requests

Successful merges:

 - rust-lang#93080 (Implement `core::slice::IterMut::as_mut_slice` and `impl<T> AsMut<[T]> for IterMut<'_, T>`)
 - rust-lang#94855 (Panic when advance_slices()'ing too far and update docs.)
 - rust-lang#96609 (Add `{Arc, Rc}::downcast_unchecked`)
 - rust-lang#96719 (Fix the generator example for `pin!()`)
 - rust-lang#97149 (Windows: `CommandExt::async_pipes`)
 - rust-lang#97150 (`Stdio::makes_pipe`)
 - rust-lang#97837 (Document Rust's stance on `/proc/self/mem`)
 - rust-lang#98159 (Include ForeignItem when visiting types for WF check)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit ce1151c into rust-lang:master Jun 20, 2022
@rustbot rustbot added this to the 1.63.0 milestone Jun 20, 2022
@sunfishcode sunfishcode deleted the sunfishcode/proc-self-mem branch June 20, 2022 17:14
@SimonSapin
Copy link
Contributor

For anyone else looking, this doc-comment ends up visible at: https://doc.rust-lang.org/nightly/std/os/unix/io/index.html#procselfmem-and-similar-os-features

@apiraino apiraino removed the to-announce Announce this issue on triage meeting label Aug 25, 2022
workingjubilee pushed a commit to tcdi/postgrestd that referenced this pull request Sep 15, 2022
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Oct 10, 2022
Pkgsrc changes:
 * Adjust patches as needed & checksum updates.

Upstream changes:

Version 1.63.0 (2022-08-11)
==========================

Language
--------
- [Remove migrate borrowck mode for pre-NLL errors.][95565]
- [Modify MIR building to drop repeat expressions with length zero.][95953]
- [Remove label/lifetime shadowing warnings.][96296]
- [Allow explicit generic arguments in the presence of `impl Trait` args.]
  [96868]
- [Make `cenum_impl_drop_cast` warnings deny-by-default.][97652]
- [Prevent unwinding when `-C panic=abort` is used regardless of
  declared ABI.][96959]
- [lub: don't bail out due to empty binders.][97867]

Compiler
--------
- [Stabilize the `bundle` native library modifier,][95818] also removing the
  deprecated `static-nobundle` linking kind.
- [Add Apple WatchOS compile targets\*.][95243]
- [Add a Windows application manifest to rustc-main.][96737]

\* Refer to Rust's [platform support page][platform-support-doc] for more
   information on Rust's tiered platform support.

Libraries
---------
- [Implement `Copy`, `Clone`, `PartialEq` and `Eq` for
  `core::fmt::Alignment`.][94530]
- [Extend `ptr::null` and `null_mut` to all thin (including extern)
  types.][94954]
- [`impl Read and Write for VecDeque<u8>`.][95632]
- [STD support for the Nintendo 3DS.][95897]
- [Make write/print macros eagerly drop temporaries.][96455]
- [Implement internal traits that enable `[OsStr]::join`.][96881]
- [Implement `Hash` for `core::alloc::Layout`.][97034]
- [Add capacity documentation for `OsString`.][97202]
- [Put a bound on collection misbehavior.][97316]
- [Make `std::mem::needs_drop` accept `?Sized`.][97675]
- [`impl Termination for Infallible` and then make the `Result` impls
  of `Termination` more generic.][97803]
- [Document Rust's stance on `/proc/self/mem`.][97837]

Stabilized APIs
---------------

- [`array::from_fn`]
- [`Box::into_pin`]
- [`BinaryHeap::try_reserve`]
- [`BinaryHeap::try_reserve_exact`]
- [`OsString::try_reserve`]
- [`OsString::try_reserve_exact`]
- [`PathBuf::try_reserve`]
- [`PathBuf::try_reserve_exact`]
- [`Path::try_exists`]
- [`Ref::filter_map`]
- [`RefMut::filter_map`]
- [`NonNull::<[T]>::len`][`NonNull::<slice>::len`]
- [`ToOwned::clone_into`]
- [`Ipv6Addr::to_ipv4_mapped`]
- [`unix::io::AsFd`]
- [`unix::io::BorrowedFd<'fd>`]
- [`unix::io::OwnedFd`]
- [`windows::io::AsHandle`]
- [`windows::io::BorrowedHandle<'handle>`]
- [`windows::io::OwnedHandle`]
- [`windows::io::HandleOrInvalid`]
- [`windows::io::HandleOrNull`]
- [`windows::io::InvalidHandleError`]
- [`windows::io::NullHandleError`]
- [`windows::io::AsSocket`]
- [`windows::io::BorrowedSocket<'handle>`]
- [`windows::io::OwnedSocket`]
- [`thread::scope`]
- [`thread::Scope`]
- [`thread::ScopedJoinHandle`]

These APIs are now usable in const contexts:

- [`array::from_ref`]
- [`slice::from_ref`]
- [`intrinsics::copy`]
- [`intrinsics::copy_nonoverlapping`]
- [`<*const T>::copy_to`]
- [`<*const T>::copy_to_nonoverlapping`]
- [`<*mut T>::copy_to`]
- [`<*mut T>::copy_to_nonoverlapping`]
- [`<*mut T>::copy_from`]
- [`<*mut T>::copy_from_nonoverlapping`]
- [`str::from_utf8`]
- [`Utf8Error::error_len`]
- [`Utf8Error::valid_up_to`]
- [`Condvar::new`]
- [`Mutex::new`]
- [`RwLock::new`]

Cargo
-----
- [Stabilize the `--config path` command-line argument.][cargo/10755]
- [Expose rust-version in the environment as
  `CARGO_PKG_RUST_VERSION`.][cargo/10713]

Compatibility Notes
-------------------

- [`#[link]` attributes are now checked more strictly,][96885]
  which may introduce errors for invalid attribute arguments that
  were previously ignored.

Internal Changes
----------------

These changes provide no direct user facing benefits, but represent
significant improvements to the internals and overall performance
of rustc and related tools.

- [Prepare Rust for LLVM opaque pointers.][94214]

[94214]: rust-lang/rust#94214
[94530]: rust-lang/rust#94530
[94954]: rust-lang/rust#94954
[95243]: rust-lang/rust#95243
[95565]: rust-lang/rust#95565
[95632]: rust-lang/rust#95632
[95818]: rust-lang/rust#95818
[95897]: rust-lang/rust#95897
[95953]: rust-lang/rust#95953
[96296]: rust-lang/rust#96296
[96455]: rust-lang/rust#96455
[96737]: rust-lang/rust#96737
[96868]: rust-lang/rust#96868
[96881]: rust-lang/rust#96881
[96885]: rust-lang/rust#96885
[96959]: rust-lang/rust#96959
[97034]: rust-lang/rust#97034
[97202]: rust-lang/rust#97202
[97316]: rust-lang/rust#97316
[97652]: rust-lang/rust#97652
[97675]: rust-lang/rust#97675
[97803]: rust-lang/rust#97803
[97837]: rust-lang/rust#97837
[97867]: rust-lang/rust#97867
[cargo/10713]: rust-lang/cargo#10713
[cargo/10755]: rust-lang/cargo#10755

[`array::from_fn`]: https://doc.rust-lang.org/stable/std/array/fn.from_fn.html
[`Box::into_pin`]: https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.into_pin
[`BinaryHeap::try_reserve_exact`]: https://doc.rust-lang.org/stable/alloc/collections/binary_heap/struct.BinaryHeap.html#method.try_reserve_exact
[`BinaryHeap::try_reserve`]: https://doc.rust-lang.org/stable/std/collections/struct.BinaryHeap.html#method.try_reserve
[`OsString::try_reserve`]: https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.try_reserve
[`OsString::try_reserve_exact`]: https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.try_reserve_exact
[`PathBuf::try_reserve`]: https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.try_reserve
[`PathBuf::try_reserve_exact`]: https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#method.try_reserve_exact
[`Path::try_exists`]: https://doc.rust-lang.org/stable/std/path/struct.Path.html#method.try_exists
[`Ref::filter_map`]: https://doc.rust-lang.org/stable/std/cell/struct.Ref.html#method.filter_map
[`RefMut::filter_map`]: https://doc.rust-lang.org/stable/std/cell/struct.RefMut.html#method.filter_map
[`NonNull::<slice>::len`]: https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.len
[`ToOwned::clone_into`]: https://doc.rust-lang.org/stable/std/borrow/trait.ToOwned.html#method.clone_into
[`Ipv6Addr::to_ipv4_mapped`]: https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.to_ipv4_mapped
[`unix::io::AsFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.AsFd.html
[`unix::io::BorrowedFd<'fd>`]: https://doc.rust-lang.org/stable/std/os/unix/io/struct.BorrowedFd.html
[`unix::io::OwnedFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/struct.OwnedFd.html
[`windows::io::AsHandle`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.AsHandle.html
[`windows::io::BorrowedHandle<'handle>`]: https://doc.rust-lang.org/stable/std/os/windows/io/struct.BorrowedHandle.html
[`windows::io::OwnedHandle`]: https://doc.rust-lang.org/stable/std/os/windows/io/struct.OwnedHandle.html
[`windows::io::HandleOrInvalid`]: https://doc.rust-lang.org/stable/std/os/windows/io/struct.HandleOrInvalid.html
[`windows::io::HandleOrNull`]: https://doc.rust-lang.org/stable/std/os/windows/io/struct.HandleOrNull.html
[`windows::io::InvalidHandleError`]: https://doc.rust-lang.org/stable/std/os/windows/io/struct.InvalidHandleError.html
[`windows::io::NullHandleError`]: https://doc.rust-lang.org/stable/std/os/windows/io/struct.NullHandleError.html
[`windows::io::AsSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.AsSocket.html
[`windows::io::BorrowedSocket<'handle>`]: https://doc.rust-lang.org/stable/std/os/windows/io/struct.BorrowedSocket.html
[`windows::io::OwnedSocket`]: https://doc.rust-lang.org/stable/std/os/windows/io/struct.OwnedSocket.html
[`thread::scope`]: https://doc.rust-lang.org/stable/std/thread/fn.scope.html
[`thread::Scope`]: https://doc.rust-lang.org/stable/std/thread/struct.Scope.html
[`thread::ScopedJoinHandle`]: https://doc.rust-lang.org/stable/std/thread/struct.ScopedJoinHandle.html

[`array::from_ref`]: https://doc.rust-lang.org/stable/std/array/fn.from_ref.html
[`slice::from_ref`]: https://doc.rust-lang.org/stable/std/slice/fn.from_ref.html
[`intrinsics::copy`]: https://doc.rust-lang.org/stable/std/intrinsics/fn.copy.html
[`intrinsics::copy_nonoverlapping`]: https://doc.rust-lang.org/stable/std/intrinsics/fn.copy_nonoverlapping.html
[`<*const T>::copy_to`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.copy_to
[`<*const T>::copy_to_nonoverlapping`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.copy_to_nonoverlapping
[`<*mut T>::copy_to`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.copy_to-1
[`<*mut T>::copy_to_nonoverlapping`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.copy_to_nonoverlapping-1
[`<*mut T>::copy_from`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.copy_from
[`<*mut T>::copy_from_nonoverlapping`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.copy_from_nonoverlapping
[`str::from_utf8`]: https://doc.rust-lang.org/stable/std/str/fn.from_utf8.html
[`Utf8Error::error_len`]: https://doc.rust-lang.org/stable/std/str/struct.Utf8Error.html#method.error_len
[`Utf8Error::valid_up_to`]: https://doc.rust-lang.org/stable/std/str/struct.Utf8Error.html#method.valid_up_to
[`Condvar::new`]: https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html#method.new
[`Mutex::new`]: https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html#method.new
[`RwLock::new`]: https://doc.rust-lang.org/stable/std/sync/struct.RwLock.html#method.new
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet