bcdev / jpy

A bi-directional Python-Java bridge used to embed Java in CPython or the other way round.
Apache License 2.0
187 stars 37 forks source link

Segfault when iterating through Python globals dictionary #177

Open pont-us opened 4 years ago

pont-us commented 4 years ago

This bug manifested itself as a failure of the unit test PyObjectTest.testDictCopy(). It can also be triggered by the following minimal snippet (Python 3.8, 64-bit Ubuntu Linux 20.04) (after setting the necessary jpy configuration properties and calling PyLib.startPython()):

final Map<PyObject, PyObject> globalsDict = PyLib.getMainGlobals().asDict();
for (Map.Entry<PyObject, PyObject> entry : globalsDict.entrySet()) {
    System.out.println("Entry: " + entry.getKey() + " -> " + entry.getValue());
}

The iteration succeeds for two loops, printing Entry: __name__ -> __main__ and Entry: __doc__ -> None. At the start of the third iteration it crashes with a segmentation fault. The culprit appears to be builtins.call("next", it) in the iterator returned by PyDictWrapper.EntrySet.iterator(). This calls through to PyObject.call(). The control flow continues into PyLib_CallAndReturnObject in org_jpy_PyLib.c, and eventually to this line:

pyReturnValue = PyObject_CallObject(pyCallable, argCount > 0 ? pyArgs : NULL);

It is this call to PyObject_CallObject in Python's C API which directly triggers the segmentation fault.

pont-us commented 4 years ago

Here's a minimal Java source file to reproduce the problem.

package test.jpysegfault;

import org.jpy.PyLib;
import org.jpy.PyObject;
import java.util.Map;

public class Main {

    public static void main(String[] args) {
        System.setProperty("jpy.pythonLib",
                "/home/pont/miniconda3/envs/jpy-38/lib/libpython3.8.so");
        System.setProperty("jpy.jpyLib",
                "/home/pont/loc/repos/jpy/build/lib.linux-x86_64-3.8/jpy.cpython-38-x86_64-linux-gnu.so");
        System.setProperty("jpy.jdlLib",
                "/home/pont/loc/repos/jpy/build/lib.linux-x86_64-3.8/jdl.cpython-38-x86_64-linux-gnu.so");
        PyLib.startPython();
        final Map<PyObject, PyObject> globalsDict = PyLib.getMainGlobals().asDict();
        for (Map.Entry<PyObject, PyObject> entry : globalsDict.entrySet()) {
            System.out.println("Entry: " + entry.getKey() + " -> " + entry.getValue());
        }
        PyLib.stopPython();
    }
}
pont-us commented 4 years ago

Attached: log file generated by running the test script above with JPy_DiagFlags = JPy_DIAG_F_ALL set in src/main/c/jpy_diag.c.

hs_err_pid18349.log

ag-tcm commented 3 years ago

@pont-us I came across this bug. Are you aware of any workaround? Thanks!