Strncat от Microsoft читает байты за пределами границ исходного буфера
Я наблюдаю интересную проблему с реализацией Microsoft strncat
, Он касается 1 байта за пределами исходного буфера. Рассмотрим следующий код:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
void main()
{
char dstBuf[1024];
char* src = malloc(112);
memset(src, 'a', 112);
dstBuf[0] = 0;
strncat(dstBuf, src, 112);
}
strncat
читает 1 байт после блока 112 байт. Поэтому, если вам не повезло получить распределение на недопустимой границе страницы, ваше приложение вылетает. Большие приложения могут прерываться в таких местах. (Обратите внимание, что такое условие может быть смоделировано с настройкой gflags PageHeap; размер блока должен делиться на размер указателя для правильного выравнивания.)
Это ожидаемое поведение или ошибка? Есть ссылки, подтверждающие это? (Я прочитал несколько описаний strncat
но они могут быть интерпретированы в обоих направлениях в зависимости от вашего первоначального мышления...)
Обновление (чтобы ответить на вопросы о доказательствах): Я прошу прощения, если это не ясно из текста выше, но это экспериментальный факт. Я наблюдаю периодические сбои в приложении на strncat
адрес чтения src+srcBufSize. В этом небольшом примере запуска с gflags PageHeap при сбое воспроизводит последовательно (100%). Так что, насколько я вижу, доказательства очень веские.
Обновление 2 (информация о компиляторе) MS Visual Studio 2005 Версия 8.0.50727.867. Платформа сборки: 64-битная версия (без репро для 32-битной). ОС, используемая для воспроизведения аварии: Windows Server 2008 R2.
Обновление 3 Проблема также воспроизводится с помощью двоичного файла, встроенного в MS Visual Studio 2012 11.0.50727.1.
Обновление 4 Ссылка для выпуска в Microsoft Connect; ссылка на обсуждение на форумах MSDN
Обновление 5 Проблема будет исправлена в следующем выпуске VS. Для старых версий исправлений не планируется. См. Ссылку "Microsoft Connect" выше.
3 ответа
Документация дляstrncat
состояния:
src - указатель на байтовую строку с нулевым символом в конце для копирования
Следовательно, реализация может предполагать, что src
входной параметр фактически заканчивается NUL, даже если он длиннее count
персонажи.
Для дальнейшего подтверждения собственная документация Microsoft гласит:
strSource
Исходная строка с нулевым символом в конце.
С другой стороны, фактический стандарт C гласит что-то вроде:
strncat
функция добавляется не более чемn
символы (нулевой символ и следующие за ним символы не добавляются) из массива, на который указываетs2
до конца строки, на которую указываетs1
,
Как указано в комментариях ниже, это определяет второй параметр s2
в виде массива, а не строки, заканчивающейся NUL. Тем не менее, это все еще неоднозначно по отношению к первоначальному вопросу, потому что эта документация описывает конечный эффект на s1
, а не поведение функции при чтении из s2
,
Конечно, это можно решить в отношении конкретной реализации Microsoft, обратившись к исходному коду C Runtime Library.
s2
не является "строкой" в strncat(s1, s2, n)
,
Так что, если Microsoft читает пасс n
байты, это не соответствует C11.
С11 7.24.2.3.1 strcat()
упоминает
msgstr "добавляет копию строки, на которую указывает s2 (включая завершающий нулевой символ), в конец строки, на которую указывает s1".
С11 7.24.2.3.2 strncat
говорит
"Функция strncat добавляет не более n символов (нулевой символ и следующие за ним символы не добавляются) из массива, на который указывает s2, до конца строки, на которую указывает s1.... Завершающий нулевой символ всегда добавлен к результату
Ясно в strncat
дело, s2
рассматривается как "массив" со строковыми ограничениями на то, сколько добавляется к s1
, Таким образом, во время конкатенации, это не нужно проверять s2
больше, чем то, что абсолютно необходимо. Финал написан \0
исходит из кода, а не s2
,
Не знаю о старом стандарте C99.
Английский язык несовершенный, больше, чем C.
В документации написано "не более n символов" (мой акцент). Нет никаких доказательств того, что strncat копирует более 112 символов. Что заставляет вас верить в это?
Код strncat может индексировать после смещения 112, но не на самом деле ссылочное смещение 113, что может вызвать сбой хранилища. Это поведение ptr определено как приемлемое в K&R.
Наконец, опять же, это проблема английского языка / рассуждения, документация, вероятно, содержит строку с нулевым символом в конце. Но не правда ли, избыточно говорить, что строка завершена нулем? Они по определению, иначе они будут массивом символов. Таким образом, документация является расплывчатой и неспецифичной. Программисту остается читать между строк. Программная документация не является юридическим документом, это описание, предназначенное для понимания специалистом в данной области.