Может ли функция wcsncpy_s привести к переполнению буфера?

Я пытаюсь понять, как работает функция 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 должен предотвратить переполнение буфера?

1

Решение

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

Вы врете функции. Вы говорите это «a имеет достаточно места для хранения 10 символов «, когда на самом деле у него достаточно места только для хранения пяти символов. Функция доверяет тому, что вы предоставляете ему достоверную информацию (как он мог знать, что это не так?)

Обратите внимание, что, хотя вы получаете ошибку времени выполнения со вторым фрагментом кода, первый фрагмент кода также неверен. Оба пишут за концом массива a,

Тем не менее, вы используете неправильную перегрузку wcsncpy_s: при компиляции кода C ++, есть дополнительная wcsncpy_s перегрузка, это шаблон, который определяет размер целевого массива. Если вы должны были изменить вызов на:

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

Шаблон выведет, что массив имеет пять элементов, и автоматически использует его в качестве размера. Это работает только тогда, когда целью является массив; это не работает, если целью является указатель на начальный элемент в массиве.

В идеале, если вы используете C ++, лучше всего избегать манипуляций со строками C: use 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> будет идентичным Обратите внимание, что получение указателя на базовый массив и получение размера этого массива происходит по одной и той же форме, поэтому как легко написать код, так и легко проверить его правильность (все, что требуется, — это простой визуальный контроль вызова). ).

6

Другие решения

В этой строке:

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

вы говорите функции, что есть место для 10 персонажи в aкогда на самом деле есть только место для 5,

3

определение wcsncpy_s

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

strDest также является буфером для вашей записи.

numberOfElements — это количество элементов в буфере strDest.

strSource — буфер, из которого вы читаете

count — количество элементов в strSource, которые нужно скопировать в strDest

так в

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 не помогает вам с этой ошибкой, потому что вы сказали, что в a доступно 10 элементов. когда на самом деле доступно только 5 элементов.

2