wcsncpy_s函数会导致缓冲区溢出吗?

时间:2012-09-28 17:13:13

标签: c++ visual-c++ buffer-overflow

我正在尝试了解wcsncpy_s函数的工作原理以及它如何防止缓冲区溢出。首先,根据MSDN,此函数的参数意味着以下内容:

strDest =目的地字符串。

numberOfElements =目标字符串的大小。

strSource =源字符串。

count =要复制的字符数,或_TRUNCATE。

现在考虑以下代码:

wchar_t a[5];
wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

printf("%d\r\n", sizeof(a));//10
printf("%d\r\n", wcslen(a));//9
wprintf(L"%s", a);//ABCDEFGHI

如果我理解这一切,“a”应该最多可容纳4个宽字符和一个空终结符,现在可以容纳9个宽字符。

现在,以下代码将导致我的应用程序因调试断言失败而突然终止(VS 2005编译器):

wchar_t a[5];
wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10);

printf("%d\r\n", sizeof(a));
printf("%d\r\n", wcslen(a));
wprintf(L"%s", a);

有人可以解释一下上面的代码,以及wcsncpy_s应该如何防止缓冲区溢出?

3 个答案:

答案 0 :(得分:6)

wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

你在骗取这个职能。你告诉它,“a有足够的空间来存储10个字符,”实际上它只有足够的空间来存储5个字符。该函数相信您正在为其提供有效信息(如何知道您不是?)

请注意,虽然第二个代码段出现运行时错误,但第一个代码段同样错误。两者都写过数组的末尾a

那就是说:你正在使用wcsncpy_s的错误重载:在编译C ++代码时,还有一个额外的wcsncpy_s重载,它是一个推导出目标数组大小的模板。如果您要将呼叫更改为:

wcsncpy_s(a, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

模板将推断出该数组有五个元素,并自动将其用作大小。仅当目标是数组时才有效;如果目标是指向数组中初始元素的指针,则它不起作用。

理想情况下,如果您使用的是C ++,最好完全避免使用C字符串操作:使用std::wstring或其他字符串类型。如果你想使用这些使用C字符串的函数,至少使用std::vector<wchar_t>std::array<wchar_t, N>而不是原始数组:搞砸代码要困难得多。例如,

std::array<wchar_t, 5> a;
wcsncpy_s(a.data(), a.size(), L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

std::vector<wchar_t>的代码将完全相同。请注意,获取指向底层数组的指针并获取该数组的大小遵循相同的形式,因此编写代码很容易并且很容易检查代码是否正确(只需要对调用进行简单的目视检查) )。

答案 1 :(得分:3)

在这一行:

wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

你告诉函数10中有a个字符的空间,实际上只有5的空间。

答案 2 :(得分:2)

wcsncpy_s的定义是

errno_t wcsncpy_s(
wchar_t *strDest,
size_t numberOfElements,
const wchar_t *strSource,
size_t count 
);

strDest也是你写作的缓冲区。

numberOfElements是strDest缓冲区内有多少个元素。

strSource是您正在阅读的缓冲区

count是要复制到strDest的strSource中的元素数

所以在

wchar_t a[5];
wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10); //ERROR

printf("%d\r\n", sizeof(a));
printf("%d\r\n", wcslen(a));
wprintf(L"%s", a);

wchar_t是2个字符宽。

问题是该函数最后会自动为您写入一个空终止符。这导致溢出导致它试图在结束时写一个。

wcsncpy_s没有帮助您解决此错误,因为您告诉它有一个可用的10个元素。实际上只有5个元素可用。