Pybind - Pytorch Segfault?

soulslicer: <p>I have a simple toy example made in pybind / c++ that compiles:</p>
<pre><code class="lang-auto">#include &lt;pybind11/pybind11.h&gt;
#include &lt;pybind11/stl.h&gt;
#include &lt;pybind11/numpy.h&gt;
#include &lt;stdexcept&gt;

#include &lt;torch/extension.h&gt;

namespace
{

namespace py = pybind11;

torch::Tensor test2(){
    auto label_map = torch::zeros({100, 100}).to(torch::kFloat32);
    return label_map;
}

} // namespace

// The first argument needs to match the name of the *.so in the BUILD file.
PYBIND11_MODULE(tools_binding, m)
{
    m.doc() = "Tools functions";

    m.def("test2",
          &amp;test2,
          "test2"
        );

}
</code></pre>
<p>However, when i invoke test2(), it segfaults with</p>
<pre><code class="lang-auto">Segmentation fault
</code></pre>
<p>If i dont return anything however:</p>
<pre><code class="lang-auto">void test2(){
    auto label_map = torch::zeros({100, 100}).to(torch::kFloat32);
}
</code></pre>
<p>Then it doesnt segfault. Whats going on?</p>
<p>I did a GDB and found the following:</p>
<pre><code class="lang-auto">Program received signal SIGSEGV, Segmentation fault.
0x00007fffef60d014 in THPVariable_NewWithVar(_typeobject*, at::Tensor) ()
</code></pre>

evilgras: <p>Hi <a class="mention" href="/u/soulslicer">@soulslicer</a>, did you ever manage to fix this issue? I think I have the exact same bug:</p>
<pre><code class="lang-auto">#include &lt;torch/extension.h&gt;

int square_sum(int size0, int size1, int size2)
{
	return size0*size0 + size1*size1 + size2*size2;
}

torch::Tensor zero_tensor(int size0, int size1, int size2)
{
	return torch::zeros({size0, size1, size2});
}

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
    m.def("square_sum", &amp;square_sum, "Square Sum");
    m.def("zero_tensor", &amp;zero_tensor, "Zero Tensor");
}
</code></pre>
<p>The <code>square_sum</code> method works, the <code>zero_tensor</code> fails with a segmentation fault, and gdb shows the same thing as you.</p>
<p>I tried it with the precompiled libtorch and with a libtorch I compiled from source. I tried with and without anaconda. I tried compiling using a <code>setup.py</code> file and with a simple Makefile. I tried changing <code>torch::Tensor</code> to <code>at::Tensor</code>. Nothing seems to work.</p>
<p>Here is some output from valgrind:</p>
<pre><code class="lang-auto">==355897== Invalid read of size 8
==355897==    at 0x11C6A347: THPVariable_NewWithVar(_typeobject*, at::Tensor, c10::impl::PyInterpreterStatus) (in /home/evilgras/opt/anaconda3/lib/python3.8/site-packages/torch/lib/libtorch_python.so)
==355897==    by 0x11C6AB0E: THPVariable_Wrap(at::Tensor) (in /home/evilgras/opt/anaconda3/lib/python3.8/site-packages/torch/lib/libtorch_python.so)
==355897==    by 0x486FD85: void pybind11::cpp_function::initialize&lt;at::Tensor (*&amp;)(int, int, int), at::Tensor, int, int, int, pybind11::name, pybind11::scope, pybind11::sibling, char [12]&gt;(at::Tensor (*&amp;)(int, int, int), at::Tensor (*)(int, int, int), pybind11::name const&amp;, pybind11::scope const&amp;, pybind11::sibling const&amp;, char const (&amp;) [12])::{lambda(pybind11::detail::function_call&amp;)#3}::_FUN(pybind11::detail::function_call&amp;) (pybind.h:47)
==355897==    by 0x486D2D9: pybind11::cpp_function::dispatcher(_object*, _object*, _object*) (pybind11.h:767)
==355897==    by 0x25C347: cfunction_call_varargs (call.c:743)
==355897==    by 0x25C347: PyCFunction_Call (call.c:773)
==355897==    by 0x24BDBB: _PyObject_MakeTpCall (call.c:159)
==355897==    by 0x2D7665: _PyObject_Vectorcall (abstract.h:125)
==355897==    by 0x2D7665: call_function (ceval.c:4963)
==355897==    by 0x2D7665: _PyEval_EvalFrameDefault (ceval.c:3469)
==355897==    by 0x2A126F: PyEval_EvalFrameEx (ceval.c:741)
==355897==    by 0x2A126F: _PyEval_EvalCodeWithName (ceval.c:4298)
==355897==    by 0x336542: PyEval_EvalCodeEx (ceval.c:4327)
==355897==    by 0x336542: PyEval_EvalCode (ceval.c:718)
==355897==    by 0x3365E3: run_eval_code_obj (pythonrun.c:1165)
==355897==    by 0x35C853: run_mod (pythonrun.c:1187)
==355897==    by 0x21D38F: pyrun_file (pythonrun.c:1084)
==355897==  Address 0x130 is not stack'd, malloc'd or (recently) free'd
==355897== 
==355897== 
==355897== Process terminating with default action of signal 11 (SIGSEGV)
==355897==  Access not within mapped region at address 0x130
==355897==    at 0x11C6A347: THPVariable_NewWithVar(_typeobject*, at::Tensor, c10::impl::PyInterpreterStatus) (in /home/evilgras/opt/anaconda3/lib/python3.8/site-packages/torch/lib/libtorch_python.so)
==355897==    by 0x11C6AB0E: THPVariable_Wrap(at::Tensor) (in /home/evilgras/opt/anaconda3/lib/python3.8/site-packages/torch/lib/libtorch_python.so)
==355897==    by 0x486FD85: void pybind11::cpp_function::initialize&lt;at::Tensor (*&amp;)(int, int, int), at::Tensor, int, int, int, pybind11::name, pybind11::scope, pybind11::sibling, char [12]&gt;(at::Tensor (*&amp;)(int, int, int), at::Tensor (*)(int, int, int), pybind11::name const&amp;, pybind11::scope const&amp;, pybind11::sibling const&amp;, char const (&amp;) [12])::{lambda(pybind11::detail::function_call&amp;)#3}::_FUN(pybind11::detail::function_call&amp;) (pybind.h:47)
==355897==    by 0x486D2D9: pybind11::cpp_function::dispatcher(_object*, _object*, _object*) (pybind11.h:767)
==355897==    by 0x25C347: cfunction_call_varargs (call.c:743)
==355897==    by 0x25C347: PyCFunction_Call (call.c:773)
==355897==    by 0x24BDBB: _PyObject_MakeTpCall (call.c:159)
==355897==    by 0x2D7665: _PyObject_Vectorcall (abstract.h:125)
==355897==    by 0x2D7665: call_function (ceval.c:4963)
==355897==    by 0x2D7665: _PyEval_EvalFrameDefault (ceval.c:3469)
==355897==    by 0x2A126F: PyEval_EvalFrameEx (ceval.c:741)
==355897==    by 0x2A126F: _PyEval_EvalCodeWithName (ceval.c:4298)
==355897==    by 0x336542: PyEval_EvalCodeEx (ceval.c:4327)
==355897==    by 0x336542: PyEval_EvalCode (ceval.c:718)
==355897==    by 0x3365E3: run_eval_code_obj (pythonrun.c:1165)
==355897==    by 0x35C853: run_mod (pythonrun.c:1187)
==355897==    by 0x21D38F: pyrun_file (pythonrun.c:1084)
</code></pre>
<p>Not sure if this is a hint, but 0x130 seems like a very low number for a memory address (from the message <code>Access not within mapped region at address 0x130</code> ).</p>
<p>Can anyone help? I’m using Ubuntu 20.04 with gcc 9.3.0 if that helps.</p>

evilgras: <p>Update: I managed to avoid the segfault when calling <code>zero_tensor</code> in my module. The trick was to add an <code>import torch</code> to my python script!</p>
<p>Previously, my python script looked like this:</p>
<pre><code class="lang-auto">#!/usr/bin/python3
import dummy

dummy.zero_tensor(3,4,5)
</code></pre>
<p>(here, <code>dummy</code> is the name of my module). The above script causes a segfault. But when I modify it to be</p>
<pre><code class="lang-auto">#!/usr/bin/python3
import dummy
import torch

dummy.zero_tensor(3,4,5)
</code></pre>
<p>… it works.</p>
<p>Why this happens:</p>
<p>In <code>torch/csrc/autograd/python_variable.cpp</code> there is a variable called <code>THPVariableClass</code>, which is initialised to <code>nullptr</code>. The method <code>THPVariable_Wrap</code> (which we can see in the valgrind trace above) calls  <code>THPVariable_NewWithVar</code> with <code>(PyTypeObject*)THPVariableClass</code> as the <code>type</code> argument, which is still the null pointer. <code>THPVariable_NewWithVar</code> then tries to call <code>type-&gt;tp_alloc(type, 0)</code> (where <code>type</code> is the <code>nullptr</code>), causing the segmentation fault.</p>
<p>Normally, it seems <code>THPVariableClass</code> is supposed to be initialised by the function <code>PyObject* THPAutograd_initExtension(PyObject* _unused, PyObject *unused)</code> (inside <code>torch/csrc/autograd/init.cpp</code>) which, it turns out, is automatically called when you do <code>import torch</code>. So adding <code>import torch</code> fixed the problem.</p>
<p>Is this is a bug in pytorch? My guess would be that importing any custom pytorch/pybind C++ module should automatically cause torch to be imported too in order for this segfault not to happen. At the very least I would say that either <code>THPVariable_Wrap</code> or <code>THPVariable_NewWithVar</code> should check that <code>THPVariableClass</code> is initialised (not null) and, if not, throw an exception along with instructions on how the user can fix the issue.</p>

ptrblck: <p>Thanks for sharing the debugging and I agree that a proper error would be preferable to a seg fault. Would you mind creating an issue on <a href="https://github.com/pytorch/pytorch/issues">GitHub</a> with your explanation and debugging steps?</p>

soulslicer: <p>correct. i just imported torch before that and it was resolved. Thanks for doing a detailed breakdown of this issue</p>

evilgras: <p>Created a github <a href="https://github.com/pytorch/pytorch/issues/63749" rel="noopener nofollow ugc">issue</a>.</p>