【问题标题】:Problem with realloc implementation resulting in Access violation导致访问冲突的 realloc 实现问题
【发布时间】:2011-03-04 18:43:32
【问题描述】:

我有一个内部的 malloc/realloc/free 的自定义字符串类;对于某些字符串附加工作正常,但在某些其他字符串将总是失败(小或大分配)。相同的代码在不同的项目上运行良好,尽管它是一个 ANSI 版本。

我相信我正确地实现了这一点,但很可能我忽略了一些东西。当我在打开日志后尝试使用“szLog”缓冲区时会发生错误。这仅包含程序文件目录的路径(总共 40 个字符)。使用没有“日志文件”前缀的相同缓冲区可以正常工作,因此 realloc 部分存在问题。是的,日志确实打开了。

我得到 0xC0000005:访问冲突读取位置 0x00660063。 仅在使用 realloc 时(但如前所述,它并不总是失败 - 在这种情况下,当输入 szLog 时 - 但其他变量字符串/缓冲区也可以)。

HeapReAlloc 是 realloc.c 中的失败函数,errno 为 22。

我已经剥离了 cmets 以尝试使帖子尽可能小!任何帮助将不胜感激。

gData.szLogStr 是 UString,IsNull 是 "x == NULL" 和 unichar 的定义> 只是 wchar_t 的 typedef

class UString : public Object
{
private:
    unichar*    mpsz;
    unichar*    mpszPrev;
    UINT        muiAlloc;
    UINT        muiLen;
public:
    ... other functions ...

    UString& operator << (const unichar* pszAdd)
    {
        if ( IsNull(pszAdd) )
            return (*this);
        if ( IsNull(mpsz) )
        {
            muiAlloc = ((str_length(pszAdd)+1) * sizeof(unichar));
            if ( IsNull((mpsz = static_cast<unichar*>(malloc(muiAlloc)))) )
            {
                SETLASTERROR(ERR_NOT_ENOUGH_MEMORY);
                muiAlloc = 0;
                return (*this);
            }
            mpszPrev = mpsz;
            muiLen = str_copy(mpsz, pszAdd, muiAlloc);
        }
        else
        {
            UINT    uiNewAlloc = (muiAlloc + (str_length(pszAdd) * sizeof(unichar)));

            if ( muiAlloc < uiNewAlloc )
            {
                uiNewAlloc *= 2;

                /* Fails */
                if ( IsNull((mpsz = static_cast<unichar*>(realloc(mpsz, uiNewAlloc)))) )
                {
                    SETLASTERROR(ERR_NOT_ENOUGH_MEMORY);
                    mpsz = mpszPrev;
                    return (*this);
                }
                mpszPrev = mpsz;
                muiAlloc = uiNewAlloc;
            }
            muiLen = str_append(mpsz, pszAdd, muiAlloc);
        }
        return (*this);
    }

这是从 main via 中调用的:

        UString     szConf;
        unichar     szLog[MAX_LEN_GENERIC];

        szConf << ppszCmdline[0];
        szConf.replace(_T(".exe"), _T(".cfg"));
        if ( GetPrivateProfileString(_T("Application"), _T("LogFile"), NULL, szLog, sizeofbuf(szLog), szConf.str()) == 0 )
        {
            UINT    uiLen = str_copy(szLog, szConf.str(), sizeofbuf(szLog)); 
            szLog[uiLen-3] = 'l';
            szLog[uiLen-2] = 'o';
            szLog[uiLen-1] = 'g';
        }

        if ( ApplicationLog::Instance().Open(szLog, CREATE_ALWAYS) )
        {
            gData.szLogStr.clear();
            /* Erroring call */
            gData.szLogStr << _T("Log file '") << szLog << _T("' opened");
            APP_LOG(LL_WriteAlways, NULL, gData.szLogStr);
            ObjMgr::Instance().DumpObjects(LogDumpedObjects);
        }

【问题讨论】:

    标签: c++ c visual-studio-2010 visual-c++


    【解决方案1】:

    您正在使用 C++ 进行编程,因此请使用 newdelete。要“更新”,分配一个足够大的新内存区域来保存新字符串,用正确的值初始化它,然后delete 旧字符串。

    【讨论】:

    • 你正在使用 C++ 编程,所以使用vector ;-p
    • 出于某些目的,realloc 确实提供了newvector(使用默认分配器)都无法匹配的真正优势。但是有一点很重要:如果你想控制分配,只需编写一个分配器,而不是类的其余部分。
    • @Steve:您可以将其扩展为“您正在使用 C++ 编程,因此请使用 std::string”;)但出于学习目的,编写自己的字符串类确实很有帮助。
    • 我正在全局覆盖 new/delete(偶尔会记录它们的内容,为简单起见使用这些字符串)。因此,我不想在字符串类中使用 new/deletes。我知道我只能为类重写它,但是使用“旧”样式可以让我在需要在其他地方使用它们时保持良好状态,因为我通常编写“C with classes”样式。 std:: 字符串缺少我需要的某些功能,而不是重载它们,我可以使用自定义类尽可能地优化。
    【解决方案2】:

    str_length() 失败时返回什么?我会跟踪 muiAlloc 的值,看看你实际上试图分配什么。这可能不是一个理智的数字。

    您确定 szLog 中的任何内容都是空终止的,并且缓冲区有空间容纳您要复制到其中的任何内容吗?尚不清楚 str_copy 是否安全。它可能是 strncpy() 的包装器,它不保证空终止符,但有些人错误地认为它确实如此。

    【讨论】:

    • 我已经验证了 str_xxx 调用的返回值(它们按预期返回),并且 str_copy 与 openbsd 的 strlcpy 几乎相同 - 它返回写入缓冲区的字符数,不包括终止 null . str_length() 按预期返回 40,进入 realloc 调用时 muiAlloc 为 22(由于缓冲区大小翻倍,uiNewAlloc 达到 204)。这些都符合预期。
    • 我刚刚想到的另一点,虽然我怀疑这是否相关:在没有首先确保 n 大于 2 的情况下,不要分配给 p[n-3]!此外,在 on-realloc 分支中(它没有崩溃),你有 muiLen = str_copy(mpsz, pszAdd, muiAlloc); -- 但 muiAlloc 是字节,而不是字符。如果它是 Unicode,那是一个错误。实际上,等等,我看到你总是将字节传递给 str_copy() 的最后一个参数:它应该是字节,而不是字符数?
    • 啊,是的,缓冲区的经典验证!添加了检查以确保该范围内的 uiLen > 0。 muiLen 是字符数(即 wcslen),而 muiAlloc 是分配的字节数(str_copy 接收缓冲区的大小)。假设 wcslen(L"123") 与 strlen("123") 相同,它们是,没有问题。我承认我的整体 unicode '处理'很差:)
    • 标记“字符”和“字节”之间的差异是 Apps Hungarian 表示法的完美应用(而不仅仅是编码静态类型“无符号整数”)。
    • 至于问题本身:我会添加诊断以打印所涉及的缓冲区(szLog、mpsz 等)的地址和大小,并将其与错误消息中给出的内存地址相关联至于所涉及的字符串的长度和大小。
    猜你喜欢
    • 2018-11-02
    • 2010-11-05
    • 1970-01-01
    • 1970-01-01
    • 2012-05-22
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    • 2016-06-15
    相关资源
    最近更新 更多