Fix crash on freeing hair system

Fix a crash when a hair system's `ParticleSettings` ID datablock was
linked from another file but couldn't be found. This results in default
settings, with `type = PART_EMITTER`, where the particle data still has
a non-NULL `hair` pointer. Previously, copies of such a particle system
would NOT copy hair data for non-hair particle systems, hence the
pointer of the copy pointed to the original data, which got freed (at
least) twice upon closing the blend file.

This is now fixed by always copying the hair data, regardless of the
particle system type.

Reviewed by: mont29

Differential Revision: https://developer.blender.org/D13245
This commit is contained in:
Sybren A. Stüvel 2021-11-16 17:18:01 +01:00
parent 07af45eec5
commit 72ee62e0da

@ -1148,7 +1148,27 @@ void psys_copy_particles(ParticleSystem *psys_dst, ParticleSystem *psys_src)
/* Copy particles and children. */
psys_dst->particles = MEM_dupallocN(psys_src->particles);
psys_dst->child = MEM_dupallocN(psys_src->child);
if (psys_dst->part->type == PART_HAIR) {
/* Ideally this should only be performed if `(psys_dst->part->type == PART_HAIR)`.
*
* But #ParticleData (`psys_dst`) is some sub-data of the #Object ID, while #ParticleSettings
* (`psys_dst->part`) is another ID. In case the particle settings is a linked ID that gets
* missing, it will be replaced (in readfile code) by a place-holder, which defaults to a
* `PART_EMITTER` type of particle settings.
*
* This leads to a situation where each particle of `psys_dst` still has a valid allocated `hair`
* data, which should still be preserved in case the missing particle settings ID becomes valid
* again.
*
* Furthermore, #free_hair() always frees `pa->hair` if it's not NULL, regardless of the
* particle type. So *not* copying here would cause a double free (or more), e.g. freeing the
* copy-on-write copy and the original data will crash Blender.
* In any case, sharing pointers between `psys_src` and `psys_dst` should be forbidden.
*
* So while we could in theory 'sanitize' the situation by setting `pa->hair` to NULL in the new
* copy (in case of non-`PART_HAIR` type), it is probably safer for now to systematically
* duplicate the `hair` data if available. */
{
ParticleData *pa;
int p;
for (p = 0, pa = psys_dst->particles; p < psys_dst->totpart; p++, pa++) {