Почему MSVC++ считает std::strcat небезопасным? (С++)

Когда я пытаюсь делать такие вещи:

char* prefix = "Sector_Data\\sector";
char* s_num = "0";
std::strcat(prefix, s_num);
std::strcat(prefix, "\\");

и так далее и тому подобное, я получаю предупреждение

warning C4996: 'strcat': This function or variable may be unsafe. Consider using strcat_s instead.

Почему strcat считается небезопасным, и есть ли способ избавиться от этого предупреждения без использования strcat_s?

Кроме того, если единственный способ избавиться от предупреждения - использовать strcat_s, как это работает (с точки зрения синтаксиса: по-видимому, оно не принимает два аргумента).


person Community    schedule 01.06.2009    source источник
comment
вы должны знать, что ваш пример ужасно ошибочен. Строковые константы (такие как: char *s = hello;) НЕ доступны для записи. Если вам повезет, произойдет сбой, если вам не повезет, это заставит приложение работать некорректно каким-то тонким образом. правильный способ сделать то, что вы хотели, выглядит следующим образом: char prefix[SIZE] = Sector_Data\\sector; где SIZE достаточно велик, чтобы вместить префикс И все, что вы собираетесь добавить к нему.   -  person Evan Teran    schedule 02.06.2009
comment
Как написано, ваш пример содержит переполнение буфера. Не просто возможное переполнение буфера, а определенное.   -  person Dietrich Epp    schedule 02.06.2009


Ответы (7)


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

strcat_s решает эту проблему, заставляя вас передавать длину буфера, в который вы копируете строку; при необходимости он усекает строку, чтобы убедиться, что буфер не переполнен.

google strcat_s, чтобы точно узнать, как его использовать.

person Drew Hoskins    schedule 01.06.2009
comment
strcat_s — это одна из библиотек функций, выпущенных как часть улучшений безопасности CRT — msdn.microsoft.com/en-us/library/8ef0s5kh(VS.80).aspx - person Sean; 02.06.2009
comment
По правде говоря, это просто нагнетание страха от имени Microsoft, чтобы убедиться, что код, который вы пишете, не может быть скомпилирован где-либо, кроме Windows. strcat_sэто не что иное, как проприетарный strncat (который существует уже 40 лет). Кроме того, strcat не является небезопасным как таковым, и вызов strcat_s с длиной, предоставляемой strlen, не является более безопасным. Что небезопасно, так это слепое принятие произвольного (и произвольной длины) пользовательского ввода, но использование нестандартной функции не решает эту проблему, это приведет только к сбою в другой функции (strlen). - person Damon; 26.04.2011
comment
@DeadMG: Это правда, что использование strcat_s означает, что вам не нужно вычитать начальную длину из общего размера, но в остальном это точно то же самое, за исключением того, что оно нестандартно и непереносимо. Но в целом моя точка зрения заключается в том, что использование безопасных функций вообще ничего не делает безопаснее. Вы либо знаете, что можете доверять некоторым данным (например, строковым литералам в вашей программе), либо знаете, что не можете доверять им (данным, введенным пользователем), и в любом случае вы должны знать размеры целевого буфера. Если это не так, все потеряно, независимо от того, какие безопасные функции вы используете. То же самое относится и к strncat, ... - person Damon; 26.04.2011
comment
... а также аналогичные функции. Я видел, как люди использовали strncpy, чтобы сделать свой код безопасным, потому что им говорили, что strcpy совершенно небезопасно, и в итоге они вызывали strlen, чтобы знать, сколько памяти выделить и сколько скопировать. Что, конечно, делало все усилия глупыми. Вот о чем я говорю: безопасными должны быть не функции, а концепция. - person Damon; 26.04.2011
comment
@Damon: Кроме того, все эти безопасные функции очень небезопасны, потому что они прерывают выполнение или нет, в зависимости от глобального состояния процесса, и вы не можете безопасно изменить его для своих собственных вызовов (состояния гонки и другой код). (возможно, выполняется параллельно) в зависимости от другой настройки), и не зависит от того, имеет ли он определенное состояние, хотя, если вы используете даже несколько источников, вы можете практически зависеть от их противоречивых требований к правильности. - person Deduplicator; 16.11.2015

Если вы используете С++, почему бы не избежать всего этого беспорядка и не использовать std::string. Тот же пример без ошибок будет выглядеть так:

std::string prefix = "Sector_Data\\sector";
prefix += "0";
prefix += "\\"

не нужно беспокоиться о размерах буферов и прочем. И если у вас есть API, который принимает const char *, вы можете просто использовать член .c_str();

some_c_api(prefix.c_str());
person Evan Teran    schedule 01.06.2009
comment
Приятно видеть хороший ответ на основную проблему. Я бы проголосовал не раз, если бы мог. - person David Thornley; 02.06.2009

Вы можете избавиться от этих предупреждений, добавив:

_CRT_SECURE_NO_WARNINGS

и

_SCL_SECURE_NO_WARNINGS

к определениям препроцессора вашего проекта.

person Dusty Campbell    schedule 01.06.2009
comment
Да, подавление предупреждающего сообщения с помощью вышеуказанного флага является лучшим выбором, если вы хотите написать переносимый код. использование strcat_s может привести к непереносимому коду, поскольку это характерно для компилятора Microsoft. - person Mohit Thakur; 14.09.2015

Это одна из функций обработки строк в C/C++, которая может привести к ошибкам переполнения буфера.

Проблема в том, что функция не знает размер буферов. Из документации MSDN:

Первый аргумент, strDestination, должен быть достаточно большим, чтобы содержать текущие strDestination и strSource вместе и закрывающий '\0'; в противном случае может произойти переполнение буфера.

strcat_s принимает дополнительный аргумент, указывающий размер буфера. Это позволяет ему проверять размеры перед выполнением concat и предотвращает переполнение. См. http://msdn.microsoft.com/en-us/library/d45bbxx4.aspx

person Herms    schedule 01.06.2009
comment
Теперь, если аргумент для strcat_s также был проверен компилятором на правильность, или поведение strcat_s зависело от ошибки, ... К сожалению, ни то, ни другое, так что это бесполезное упражнение. - person Deduplicator; 16.11.2015

Потому что у него нет возможности проверить, будет ли строка назначения (префикс) в вашем случае записана за ее пределами. По сути, strcat работает зацикливая, копируя байт за байтом исходную строку в место назначения. Он останавливается, когда видит значение «0» (обозначаемое как «\ 0»), называемое нулевым терминалом. Так как C не имеет встроенной проверки границ, а целевая строка — это просто место в памяти, strcat будет продолжать работать до бесконечности, даже если она минует исходную строку или целевую строку. str не имеет нулевого терминала.

Приведенные выше решения зависят от платформы вашей среды Windows. Если вы хотите что-то независимое от платформы, вам придется спорить с strncat:

strncat(char* dest, const char* src, size_t count)

Это еще один вариант, если использовать его с умом. Вы можете использовать count, чтобы указать максимальное количество символов для копирования. Для этого вам нужно выяснить, сколько места доступно в dest (сколько вы выделили — strlen(dest)) и передать это как count.

person Doug T.    schedule 01.06.2009
comment
Даже strncat небезопасен. Из MSDN: strncat не проверяет достаточно места в strDest; следовательно, это потенциальная причина переполнения буфера. Имейте в виду, что count ограничивает количество добавляемых символов; это не предел размера strDest. См. пример ниже. Дополнительные сведения см. в разделе Предотвращение переполнения буфера. - person Drew Hoskins; 02.06.2009
comment
Проверка наличия достаточного места в целевом буфере выходит за рамки языка и требует дополнительных возможностей ОС/компилятора. - person Doug T.; 02.06.2009
comment
MSDN не имеет смысла в strncat(). Это заставляет программиста вводить размер, а ошибиться в двух размерах можно так же легко, как и в одном. Проблема с strncat(), как и с strncpy(), заключается в том, что он не делает то же самое, что и strcat() ограниченной длины (или strcpy()). - person David Thornley; 02.06.2009

Чтобы отключить предупреждение, вы можете сделать это.

#pragma warning(disable:4996)

Кстати, я настоятельно рекомендую вам использовать strcat_s().

person young    schedule 01.06.2009

Есть две проблемы с strcat. Во-первых, вы должны выполнять всю свою проверку вне функции, выполняя работу, которая почти такая же, как и функция:

if(pDest+strlen(pDest)+strlen(pScr) < destSize)

Вы должны пройти по всей длине обеих строк, чтобы убедиться, что они подходят, прежде чем пройти по всей их длине СНОВА, чтобы сделать копию. Из-за этого многие программисты просто предположат, что он подойдет, и пропустят тест. Хуже того, может случиться так, что, когда код впервые написан, он ГАРАНТИРОВАНО подходит, но когда кто-то добавляет еще один strcat или меняет размер буфера или константу где-то еще в программе, у вас возникают проблемы.

Другая проблема заключается в том, что pSrc и pDst перекрываются. В зависимости от вашего компилятора, strcat вполне может быть простым циклом, который проверяет символ за раз на наличие 0 в pSrc. Если pDst перезапишет этот 0, вы попадете в цикл, который будет работать до тех пор, пока ваша программа не рухнет.

person Dolphin    schedule 01.06.2009