Многократные вызовы для импорта модуля Python из C приводят к SEGFAULT - PullRequest
1 голос
/ 29 апреля 2019

Цель этой программы (или, по крайней мере, той части, о которой идет речь), состоит в том, чтобы импортировать мой модуль / программу на python, чтобы можно было использовать функции из C. Это делается путем первого вызова import name с помощью imp и load_source, затем с помощью возвращенного объекта python вызвать мой модуль python для загрузки с import module.

Проблема возникает при многократном вызове функции call_symbolic_trace.В первый раз, когда она была вызвана, программа работает, как и ожидалось, но после этого она приводит к segfault.

Я точно определил, что segfault происходит в строке module = PyObject_CallObject(fcn, imp_args); из функции import_module.

PyObject *import_name(const char *module_name, const char *fcn_name)
{
    PyObject *module = PyImport_Import(PyUnicode_FromString(module_name));
    return PyObject_GetAttrString(module, fcn_name);
}


PyObject *import_module(PyObject *fcn)
{
    int r;
    char *dir;
    PyObject *imp_args, *module;
    char exe_filename[BUFSIZ], path[BUFSIZ];

    r = readlink("/proc/self/exe", exe_filename, BUFSIZ);
    if (r < 0) {
        perror("readlink failed");
        exit(EXIT_FAILURE);
    } else if (r >= BUFSIZ) {
        fprintf(stderr, "Needed more bytes but buffer is %d bytes\n", BUFSIZ);
        exit(EXIT_FAILURE);
    }

    exe_filename[r] = '\0';
    dir = dirname(exe_filename);
    r = snprintf(path, BUFSIZ,
                 "%s/../src/symbolic_trace/symbolic_class_python3.py", dir);

    if (r < 0) {
        perror("snprintf failed");
        exit(EXIT_FAILURE);
    } else if (r >= BUFSIZ) {
        fprintf(stderr, "Needed %d bytes but buffer is %d bytes\n", r, BUFSIZ);
        exit(EXIT_FAILURE);
    }

    PyGILState_STATE module_state;
    module_state = PyGILState_Ensure();

    if (!PyCallable_Check(fcn)) {
        fprintf(stderr, "import_module: expected a callable\n");
        goto fail;
    }

    imp_args = Py_BuildValue("(ss)", "symbolic_class_python3", path);
    module = PyObject_CallObject(fcn, imp_args);
    Py_XDECREF(imp_args);

    if (PyErr_Occurred()) {
        printf("filepath: %s\n", path);
        printf("exe: %s\n", exe_filename);
        printf("dir: %s\n", dir);
        PyErr_Print();
        puts("import error occurred");
        goto fail;
    }

    PyGILState_Release(module_state);
    return module;

fail:
    Py_XDECREF(module);
    PyGILState_Release(module_state);
    abort();
}


void call_symbolic_trace(elf *e, graph *g)
{
    PyObject *imp_fcn = import_name("imp", "load_source");
    PyObject *tracer = import_module(imp_fcn);
    <mode code>
    Py_XDECREF(imp_fcn);
    Py_XDECREF(tracer);
}

Использование GDB для получения обратной трассировки дает то, что показано ниже, из которого я не могу сделать головы или хвосты.

0x00007ffff712c710 in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff7153130 in _PyLong_New () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff7155f5a in PyLong_FromLong () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff7279774 in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff727938f in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff7279e02 in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff727a6eb in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff713b029 in PyCFunction_Call () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff72471c5 in PyEval_EvalFrameEx () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff72d7cbc in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff7245f49 in PyEval_EvalFrameEx () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff7247649 in PyEval_EvalFrameEx () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff7247649 in PyEval_EvalFrameEx () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff7247649 in PyEval_EvalFrameEx () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff72d7cbc in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff72d7d93 in PyEval_EvalCodeEx () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff715fac8 in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff721455e in PyObject_Call () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00007ffff72d6947 in PyEval_CallObjectWithKeywords () from /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0
0x00000000004040b1 in import_module (fcn=0x7ffff5bd9488) at src/symbolic_trace/symbolic_trace.c:62
0x0000000000405ea4 in call_symbolic_trace (e=0x66c250, g=0x6e7760, bbs=0x7fffffffdd10, root=0x13110f0, sl=0x7fffffffdd00, targets=0x7fffffffdbe0) at src/symbolic_trace/symbolic_trace.c:552

Я не могу понять, почему первоначальный вызов для импорта модуля будет работать успешно, но любые последующие вызовы для повторения процесса импорта модуля будут SEGFAULT.Проходя через мой C-код, я почти уверен, что все объекты python разыменованы с Py_XDECREF, поэтому я не знаю, что является причиной этой проблемы.

...