Является ли использование устаревшего компилятора C угрозой безопасности?


140

У нас в производстве есть несколько систем сборки, о которых никто не заботится, и на этих машинах работают старые версии GCC, такие как GCC 3 или GCC 2.

И я не могу уговорить руководство обновить его до более свежего: мол, «если не сломалось, не чини».

Поскольку мы поддерживаем очень старую кодовую базу (написанную в 80-х), этот код C89 отлично компилируется на этих компиляторах.

Но я не уверен, что использовать эти старые вещи - хорошая идея.

У меня вопрос:

Может ли использование старого компилятора C поставить под угрозу безопасность скомпилированной программы?

ОБНОВИТЬ:

Тот же код создается Visual Studio 2008 для целей Windows, а MSVC еще не поддерживает C99 или C11 (я не знаю, поддерживает ли более новый MSVC), и я могу собрать его на своем компьютере с Linux, используя последнюю версию GCC. Так что, если мы просто добавим новый GCC, он, вероятно, будет работать так же хорошо, как и раньше.


5
Интересный вопрос - его тоже стоит быстро прочитать - developers.slashdot.org/story/13/10/29/2150211/… .. так что новые компиляторы также могут поставить под угрозу безопасность при оптимизации.
Нил

6
Поддерживают ли эти старые версии gcc компиляцию в PIC / PIE для ASLR? Поддерживают ли они стековые канарейки? W ^ X (NX)? Если нет, то отсутствие средств защиты от уязвимостей - хорошая причина для обновления.
EOF

12
Простой просмотр предупреждений от gcc 4.x может сразу выявить целый ряд существующих дыр в безопасности, о которых вы не знали.
OrangeDog

7
@OrangeDog: Почему именно gcc 4.x? gcc6 - это текущая серия выпусков, а gcc 5 существует уже некоторое время. Но да, исправление любых проблем, выявленных с -O3 -Wall -Wextra -fsanitize=undefinedпомощью современных gcc и clang, должно помочь.
Питер Кордес

4
@OrangeDog GCC перешел на маркетинговые номера версий. GCC 5 заслужил резкое повышение версии, потому что они изменили стандарты C и C ++ по умолчанию и libstdc ++ ABI. GCC 6 должен был называться 5.1.
zwol

Ответы:


103

На самом деле я бы сказал наоборот.

Есть ряд случаев, когда поведение не определено стандартом C, но когда очевидно, что произойдет с «глупым компилятором» на данной платформе. Например, разрешение переполнения знакового целого числа или доступ к одной и той же памяти через переменные двух разных типов.

Последние версии gcc (и clang) начали рассматривать эти случаи как возможности оптимизации, не заботясь о том, изменяют ли они поведение двоичного файла в состоянии «неопределенное поведение». Это очень плохо, если ваша кодовая база была написана людьми, которые относились к C как к «портативному ассемблеру». Со временем оптимизаторы начали обращать внимание на все большие и большие фрагменты кода при выполнении этих оптимизаций, увеличивая вероятность того, что двоичный файл в конечном итоге будет делать что-то иное, чем «то, что будет делать двоичный файл, созданный глупым компилятором».

Есть переключатели компилятора для восстановления «традиционного» поведения (-fwrapv и -fno-strict-aliasing для двух, о которых я упоминал выше), но сначала вы должны узнать о них.

Хотя в принципе ошибка компилятора может превратить совместимый код в брешь в безопасности, я считаю, что риск этого незначительный в общем плане.


13
Но этот аргумент работает в обоих направлениях. Если компилятор имеет предсказуемое "неопределенное поведение", то, возможно, будет проще использовать его злонамеренно ...
Андре

18
@Andre В любом случае скомпилированный код имеет предсказуемое неопределенное поведение. То есть, как только код был скомпилирован, любое непредсказуемое поведение теперь предсказуемо в этой конкретной скомпилированной версии.
user253751

6
people who treated C like a "portable assembler"разве это не то, что есть C?
Макс

10
@Max Этот ответ точно предупреждает о том, что понятие «переносимый ассемблер», по крайней мере, на практике устарело из-за современных оптимизаторов. И я бы сказал, что это никогда не было концептуально правильным с самого начала.
Теодорос Хатцигианнакис

7
Здесь нет сочувствия к тем, кто полагается на неопределенное поведение и позже начинает пожинать то, что посеял. Это не означает, что новые компиляторы по своей сути менее безопасны - это означает, что несовместимый код был бомбой замедленного действия. Следует соответствующим образом распределить вину.
underscore_d

52

Оба варианта действий сопряжены с риском.


У старых компиляторов есть преимущество зрелости, и все, что в них было сломано, вероятно (но нет гарантии) было успешно исправлено.

В этом случае новый компилятор - потенциальный источник новых ошибок.


С другой стороны, новые компиляторы поставляются с дополнительными инструментами :

  • GCC и Clang теперь имеют дезинфицирующие средства, которые могут использовать среду выполнения для обнаружения неопределенного поведения различного рода (Чендлер Каррут из команды Google Compiler заявил в прошлом году, что он ожидает, что они достигнут полного охвата)
  • Clang, по крайней мере, имеет усиление защиты , например, Control Flow Integrity - это обнаружение перебоев в потоке управления, есть также средства защиты от атак с разбиванием стека (путем отделения части стека потока управления от части данных) ; функции усиления обычно требуют низких накладных расходов (<1% накладных расходов ЦП)
  • Clang / LLVM также работает над libFuzzer , инструментом для создания инструментальных модульных тестов фаззинга , которые интеллектуально исследуют входное пространство тестируемой функции (путем настройки входных данных для использования еще не изученных путей выполнения)

Обработка двоичного файла с помощью дезинфицирующих средств (Address Sanitizer, Memory Sanitizer или Undefined Behavior Sanitizer) с последующим его фаззингом (например, с использованием American Fuzzy Lop ) выявила уязвимости в ряде известных программ, см., Например, эту статью LWN.net .

Эти новые инструменты и все будущие инструменты будут для вас недоступны, если вы не обновите компилятор.

Оставаясь на недостаточно мощном компиляторе, вы кладете голову в песок и скрещиваете пальцы, что уязвимости не обнаружено. Если ваш продукт - целевая аудитория, я призываю вас пересмотреть свое мнение.


Примечание: даже если вы НЕ обновляете производственный компилятор, вы все равно можете захотеть использовать новый компилятор для проверки на уязвимость; Имейте в виду, что, поскольку это разные компиляторы, гарантии уменьшаются.


1
+1 за то, что удосужился упомянуть случаи, в которых новые компиляторы могут быть более безопасными, а не нагромождать «b-но мой старый добрый UB» других ответов. это помимо многих других улучшений, которые они предлагают, которые напрямую не связаны с безопасностью, но дают еще больший стимул к тому, чтобы быть разумно современным.
underscore_d

Хотя это похоже на защиту «безопасности через безвестность»; ошибки, которые влияют на старые компиляторы, известны и общедоступны. Хотя я согласен с тем, что в новых компиляторах будут появляться ошибки, эти ошибки еще не являются общедоступными, как в предыдущих версиях, что является некоторой безопасностью, если вы часто обновляете приложение.
The6P4C

Чендлер Каррут такой милый и говорит о таких чудесных вещах. Я бы вышла за него замуж, если бы могла.
Даниэль Камил Козар

46

Ваш скомпилированный код содержит ошибки, которые могут быть использованы. Ошибки происходят из трех источников: ошибки в исходном коде, ошибки в компиляторе и библиотеках и неопределенное поведение в исходном коде, которое компилятор превращает в ошибку. (Неопределенное поведение - это ошибка, но еще не ошибка в скомпилированном коде. Например, i = i ++; в C или C ++ это ошибка, но в вашем скомпилированном коде она может увеличивать i на 1 и иметь значение Ok, или установить я в какой-то барахло и буду жучком).

Вероятно, количество ошибок в вашем скомпилированном коде невелико из-за тестирования и исправления ошибок из-за отчетов об ошибках клиентов. Так что изначально могло быть большое количество ошибок, но теперь их нет.

Если вы обновитесь до более нового компилятора, вы можете потерять ошибки, которые были внесены ошибками компилятора. Но все эти ошибки будут ошибками, которые, насколько вам известно, никто не нашел и никто не использовал. Но новый компилятор может иметь ошибки сам по себе, и, что важно, новые компиляторы имеют более сильную тенденцию превращать неопределенное поведение в ошибки в скомпилированном коде.

Так что в вашем скомпилированном коде будет много новых ошибок; все ошибки, которые могут найти и использовать хакеры. И если вы не проведете много тестирования и не оставите свой код клиентам на долгое время для поиска ошибок, он будет менее безопасным.


6
Другими словами ... нет простого способа определить, какие проблемы создает компилятор, и, переключая все, что вы делаете, вы получаете другой набор неизвестных проблем?
Джереми Като

1
@JeremyKato: ну, в некоторых случаях вы также получаете другой набор известных проблем. Я не уверен, какие известные недостатки безопасности есть в самом компиляторе, но для конкретного примера предположим, что обновление до нового компилятора означает также возможность использовать последнюю версию libc (в то время как использование старой означает невозможность для этого), тогда вы будете знать , что исправляете этот недостаток по адресуgetaddrinfo() : access.redhat.com/articles/2161461 . Этот пример на самом деле не является недостатком безопасности компилятора, но через 10+ лет обязательно появятся известные исправленные недостатки.
Стив Джессоп,

2
Хех, на самом деле этот недостаток был введен только в 2008 году, так что спрашивающий может быть в безопасности. Но я хочу сказать не об этом конкретном примере, а о том, что существуют известные ошибки, которые старая инструментальная цепочка добавит в ваш код. Итак, когда вы обновляете, действительно, вы вводите новый набор неизвестных, но это еще не все, что вы делаете . По сути, вам просто нужно угадать, «более безопасны» ли вы, оставляя известный критический недостаток, который исправляет новейшая цепочка инструментов, или принимаете неизвестные последствия, снова бросая кости на все неопределенное поведение в вашем собственном коде.
Стив Джессоп,

19

Если он не сломался, не чините его

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

Однако, если кодовая база устарела и была проведена работа по смягчению слабых мест используемого K&R C, таких как отсутствие безопасности типов, небезопасные fgets и т. Д., Взвесите вопрос « Будет ли обновлен компилятор до более современного C99? Стандарты / C11 ломают все? "

При условии, что существует четкий путь для перехода на новые стандарты C, который может вызвать побочные эффекты, может быть лучше попытаться выполнить форк старой кодовой базы, оценить ее и добавить дополнительные проверки типов, проверки работоспособности и определить, выполняется ли обновление до более новый компилятор имеет какое-либо влияние на наборы данных ввода / вывода.

Затем вы можете показать своему боссу: « Вот обновленная база кода, отремонтированная, в большей степени в соответствии с принятыми в отрасли стандартами C99 / C11 ... ».

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

РЕДАКТИРОВАТЬ

Просто откинулся на несколько минут, понял это, код, сгенерированный K&R, может работать на 16-битной платформе, есть вероятность, что обновление до более современного компилятора может действительно сломать базу кода, я думаю с точки зрения архитектуры, будет сгенерирован 32-битный код , это может иметь смешные побочные эффекты на структурах , используемых для ввода / вывода массивов данных, что является еще одним огромным фактором взвешивать тщательно.

Кроме того, поскольку OP упомянул об использовании Visual Studio 2008 для создания базы кода, использование gcc может вызвать внесение в среду либо MinGW, либо Cygwin, что может повлиять на изменение среды, если только цель не предназначена для Linux, тогда это будет стоит попробовать, возможно, придется включить дополнительные переключатели в компилятор, чтобы минимизировать шум в старой кодовой базе K&R, другая важная вещь - провести большое тестирование, чтобы убедиться, что ни одна функциональность не нарушена, может оказаться болезненным упражнением.


Тот же код создается Visual Studio 2008 для целей Windows, а MSVC еще не поддерживает C99 или C11 (я не знаю, поддерживает ли более новый MSVC), и я могу собрать его на своем компьютере с Linux, используя последнюю версию GCC. Так что, если мы просто добавим новый GCC, он, вероятно, будет работать так же хорошо, как и раньше.
Calmarius

@Calmarius, спасибо за внимание, может быть, лучше отредактировать свой вопрос, чтобы включить комментарий, это важно :) И должен был быть там; D
t0mm13b

@Calmarius отредактировал мой ответ, что я думаю по недавно обновленному вопросу.
t0mm13b

2
«Может быть запущен на 16-битной платформе, скорее всего, обновление до более современного компилятора может действительно нарушить кодовую базу, я думаю с точки зрения архитектуры, 32-битный код». Я не думаю, что вопрос заключается в переносе кода на новую, определяемую реализацией параметры.
Паскаль Куок

Согласовано. Это возможно , что исполняемая уязвимость может быть создана ошибкой компилятора. Но гораздо более вероятно, что код содержит уязвимости во время выполнения из-за таких вещей, как переполнение буфера и стека. Итак, когда вы вкладываете время в повышение безопасности этой базы кода, вам следует вкладывать его в такие вещи, как проверка длины входных строк, чтобы убедиться, что они не превышают лимитов вашей программы. Получение более нового компилятора не очень поможет. Очень поможет переписывание кода с нуля на языке с нативными объектами строк и массивов. Но ваш босс не будет за это платить.
О. Джонс

9

Может ли использование старого компилятора C поставить под угрозу безопасность скомпилированной программы?

Конечно, может, если старый компилятор содержит известные ошибки, которые, как вы знаете, могут повлиять на вашу программу.

Вопрос в том, не так ли? Чтобы знать наверняка, вам нужно будет прочитать весь журнал изменений от вашей версии до текущей даты и проверить каждую ошибку, исправленную за эти годы.

Если вы не обнаружите свидетельств ошибок компилятора, которые могли бы повлиять на вашу программу, обновление GCC просто ради этого кажется немного параноидальным. Вы должны иметь в виду, что более новые версии могут содержать новые ошибки, которые еще не обнаружены. В последнее время было внесено множество изменений с поддержкой GCC 5 и C11.

При этом код, написанный в 80-х, скорее всего, уже заполнен до краев дырами в безопасности и полагается на плохо определенное поведение, независимо от компилятора. Здесь мы говорим о предварительном стандарте C.


6
Я не думаю, что это паранойя; Думаю, ОП пытается придумывать причины, чтобы убедить своего босса. Вероятно, OP действительно хочет новый компилятор, потому что они улучшают asm (включая межфайловую оптимизацию с LTO), имеют более полезную диагностику / предупреждения и позволяют использовать современные языковые функции и синтаксис. (например, C11 stdatomic).
Питер Кордес

9

Существует угроза безопасности, когда злонамеренный разработчик может проникнуть через черный ход через ошибку компилятора. В зависимости от количества известных ошибок в используемом компиляторе бэкдор может выглядеть более или менее незаметным (в любом случае, дело в том, что код правильный, даже если он запутан, на уровне исходного кода. Анализ исходного кода и тестирование с использованием компилятор без ошибок не найдет бэкдор, потому что бэкдор не существует в этих условиях). Для получения дополнительных очков отрицания злонамеренный разработчик может также самостоятельно искать ранее неизвестные ошибки компилятора. Опять же, качество маскировки будет зависеть от выбора найденных ошибок компилятора.

Эта атака проиллюстрирована на программе sudo в этой статье . bcrypt написал отличное продолжение минификаторов Javascript .

Помимо этого беспокойства, эволюция компиляторов была использовать неопределенное поведение более и более и более агрессивно, так что старый C код , который был написан в духе доброй воли будет на самом деле более безопасным скомпилирован с компилятором от времени, или составленного на -O0 (но некоторые новые программные оптимизации использования UB вводятся в новых версиях компиляторов даже при -O0 ).


7

Старые компиляторы могут не иметь защиты от известных хакерских атак. Например, защита от разрушения стека не была представлена до GCC 4.1 . Так что да, код, скомпилированный с помощью старых компиляторов, может быть уязвим в том смысле, от которого защищают новые компиляторы.


6

Еще один аспект, о котором нужно беспокоиться, - это разработка нового кода .

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

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


5

Неа

Причина проста: в старом компиляторе могут быть старые ошибки и эксплойты, но в новом компиляторе будут новые ошибки и эксплойты.

Вы не «исправляете» какие-либо ошибки, обновляя компилятор до нового. Вы переключаете старые ошибки и эксплойты на новые ошибки и эксплойты.


3
Это кажется очень упрощенным: у нового компилятора могут быть свои слабые места, но я ожидаю, что их будет меньше, чем в старом компиляторе, и он, вероятно, обнаружит несколько уязвимостей кода, которые стали известны с тех пор.
PJTraill 02

Но у нового компилятора могут быть неизвестные новые слабые места. Сам по себе компилятор не представляет угрозы безопасности, требующей обновления. Вы не уменьшаете свою площадь. Вы торгуете известным набором проблем за неизвестный набор.
coteyr 01

Инструменты, помогающие находить ошибки, значительно улучшились с первых дней существования GCC, и эти инструменты (статический анализ, инструментальный динамический анализ / дезинфекция кода, фаззеры и т. Д.) Также были применены к коду компилятора, чтобы помочь улучшить качество. В эпоху GCC 2 было намного сложнее найти все классы ошибок. Сравнение ошибок компилятора по версиям - см. Стр. 7: cs.utah.edu/~regehr/papers/pldi11-preprint.pdf GCC 4.5 и LLVM 2.8 (последний на момент публикации) содержат наименьшее количество ошибок от фаззинга.
Jetski S-type

2

Что ж, существует более высокая вероятность того, что любые ошибки в старом компиляторе хорошо известны и задокументированы, чем при использовании нового компилятора, поэтому можно предпринять действия, чтобы избежать этих ошибок, кодируя их. Так что этого недостаточно в качестве аргумента для обновления. У нас те же обсуждения, где я работаю, мы используем GCC 4.6.1 на базе кода для встраиваемого программного обеспечения, и существует большое нежелание (среди руководства) обновляться до последней версии компилятора из-за опасений новых, недокументированных ошибок.


0

Ваш вопрос состоит из двух частей:

  • Явно: «Есть ли больший риск при использовании старого компилятора» (более или менее, как в вашем заголовке)
  • Неявно: «Как я могу убедить руководство перейти на новую версию»

Возможно, вы сможете ответить на оба вопроса, обнаружив уязвимость в существующей кодовой базе и показывая, что более новый компилятор обнаружил бы ее. Конечно, ваше руководство может сказать: «Вы обнаружили это с помощью старого компилятора», но вы можете указать, что это требует значительных усилий. Или вы запускаете его через новый компилятор, чтобы найти уязвимость, а затем использовать ее, если вы можете / можете скомпилировать код с новым компилятором. Возможно, вам понадобится помощь дружелюбного хакера, но это зависит от доверия к ним и от возможности / разрешения показать им код (и использовать новый компилятор).

Но если ваша система не подвержена хакерским атакам, вам, возможно, стоит больше интересоваться, повысит ли ваша эффективность обновление компилятора: MSVS 2013 Code Analysis довольно часто обнаруживает потенциальные ошибки намного раньше, чем MSVS 2010, и более или менее поддерживает C99 / C11 - не уверен, что это официально, но объявления могут следовать за операторами, и вы можете объявлять переменные в for-loops.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.