CString being noalias is a footgun
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
Internally, String is implemented as a Vec<u8>. However, CString is implemented as a Box<[u8]>.
This means that CString is currently treated as noalias, like Box (but not Vec) is. (See https://github.com/rust-lang/unsafe-code-guidelines/issues/326.) This means that there are additional rules of how pointers pointing into the CString buffer can be used, and violating these rules lead to UB. As far as I can tell, the fact that this applies to CString is not documented anywhere.
I don't know if this has produced any issues in practice, but I believe that this is an undesirable footgun that should be eliminated (e.g., by changing CString to use NonNull<[u8]> instead), especially since CString is intended to be turned into a pointer and passed to FFI, potentially resulting in subtle UB that Miri can't detect.
For example, consider the following code, which is a stand-in for how a program with C FFI might use CString:
use std::ffi::{CString, c_char, CStr};
fn main() {
let my_string = CString::new("abc").unwrap();
let ptr: *const c_char = my_string.as_ptr();
// Suppose that `ptr` is given to some C FFI function here
let _moved_my_string = my_string;
// Suppose that `ptr` is re-obtained from C FFI here
let _my_str = unsafe { CStr::from_ptr(ptr) };
}
This code might seem intuitively fine to many Rust users, but it causes UB according to Miri.
Error from Miri
error: Undefined Behavior: attempting a read access using <2513> at alloc1005[0x0], but that tag does not exist in the borrow stack for this location
--> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ffi/c_str.rs:740:22
|
740 | unsafe { strlen(s) }
| ^^^^^^^^^
| |
| attempting a read access using <2513> at alloc1005[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at alloc1005[0x0..0x1]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <2513> was created by a SharedReadOnly retag at offsets [0x0..0x4]
--> src/main.rs:5:30
|
5 | let ptr: *const c_char = my_string.as_ptr();
| ^^^^^^^^^^^^^^^^^^
help: <2513> was later invalidated at offsets [0x0..0x4] by a Unique retag (of a reference/box inside this compound value)
--> src/main.rs:7:28
|
7 | let _moved_my_string = my_string;
| ^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `core::ffi::c_str::strlen::runtime` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ffi/c_str.rs:740:22: 740:31
= note: inside `core::ffi::c_str::strlen` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/intrinsics/mod.rs:3886:9: 3886:61
= note: inside `std::ffi::CStr::from_ptr::<'_>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ffi/c_str.rs:267:28: 267:39
note: inside `main`
--> src/main.rs:9:28
|
9 | let _my_str = unsafe { CStr::from_ptr(ptr) };
| ^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
(It's debatable whether CString not having extra capacity is good or not, but that's a separate issue.)
@rustbot labels +A-FFI +A-box +T-libs-api +T-opsem
Meta
The Miri error was from running the code on the playground with rust version 1.86.0-nightly (2025-02-08 43ca9d18e333797f0aa3).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the supplied src/main.rs example under Miri, then inspect library/core/src/ffi/c_str.rs and the CString representation. Determine the accepted representation or semantics change that removes the described noalias hazard, and verify that the moved-pointer example no longer triggers the reported UB while preserving CString and CStr behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100