Почему мое приложение падает, когда я освобождаю char *, выделенный из DLL, сгенерированной с помощью CFFI? - PullRequest
0 голосов
/ 20 марта 2019

Я использую CFFI для создания DLL:

import cffi

ffibuilder = cffi.FFI()

ffibuilder.embedding_api('''
    char* get_string();
''')

ffibuilder.set_source('my_plugin', '')

ffibuilder.embedding_init_code('''
    from my_plugin import ffi, lib

    @ffi.def_extern()
    def get_string():
        val = "string"
        return lib.strdup(val.encode())
''')

ffibuilder.cdef('''
    char *strdup(const char *);
''')

ffibuilder.compile(target='my-plugin.*', verbose=True)

Я генерирую DLL, запустив этот предыдущий скрипт. Теперь я создаю этот пример кода C ++ для использования моей DLL:

#include <iostream>
#include <windows.h>

typedef char* (__stdcall *get_string_t)();

int main()
{
    HINSTANCE hGetProcIDDLL = LoadLibrary("my-plugin.dll");

    if (!hGetProcIDDLL) {
        std::cout << "could not load the dynamic library" << std::endl;
        return -1;
    }

    get_string_t get_string = (get_string_t)GetProcAddress(hGetProcIDDLL, "get_string");
    if (!get_string) {
        std::cout << "could not locate the function" << std::endl;
        return -1;
    }

    char* val = get_string();

    std::cout << "Value = " << val << std::endl;

    free(val); // Crash !

    std::cout << "End" << std::endl;

    return 0;
}

Я компилирую с использованием компилятора Visual Studio 2010, и при запуске приложения происходит сбой во время инструкции free:

> cl get_string.cpp
Compilateur d'optimisation Microsoft (R) 32 bits C/C++ version 16.00.40219.01 pour 80x86
Copyright (C) Microsoft Corporation. Tous droits réservés.

get_string.cpp
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323) : warning C4530: Gestionnaire d'exceptions C++ utilisé, mais les sémantiques de déroulement n'ont pas été activées. Spécifiez /EHsc
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:get_string.exe
get_string.obj

> get_string.exe
Value = string

Я следую указаниям, данным в этом ответе . Что я должен сделать, чтобы освободить память и избежать сбоя моего приложения? Действительно, если я удалю инструкцию free, мое приложение будет работать хорошо, но это не чистое решение.

1 Ответ

2 голосов
/ 20 марта 2019

Это опасная практика - размещать в одном месте, а затем освобождать через границу DLL. Избегайте этого, если вы не знаете, что делаете все правильно (та же версия CRT и т. Д.). Так говорил Microsoft :

Когда вы передаете объекты C Run-time (CRT), такие как дескрипторы файлов, локали и переменные среды, в или из DLL (вызовы функций через границу DLL), может произойти непредвиденное поведение, если DLL, а также файлы, вызывающие в DLL, используют разные копии библиотек CRT.

Связанная проблема может возникнуть, когда вы выделяете память (либо явно с помощью new или malloc, либо неявно с помощью strdup, strstreambuf :: str и т. Д.), А затем пропускаете указатель через границу DLL для освобождения. Это может вызвать нарушение доступа к памяти или повреждение кучи, если DLL и ее пользователи используют разные копии библиотек CRT.

Одним из решений этой проблемы является предоставление функции free из вашей DLL, которая переворачивает выделенный объект, чтобы клиент мог вызвать на нем вашу функцию free, или в C ++ вы можете использовать умный указатель с пользовательским Средство удаления, чтобы сделать это правильно.

...