当我释放由CFFI生成的DLL分配的char *时,为什么我的应用程序崩溃?

时间:2019-03-20 16:19:21

标签: python c++ memory python-cffi

我正在使用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

我遵循this answer中给出的指示。我应该怎么做才能释放内存并避免我的应用崩溃?确实,如果我删除了free指令,我的应用程序运行良好,但这不是一个干净的解决方案。

1 个答案:

答案 0 :(得分:2)

在一个位置进行分配然后跨DLL边界释放是一种危险的做法。除非您知道自己做的正确(避免使用CRT版本等),否则请避免使用它。 Thus spake Microsoft

  

当您将C运行时(CRT)对象(例如文件句柄,语言环境和环境变量)传入或传出DLL(跨越DLL边界的函数调用)时,如果DLL以及调用DLL的文件使用CRT库的不同副本。

     

当您分配内存(使用new或malloc显式或使用strdup,strstreambuf :: str等隐式)然后将指针传递到DLL边界以释放时,可能会发生相关问题。如果DLL及其用户使用CRT库的不同副本,则可能导致内存访问冲突或堆损坏。

对此的一种解决方案是从DLL中公开一个free函数,该函数正在翻转已分配的对象,以便客户端可以在其上调用您的free函数,或者在C ++中,您可以使用智能的带有自定义删除器的指针可以正确执行此操作。