使用wcsncpy_s复制字符串时缓冲区太小

时间:2010-07-09 18:02:25

标签: c++ debugging string mfc buffer-overflow

这个C ++代码有点蹩脚,但我需要维护它。我似乎无法弄清楚“缓冲太小”的问题。我正在使用Visual Studio 2010.我将根据我在调试器中看到的值来提供重现所需的最少代码。对不起,我不会测试实际代码片段本身。此外,由于我的系统剪贴板在我调试时“忙”,我不能只复制和粘贴,因此某些错误可能会在某处蔓延,但我会仔细检查一下。相信我,你不想看到整个功能 - 它有点太长,没有任何意义:)

来自tchar.h

#define _tcsncpy_s wcsncpy_s

来自afxstr.h:

typedef ATL::CStringT< TCHAR, StrTraitMFC_DLL< TCHAR > > CString;

来自WinNT.h:

typedef WCHAR TCHAR, *PTCHAR;

哦,伙计,这些宏似乎永远不会结束。我会在这里停下来 最后,来自myfile.cpp:

CString str; // Actually a function parameter with value "07/02/2010"
DWORD nIndex = 10;
DWORD nLast = 0;
LPCTSTR psz = (LPCTSTR) str; // Debugger says that it also gets "07/02/2010".

CString s;
_tcsncpy_s(
    s.GetBuffer((int) (nIndex - nLast + 1)), // I added the " + 1" part hoping to fix a bug, but that changed nothing
    nIndex - nLast,
    psz + nLast,
    (size_t) (nIndex - nLast)
);

有了这个,我点击了一个断言,调试器打开tcsncpy_s.inl,最后带有以下代码:

  53    ...
  54    if (available == 0)
  55    {
  56        if (_COUNT == _TRUNCATE)
  57        {
  58            _DEST[_SIZE - 1] = 0;
  59            _RETURN_TRUNCATE;
  60        }
  61        RESET_STRING(_DEST, _SIZE);
=>62        _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE);
  63    }
  64    _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1);
  65    _RETURN_NO_ERROR;
  66 }
  67
  68

调试器指向第62行:_RETURN_BUFFER_TOO_SMALL。不幸的是,我无法在tcsncpy_s.inl中查看事物的价值。也许经验丰富的编码员可以告诉我这里发生了什么?我相信(也许是错误的)这段代码很老,而且不是用Unicode编写的。什么是解决这个坚持旧的N蹩脚枪支的最好的方法(没有C ++ 0X技巧或其他花哨的东西请) - 我只是想在子弹伤口上贴一个补丁。

3 个答案:

答案 0 :(得分:3)

传递给wcsncpy_s()的大小是缓冲区大小,而不是缓冲区可以存储的字符数。它包括零终止符。你需要加1。

答案 1 :(得分:3)

strncpy_s的第四个参数是要从源缓冲区复制的字符数,并且它不考虑终止空值 - 即,实际上,如果源缓冲区包含具有{{的字符串1}}或更多字符,然后(nIndex - nLast)将被复制,然后将附加空字符。因此,目标缓冲区必须准备好接受(nIndex - nLast)个字符以便也考虑该空值。

现在你的+1似乎就是这样,但你也应该在(nIndex - nLast + 1)的第二个参数中反映它,它告诉它缓冲区有多大。将其更改为strncpy_s,它应该有效。

答案 2 :(得分:2)

来自wcsncpy_s()http://msdn.microsoft.com/en-us/library/5dae5d43.aspx)的文档:

  

这些函数尝试将strSource的前D个字符复制到strDest,其中D是count的较小者和strSource的长度。 如果这些D字符符合strDest(其大小以numberOfElements给出)并仍为空终止符留出空间,则会复制这些字符并附加终止空值;否则,strDest [0]设置为空字符,并调用无效参数处理程序

因此,必须以字符(元素)指定计数,而不是字节,这是你已经在做的,并且目标缓冲区大小必须考虑空终结符字符,你在缓冲区中腾出空间,但没有告诉wcsncpy_s()(此处称为_tcsncpy()):

_tcsncpy_s(
    s.GetBuffer((int) (nIndex - nLast + 1)), // I added the " + 1" part hoping to fix a bug, but that changed nothing
    nIndex - nLast + 1,
    psz + nLast,
    (size_t) (nIndex - nLast)
);