[Python] null pointer access violation when calling Python function

Hi, I have an object "m_RegisterEvents" which holds a reference to a Python function. This Python function is a callback given by a Python script. To call this function, I simply do: void subsystem_script::register_events() { m_RegisterEvents(); } And it works. However, if a Python exception is thrown (and converted to the C++ exception boost::python::error_already_set), I get a segfault in the following function (m_ptr is null, hence the segfault): inline api::object_base::~object_base() { Py_DECREF(m_ptr); } This destructor is directly called at the end of the subsystem_script::register_events() function, leading me to believe that it is a temporary object being destructed. The funny thing is that if I do the following, then it works again. void subsystem_script::register_events() { try { m_RegisterEvents(); } catch (const error_already_set &) { } } However, doing a "throw;" in the catch to re-throw the exception does the same segfault. Converting the exception by throwing another object (e.g. std::runtime_error()) works. I must admit that I'm totally puzzled. Using Boost 1.33.1 with Visual C++ 2005. Tanguy

I narrowed the problem a bit. The problem arise when a boost::python::error_already_set is thrown but never caught (i.e. adding a try/catch block in main() solves the problem). It seems to me that an uncaught exception should not cause a segfault in Boost.Python. Should it? Tanguy

Tanguy Fautre <tanguy.fautre@spaceapplications.com> writes:
I narrowed the problem a bit.
The problem arise when a boost::python::error_already_set is thrown but never caught (i.e. adding a try/catch block in main() solves the problem).
It seems to me that an uncaught exception should not cause a segfault in Boost.Python. Should it?
No. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Tanguy Fautre <tanguy.fautre@spaceapplications.com> writes:
Hi,
I have an object "m_RegisterEvents" which holds a reference to a Python function. This Python function is a callback given by a Python script.
To call this function, I simply do:
void subsystem_script::register_events() { m_RegisterEvents(); }
And it works.
However, if a Python exception is thrown (and converted to the C++ exception boost::python::error_already_set), I get a segfault in the following function (m_ptr is null, hence the segfault):
inline api::object_base::~object_base() { Py_DECREF(m_ptr); }
This destructor is directly called at the end of the subsystem_script::register_events() function, leading me to believe that it is a temporary object being destructed.
Well, are you sure you're not doing something illegal somewhere? It's supposed to be an invariant that object (and object_base) never has a null m_ptr. That's why we're not using Py_XDECREF. So, either Boost.Python is doing something to break that invariant, or some code (maybe yours, maybe mine) is incorrectly initializing that object, or there's a bug in the compiler's exception unwinding.
The funny thing is that if I do the following, then it works again.
void subsystem_script::register_events() { try { m_RegisterEvents(); }
catch (const error_already_set &) {
} }
However, doing a "throw;" in the catch to re-throw the exception does the same segfault. Converting the exception by throwing another object (e.g. std::runtime_error()) works.
I must admit that I'm totally puzzled.
Very strange. All I can say is what I always tell people: reduce your example to a _truly_ minimal one that reproduces the behavior (one C++ file and one python file) and either you'll discover what you did wrong or you'll give me enough to fix the problem. And please bring your Boost.Python questions to the C++-sig <http://boost.org/more/mailing_lists.htm#cplussig>. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tanguy Fautre <tanguy.fautre@spaceapplications.com> writes:
[...]
However, if a Python exception is thrown (and converted to the C++ exception boost::python::error_already_set), I get a segfault in the following function (m_ptr is null, hence the segfault):
inline api::object_base::~object_base() { Py_DECREF(m_ptr); }
This destructor is directly called at the end of the subsystem_script::register_events() function, leading me to believe that it is a temporary object being destructed.
Well, are you sure you're not doing something illegal somewhere? It's supposed to be an invariant that object (and object_base) never has a null m_ptr. That's why we're not using Py_XDECREF.
So, either Boost.Python is doing something to break that invariant, or some code (maybe yours, maybe mine) is incorrectly initializing that object, or there's a bug in the compiler's exception unwinding.
The funny thing is that if I do the following, then it works again.
void subsystem_script::register_events() { try { m_RegisterEvents(); }
catch (const error_already_set &) {
} }
However, doing a "throw;" in the catch to re-throw the exception does the same segfault. Converting the exception by throwing another object (e.g. std::runtime_error()) works.
I must admit that I'm totally puzzled.
Very strange. All I can say is what I always tell people: reduce your example to a _truly_ minimal one that reproduces the behavior (one C++ file and one python file) and either you'll discover what you did wrong or you'll give me enough to fix the problem. And please bring your Boost.Python questions to the C++-sig <http://boost.org/more/mailing_lists.htm#cplussig>.
After doing a minimal example reproducing this error, I think I found the problem. According to the C++ Programming Language (3rd ed), Section 14.7: "It is implementation-defined whether destructors are invoked when a program is terminated because of an uncaught exception.". That, I did not know. I thought destructor were always called. GCC 4.x and Visual C++ 2005 are indeed calling std::terminate without calling the destructors. Except... Except when debugging with VC++ 2005. When debugging, you have the choice to continue after a uncaught exception is thrown as if nothing happened. This effectively leads to a segfault as described as above. So it's not a problem with my application nor Boost.Python. Just my misunderstanding of uncaught exceptions and of Visual C++. Sorry I bothered you with this false alert. Tanguy

Tanguy Fautre <tanguy.fautre@spaceapplications.com> writes:
And please bring your Boost.Python questions to the C++-sig <http://boost.org/more/mailing_lists.htm#cplussig>.
After doing a minimal example reproducing this error, I think I found the problem.
According to the C++ Programming Language (3rd ed), Section 14.7: "It is implementation-defined whether destructors are invoked when a program is terminated because of an uncaught exception.".
Yes.
That, I did not know. I thought destructor were always called.
GCC 4.x and Visual C++ 2005 are indeed calling std::terminate without calling the destructors. Except... Except when debugging with VC++ 2005. When debugging, you have the choice to continue after a uncaught exception is thrown as if nothing happened. This effectively leads to a segfault as described as above.
I think changing the semantics of "throw x" from "unwind the stack and maybe terminate" to "continue as though nothing has happened" seems like it could break any program.
So it's not a problem with my application nor Boost.Python. Just my misunderstanding of uncaught exceptions and of Visual C++.
Sorry I bothered you with this false alert.
'S ok. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (2)
-
David Abrahams
-
Tanguy Fautre