Description
Bug summary
When trying to use the TkAgg
backend in a Windows process (a Python process itself or another process with Python embedded) that has a large number of modules loaded (over 1024), the following exception might get raised.
RuntimeError: Failed to load Tcl_SetVar
Code for reproduction
# Within a Windows process with a large number of modules loaded
import matplotlib as mpl
mpl.use('TkAgg')
Actual outcome
Loading the TkAgg backend fails with the following error:
RuntimeError: Failed to load Tcl_SetVar
Expected outcome
The TkAgg backend loads correctly.
Additional information
The reason for this error is that the function load_tkinter_funcs
, which iterates through each module loaded by the process in search of the ones containing Tcl and Tk functions, calls EnumProcessModules
with an output array with a fixed size of 1024. If the process has more than 1024 modules loaded, some of them will not be inspected, and so the code may fail to find it.
Instead, as explained by the documentation of EnumProcessModules
, the returned needed array size should be checked:
To determine if the lphModule array is too small to hold all module handles for the process, compare the value returned in lpcbNeeded with the value specified in cb. If lpcbNeeded is greater than cb, increase the size of the array and call EnumProcessModules again.
This implies using dynamically allocated memory. Alternatively, the size of the array could be increased further and hope this pushes away the problem far enough.
In any case, the current code is not safe, as it iterates the array a number of times depending on the needed size, so if that value is greater than the size of the array, the code will accessing memory out of the boundaries of the array. It should only iterate up to the smaller of the needed size and the actual size.
Operating system
Windows 10
Matplotlib Version
3.4.2
Matplotlib Backend
TkAgg
Python version
3.7.7
Jupyter version
No response
Installation
pip