ruststack-overflow

Stack overflow when running Rust code with too much include_bytes!(...) in debug mode


I have a moderately-sized Rust project which compiles warning-free succesfully and runs successfully with cargo run --release. However, cargo run fails with error: process didn't exit successfully: `target\\debug\\blah.exe` (exit code: 0xc00000fd, STATUS_STACK_OVERFLOW).

I was able to track the problem to the place where I do include_bytes!(...) for eight different files; a couple of them are larger than others (around 340 Kb; the rest is about 130, 90, 75, 50, 45, and 10). Removing the load for any of two larger files (and only for those two) stops cargo run from crashing. Just in case I have tried to use static instead of local let -- with the same result, cargo run crashes with stack overflow, cargo run --release does not. I have the same result both for stable-x86_64-pc-windows-msvc and nightly-x86_64-pc-windows-msvc. Stable is default, rustc --version reports 1.73.0. I would like to be able to do cargo run without crashing.

The following code illustrates the problem:

use sdl2::mixer::Chunk;
fn main() {
    let tmp1 = *include_bytes!("test.wav");
    let tmp2 = *include_bytes!("test.wav");
    let tmp3 = *include_bytes!("test.wav");

    let chunk1 = Chunk::from_raw_buffer(Box::new(tmp1));
    let chunk2 = Chunk::from_raw_buffer(Box::new(tmp2));
    let chunk3 = Chunk::from_raw_buffer(Box::new(tmp3));
}

where test.wav is an appropriately big file located next to main.rs.


Solution

  • The core problem is that in Rust, arrays implement Copy and by dereferencing the array reference produced by include_bytes! you directly put a copy of the array onto the stack.

    In release builds putting the array onto the stack and subsequently creating a boxed slice from it is very likely optimized away to directly create the box from the reference, but that optimization is usually missed in debug builds.

    Instead, if the method you want to call expects a Box<[T]> you should just directly convert the array reference that include_bytes! produces into a boxed slice:

    let tmp1: Box<[u8]> = include_bytes!("test.wav")[..].into();
    

    That way you avoid allocating a needlessly large array on the stack.