Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
cz-kaga opened this issue Apr 23, 2024 · 1 comment · Fixed by #13080
Closed

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

cz-kaga opened this issue Apr 23, 2024 · 1 comment · Fixed by #13080
Labels
A-App Bevy apps and plugins C-Bug An unexpected or incorrect behavior P-Crash A sudden unexpected crash

Comments

@cz-kaga
Copy link

cz-kaga commented Apr 23, 2024

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 cz-kaga added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Apr 23, 2024
@BD103 BD103 added P-Crash A sudden unexpected crash A-App Bevy apps and plugins and removed S-Needs-Triage This issue needs to be labelled labels Apr 23, 2024
@BD103
Copy link
Member

BD103 commented Apr 23, 2024

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.

github-merge-queue bot pushed a commit that referenced this issue May 20, 2024
# Objective

- The current implementation for dynamic plugins is unsound. Please see
#11969 for background and justification.
- Closes #11969 and closes #13073.

## Solution

- Deprecate all dynamic plugin items for Bevy 0.14, with plans to remove
them for Bevy 0.15.

## Discussion

One thing I want to make clear is that I'm not opposed to dynamic
plugins _in general_. I think they can be handy, especially for DLC and
modding, but I think the current system is the wrong approach. It's too
much of a footgun for the meager benefit is provides.

---

## Changelog

- Deprecated the current dynamic plugin system.
- Dynamic plugins will be removed in Bevy 0.15. For now you can continue
using them by marking your code with `#[allow(deprecated)]`.

## Migration Guide

If possible, remove all usage of dynamic plugins.

```rust
// Old
#[derive(DynamicPlugin)]
pub struct MyPlugin;

App::new()
    .load_plugin("path/to/plugin")
    .run();

// New
pub struct MyPlugin;

App::new()
    .add_plugins(MyPlugin)
    .run();
```

If you are unable to do that, you may temporarily silence the
deprecation warnings.

```rust
#[allow(deprecated)]
```

Please note that the current dynamic plugin system will be removed by
the next major Bevy release, so you will have to migrate eventually. You
may be interested in these safer alternatives:

- [Bevy Assets - Scripting]: Scripting and modding libraries for Bevy
- [Bevy Assets - Development tools]: Hot reloading and other development
functionality
- [`stabby`]: Stable Rust ABI

[Bevy Assets - Scripting]: https://bevyengine.org/assets/#scripting
[Bevy Assets - Development tools]:
https://bevyengine.org/assets/#development-tools
[`stabby`]: https://github.com/ZettaScaleLabs/stabby
salvadorcarvalhinho pushed a commit to salvadorcarvalhinho/bevy that referenced this issue May 25, 2024
# Objective

- The current implementation for dynamic plugins is unsound. Please see
bevyengine#11969 for background and justification.
- Closes bevyengine#11969 and closes bevyengine#13073.

## Solution

- Deprecate all dynamic plugin items for Bevy 0.14, with plans to remove
them for Bevy 0.15.

## Discussion

One thing I want to make clear is that I'm not opposed to dynamic
plugins _in general_. I think they can be handy, especially for DLC and
modding, but I think the current system is the wrong approach. It's too
much of a footgun for the meager benefit is provides.

---

## Changelog

- Deprecated the current dynamic plugin system.
- Dynamic plugins will be removed in Bevy 0.15. For now you can continue
using them by marking your code with `#[allow(deprecated)]`.

## Migration Guide

If possible, remove all usage of dynamic plugins.

```rust
// Old
#[derive(DynamicPlugin)]
pub struct MyPlugin;

App::new()
    .load_plugin("path/to/plugin")
    .run();

// New
pub struct MyPlugin;

App::new()
    .add_plugins(MyPlugin)
    .run();
```

If you are unable to do that, you may temporarily silence the
deprecation warnings.

```rust
#[allow(deprecated)]
```

Please note that the current dynamic plugin system will be removed by
the next major Bevy release, so you will have to migrate eventually. You
may be interested in these safer alternatives:

- [Bevy Assets - Scripting]: Scripting and modding libraries for Bevy
- [Bevy Assets - Development tools]: Hot reloading and other development
functionality
- [`stabby`]: Stable Rust ABI

[Bevy Assets - Scripting]: https://bevyengine.org/assets/#scripting
[Bevy Assets - Development tools]:
https://bevyengine.org/assets/#development-tools
[`stabby`]: https://github.com/ZettaScaleLabs/stabby
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-App Bevy apps and plugins C-Bug An unexpected or incorrect behavior P-Crash A sudden unexpected crash
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants