sprintf_s с слишком маленьким буфером - PullRequest
10 голосов
/ 01 октября 2009

Следующий код вызывает ошибку и убивает мое приложение. Это имеет смысл, так как длина буфера составляет всего 10 байтов, а длина текста - 22 байта (переполнение буфера).

char buffer[10];    
int length = sprintf_s( buffer, 10, "1234567890.1234567890." ); 

Как мне перехватить эту ошибку, чтобы я мог сообщить о ней, вместо того, чтобы разбить мое приложение?

Edit:

После прочтения комментариев ниже я пошел с _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 ]

16 голосов
/ 01 октября 2009

Это по замыслу. Весь смысл sprintf_s и других функций семейства *_s заключается в том, чтобы отлавливать ошибки переполнения буфера и рассматривать их как нарушения предварительных условий . Это означает, что они не предназначены для восстановления. Это предназначено только для обнаружения ошибок - вам никогда не следует вызывать sprintf_s, если вы знаете, что строка может быть слишком большой для буфера назначения. В этом случае сначала используйте strlen, чтобы проверить и решить, нужно ли вам обрезать.

5 голосов
/ 22 ноября 2014

Это работает с 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/

5 голосов
/ 02 октября 2009

Вместо sprintf_s вы можете использовать snprintf (a.k.a _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 */
}
0 голосов
/ 02 октября 2009

См. Раздел 6.6.1 TR24731 , который является ISO C комитетом версии функциональности, реализованной Microsoft. Он обеспечивает функции set_constraint_handler(), abort_constraint_handler() и ignore_constraint_handler().

Есть комментарии Павла Минаева о том, что реализация Microsoft не соответствует предложению TR24731 («Технический отчет типа 2»), поэтому вы не сможете вмешаться или вам придется сделать что-то другое из того, что указывает TR, должно быть сделано. Для этого внимательно изучите MSDN.

0 голосов
/ 01 октября 2009

Похоже, вы пишете в MSVC какого-то рода?

Я думаю, что документы MSDN для sprintf_s говорят, что он утверждает, что умирает, поэтому я не слишком уверен, если вы можете программно поймать это.

Как предположил Л.Бушкин, вам гораздо лучше использовать классы, управляющие строками.

0 голосов
/ 01 октября 2009

Из MSDN:

Другое основное отличие sprintf_s от sprintf состоит в том, что sprintf_s принимает параметр длины, определяющий размер выходного буфера в символах. Если буфер слишком мал для печатаемого текста, то для буфера устанавливается пустая строка и вызывается недопустимый обработчик параметров. В отличие от snprintf, sprintf_s гарантирует, что буфер будет нулевым (если размер буфера не равен нулю).

Так что в идеале то, что вы написали, должно работать правильно.

...