sprintf_s с слишком маленьким буфером
Следующий код вызывает ошибку и убивает мое приложение. Это имеет смысл, поскольку длина буфера составляет всего 10 байтов, а длина текста - 22 байта (переполнение буфера).
char buffer[10];
int length = sprintf_s( buffer, 10, "1234567890.1234567890." );
Как я могу поймать эту ошибку, чтобы я мог сообщить о ней, а не сбой моего приложения?
Редактировать:
После прочтения комментариев ниже я пошел с _snprintf_s. Если он возвращает значение -1, то буфер не был обновлен.
length = _snprintf_s( buffer, 10, 9, "123456789" );
printf( "1) Length=%d\n", length ); // Length == 9
length = _snprintf_s( buffer, 10, 9, "1234567890.1234567890." );
printf( "2) Length=%d\n", length ); // Length == -1
length = _snprintf_s( buffer, 10, 10, "1234567890.1234567890." );
printf( "3) Length=%d\n", length ); // Crash, it needs room for the NULL char
6 ответов
Вместо sprintf_s
Вы могли бы использовать snprintf
(ака _snprintf
на окнах).
#ifdef WIN32
#define snprintf _snprintf
#endif
char buffer[10];
int length = snprintf( buffer, 10, "1234567890.1234567890." );
// unix snprintf returns length output would actually require;
// windows _snprintf returns actual output length if output fits, else negative
if (length >= sizeof(buffer) || length<0)
{
/* error handling */
}
Это по замыслу. Весь смысл sprintf_s
и другие функции из *_s
семейство, заключается в том, чтобы отлавливать ошибки переполнения буфера и рассматривать их как нарушение предварительных условий Это означает, что они не предназначены для восстановления. Это разработано, чтобы ловить только ошибки - вы никогда не должны звонить sprintf_s
если вы знаете, строка может быть слишком большой для буфера назначения. В этом случае используйте strlen
Сначала нужно проверить и решить, нужно ли обрезать.
Это работает с VC++ и даже безопаснее, чем использование snprintf (и, конечно, безопаснее, чем _snprintf):
void TestString(const char* pEvil)
{
char buffer[100];
_snprintf_s(buffer, _TRUNCATE, "Some data: %s\n", pEvil);
}
Флаг _TRUNCATE указывает, что строка должна быть усечена. В этой форме размер буфера фактически не передается, что (как ни парадоксально!) Делает его таким безопасным. Компилятор использует магию шаблона для определения размера буфера, что означает, что он не может быть указан неправильно (неожиданно распространенная ошибка). Этот метод может быть применен для создания других безопасных упаковщиков строк, как описано в моем посте здесь: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
Из MSDN:
Другое основное отличие sprintf_s от sprintf состоит в том, что sprintf_s принимает параметр длины, определяющий размер выходного буфера в символах. Если буфер слишком мал для печатаемого текста, то для буфера устанавливается пустая строка и вызывается недопустимый обработчик параметров. В отличие от snprintf, sprintf_s гарантирует, что буфер будет нулевым (если размер буфера не равен нулю).
Так что в идеале то, что вы написали, должно работать правильно.
См. Раздел 6.6.1 документа TR24731, который представляет собой версию комитета ISO C, реализованную Microsoft. Обеспечивает функции set_constraint_handler()
, abort_constraint_handler()
а также ignore_constraint_handler()
функции.
Есть комментарии от Павла Минаева о том, что реализация Microsoft не соответствует предложению TR24731 ("Технический отчет типа 2"), поэтому вы не сможете вмешаться или вам придется сделать что-то отличное от того, что TR указывает, что должно быть сделано. Для этого внимательно изучите MSDN.
Похоже, вы пишете на MSVC какой-то?
Я думаю, что документы MSDN для sprintf_s говорят, что он утверждает, что умирает, поэтому я не слишком уверен, если вы можете программно поймать это.
Как предположил Л.Бушкин, вам гораздо лучше использовать классы, управляющие строками.