Hex-Rays' blog

Extending IDAPython in IDA 6.5: Be careful about the GIL – Hex Rays

Written by   Arnaud Diederen | Apr 14, 2014

Target audience

You may want to read this if you have been writing an IDA C++ plugin, that itself uses the CPython runtime.

Prior art

In 2010, Elias Bachaalany wrote a blog post about extending IDAPython: http://www.hexblog.com/?p=126
Note that this is not about writing your own plugins in Python. Rather, that blog post instruct on how you may want to write a plugin that, itself, links against libpython27, so that it interacts nicely with IDAPython (which links against the same libpython27 library).

Before 6.5

The following plugin body was enough to have a working C++ plugin, that will execute a simple Python statement:

void idaapi run(int)
{
PyRun_SimpleString("print \"Hello from plugin\"");
}

Notes about the previous example

It is worth pointing out that, whenever one wants to execute any CPython code (such as the ‘PyRun_SimpleString’ call above), one must have acquired what is called in CPython-land as the Global Interpreter Lock, or GIL: https://wiki.python.org/moin/GlobalInterpreterLock.
It may seem strange, therefore, that the code above even works, since there’s nothing that even remotely looks like a “GIL-acquiring” sequence of instructions.
The reason the code above used to work is because IDAPython was not releasing the GIL: it was acquiring it at startup-time, and never releasing it. Ever.

IDA 6.5 & later versions

In IDA 6.5, many efforts have been made to improve the use of multithreading in IDAPython: The IDAPython plugin acquires the GIL just in time to execute CPython code and then releases it, as it should. This will permit a reliable use of threading in IDAPython (in some circumstances, when using multithreading, IDA < 6.5 could freeze because of the GIL not being released.)
Starting with 6.5, such a plugin would require to be written in the following manner:

void idaapi run(int)
{
PyGILState_STATE state = PyGILState_Ensure(); // Acquire the GIL
PyRun_SimpleString("print \"Hello from plugin\"");
PyGILState_Release(state); // Release the GIL
}

Note the use of the CPython-defined PyGILState_* helpers.

Existing plugins

Unfortunately, we couldn’t do anything in this case to ensure backwards-compatibility: existing plugins will have to be adapted & recompiled. Sorry about this, but we just could not find a reasonable solution to maintain bw-compat in this case; hence this blog post.
So far, we have received a grand total of 1 report for this issue, so this doesn’t appear to impact many of our users. But in case you are running into problems with your previously-compiled CPython-aware plugin, hopefully this will have explained why.