Skip to content

Undefined Behaviour find by Miri #4

@yilin0518

Description

@yilin0518

Hi!

We are a team of researchers studying memory safety in Rust. As part of our ongoing research, we tested str_stack (version: 0.1.0) and found that the following code snippet is reported as undefined behavior by Miri:

Minimal Problematic Snippet

#![feature(allocator_api)]
extern crate alloc;
use str_stack::*;
fn main() {
    let mut v4 = <StrStack as std::default::Default>::default();
    println!("v4: {:?}",v4);
}

Miri Error Excerpt

error: Undefined Behavior: `assume` called with `false`
  --> /workspace/src/lib.rs:63:30
   |
63 |                 let start = *self.ends.get_unchecked(0);
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
   |
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
   = note: BACKTRACE:
   = note: inside `<str_stack::Iter<'_> as std::iter::Iterator>::next` at /workspace/src/lib.rs:63:30: 63:56
   = note: inside `std::fmt::DebugList::<'_, '_>::entries::<&str, str_stack::Iter<'_>>` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/builders.rs:787:22: 787:29
   = note: inside `<str_stack::StrStack as std::fmt::Debug>::fmt` at /workspace/src/lib.rs:50:9: 50:44
   = note: inside `core::fmt::rt::Argument::<'_>::fmt` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/rt.rs:152:76: 152:95
   = note: inside `std::fmt::write` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:1684:17: 1686:80
   = note: inside `std::io::default_write_fmt::<std::io::StdoutLock<'_>>` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:614:11: 614:40
   = note: inside `<std::io::StdoutLock<'_> as std::io::Write>::write_fmt` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:1969:13: 1969:42
   = note: inside `<&std::io::Stdout as std::io::Write>::write_fmt` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:834:9: 834:36
   = note: inside `<std::io::Stdout as std::io::Write>::write_fmt` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:808:9: 808:33
   = note: inside `std::io::stdio::print_to::<std::io::Stdout>` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:1164:21: 1164:47
   = note: inside `std::io::_print` at /root/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:1275:5: 1275:37
note: inside `main`
  --> src/main.rs:12:5
   |
12 |     println!("v4: {:?}",v4);
   |     ^^^^^^^^^^^^^^^^^^^^^^^
   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

Root-Cause Hypothesis

After analyzing the Miri report and the source code, we assume the UB is rooted in:

  • Suspect location: src/lib.rs:63 (Iter::next) and src/lib.rs:50 (StrStack::fmt)
  • Invariant being broken: Iter assumes ends.len() >= 1, but the derived Default leaves ends empty ([]).
  • Causality chain: input creates StrStack::default() -> Debug formatting calls StrStack::fmt -> Iter::next runs get_unchecked(0/1) on an empty ends vector -> Miri reports Undefined Behavior (assume called with false).

Command used:

MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-tree-borrows" RUSTFLAGS=-Awarnings RUST_BACKTRACE=1 cargo miri run

Possible Fix

  • Implement Default manually as StrStack::new() (or initialize ends with [0]) so iterator invariants hold before any safe API reads.
  • Add a regression test that calls format!("{:?}", StrStack::default()) and runs under Miri in CI.

We would appreciate it if you could take a look and confirm whether this behavior indicates a real issue, or if it is a false positive / expected limitation of Miri.

Thank you very much for your time and for maintaining this great project!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions