Почему strlcpy и strlcat считаются небезопасными? - PullRequest
73 голосов
/ 22 января 2010

Я понимаю, что strlcpy и strlcat были разработаны как безопасные замены для strncpy и strncat. Тем не менее, некоторые люди все еще считают, что они небезопасны, и просто вызывают проблемы другого типа .

Может ли кто-нибудь привести пример того, как использование strlcpy или strlcat (то есть функция, которая всегда null завершает свои строки) может привести к проблемам с безопасностью?

Ульрих Дреппер и Джеймс Антилл утверждают, что это правда, но никогда не приводят примеров и не проясняют этот момент.

Ответы [ 7 ]

101 голосов
/ 22 января 2010

Во-первых, strlcpy никогда не предназначался как безопасная версия strncpystrncpy никогда не предназначался как безопасная версия strcpy). Эти две функции совершенно не связаны. strncpy - это функция, которая вообще не имеет отношения к C-строкам (то есть строки с нулевым символом в конце). Тот факт, что он имеет префикс str... в названии, является просто исторической ошибкой. История и цель strncpy хорошо известны и хорошо документированы. Это функция, созданная для работы с так называемыми строками «фиксированной ширины» (не с C-строками), используемой в некоторых исторических версиях файловой системы Unix. Некоторые программисты сегодня смущаются из-за его названия и считают, что strncpy как-то должно служить функцией копирования C-строк ограниченной длины («безопасный» брат strcpy), что на самом деле полная чушь и приводит к плохому практика программирования. Стандартная библиотека C в ее текущей форме не имеет функции для копирования C-строк ограниченной длины. Вот где подходит strlcpy. strlcpy - это действительно функция копирования ограниченной длины, созданная для работы с C-строками. strlcpy правильно делает все, что должна делать функция копирования ограниченной длины. Единственная критика, к которой можно стремиться, это то, что она, к сожалению, не стандартна.

Во-вторых, strncat, с другой стороны, действительно является функцией, которая работает с C-строками и выполняет конкатенацию ограниченной длины (это действительно «безопасный» брат strcat). Чтобы правильно использовать эту функцию, программист должен проявить особую осторожность, так как параметр размера, который принимает эта функция, на самом деле не размер буфера, который получает результат, а размер его оставшейся части (также, символ-терминатор). считается неявно). Это может сбивать с толку, поскольку для того, чтобы привязать этот размер к размеру буфера, программист должен не забыть выполнить некоторые дополнительные вычисления, которые часто используются для критики strncat. strlcat заботится об этих проблемах, изменяя интерфейс так, чтобы не требовалось никаких дополнительных вычислений (по крайней мере, в вызывающем коде). Опять же, единственное основание, на котором я вижу, можно критиковать, это то, что функция не является стандартной. Кроме того, функции из группы strcat - это то, что вы не увидите в профессиональном коде очень часто из-за ограниченного удобства использования самой идеи конкатенации строк на основе повторного сканирования.

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

30 голосов
/ 22 января 2010

Критика Ульриха основана на идее, что усечение строки, которое не обнаруживается программой, может привести к проблемам безопасности из-за неверной логики. Поэтому, чтобы быть в безопасности, вам необходимо проверить усечение. Сделать это для конкатенации строк означает, что вы делаете проверку в соответствии с этим:

if (destlen + sourcelen > dest_maxlen)
{
    /* Bug out */
}

Теперь strlcat действительно выполняет эту проверку, если программист не забывает проверять результат - так что вы можете безопасно его использовать:

if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen)
{
    /* Bug out */
}

Смысл Ульриха в том, что, поскольку вы должны иметь destlen и sourcelen вокруг (или пересчитывать их, как это делает strlcat эффективно), вы можете в любом случае использовать более эффективный memcpy: 1014 *

if (destlen + sourcelen > dest_maxlen)
{
    goto error_out;
}
memcpy(dest + destlen, source, sourcelen + 1);
destlen += sourcelen;

(В приведенном выше коде dest_maxlen - это максимальная длина строки, которая может быть сохранена в dest - на единицу меньше, чем размер буфера dest. dest_bufferlen - это полный размер dest buffer).

20 голосов
/ 22 января 2010

Когда люди говорят: «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. Это делает его даже менее полезным, чем в противном случае.

11 голосов
/ 22 января 2010

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

7 голосов
/ 22 января 2010

Есть две «проблемы», связанные с использованием функций strl:

  1. Вы должны проверить возвращаемые значения чтобы избежать усечения.

Разработчики черновиков стандарта c1x и Drepper утверждают, что программисты не будут проверять возвращаемое значение. Drepper говорит, что мы должны как-то знать длину и использовать memcpy и вообще избегать строковых функций. Комитет по стандартам утверждает, что secure strcpy должен возвращать ненулевое значение при усечении, если иное не указано флагом _TRUNCATE. Идея состоит в том, что люди чаще используют if (strncpy_s (...)).

  1. Не может использоваться на нестроковых.

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

Преимуществом предлагаемых стандартных функций является то, что вы можете узнать, сколько данных вы пропустили с помощью функций strl .

3 голосов
/ 26 июня 2016

Я не думаю, что strlcpy и strlcat считаются небезопасными или, по крайней мере, это не причина, по которой они не включены в glibc - в конце концов, glibc включает в себя strncpy и дажеstrcpy.

Они получили критику за то, что они якобы неэффективны, а не небезопасны .

Согласно статье Secure Portability Дэмиена Миллера:

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 и других дистрибутивах.Вы также можете просто взять копию и использовать ее в своем проекте - она ​​короткая и под разрешающей лицензией:

0 голосов
/ 21 марта 2019

Безопасность не является логическим значением. Функции C не являются полностью «безопасными» или «небезопасными», «безопасными» или «небезопасными». При неправильном использовании простая операция присваивания в C может быть «небезопасной». strlcpy () и strlcat () могут использоваться безопасно (надежно) так же, как strcpy () и strcat () могут использоваться безопасно, когда программист предоставляет необходимые гарантии правильного использования.

Основной смысл всех этих функций строки C, стандартных и нестандартных, - это уровень, до которого они делают безопасное / безопасное использование easy . strcpy () и strcat () не являются тривиальными для безопасного использования; это подтверждается количеством раз, когда программисты на С ошибались в течение многих лет, и за этим последовали неприятные уязвимости и эксплойты. strlcpy () и strlcat (), и в этом отношении strncpy () и strncat (), strncpy_s () и strncat_s (), bit проще в использовании, но все же нетривиально. Они небезопасны / небезопасны? Не более, чем memcpy () при неправильном использовании.

...