Почему strlcpy и strlcat считаются небезопасными?
Я это понимаю strlcpy
а также strlcat
были разработаны как безопасные замены для strncpy
а также strncat
, Тем не менее, некоторые люди по-прежнему считают, что они небезопасны, и просто вызывают проблемы другого типа.
Может кто-нибудь привести пример использования strlcpy
или же strlcat
(т. е. функция, которая всегда завершает свои строки нулем) может привести к проблемам с безопасностью?
Ульрих Дреппер и Джеймс Антилл утверждают, что это правда, но никогда не приводят примеров и не проясняют этот момент.
8 ответов
Во-первых, strlcpy
никогда не задумывался как безопасная версия strncpy
(а также strncpy
никогда не задумывался как безопасная версия strcpy
). Эти две функции совершенно не связаны. strncpy
это функция, которая вообще не имеет отношения к C-строкам (то есть к строкам с нулевым символом в конце). Тот факт, что он имеет str...
Приставка в его названии просто историческая ошибка. История и цель strncpy
хорошо известен и хорошо документирован. Это функция, созданная для работы с так называемыми строками "фиксированной ширины" (не с C-строками), используемой в некоторых исторических версиях файловой системы Unix. Некоторые программисты сегодня путаются с его именем и считают, что strncpy
каким-то образом должен выполнять функцию копирования C-строки ограниченной длины ("безопасный" брат strcpy
), что в действительности является полной чушью и ведет к плохой практике программирования. Стандартная библиотека C в ее текущей форме не имеет функции для копирования C-строк ограниченной длины. Это где strlcpy
подходит. strlcpy
это действительно функция копирования ограниченной длины, созданная для работы с C-строками. strlcpy
правильно делает все, что должна делать функция копирования ограниченной длины. Единственная критика, к которой можно стремиться, это то, что она, к сожалению, не является стандартной.
Во-вторых, strncat
с другой стороны, это действительно функция, которая работает с C-строками и выполняет конкатенацию ограниченной длины (это действительно "безопасный" брат strcat
). Чтобы правильно использовать эту функцию, программист должен проявить особую осторожность, так как параметр размера, который принимает эта функция, на самом деле является не размером буфера, который получает результат, а размером его оставшейся части (также символом-терминатором). считается неявно). Это может сбивать с толку, поскольку для того, чтобы привязать этот размер к размеру буфера, программист должен помнить о необходимости выполнения некоторых дополнительных вычислений, которые часто используются для критики strncat
, strlcat
заботится об этих проблемах, изменяя интерфейс так, чтобы не требовались дополнительные вычисления (по крайней мере, в вызывающем коде). Опять же, единственное основание, на котором я вижу, можно критиковать, это то, что функция не является стандартной. Кроме того, функции от strcat
group - это то, что вы не увидите в профессиональном коде очень часто из-за ограниченного удобства использования самой идеи конкатенации строк на основе повторного сканирования.
Что касается того, как эти функции могут привести к проблемам с безопасностью... Они просто не могут. Они не могут привести к проблемам безопасности в большей степени, чем сам язык C может "привести к проблемам безопасности". Видите ли, в течение долгого времени существовало сильное мнение, что язык C++ должен двигаться в направлении превращения в какой-то странный вид Java. Это чувство иногда распространяется и на область языка Си, что приводит к довольно бессмысленной и вынужденной критике особенностей языка Си и особенностей стандартной библиотеки языка Си. Я подозреваю, что мы можем иметь дело с чем-то подобным и в этом случае, хотя я, конечно, надеюсь, что все не так уж и плохо.
Критика Ульриха основана на идее, что усечение строки, которое не обнаруживается программой, может привести к проблемам безопасности из-за неверной логики. Поэтому, чтобы быть в безопасности, вам нужно проверять усечение. Сделать это для конкатенации строк означает, что вы делаете проверку в соответствии с этим:
if (destlen + sourcelen > dest_maxlen)
{
/* Bug out */
}
Сейчас, strlcat
действительно делает эту проверку, если программист помнит, чтобы проверить результат - так что вы можете использовать его безопасно:
if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen)
{
/* Bug out */
}
Суть Ульриха в том, что, поскольку вы должны иметь destlen
а также sourcelen
вокруг (или пересчитать их, что к чему strlcat
эффективно), вы могли бы просто использовать более эффективный memcpy
тем не мение:
if (destlen + sourcelen > dest_maxlen)
{
goto error_out;
}
memcpy(dest + destlen, source, sourcelen + 1);
destlen += sourcelen;
(В приведенном выше коде dest_maxlen
максимальная длина строки, которая может быть сохранена в dest
- на один размер меньше dest
буфер. dest_bufferlen
это полный размер dest buffer
).
Когда люди говорят:strcpy()
опасно, использовать strncpy()
вместо " (или аналогичные заявления о strcat()
и т.д., но я собираюсь использовать strcpy()
здесь, как мой фокус), они означают, что нет проверки границ в strcpy()
, Таким образом, слишком длинная строка приведет к переполнению буфера. Они верны. С помощью strncpy()
в этом случае будут предотвращены переполнения буфера.
я так чувствую strncpy()
на самом деле не исправляет ошибки: это решает проблему, которую хороший программист может легко избежать.
Как программист C, вы должны знать размер места назначения, прежде чем пытаться копировать строки. Это предположение в strncpy()
а также strlcpy()
Последние параметры тоже: вы предоставляете им этот размер. Вы также можете узнать исходный размер, прежде чем копировать строки. Тогда, если пункт назначения недостаточно велик, не звонитеstrcpy()
, Или перераспределите буфер, или сделайте что-нибудь еще.
Почему я не люблю strncpy()
?
strncpy()
В большинстве случаев это плохое решение: ваша строка будет обрезана без какого-либо уведомления - я бы лучше написал дополнительный код, чтобы самому это выяснить, а затем предпринял действия, которые я хочу предпринять, вместо того, чтобы позволить какой-то функции решить мне о том, что делать.strncpy()
очень неэффективно. Он записывает каждый байт в буфере назначения. Вам не нужны эти тысячи'\0'
в конце вашего пункта назначения.- Он не пишет завершающий
'\0'
если пункт назначения недостаточно велик. Так что, вы все равно должны сделать это сами. Сложность этого не стоит.
Теперь мы приходим к strlcpy()
, Изменения от strncpy()
сделать это лучше, но я не уверен, что конкретное поведение strl*
гарантирует их существование: они слишком конкретны. Вы все еще должны знать размер пункта назначения. Это более эффективно, чем strncpy()
потому что он не обязательно записывает каждый байт в месте назначения. Но это решает проблему, которую можно решить, выполнив: *((char *)mempcpy(dst, src, n)) = 0;
,
Я не думаю, что кто-то говорит, что strlcpy()
или же strlcat()
может привести к проблемам с безопасностью, то, что они (и я) говорят о том, что они могут привести к ошибкам, например, когда вы ожидаете, что будет написана полная строка вместо ее части.
Основной вопрос здесь: сколько байт для копирования? Программист должен знать это, а если нет, strncpy()
или же strlcpy()
не спасет его.
strlcpy()
а также strlcat()
не являются стандартными, ни ISO C, ни POSIX. Таким образом, их использование в переносимых программах невозможно. По факту, strlcat()
имеет два разных варианта: реализация Solaris отличается от других для крайних случаев, имеющих длину 0. Это делает его даже менее полезным, чем в противном случае.
Я думаю, что Ульрих и другие думают, что это даст ложное чувство безопасности. Случайное усечение строк может иметь последствия для безопасности для других частей кода (например, если путь к файловой системе усекается, программа может не выполнять операции с намеченным файлом).
Есть две "проблемы", связанные с использованием функций strl:
- Вы должны проверить возвращаемые значения, чтобы избежать усечения.
Авторы черновика стандарта c1x и Drepper утверждают, что программисты не будут проверять возвращаемое значение. Drepper говорит, что мы должны как-то знать длину и использовать memcpy и вообще избегать строковых функций. Комитет по стандартам утверждает, что secure strcpy должен возвращать ненулевое значение при усечении, если иное не установлено _TRUNCATE
флаг. Идея состоит в том, что люди чаще используют if(strncpy_s(...)).
- Не может использоваться на нестроковых.
Некоторые люди думают, что строковые функции никогда не должны завершаться сбоем, даже если они являются поддельными. Это влияет на стандартные функции, такие как strlen, которые в нормальных условиях перестанут работать. Новый стандарт будет включать в себя множество таких функций. У проверок, конечно, есть потеря производительности.
Преимуществом предлагаемых стандартных функций является то, что вы можете знать, сколько данных вы пропустили с помощью функций strl.
Я не думаю strlcpy
а также strlcat
они считаются небезопасными или, по крайней мере, это не причина, по которой они не включены в glibc - ведь glibc включает в себя strncpy и даже strcpy.
Их критика заключалась в том, что они якобы неэффективны, а не небезопасны.
Согласно статье " Безопасная переносимость " Дэмиена Миллера:
API-интерфейсы strlcpy и strlcat правильно проверяют границы целевого буфера, во всех случаях nul-terminate и возвращают длину исходной строки, что позволяет обнаруживать усечение. Этот API был принят большинством современных операционных систем и многими автономными программными пакетами, включая OpenBSD (там, где он возник), Sun Solaris, FreeBSD, NetBSD, ядро Linux, rsync и проект GNOME. Заметным исключением является стандартная библиотека C GNU, glibc [12], сопровождающий которой категорически отказывается включать эти улучшенные API, помечая их как "ужасно неэффективное дерьмо BSD" [4], несмотря на то, что в большинстве случаев они были быстрее, чем API они заменяют [13]. В результате более 100 пакетов программного обеспечения, представленных в дереве портов OpenBSD, поддерживают свои собственные замены strlcpy и / или strlcat или эквивалентные API - не идеальное положение дел.
Вот почему они не доступны в glibc, но это не правда, что они не доступны в Linux. Они доступны в Linux в libbsd:
Они упакованы в Debian, Ubuntu и другие дистрибутивы. Вы также можете просто взять копию и использовать ее в своем проекте - она короткая и под разрешающей лицензией:
Безопасность не является логическим. Функции C не являются полностью "безопасными" или "небезопасными", "безопасными" или "небезопасными". При неправильном использовании простая операция присваивания в C может быть "небезопасной". strlcpy() и strlcat () можно использовать безопасно (надежно) так же, как strcpy () и strcat () можно безопасно использовать, если программист предоставляет необходимые гарантии правильного использования.
Основным моментом всех этих строковых функций C, стандартных и нестандартных, является уровень, до которого они упрощают безопасное / надежное использование. Безопасное использование strcpy () и strcat () нетривиально; это подтверждается количеством раз, когда программисты C ошибались за эти годы, и в результате возникали опасные уязвимости и эксплойты. strlcpy () и strlcat () и, в этом отношении, strncpy () и strncat(), strncpy_s() и strncat_s() немного проще в использовании, но все же нетривиально. Они небезопасны / небезопасны? Не более чем memcpy () при неправильном использовании.
strlcpy
может вызвать
SIGSEGV
, если
src
не является
NUL
-завершено.
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */