Является ли sprintf(буфер, "%s […]", буфер, […]) безопасным?

Я видел использование этого шаблона для объединения строк в некоторый код, над которым я работал:

sprintf(buffer, "%s <input type='file' name='%s' />\r\n", buffer, id);
sprintf(buffer, "%s</td>", buffer);

и я вполне уверен, что это не безопасно C. Вы заметите, что buffer это и выход, и первый вход.

Помимо очевидной возможности переполнения буфера, я полагаю, что нет гарантии, что буфер не будет изменен между началом и концом функции (т. Е. Нет никакой гарантии относительно состояния буфера во время выполнение функции). Подпись sprintf дополнительно указывает, что целевая строка restrictредактор

Я также вспоминаю сообщение о спекулятивной записи в memcpy и не вижу причин, по которым какая-то библиотека C могла бы делать то же самое в sprintf. В этом случае, конечно, он будет писать его источнику. Так это поведение безопасно?

К вашему сведению, я предложил:

char *bufEnd = buffer + strlen(buffer);
/* sprintf returns the number of f'd and print'd into the s */
bufEnd += sprintf(bufEnd, " <input type='file' name='%s' />\r\n", id);

заменить это.

3 ответа

Решение

Из документации по glibc sprintf():

Поведение этой функции не определено, если копирование происходит между перекрывающимися объектами, например, если s также задано в качестве аргумента для печати под управлением преобразования "%s".

Это может быть безопасно в конкретной реализации; но вы не могли рассчитывать на то, что это портативное устройство.

Я не уверен, что ваше предложение будет безопасным во всех случаях. Вы все еще можете быть перекрывающимися буферами. Уже поздно, и моя жена глючит со мной, но я думаю, что у вас все еще может быть случай, когда вы захотите снова использовать исходную строку в объединенной строке и перезаписать нулевой символ, так что реализация sprintf может не знать, где повторно используется Строка заканчивается.

Возможно, вы просто захотите прикрепить snprint() к временному буферу, а затем strncat() в исходный буфер.

В этом конкретном случае это будет работать, потому что строка в buffer будет первым, что собирается войти в buffer (опять же, бесполезно), поэтому вы должны использовать strcat() вместо этого, чтобы получить [почти] тот же эффект.

Но если вы пытаетесь объединить strcat() с формирующими возможностями sprintf(), вы можете попробовать это:

sprintf(&buffer[strlen(buffer)], " <input type='file' name='%s' />\r\n", id);

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

int i = strlen(buffer);
i += sprintf(&buffer[i], " <input type='file' name='%s' />\r\n", id);
i += sprintf(&buffer[i], "</td>");

или же:

int i = strlen(buffer);
i += sprintf(&buffer[i], " <input type='file' name='%s' />\r\n", id);
strcat(&buffer[i], "</td>");

И до того, как люди начинают сходить с ума по этому поводу ("Это небезопасно! Вы можете переполнить буфер!"), Я просто обращаюсь к разумному способу создания отформатированной строки в C/C++.

Другие вопросы по тегам