Язык C имеет славу крайне небезопасного языка. Так ли это на самом деле?
Хотя язык C и плох во многих отношениях, он не так плох, как мог бы быть. Как известно, язык C появился, когда Денис Ритчи работал над компилятором языка B, который был куда хуже. Эволюционные улучшения, которые Ритчи вносил в язык по ходу работы, и послужили основой для нового языка.
Стандарт языка C, если оставить за скобками предмет стандартизации, составлен весьма неплохо. Достаточно точно описывая неопределённое поведение, которое многие ошибочно приписывают к недостаткам языка, стандарт создаёт основу для возможности создания компилятора, позволяющего проверять не только отсутствие нарушения печально известных ошибок памяти, но и такие логические ошибки, которые другие, якобы более безопасные языки, обязывают оставить без внимания.
Такая возможность не является чисто гипотетической, она давно воплощена в ведущих компиляторах языка. Пусть и не идеальное, но достаточно хорошее воплощение защиты можно применять не только в отладочных целях, но даже оставлять её в выпускных версиях программ. Отсутствие надёжной защиты является не неизбежным следствием языка C, а вопросом выбора разработчиков и сопровождающих. И что же они выбирают в массе своей? Они выбирают всё выключить, оставляя только наименее затратные по скорости защиты, но которые также неполны и недостаточно надёжны.
Я хотел посмотреть, нельзя ли исправить это положение дел со стороны пользователя? Для этого я воспользовался дистрибутивом GNU/Linux NixOS, позволяющим в конфигурации добавить нужные настройки компилятора в пакет, не меняя самого пакета. Это действительно сработало, но опыт выявил, что значительное количество пакетов либо после этого работают некорректно, например, bash, либо даже не собираются из-за проваливающихся тестов или падающей кодогенерации, например, glibс. А ведь это одни из важнейший частей системы, которые должны были проверяться с особой тщательностью. На деле, эти программные коды не то, что не выпускаются с защитами, что вполне ожидаемо, но даже не тестируются с ними, что уже выглядит проявлением большой глупости.
Любое воплощение неопределённого поведения не меняет поведение правильного кода. Оно влияет на поведение только в тех местах, где происходят нарушения обоснованных правил языка. Именно поэтому это должно проверяться. Разработчики же часто считают, что если они заставили методом тыка неправильный код работать ожидаемым для них способом в некоторых случаях, то всё в порядке. И если что-то всё равно указывает на наличие проблемы, то именно оно эту проблему и создаёт. Если проверку выключить, то и проблемы «не станет».
Естественно, мой опыт по включению защиты на этом завершился, потому что превращаться в сопровождающего наследия Linux у меня нет никакого желания. Здесь нужна системная робота со стороны всех участников.
Единственным светлым пятном на этом фоне оказалось само ядро Linux. Ядро требует особого подхода, поэтому включить обычные проверки для него не представляется возможным. Тем не менее, вся работа по их внедрению разработчиками уже проведена, и пусть и по-другому, но проверки удалось включить. Я не был уверен, что ядро не начнёт постоянно падать, но, на удивление, оно показало стабильную работу длительное время. Это многое проясняет.
Подытоживая, можно сказать, что проблема, конечно, в разработчиках. Но вовсе не в том смысле, который вкладывают сторонники волшебного заклинания «просто надо писать код правильно», как-будто люди способны не ошибаться. Проблема в том, что разработчики, во-первых, сначала выбрали C, неважно, со скрипом или с песней, а во-вторых оказались не способны обеспечить хотя бы те защиты кода C, которые уже есть и не требуют для использования даже малых затрат с их стороны.


Комментариев нет:
Отправить комментарий