The vapoursynth-module tracks its ScriptEnvironment data by its ID.
If another filter tries to enter an environment these steps are executed:
It checks if vsscript is enabled. If not, skip all further steps.
It stores the current value of the _environment_id-variable onto the _environment_id_stack-list.
It sets _environment_id to the new environment id.
To exit the environment afterwards these steps are followed:
Store the last value stored on the _environment_id_stack-list to _environment_id
This is all well and good, until you consider that both variables are shared between all threads. This is especially problematic as filters like std.FrameEval and std.ModifyFrame call back to Python code from their respective worker thread.
Here is what might happen:
The editor opens a new script-environment and executes a script there.
The script uses a long running std.FrameEval-filter.
The script ends and registers an output where the above filter is inside the filter-tree.
The requests a frame triggering the FrameEval-callback.
The user restarts the script.
The editor opens a new script-environment and executes another script there.
The old FrameEval-callback is still running and now tries to allocate a new filter. It will fail as the previous script-environment reset the environment.
The same thing will almost certainly happen with editors that support previewing multiple files at once.
How to reproduce?
I attached a zip-file with a proof of concept that causes VideoNode.set_output to attach itself to the wrong node (or worse, failing to create a filter). It consists of a module wrapping core vpy-functions (vsscript) and a script (confuse-environment.py) that reproduces the error.
confuse-environment.zip
Why is this bad
VSScript is effectively unstable when more than one Environment is in use. This is traditionally the case in VapoursynthEditor. Especially since the core is managed by the Python Garbage Collector. If any module holds reference over the core, the core does not get freed.
What happens?
The vapoursynth-module tracks its ScriptEnvironment data by its ID. If another filter tries to enter an environment these steps are executed:
_environment_id
-variable onto the_environment_id_stack
-list._environment_id
to the new environment id.To exit the environment afterwards these steps are followed:
_environment_id_stack
-list to_environment_id
This is all well and good, until you consider that both variables are shared between all threads. This is especially problematic as filters like
std.FrameEval
andstd.ModifyFrame
call back to Python code from their respective worker thread.Here is what might happen:
std.FrameEval
-filter.The same thing will almost certainly happen with editors that support previewing multiple files at once.
How to reproduce?
I attached a zip-file with a proof of concept that causes
VideoNode.set_output
to attach itself to the wrong node (or worse, failing to create a filter). It consists of a module wrapping core vpy-functions (vsscript) and a script (confuse-environment.py) that reproduces the error. confuse-environment.zipWhy is this bad
VSScript is effectively unstable when more than one Environment is in use. This is traditionally the case in VapoursynthEditor. Especially since the core is managed by the Python Garbage Collector. If any module holds reference over the core, the core does not get freed.
How to fix.
Make both variables thread-local.