Безопасно ли использовать snprintf и друзей?

Недавно на SO возник вопрос (С какой стати кто-нибудь использует strncpy вместо strcpy?), на который были ответы (ответ 1, ответ 2), из-за чего я сомневался в других строковых функциях с 'n' в имени, таких как snprintf (которые я широко использовал). Безопасно ли использовать snprintf? И вообще какие безопасные функции из семейства 'n'?


person bandi    schedule 13.08.2009    source источник
comment
Связано: остерегайтесь различного поведения snprintf/swprintf/snwprintf/etc и %ls и %s на разных платформах. Все они разные.   -  person Jared Oberhaus    schedule 13.08.2009
comment
Только на сломанных, несовместимых реализациях. Стандарт C очень четко определяет, что должны делать эти спецификаторы формата.   -  person R.. GitHub STOP HELPING ICE    schedule 11.08.2010


Ответы (5)


strncpy() - это странная функция, которая действительно неправильно названа - ее первоначальная цель - гарантировать, что буфер был полностью инициализирован содержимым строки (без переполнения цели) и напоминанием о буфере с нулями. Насколько я понимаю, первоначальной целью была обработка записей в каталоге файловой системы — целевой буфер на самом деле не был строкой в ​​том же смысле, что и другие функции strxxx() в библиотеке C. Основная проблема с strncpy() заключается в том, что если исходная строка больше буфера назначения, результат не будет заканчиваться нулем.

Большинство других функций 'n', которые имеют дело со строками, правильно завершают строку, но есть исключения, такие как ублюдочная _snprintf() от Microsoft. Правильный C99 snprintf() всегда будет завершать нулем строку назначения (пока размер буфера назначения больше 0).

Существует «Технический отчет», TR 24731, в котором предлагается набор альтернатив проверки границ для функций, которые имеют дело со строками и буферами памяти. Одна из целей TR состоит в том, чтобы параметры, результаты и поведение при ошибках функций были более схожими для всех функций. TR, кажется, имеет несколько неоднозначное признание, и я не думаю, что он широко реализован, кроме как для компилятора Microsoft (я думаю, что MS была основной движущей силой TR). вы можете получить больше информации здесь:

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

person Michael Burr    schedule 13.08.2009
comment
Кроме того, реализация MS не соответствует требованиям. stackoverflow.com/ вопросы/372980/ - person Deduplicator; 20.02.2015

Хотя snprintf не будет переполнять буфер, если вы дадите ему правильные аргументы, имейте в виду, что он разделяет все уязвимости строки формата с другими членами семейства *printf. Например, спецификатор %n опасен, потому что злоумышленники могут использовать его для записи произвольных байтов в произвольные области памяти. См. FIO30- С. Исключить пользовательский ввод из строк формата из вики CERT C Coding Standards.

person Andrew Keeton    schedule 13.08.2009

Это безопасно, если вы указываете правильную длину буфера.

person akif    schedule 13.08.2009
comment
Какую длину вы предоставляете (и независимо от того, завершаете ли вы вручную конец буфера нулем) зависит от того, на какой платформе вы работаете. - person Jared Oberhaus; 13.08.2009

snprintf гарантирует, что буфер не будет перезаписан, но не гарантирует нулевое завершение. Вы можете использовать sprintf_s в MSVC, если хотите быть нестандартным.

См. http://msdn.microsoft.com/en-us/library/2ts7cx93(VS.71).aspx

person Drew Hoskins    schedule 13.08.2009
comment
На самом деле нет, snprintf вообще не гарантирует переполнение буфера. Это зависит от указанного вами размера буфера, который может быть неправильным. - person akif; 13.08.2009
comment
Само собой разумеется, что нет функции CRT, которая волшебным образом знает, насколько велик фактически переданный буфер. - person Drew Hoskins; 13.08.2009
comment
Что ж, тогда само собой разумеется, что ни одна функция CRT не является безопасной, и нет особого смысла людям притворяться, что добавление _s к их именам нарушает это правило ;-) - person Steve Jessop; 13.08.2009
comment
-1: Вы должны уточнить, что вы говорите о сломанной реализации Microsoft, а не о стандарте C. - person R.. GitHub STOP HELPING ICE; 11.08.2010

Осторожно: разные платформы по-разному относятся к нулевому завершению строки, переданной в snprintf.

person Jared Oberhaus    schedule 13.08.2009
comment
snprintf, как указано в ISO C99, гарантированно завершается нулем. - person Pavel Minaev; 13.08.2009
comment
Это то, что говорит ISO C99: однако поведение на некоторых платформах отличается. Вам придется экспериментировать и проверять каждую платформу, чтобы быть уверенным. - person Jared Oberhaus; 13.08.2009
comment
Можете ли вы привести пример платформы, которая не соответствует стандарту C в этом отношении? - person Martin v. Löwis; 13.08.2009
comment
Вариант Microsoft, _snprintf(), не завершает назначение, если буфер недостаточно велик: msdn.microsoft.com/en-us/library/2ts7cx93.aspx - person Michael Burr; 13.08.2009
comment
Зачем кому-то беспокоиться о несоответствии нестандартной функции с символом подчеркивания в начале имени, когда можно просто обернуть ее функцией со стандартным именем и заодно исправить ошибку отсутствия завершения? - person R.. GitHub STOP HELPING ICE; 11.04.2011
comment
Я бы проголосовал за это, если бы мог. Это абсолютно неверно. Если вы решите использовать нестандартный _snprintf с другим именем, его поведение может быть другим. Однако это не то, о чем спрашивал заявитель. Похоже, что snprintf недоступен в Windows; тем не менее, вы можете написать свой собственный без особых трудностей. - person cmccabe; 05.11.2012