box2d-optimized icon indicating copy to clipboard operation
box2d-optimized copied to clipboard

Allocator problem

Open ForserX opened this issue 3 years ago • 9 comments

image

image

Double free

ForserX avatar Sep 24 '22 02:09 ForserX

+1

vertver avatar Sep 27 '22 21:09 vertver

Do you have a test case / example that can reproduce that?

mtsamis avatar Sep 28 '22 05:09 mtsamis

ForserX#6279

ForserX avatar Sep 28 '22 06:09 ForserX

This reproducing if you're trying to destroy more than 5-10 objects per step/tick.

Destroy code:

void
physics::physics_body::destroy()
{
	destroyed = true;
	for (auto joint = body->GetJointList(); joint != nullptr; ) {
		const auto next_joint = joint->next;
		get_world().DestroyJoint(joint->joint);
		joint = next_joint;
	}
	
	get_world().DestroyBody(body);
	body = nullptr;
}

pre_tick() code:

        for (const auto body : scheduled_to_delete_bodies) {
		body->destroy();
		if (bodies.contains(body)) {
			bodies.erase(body);
		}
	}
	
	scheduled_to_delete_bodies.clear();

My guess is that the crash is because b2Contact and b2Fixture which is stored inside b2Body have been already cleared in another object, but the pointers have not been nullptr'ed (they are just 0xFDFDFDFD). Also, my suggestions, this body was already free somewhere before, and that is the reason why all properties marked as invalid by memory allocator. image image

vertver avatar Sep 28 '22 18:09 vertver

this issue was fixed by this code

	stl::hash_set<b2Contact*> contacts;
	for (const auto body : scheduled_to_delete_bodies) {
		const auto phys_body = body->get_body();
		if (phys_body != nullptr) {
			// In this case, we're trying store contact pointer to schedule contact free
			for (int i = 0; i < phys_body->GetContactCount(); i++) {
				const auto contact = phys_body->GetContact(i);
				if (contact != nullptr) {
					contacts.insert(contact);
				}
			}

			// Second - try to delete all referenced links in body
			phys_body->ClearContacts();
		}
	}

	// Yep, this is most interesting part: we're deleting all founded by iterating
	// contacts and free map after processing
	for (const auto contact : contacts) {
		world_holder->GetContactManager().Destroy(contact);
	}

vertver avatar Oct 01 '22 10:10 vertver

Yes this makes some sense; after having a look at it I think that the way b2World::RemoveDeadContacts works is incorrect when combined with how contact destruction logic. I believe the condition contact->m_flags & b2Contact::e_persistFlag can also access free'd memory in that case.

Thanks for providing a workaround to the issue, I'll try to reproduce this and explore other solutions as well when I have some time.

mtsamis avatar Oct 01 '22 11:10 mtsamis

I have same issue.

RonYanDaik avatar Apr 05 '23 09:04 RonYanDaik

stl::hash_set<b2Contact*> contacts;
	for (const auto body : scheduled_to_delete_bodies) {
		const auto phys_body = body->get_body();
		if (phys_body != nullptr) {
			// In this case, we're trying store contact pointer to schedule contact free
			for (int i = 0; i < phys_body->GetContactCount(); i++) {
				const auto contact = phys_body->GetContact(i);
				if (contact != nullptr) {
					contacts.insert(contact);
				}
			}

			// Second - try to delete all referenced links in body
			phys_body->ClearContacts();
		}
	}

	// Yep, this is most interesting part: we're deleting all founded by iterating
	// contacts and free map after processing
	for (const auto contact : contacts) {
		world_holder->GetContactManager().Destroy(contact);
	}

Where this code should be placed?

RonYanDaik avatar Apr 05 '23 14:04 RonYanDaik

Check this: https://github.com/ForserX/Asura2D/blob/master/src/engine/physics/physics_world.cpp

ForserX avatar Apr 05 '23 14:04 ForserX