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

Particle manager accesses particle objects after they are destroyed #3214

Open
SamVanheer opened this issue Dec 24, 2021 · 0 comments
Open

Comments

@SamVanheer
Copy link

The particle manager has a use-after-free bug that results in a particle that is considered visible, but that has been destroyed still being accessed in order to draw it.

The code that causes this works like this (in CMiniMem::ProcessAll):

if (effect->CheckVisibility())
{
	auto player = gEngfuncs.GetLocalPlayer();
	effect->SetPlayerDistance((player->origin - effect->m_vOrigin).LengthSquared());

	m_pVisibleParticles[m_iParticlesDrawn++].pVisibleParticle = effect;
}

if (!IsGamePaused())
{
	effect->Think(time);
}

if (0 != effect->m_flDieTime && time >= effect->m_flDieTime)
{
	effect->Die();

	deleteBlock(particle);
}

The code first checks if the particle is visible, and if it is it is added to the list of visible particles. Then it checks if the particle has reached its death time and is deleted.

The particle is not removed from the list of visible particles. Its destructor is not called and the memory remains allocated (particle memory is allocated from a single large pool of memory). When the particle is drawn later on the object is still valid, though its memory is flagged as free and could be re-allocated if another particle were to create a new particle between the old particle's deletion and the particle being drawn.

To fix this the particle should be checked for deletion before checking the visibility. If the particle is deleted then it should skip checking visibility altogether. The particle should also be updated before being checked for deletion, so that if its death time is changed it will work properly:

if (!IsGamePaused())
{
	effect->Think(time);
}

if (0 != effect->m_flDieTime && time >= effect->m_flDieTime)
{
	effect->Die();

	deleteBlock(particle);
}
else
{
	if (effect->CheckVisibility())
	{
		auto player = gEngfuncs.GetLocalPlayer();
		effect->SetPlayerDistance((player->origin - effect->m_vOrigin).LengthSquared());

		m_pVisibleParticles[m_iParticlesDrawn++].pVisibleParticle = effect;
	}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant