bevy icon indicating copy to clipboard operation
bevy copied to clipboard

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

Open cz-kaga opened this issue 10 months ago • 1 comments

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

cz-kaga avatar Apr 23 '24 15:04 cz-kaga

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.

BD103 avatar Apr 23 '24 21:04 BD103