X Tutup
Skip to content

Jolt: Add null checks to jolt_free and jolt_aligned_free#112363

Merged
Repiteo merged 1 commit intogodotengine:masterfrom
Open-Industry-Project:jolt-handle-nullptr
Nov 5, 2025
Merged

Jolt: Add null checks to jolt_free and jolt_aligned_free#112363
Repiteo merged 1 commit intogodotengine:masterfrom
Open-Industry-Project:jolt-handle-nullptr

Conversation

@ryevdokimov
Copy link
Contributor

Fixes getting an error: ERROR: Parameter "p_ptr" is null. at: Memory::free_static (core\os\memory.cpp:183) when creating new scene tabs while physics is enabled in the editor via PhysicsServer3D.set_active(true) by adding null checks to jolt_free() and jolt_aligned_free() in the jolt adapter layer.

I believe this happens during QuadTree optimizations when switching tabs. A nullptr will get passed to the free functions since physics bodies no longer exist.

@jrouwe, discussed with @mihe in RocketChat, and mentioned giving you a ping.

Stack Trace:

godot.windows.editor.dev.x86_64.exe!Memory::free_static(void * p_ptr, bool p_pad_align) Line 184 C++ godot.windows.editor.dev.x86_64.exe!jolt_free(void * p_mem) Line 56 C++ godot.windows.editor.dev.x86_64.exe!JPH::QuadTree::NodeID::operator delete[](void * inPointer) Line 33 C++ godot.windows.editor.dev.x86_64.exe!JPH::QuadTree::UpdatePrepare(const JPH::Array<JPH::Body *,JPH::STLAllocator<JPH::Body *>> & inBodies, JPH::Array<JPH::QuadTree::Tracking,JPH::STLAllocator<JPH::QuadTree::Tracking>> & ioTracking, JPH::QuadTree::UpdateState & outUpdateState, bool inFullRebuild) Line 391 C++ godot.windows.editor.dev.x86_64.exe!JPH::BroadPhaseQuadTree::Optimize() Line 88 C++ godot.windows.editor.dev.x86_64.exe!JPH::PhysicsSystem::OptimizeBroadPhase() Line 111 C++ godot.windows.editor.dev.x86_64.exe!JoltSpace3D::remove_object(const JPH::BodyID & p_jolt_id) Line 451 C++ godot.windows.editor.dev.x86_64.exe!JoltObject3D::_remove_from_space() Line 45 C++ godot.windows.editor.dev.x86_64.exe!JoltObject3D::set_space(JoltSpace3D * p_space) Line 91 C++ godot.windows.editor.dev.x86_64.exe!JoltPhysicsServer3D::body_set_space(RID p_body, RID p_space) Line 548 C++ godot.windows.editor.dev.x86_64.exe!CommandQueueMT::Command<PhysicsServer3D,void (__cdecl PhysicsServer3D::*)(RID,RID),0,RID &,RID &>::call_impl<0,1>(IndexSequence<0,1> __formal) Line 69 C++ godot.windows.editor.dev.x86_64.exe!CommandQueueMT::Command<PhysicsServer3D,void (__cdecl PhysicsServer3D::*)(RID,RID),0,RID &,RID &>::call() Line 62 C++ godot.windows.editor.dev.x86_64.exe!CommandQueueMT::_flush() Line 170 C++ godot.windows.editor.dev.x86_64.exe!CommandQueueMT::flush_all() Line 237 C++ godot.windows.editor.dev.x86_64.exe!PhysicsServer3DWrapMT::_thread_loop() Line 47 C++ godot.windows.editor.dev.x86_64.exe!call_with_variant_args_helper<PhysicsServer3DWrapMT>(PhysicsServer3DWrapMT * p_instance, void(PhysicsServer3DWrapMT::*)() p_method, const Variant * * p_args, Callable::CallError & r_error, IndexSequence<> __formal) Line 223 C++ godot.windows.editor.dev.x86_64.exe!call_with_variant_args<PhysicsServer3DWrapMT>(PhysicsServer3DWrapMT * p_instance, void(PhysicsServer3DWrapMT::*)() p_method, const Variant * * p_args, int p_argcount, Callable::CallError & r_error) Line 337 C++ godot.windows.editor.dev.x86_64.exe!CallableCustomMethodPointer<PhysicsServer3DWrapMT,void>::call(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 103 C++ godot.windows.editor.dev.x86_64.exe!Callable::callp(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 57 C++ godot.windows.editor.dev.x86_64.exe!Callable::call<>() Line 949 C++ godot.windows.editor.dev.x86_64.exe!WorkerThreadPool::_process_task(WorkerThreadPool::Task * p_task) Line 145 C++ godot.windows.editor.dev.x86_64.exe!WorkerThreadPool::_thread_function(void * p_user) Line 218 C++ godot.windows.editor.dev.x86_64.exe!Thread::callback(unsigned __int64 p_caller_id, const Thread::Settings & p_settings, void(*)(void *) p_callback, void * p_userdata) Line 64 C++ godot.windows.editor.dev.x86_64.exe!std::invoke<void (__cdecl*)(unsigned __int64,Thread::Settings const &,void (__cdecl*)(void *),void *),unsigned __int64,Thread::Settings,void (__cdecl*)(void *),void *>(void(*)(unsigned __int64, const Thread::Settings &, void(*)(void *), void *) && _Obj, unsigned __int64 && _Arg1, Thread::Settings && <_Args2_0>, void(*)(void *) && <_Args2_1>, void * && <_Args2_2>) Line 1680 C++ godot.windows.editor.dev.x86_64.exe!std::thread::_Invoke<std::tuple<void (__cdecl*)(unsigned __int64,Thread::Settings const &,void (__cdecl*)(void *),void *),unsigned __int64,Thread::Settings,void (__cdecl*)(void *),void *>,0,1,2,3,4>(void * _RawVals) Line 61 C++ godot.windows.editor.dev.x86_64.exe!thread_start<unsigned int (__cdecl*)(void *),1>(void * const parameter) Line 97 C++ kernel32.dll!00007ffe79e5e8d7() Unknown ntdll.dll!00007ffe7a1ec53c() Unknown

@ryevdokimov ryevdokimov requested a review from a team as a code owner November 3, 2025 23:48
@ryevdokimov ryevdokimov changed the title Jolt: Handle null pointers in allocator wrapper functions Jolt: Add null checks to jolt_free and jolt_aligned_free Nov 4, 2025
@Chaosus Chaosus added this to the 4.6 milestone Nov 4, 2025
@akien-mga akien-mga changed the title Jolt: Add null checks to jolt_free and jolt_aligned_free Jolt: Add null checks to jolt_free and jolt_aligned_free Nov 4, 2025
@mihe
Copy link
Contributor

mihe commented Nov 4, 2025

jrouwe, discussed with mihe in RocketChat, and mentioned giving you a ping.

I figured since a lot of allocators typically just no-op when freeing a nullptr (where Godot instead emits an error) Jolt might be making similar assumption about its allocator callbacks.

Looking at the code for JPH::QuadTree::UpdatePrepare it would seem like node_ids can in fact be nullptr if we have no bodies, so I'm guessing this is expected behavior?

Copy link
Contributor

@jrouwe jrouwe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Jolt indeed assumes that it can delete/free a nullptr

@jrouwe
Copy link
Contributor

jrouwe commented Nov 4, 2025

B.t.w. a reallocate should also accept a nullptr as old block.

jrouwe added a commit to jrouwe/JoltPhysics that referenced this pull request Nov 4, 2025
@ryevdokimov
Copy link
Contributor Author

Looks like it already does:

void *jolt_realloc(void *p_mem, size_t p_old_size, size_t p_new_size) {
return Memory::realloc_static(p_mem, p_new_size);
}

godot/core/os/memory.cpp

Lines 132 to 135 in 9d84f3d

void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
if (p_memory == nullptr) {
return alloc_static(p_bytes, p_pad_align);
}

@mihe mihe added the cherrypick:4.5 Considered for cherry-picking into a future 4.5.x release label Nov 5, 2025
@Repiteo Repiteo merged commit 8c119e1 into godotengine:master Nov 5, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Nov 5, 2025

Thanks!

@ryevdokimov ryevdokimov deleted the jolt-handle-nullptr branch November 5, 2025 19:01
@akien-mga
Copy link
Member

Cherry-picked for 4.5.2.

@akien-mga akien-mga removed the cherrypick:4.5 Considered for cherry-picking into a future 4.5.x release label Jan 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

X Tutup