rust-lang / rust-lang/rust

CString being noalias is a footgun

Open
#136,770 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-box A-FFI C-bug T-lang T-libs T-opsem
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.