bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
35.99k stars 3.55k forks source link

User-mode data execution prevention (DEP) violation when loading Dynamic Plugins #13073

Closed cz-kaga closed 5 months ago

cz-kaga commented 6 months ago

Bevy version

0.13.2

[Optional] Relevant system information

OS: Windows 11 Pro for Workstations - 23H2 - 22631.3447 CPU: AMD Ryzen 7 4800H with Radeon Graphics 2.90 GHz GPU: NVIDIA GeForce GTX 1650Ti for Laptop RAM: 64GB Rust Version: rustc 1.77.2 (25ef9e3d8 2024-04-09) Cargo Version: cargo 1.77.2 (e52e36006 2024-03-26)

What you did

I am tring to test whether Bevy can share component type throuth different dynamic plugins(i.e. System in one dylib can detect component spawned by System in another dylib). So I create three crates inside the main project. One defines Components, Others depend this static lib and run Systems. In main.rs of this project, I enable bevy's "bevy_dynamic_plugin" feature then invoke load_plugin()

What went wrong

Whether test success or fail, dynamic_plugin should built successfully, but I encountered Exception: Exception 0xc0000005 encountered at address 0x000000: User-mode data execution prevention (DEP) violation at location 0x00000000 and then bevy crashed.

Additional information

I debug the application several times. I'm sure that dlls are successfully loaded. The last step before exception is as follows: a9b4a057e0373f86602bab2a9a00ff3e then, it crashes: afcc0b6bf2ef46794b42fc215e007d81 The top of trace stack is: 725f8d3a50268044c9ed7cc6876356af

It appears that the address of self is 0x0 or 0x4 or many other address nearby 0x0, but actually self has its own address. What happend when app calls plugin.build(self)? I switched lib type to cdylib, but it makes nonsense. The content of dll is quite simple(This problem exists whether these lines are commented or not ):

use bevy::app::DynamicPlugin;
use bevy::prelude::{App, Commands, Component, Plugin, Query, Startup, Update};
use xk_python::Test1;

#[derive(DynamicPlugin)]
pub struct XkUiplugin;

impl Plugin for XkUiplugin {
    fn build(&self, app: &mut App) {
        // println!("build ui");
        // app.add_systems(Update, test_ui);
    }
}

same to main.rs

pub mod prelude;

use bevy::prelude::*;
use std::env;

pub fn main() {
    unsafe {
        println!("{}", String::from(env::current_dir().unwrap().to_str().unwrap()) + "\\xk_ui.dll");
        App::new()
            .load_plugin( "D:\\WorkSpace\\Project\\FormalProject\\XKGIS\\XKGIS-Core\\target\\debug\\xk_ui.dll")
            .load_plugin("D:\\WorkSpace\\Project\\FormalProject\\XKGIS\\XKGIS-Core\\target\\debug\\xk_show_screen.dll")
            .add_plugins(MinimalPlugins)
            .run();
    }
}

My friend helped me checked dll content with IDA, he says the dll files I used are completely normal. Debugger of RustRover gives me assembly code, I don't know whether it can help.

bevy_dynamic_plugin::loader::impl$0::load_plugin<ref$<str$> >(*mut bevy_app::app::App,ref$<str$>):
    pushq  %rbp                     
    subq   $0xe0, %rsp              
    leaq   0x80(%rsp), %rbp         
    movq   $-0x2, 0x58(%rbp)        
    movq   %rcx, -0x50(%rbp)        
    movq   %rcx, 0x18(%rbp)         
    movq   %rdx, 0x20(%rbp)         
    movq   %r8, 0x28(%rbp)          
    movb   $0x0, 0x17(%rbp)         
    leaq   -0x18(%rbp), %rcx        
    callq  0x140002cb0                ; bevy_dynamic_plugin::loader::dynamically_load_plugin<ref$<str$> >(ref$<str$>) at loader.rs:29
    cmpq   $0x2, -0x18(%rbp)        
    je     0x140001852                ; <+130> [inlined] enum2$<core::result::Result<tuple$<libloading::safe::Library,alloc::boxed::Box<dyn$<bevy_app::plugin::Plugin>,alloc::alloc::Global> >,enum2$<bevy_dynamic_plugin::loader::DynamicPluginLoadError> > >::unwrap(enum2$<core::result::Result<tuple$<libloading::safe::Library,alloc::boxed::Box<dyn$<bevy_app::plugin::Plugin>,alloc::alloc::Global> >,enum2$<bevy_dynamic_plugin::loader::DynamicPluginLoadError> > >,*mut core::panic::location::Location) + 77 at loader.rs:59
    movq   0x8(%rbp), %rax          
    movq   %rax, 0x50(%rbp)         
    movups -0x18(%rbp), %xmm0       
    movups -0x8(%rbp), %xmm1        
    movaps %xmm1, 0x40(%rbp)        
    movaps %xmm0, 0x30(%rbp)        
    leaq   0x3d8c95(%rip), %rcx     
    movq   %rsp, %rax               
    movq   %rcx, 0x20(%rax)         
    leaq   0x3d8bc7(%rip), %rcx     
    leaq   0x3d8bf0(%rip), %r9        ; impl$<enum2$<bevy_dynamic_plugin::loader::DynamicPluginLoadError>, core::fmt::Debug>::vtable$
    movl   $0x2b, %edx              
    leaq   0x30(%rbp), %r8          
    callq  0x1403d9830                ; core::result::unwrap_failed() at result.rs:1648
    jmp    0x140001850                ; <+128> [inlined] enum2$<core::result::Result<tuple$<libloading::safe::Library,alloc::boxed::Box<dyn$<bevy_app::plugin::Plugin>,alloc::alloc::Global> >,enum2$<bevy_dynamic_plugin::loader::DynamicPluginLoadError> > >::unwrap(enum2$<core::result::Result<tuple$<libloading::safe::Library,alloc::boxed::Box<dyn$<bevy_app::plugin::Plugin>,alloc::alloc::Global> >,enum2$<bevy_dynamic_plugin::loader::DynamicPluginLoadError> > >,*mut core::panic::location::Location) + 75 at loader.rs:59
    ud2                             
    movq   (%rbp), %rax             
    movq   %rax, -0x20(%rbp)        
    movups -0x10(%rbp), %xmm0       
    movaps %xmm0, -0x30(%rbp)       
    movb   $0x1, 0x17(%rbp)         
    movq   -0x30(%rbp), %rax        
    movq   %rax, -0x48(%rbp)        
    movq   -0x28(%rbp), %rcx        
    movq   -0x20(%rbp), %rax        
    movq   %rcx, -0x40(%rbp)        
    movq   %rax, -0x38(%rbp)        
    movb   $0x0, 0x17(%rbp)         
    movq   -0x48(%rbp), %rcx        
    callq  0x140002810                ; core::mem::forget<libloading::safe::Library>(libloading::safe::Library) at mod.rs:148
    jmp    0x14000188d                ; <+189> at loader.rs:61
    movq   -0x50(%rbp), %rdx        
    movq   -0x40(%rbp), %rcx        
    movq   -0x38(%rbp), %rax        
    movq   0x50(%rax), %rax         
    callq  *%rax                    
    jmp    0x1400018a1                ; <+209> at loader.rs:63
    leaq   -0x40(%rbp), %rcx        
    callq  0x1400c4bf0                ; core::ptr::drop_in_place<alloc::boxed::Box<dyn$<bevy_app::plugin::Plugin>,alloc::alloc::Global> >(*mut alloc::boxed::Box<dyn$<bevy_app::plugin::Plugin>,alloc::alloc::Global>) at mod.rs:507
    jmp    0x1400018ac                ; <+220> at loader.rs:63
    movq   -0x50(%rbp), %rax        
    movb   $0x0, 0x17(%rbp)         
    addq   $0xe0, %rsp              
    popq   %rbp                     
    retq                            
    nopl   (%rax)                   
    movq   %rdx, 0x10(%rsp)         
    pushq  %rbp                     
    subq   $0x30, %rsp              
    leaq   0x80(%rdx), %rbp         
    leaq   0x30(%rbp), %rcx         
    callq  0x140001dc0                ; core::ptr::drop_in_place<enum2$<bevy_dynamic_plugin::loader::DynamicPluginLoadError> >(*mut enum2$<bevy_dynamic_plugin::loader::DynamicPluginLoadError>) at mod.rs:507
    nop                             
    addq   $0x30, %rsp              
    popq   %rbp                     
    retq                            
    nopw   %cs:(%rax,%rax)          
    movq   %rdx, 0x10(%rsp)         
    pushq  %rbp                     
    subq   $0x30, %rsp              
    leaq   0x80(%rdx), %rbp         
    leaq   -0x40(%rbp), %rcx        
    callq  0x1400c4bf0                ; core::ptr::drop_in_place<alloc::boxed::Box<dyn$<bevy_app::plugin::Plugin>,alloc::alloc::Global> >(*mut alloc::boxed::Box<dyn$<bevy_app::plugin::Plugin>,alloc::alloc::Global>) at mod.rs:507
    nop                             
    addq   $0x30, %rsp              
    popq   %rbp                     
    retq                            
    nopw   %cs:(%rax,%rax)          
    movq   %rdx, 0x10(%rsp)         
    pushq  %rbp                     
    subq   $0x30, %rsp              
    leaq   0x80(%rdx), %rbp         
    testb  $0x1, 0x17(%rbp)         
    jne    0x14000193f                ; <+367> at loader.rs:63
    jmp    0x140001939                ; <+361> at loader.rs:57
    addq   $0x30, %rsp              
    popq   %rbp                     
    retq                            
    leaq   -0x48(%rbp), %rcx        
    callq  0x1400100b0                ; core::ptr::drop_in_place<libloading::safe::Library>(*mut libloading::safe::Library) at mod.rs:507
    jmp    0x140001939                ; <+361> at loader.rs:57

As I reruned the app, I found that addresses in vtable of dyn Plugin changed unnormally. fc61644124a060bd6268cbb2ce3724e8

BD103 commented 6 months ago

Bevy's dynamic plugin interface is almost certainly unsound, and I think this may be one such case. You may find #11969 interesting in this regard. Other bits of code that are related include dynamically_load_plugin, the CreatePlugin type, and the DynamicPlugin derive.