Когда я пытаюсь скомпилировать код C, который использует gets()
функцию с GCC, я получаю это предупреждение:
(.text + 0x34): предупреждение: функция `gets 'опасна и не должна использоваться.
Я помню, что это как-то связано с защитой стека и безопасностью, но я точно не знаю, почему.
Как я могу удалить это предупреждение и почему есть такое предупреждение об использовании gets()
?
Если gets()
это так опасно, то почему мы не можем удалить это?
c
fgets
buffer-overflow
gets
Винит Дхатрак
источник
источник
gets()
Buffer_overflow_attackgets()
Ответы:
Для
gets
безопасного использования вы должны точно знать, сколько символов вы будете читать, чтобы вы могли сделать свой буфер достаточно большим. Вы будете знать это только в том случае, если точно знаете, какие данные вы будете читать.Вместо того
gets
, чтобы использовать , вы хотите использоватьfgets
, который имеет подпись(
fgets
если он читает всю строку, оставит'\n'
в строке; вам придется иметь дело с этим.)Он оставался официальной частью языка до стандарта ISO C 1999 года, но был официально удален стандартом 2011 года. Большинство реализаций C по-прежнему поддерживают его, но по крайней мере gcc выдает предупреждение для любого кода, который его использует.
источник
gets()
который заставляет компилятор выдавать предупреждение при использовании.Почему
gets()
опасноПервый интернет-червь ( Morris Internet Worm ) сбежал около 30 лет назад (1988-11-02), и он использовал
gets()
и переполнение буфера в качестве одного из своих методов распространения от системы к системе. Основная проблема заключается в том, что функция не знает, насколько большой буфер, поэтому она продолжает чтение, пока не найдет новую строку или не встретит EOF, и может переполнить границы заданного буфера.Вы должны забыть, что когда-либо слышали о
gets()
существовании.Стандарт C11 ISO / IEC 9899: 2011 исключен
gets()
как стандартная функция, которая называется «Good Thing ™» (она была официально помечена как «устаревшая» и «устарела» в ISO / IEC 9899: 1999 / Cor.3: 2007 - Техническое исправление 3 для C99, а затем удалены в C11). К сожалению, из-за обратной совместимости он будет храниться в библиотеках в течение многих лет (что означает «десятилетия»). Если бы это было до меня, реализацияgets()
стала бы:Учитывая, что ваш код в любом случае рухнет, рано или поздно лучше решить проблему раньше, чем позже. Я был бы готов добавить сообщение об ошибке:
Современные версии системы компиляции Linux генерируют предупреждения, если вы ссылаетесь,
gets()
а также для некоторых других функций, которые также имеют проблемы с безопасностью (mktemp()
,…).Альтернативы
gets()
fgets ()
Как и все остальные, канонической альтернативой
gets()
являетсяfgets()
указаниеstdin
потока файлов.Что еще никто не упомянул, так это то, что
gets()
он не включает перевод строки, ноfgets()
включает. Таким образом, вам может понадобиться обертка,fgets()
которая удаляет символ новой строки:Или лучше:
Кроме того, как отмечает caf в комментарии, а paxdiablo показывает в своем ответе,
fgets()
вы можете оставить данные в строке. Мой код оболочки оставляет эти данные для чтения в следующий раз; вы можете легко изменить его, чтобы поглотить остальную часть строки данных, если вы предпочитаете:Остаточная проблема заключается в том, как сообщать о трех различных состояниях результата - EOF или ошибке, чтение строки не усечено, а частично прочитано, но данные были усечены.
Эта проблема не возникает
gets()
из-за того, что она не знает, где заканчивается ваш буфер, и весело растоптывает за ее пределами, нанося ущерб вашей красиво улаженной структуре памяти, часто испорчивая возвратный стек ( переполнение стека ), если буфер выделяется на стек, или попирание управляющей информации, если буфер выделен динамически, или копирование данных по другим ценным глобальным (или модульным) переменным, если буфер статически распределен. Ничто из этого не является хорошей идеей - они воплощают фразу «неопределенное поведение».Существует также TR 24731-1 (Технический отчет от Стандартного комитета C), который предоставляет более безопасные альтернативы различным функциям, включая
gets()
:Компиляторы Microsoft Visual Studio реализуют приближение к стандарту TR 24731-1, но существуют различия между сигнатурами, реализованными Microsoft, и сигнатурами в TR.
Стандарт C11, ISO / IEC 9899-2011, включает TR24731 в Приложении K в качестве необязательной части библиотеки. К сожалению, он редко реализуется в Unix-подобных системах.
getline()
- POSIXPOSIX 2008 также предоставляет безопасную альтернативу
gets()
вызываемымgetline()
. Он динамически распределяет место для линии, поэтому вам, в конечном итоге, придется ее освободить. Таким образом, он снимает ограничение на длину строки. Он также возвращает длину данных, которые были прочитаны, или-1
(и неEOF
!), Что означает, что нулевые байты на входе могут быть надежно обработаны. Существует также вариант «выберите свой собственный односимвольный разделитель»getdelim()
; это может быть полезно, если вы имеете дело с выводом, изfind -print0
которого, например, концы имен файлов помечены'\0'
символом ASCII NUL .источник
fgets()
вашаfgets_wrapper()
версия оставит завершающую часть слишком длинной строки во входном буфере для чтения следующей входной функцией. Во многих случаях вы захотите прочитать и выбросить эти символы.getline()
и его родственникgetdelim()
, которые возвращают длину «строки», читаемой командами, выделяя пространство, необходимое для сохранения всей строки. Даже это может вызвать проблемы, если в итоге вы получите однострочный файл JSON размером несколько гигабайт; Вы можете позволить себе всю эту память? (И пока мы на этом, можем ли мы иметьstrcpy()
иstrcat()
варианты, которые возвращают указатель на нулевой байт в конце? И т.д.)fgets()
том, что если файл содержит нулевой байт, вы не можете сказать, сколько данных есть после нулевого байта до конца строки (или EOF).strlen()
может сообщать только до нулевого байта в данных; после этого это догадки и, следовательно, почти наверняка неправильно.gets()
существовало». Когда я делаю это, я снова сталкиваюсь с этим и возвращаюсь сюда. Вы взламываете stackoverflow для получения голосов?Потому что
gets
не делает каких - либо проверки при получении байт из стандартного ввода и положить их куда - то. Простой пример:Теперь, во-первых, вы можете ввести, сколько символов вы хотите,
gets
не будет заботиться об этом. Во-вторых, байты по размеру массива, в который вы их помещаете (в данном случаеarray1
), будут перезаписывать все, что они находят в памяти, потому чтоgets
будут записывать их. В предыдущем примере это означает, что если вы введете,"abcdefghijklmnopqrts"
возможно, непредсказуемо, он будет также перезаписанarray2
или что-то еще.Эта функция небезопасна, поскольку предполагает согласованный ввод. НИКОГДА НЕ ИСПОЛЬЗУЙТЕ ЕГО!
источник
gets
его непригодным для использования, так это то, что он не имеет параметра длины / числа массива, который он принимает; если бы это было там, это была бы просто другая обычная функция языка C.gets
, и почему ни один стандартный вариант fgets не был сделан таким удобным для случаев использования, когда перевод строки не желателен как часть ввода?gets
, как следует из названия, был разработан для получения строкиstdin
, однако причина отсутствия параметра размера могла быть в духе C : Доверьтесь программисту. Эта функция была удалена в C11, и данная заменаgets_s
принимает размер входного буфера. Я понятия не имею оfgets
части, хотя.gets
может быть оправдан, был бы, если бы кто-то использовал систему ввода-вывода с аппаратной буферизацией, которая физически была неспособна передать строку определенной длины и предполагаемый срок жизни программы. был короче, чем срок службы оборудования. В этом случае, если аппаратное обеспечение не способно передавать строки длиной более 127 байт, это может быть оправданоgets
в 128-байтовом буфере, хотя я думаю, что преимущества возможности указать более короткий буфер при ожидании меньшего ввода более чем оправдывают Стоимость.gets
иstrcat
безопасно принимать столько, сколько потребуется.Не следует использовать,
gets
поскольку он не может остановить переполнение буфера. Если пользователь вводит больше данных, чем может поместиться в вашем буфере, вы, скорее всего, в конечном итоге будете повреждены или хуже.Фактически, ISO фактически предприняли шаг к удалению
gets
из стандарта C (начиная с C11, хотя он был признан устаревшим в C99), который, учитывая, насколько высоко они оценивают обратную совместимость, должен указывать на то, насколько плохой была эта функция.Правильнее всего использовать
fgets
функцию сstdin
дескриптором файла, поскольку вы можете ограничить количество символов, читаемых пользователем.Но это также имеет свои проблемы, такие как:
С этой целью почти каждый кодер C в какой-то момент своей карьеры также напишет более полезную оболочку
fgets
. Вот мой:с некоторым тестовым кодом:
Он обеспечивает ту же защиту, что и
fgets
в случае предотвращения переполнения буфера, но также уведомляет вызывающую сторону о том, что произошло, и удаляет лишние символы, чтобы они не влияли на вашу следующую операцию ввода.Не стесняйтесь использовать его по своему усмотрению, поэтому я выпускаю его под лицензией «делай, что хочешь, черт побери» :-)
источник
gets()
ни в разделе 7.19.7.7, где он определен, ни в разделе 7.26.9 Будущие указания библиотеки и подраздел для<stdio.h>
. Нет даже сноски, что это опасно. (Сказав это, я вижу «Это не рекомендуется в ISO / IEC 9899: 1999 / Cor.3: 2007 (E))» в ответ по Ю. Хао ) Но C11 же удалить его от стандартного - и не раньше времени.!int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)
скрываетsize_t
вint
преобразованииsz
.sz > INT_MAX || sz < 2
поймает странные значенияsz
.if (buff[strlen(buff)-1] != '\n') {
это хакерский эксплойт, поскольку первый введенный символ злого пользователя может быть встроенным нулевым символом, отображающимbuff[strlen(buff)-1]
UB.while (((ch = getchar())...
имеет проблемы, если пользователь вводит нулевой символ.fgets .
Читать со стандартного ввода:
источник
Вы не можете удалить функции API, не нарушая API. При желании многие приложения больше не будут компилироваться или запускаться вообще.
Это причина того, что одна ссылка дает:
источник
Недавно я прочитал в посте USENET
comp.lang.c
, которыйgets()
удаляется из Стандарта. WooHooисточник
gcc -std=c2012 -pedantic ...
помощью get (), вы не получите. (Я только что составил-std
параметр)В C11 (ISO / IEC 9899: 201x)
gets()
был удален. (Это устарело в ISO / IEC 9899: 1999 / Cor.3: 2007 (E))В дополнение к
fgets()
, C11 представляет новую безопасную альтернативуgets_s()
:Тем не менее, в разделе Рекомендуемая практика ,
fgets()
все еще является предпочтительным.источник
gets()
опасно, потому что пользователь может завершить работу программы, набрав слишком много в приглашении. Он не может определить конец доступной памяти, поэтому, если вы выделите слишком мало памяти для этой цели, это может вызвать сбой сегмента и сбой. Иногда кажется маловероятным, чтобы пользователь набрал 1000 букв в приглашении, предназначенном для имени человека, но, как программисты, мы должны сделать наши программы пуленепробиваемыми. (это также может представлять угрозу безопасности, если пользователь может вывести из строя системную программу, отправив слишком много данных).fgets()
позволяет указать, сколько символов вынимается из стандартного входного буфера, чтобы они не переполняли переменную.источник
Я хотел бы направить искреннее приглашение всем сопровождающим библиотеки C, которые все еще включены
gets
в их библиотеки «на тот случай, если кто-то все еще зависит от этого»: пожалуйста, замените вашу реализацию эквивалентомЭто поможет убедиться, что никто не зависит от этого. Спасибо.
источник
Функция C get опасна и была очень дорогой ошибкой. Тони Хоар особо отмечает это в своем выступлении «Нулевые ссылки: ошибка в миллиард долларов»:
http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
Целый час стоит посмотреть, но за просмотр его комментариев от 30 минут с конкретной критикой около 39 минут.
Надеемся, что это подстегивает ваш аппетит ко всему выступлению, что привлекает внимание к тому, как нам нужны более формальные доказательства правильности в языках и как виноваты языковые разработчики, а не программист. Похоже, что это была весьма сомнительная причина, по которой разработчики плохих языков возлагали вину на программистов под маской «свободы программиста».
источник