Fix #156647: Use-after-free with Stop at End Frame animation playback

The `stop_playback()` function frees the playback timer, which causes
the rest of the function to access already-freed data. This is resolved
by setting a flag, and only calling `stop_playback()` once all other
processing is finished.

Pull Request: https://projects.blender.org/blender/blender/pulls/156758
This commit is contained in:
Sybren A. Stüvel 2026-04-03 11:11:03 +02:00
parent 1ca332e4f9
commit e800a7a1bb

View file

@ -6196,6 +6196,11 @@ static wmOperatorStatus screen_animation_step_invoke(bContext *C,
}
}
/* Calling stop_playback() frees the animation timer `wt`, and `sad` with it. Instead of calling
* that function directly, set this boolean to `true`, which will call the function at the end of
* this function. */
bool do_stop_playback = false;
/* Handle reaching the extreme frames. */
const int start_frame = PSFRA;
const int end_frame = PEFRA;
@ -6207,7 +6212,7 @@ static wmOperatorStatus screen_animation_step_invoke(bContext *C,
switch (scene->playback_loop_mode) {
case SCE_LOOP_MODE_STOP_START_FRAME:
stop_playback(C);
do_stop_playback = true;
ATTR_FALLTHROUGH;
case SCE_LOOP_MODE_INFINITE:
scene->r.cfra = is_playing_forward ? start_frame : end_frame;
@ -6218,11 +6223,11 @@ static wmOperatorStatus screen_animation_step_invoke(bContext *C,
* clamping). If this turns out to be undesired, the `is_extreme_frame` computation will
* have to take the loop mode into account. */
CLAMP(scene->r.cfra, start_frame, end_frame);
stop_playback(C);
do_stop_playback = true;
break;
case SCE_LOOP_MODE_RESTORE:
scene->r.cfra = sad->sfra;
stop_playback(C);
do_stop_playback = true;
break;
case SCE_LOOP_MODE_BOUNCE:
if (is_playing_forward) {
@ -6315,6 +6320,10 @@ static wmOperatorStatus screen_animation_step_invoke(bContext *C,
* any way to avoid this? */
wt->time_step = (1.0 / scene->frames_per_second());
if (do_stop_playback) {
stop_playback(C);
}
return OPERATOR_FINISHED;
}