Skip to content

MSVC crash only in DEBUG when running embedded sample from docs #1293

@Toninoso

Description

@Toninoso

Environment:
Visual Studio 2015 Update 3
Python 3.6.4 for Windows, 32-bit
pybind11 2.2.1

Running Native (C++) Unit test method, using the example from the pybind11 docs:

 TEST_METHOD(Pybind11_HelloWorld_Runs)
 {
       py::scoped_interpreter guard{}; // start the interpreter and keep it alive
       py::print("Hello, World!"); // use the Python API
 }

Everything runs fine in Release build. Running the console version of the MS test runner I see the output "Hello, World!" on the console. Unit Test succeeds.

Now I switch to Debug build where I link to python36_d.lib (using explicit link command) instead of python36.lib. Running the test above crashes accessing NULL pointer, preceded by several throws.

This is the stack when the first exception gets thrown:

KernelBase.dll!_RaiseException@16�()	Unknown
vcruntime140d.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 136	C++
Python.Core.Utest.debug.dll!pybind11::detail::accessor_policies::tuple_item::get(pybind11::handle obj, unsigned int index) Line 544	C++
Python.Core.Utest.debug.dll!pybind11::detail::accessor<pybind11::detail::accessor_policies::tuple_item>::get_cache() Line 467	C++
Python.Core.Utest.debug.dll!pybind11::detail::accessor<pybind11::detail::accessor_policies::tuple_item>::operator pybind11::object() Line 461	C++
Python.Core.Utest.debug.dll!pybind11::str::str<pybind11::detail::accessor_policies::tuple_item>(const pybind11::detail::accessor<pybind11::detail::accessor_policies::tuple_item> & a) Line 839	C++
Python.Core.Utest.debug.dll!pybind11::detail::print(pybind11::tuple args, pybind11::dict kwargs) Line 1678	C++
Python.Core.Utest.debug.dll!pybind11::print<1,char const (&)[14]>(const char[14] & <args_0>) Line 1710	C++
Python.Core.Utest.debug.dll!PythonCoreUtest::Interpreter::Pybind11_HelloWorld_Runs() Line 16	C++

that is, inside:

struct tuple_item {
    using key_type = size_t;
    static object get(handle obj, size_t index) {
        PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
        if (!result) { throw error_already_set(); }
        return reinterpret_borrow<object>(result);
    }

result pointer is NULL.

It looks that arguments get corrupted already when simple collector packs them into a tuple:

template <return_value_policy policy = return_value_policy::automatic_reference, typename... Args>
void print(Args &&...args) {
    auto c = detail::collect_arguments<policy>(std::forward<Args>(args)...);
    detail::print(c.args(), c.kwargs());
}

that is, before detail::print gets invoked, although I am guessing, not fully understanding the meaning and expected values of all the members of the pybind11 objects yet.

After that, there are a few more places resulting in throw error_already_set() and finally, there is access violation inside the call to PyObjectStr()

    /// Return string representation -- always returns a new reference, even if already a str
    static PyObject *raw_str(PyObject *op) {
        PyObject *str_value = PyObject_Str(op);

which crashes the test run.

Running relatively similar 'pure' CPython unit test method, as below, doesn't crash either in Debug or Release builds, the same scenario as above.

       TEST_METHOD(CPython_HelloWorld_Runs)
        {
              Py_Initialize();
              PyRun_SimpleString("print('Hello World!')\n");
              Py_Finalize();
        }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions