发生缓冲区溢出

Posted

技术标签:

【中文标题】发生缓冲区溢出【英文标题】:A buffer overrun has occurred 【发布时间】:2014-03-16 12:29:30 【问题描述】:

几天前,我公司出于安全考虑将我们的操作系统从 Windows XP 更改为 Windows 7(32 位)。

我在 VS 2008、QT 4.8.1 和 Boost 中使用 C++。我的应用程序中的结构成员对齐是 1 个字节 (/Zp1)。

修改后发现一个bug:

我加载使用QPluginLoader 创建的库,然后使用qobject_cast 将其转换为我的界面。在我的库中,我可能会加载另一个。

当我在操作系统更改后在调试模式下测试我的应用程序时,一切正常。一切正常,然后我将其更改为 Release,我的应用程序开始崩溃。

当我用 VS 2008 调试它时,我看到了这个错误:

my_app.exe 中,损坏了程序的内部状态。按 Break 调试程序或按 Continue 终止程序。

有关详细信息,请参阅帮助主题“如何调试缓冲区溢出问题”。

该应用程序在 Windows XP 上仍然可以正常工作(发布和调试模式)。

一开始,我以为是因为依赖或者我错过了什么。在发布模式下测试和跟踪我的应用程序几个小时后,我终于发现了问题所在。当一个方法试图将一个值(任何东西)返回给另一个在库中调用它的方法时,我的应用程序崩溃了。

例如:

方法 B 返回 Bool,我在方法 A 中调用它。当方法 B 完成并准备将 Bool 值返回给方法 A 时,wy 应用程序崩溃。如果方法返回一个值,当它想要返回加载库的方法时,它也会崩溃。不知何故,使用不返回任何值(void)的方法是可以的。

为了确定究竟出了什么问题,我的方法中的代码,我制作了另一个应用程序(测试器)并开始逐部分添加我的代码以查找错误。我相信这与QT有关。

请检查下面的代码,如果你知道哪里出了问题,请告诉我。

我有 main.cpp 加载这样的插件:

int main(int argc, char *argv[])

    QCoreApplication a(argc, argv);

    QString filename = "correct path";

    QPluginLoader* pluginLoader = new QPluginLoader(filename);

    QObject *plugin = pluginLoader->instance();

    PluginInterface* plugin_instance;

    if (plugin) 
    
        
        SecureZeroMemory(&plugin_instance, sizeof(PluginInterface));

        plugin_instance = qobject_cast<PluginInterface *>(plugin);

    

    bool result = plugin_instance->initialize();

    return a.exec();

这是我的界面:

class PluginInterface

public:

    virtual bool initialize() = 0;
;

QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(PluginInterface,"some text");
QT_END_NAMESPACE

我的图书馆是这样的:

插件.hpp

class Plugin : public QObject, public PluginInterface

    Q_OBJECT
    Q_INTERFACES(PluginInterface)

public:
    Plugin();
    bool initialize();  
;

插件.cpp

#define SUPPORTED_VERSIONS  0x01011403
#define WFS_TRACE_NONE      0

bool Plugin::initialize()

    WFSVERSION wfs_version;
    HRESULT hRes;

    cout << "WFSStartUp" << endl;

    hRes = WFSStartUp(SUPPORTED_VERSIONS, &wfs_version);

    WORD version = wfs_version.wVersion;

    cout << "version : " << version << endl;

    DWORD dwTrace = WFS_TRACE_NONE;

    cout << "WFSCreateAppHandle" << endl;

    HRESULT hRes2;
    HANDLE app_handle;
    hRes2 = WFSCreateAppHandle(&app_handle);

    cout << "Result : " << hRes2 << endl;

    return true;

Q_EXPORT_PLUGIN2(my_plugin, Plugin);

这是我的测试应用程序的输出:

WFS 启动 版本:63424 WFSCreateAppHandle 结果:0 应用程序崩溃!!

Plugin::initialize() 中的代码可以正常工作。我将其复制/粘贴到 main() 并运行应用程序以确保它。

这里有什么问题?我错过了什么?

【问题讨论】:

这看起来像是一个调用约定问题。 【参考方案1】:

这段代码:

PluginInterface* plugin_instance;

if (plugin) 


    SecureZeroMemory(&plugin_instance, sizeof(PluginInterface));

    plugin_instance = qobject_cast<PluginInterface *>(plugin);


如果SecureZeroMemory 像它的名字所说的那样做,那将非常容易引发崩溃。

要将指针归零,只需将其设置为 0。

但是,还要提一下,当您接下来要做的事情是分配给它时,那将是毫无意义的,如上所述。

【讨论】:

我已经在没有 SecureZeroMemory 的情况下进行了测试,但它仍然崩溃,SecureZeroMemory 不是导致崩溃的原因! @M.H.您对单一原因的假设是有缺陷的。您认为SecureZeroMemory 调用没有做错的明显结论是不正确的。 好吧,当我看到你的答案时,我也发布了它的错误,无论如何,我的主要问题仍然存在,还有其他想法吗?! @M.H.没有关于代码的问题。但我认为 Windows XP 应用程序和 Windows 7 的 32 位应用程序之间的区别可能是默认项目设置不同。安全检查。我怀疑你还有一些调试时间。【参考方案2】:

我已经在没有 SecureZeroMemory 的情况下进行了测试,但它仍然崩溃,SecureZeroMemory 不是导致崩溃的原因!

很可能在代码的其他地方也有类似的错误——你没有显示的地方。

至少,您是否使用相同的编译器并使用相同的设置来编译应用程序中的所有内容(所有库、DLL 等)?如果你使用“/Zp1”,你必须对每一段代码都使用它,包括Qt本身。这样的编译器标志使使用它们编译的代码与没有它们编译的代码二进制不兼容。

此外,“/Zp1”是性能杀手。如果您想打包结构以将它们转储到磁盘等,那么您将走错路。您使用的结构不应该被打包。仅当您将其复制到磁盘/从磁盘复制时,您才应该在内部定义另一个已打包的结构,并且您首先将数据从未打包的结构复制到打包的结构,然后才转储到磁盘。您应该使用适当的编译指示/属性来打包单个结构,不是一切!

【讨论】:

我有理由需要使用“/Zp1”并且我的代码中的每一个和平都有这个【参考方案3】:

我不得不更改几个 Visual Studio 构建选项来解决这个问题!

优化 = 自定义(配置属性 -> C/C++ -> 优化) 内联函数扩展 = 默认(配置 属性 -> C/C++ -> 优化)

【讨论】:

以上是关于发生缓冲区溢出的主要内容,如果未能解决你的问题,请参考以下文章

Java:忽略输入流 - 缓冲区会溢出并发生坏事吗?

McAfee提示“缓冲区溢出”,该怎么处理?

c++缓冲区溢出问题

在缓冲区溢出中,新的返回地址如何完美对齐?

--栈溢出2

windows缓冲区溢出