-
-
Notifications
You must be signed in to change notification settings - Fork 24.9k
Crash on exit when using OpenXRExtensionWrapperExtension #88613
Description
Tested versions
- Reproducible in 4.3-dev [fb10e67]
System information
Windows 11, gl_compatibility, NVidia RTX 3070 TI
Issue description
When terminating a Godot application which uses any OpenXRExtensionWrapperExtension objects, the application terminates in a call of memdelete(extension_wrapper) with a pointer that isn't a valid block of heap memory.
The problem is caused by the inheritance structure of OpenXRExtensionWrapperExtension:
| class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider { |
The problem occurs when OpenXRExtensionWrapperExtension::register_extension_wrapper() registers this class with OpenXRAPI::register_extension_wrapper() AS AN OpenXRExtensionWrapper:
godot/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
Lines 233 to 235 in 652438a
| void OpenXRExtensionWrapperExtension::register_extension_wrapper() { | |
| OpenXRAPI::register_extension_wrapper(this); | |
| } |
The OpenXRAPI extension wrappers saves OpenXRExtensionWrapper instances in a Vector<OpenXRExtensionWrapper *> and later deletes them using memdelete.
The problem is that the memory layout of OpenXRExtensionWrapperExtension is as follows (sizes specific to Windows/X64):
- Object base: 408 bytes
- OpenXRExtensionWrapper base: 8 bytes
- OpenXRCompositionLayerProvider base: 8 bytes
- OpenXRExtensionWrapperExtension members: 688 bytes
- Total size = 1112 bytes
When the OpenXRExtensionWrapperExtension::register_extension_wrapper() registers itself, it has to cast its this pointer to an OpenXRExtensionWrapper* which involves adding 408 to the pointer value to jump over the Object. When memdelete() is called on this pointer, it fails because the pointer isn't a block of allocated memory.
This pattern is fully supported in C++ using the normal new/delete, as the compiler will generate a "virtual deleting destructor" in the objects VTABLE, so a delete by any class type will correctly call the real destructor, then adjust the pointer to the start of the object and delete the memory.
Godot's use of memdelete() appears to bypass this machinery preventing safe deletion of multiple-inheritance classes.
Steps to reproduce
Run Godot with the godot_openxr_vendors extension (or any extension providing OpenXR extensions) and then terminate the application.
Minimal reproduction project (MRP)
N/A