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

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

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

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

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

Я также вспоминаю отчет спекулятивное письмо в 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' />rn", id);

чтобы заменить это.

3 ответов


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

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

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

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

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


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

но, если вы пытаетесь объединить 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>");

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