Skip to content

STATUS_HEAP_CORRUPTION on startup during RenderStartup. #20379

@andriyDev

Description

@andriyDev

Bevy version

main (commit: ff40a6a)

Relevant system information

Windows 11.

AdapterInfo { name: "NVIDIA GeForce RTX 3080", vendor: 4318, device: 8714, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "576.88", backend: Vulkan }

What you did

Repeatedly run the clear_color example.

What went wrong

Crash during RenderStartup with STATUS_HEAP_CORRUPTION.

Additional information

So far there doesn't seem to be a reliable way to reproduce it. The best I've got is this: first, apply the following patch:

From e5027d20bd714f2c11e1db3077ce4f3e2a4debf8 Mon Sep 17 00:00:00 2001
From: andriyDev <andriydzikh@gmail.com>
Date: Fri, 1 Aug 2025 22:20:29 -0700
Subject: [PATCH] dbg

---
 crates/bevy_render/src/lib.rs  | 3 +++
 examples/window/clear_color.rs | 7 +++++++
 2 files changed, 10 insertions(+)

diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs
index 994bc35bb..a0b69a4fd 100644
--- a/crates/bevy_render/src/lib.rs
+++ b/crates/bevy_render/src/lib.rs
@@ -586,8 +586,11 @@ unsafe fn initialize_render_app(app: &mut App) {
             // Run the `RenderStartup` if it hasn't run yet. This does mean `RenderStartup` blocks
             // the rest of the app extraction, but this is necessary since extraction itself can
             // depend on resources initialized in `RenderStartup`.
+            println!("Started RenderStartup");
             render_world.run_schedule(RenderStartup);
             should_run_startup = false;
+            println!("Finished RenderStartup");
+            std::process::exit(0);
         }
 
         {
diff --git a/examples/window/clear_color.rs b/examples/window/clear_color.rs
index 1933c4e7c..427234d1f 100644
--- a/examples/window/clear_color.rs
+++ b/examples/window/clear_color.rs
@@ -10,6 +10,7 @@ fn main() {
         .add_plugins(DefaultPlugins)
         .add_systems(Startup, setup)
         .add_systems(Update, change_clear_color)
+        .add_systems(Update, exit)
         .run();
 }
 
@@ -22,3 +23,9 @@ fn change_clear_color(input: Res<ButtonInput<KeyCode>>, mut clear_color: ResMut<
         clear_color.0 = PURPLE.into();
     }
 }
+
+fn exit(frame: Res<bevy::diagnostic::FrameCount>, mut app_exit: EventWriter<AppExit>) {
+    if frame.0 > 100 {
+        app_exit.write(AppExit::Success);
+    }
+}
-- 
2.49.0.windows.1

Next, run the following bash script (e.g., in Git Bash):

i=0; while cargo r --example clear_color ; do echo $i; i=$((i + 1)); done

Go grab a coffee... this can take a while to fail. I've gotten the heap corruption at iteration 389, though it usually happens sooner than that.

I suspect some operation we are doing isn't actually thread-safe under the hood. Making RenderStartup use the single-threaded executor seems to fix it (I got to iteration 750). I went through and listed out all the rendering operations I could find in every RenderStartup usage on main. Here is the resulting table. None of these scream "not multithread safe", especially since we do these at runtime all the time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-RenderingDrawing game state to the screenC-BugAn unexpected or incorrect behaviorD-ModestA "normal" level of difficulty; suitable for simple features or challenging fixesD-UnsafeTouches with unsafe code in some wayP-CrashA sudden unexpected crash

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions