Недоступный код
В компьютерном программировании недостижимый код — это часть исходного кода программы, которая никогда не может быть выполнена, поскольку не существует пути потока управления к коду от остальной части программы. [1]
Недостижимый код иногда также называют мертвым кодом . [2] [3] хотя мертвый код может также относиться к коду, который выполняется, но не влияет на вывод программы. [4]
Недоступный код обычно считается нежелательным по нескольким причинам:
- Он использует память без необходимости
- ЦП. Это может привести к ненужному использованию кэша инструкций
- Это также может уменьшить локальность данных.
- Время и усилия могут быть потрачены на тестирование, поддержку и документирование кода, который никогда не используется.
- Иногда автоматизированный тест — единственное, что использует код.
Недоступный код может иметь некоторые законные применения, например, предоставление библиотеки функций для вызова или перехода к ним вручную через отладчик , когда программа остановлена после точки останова . Это особенно полезно для проверки и вывода на печать внутреннего состояния программы. Возможно, имеет смысл иметь такой код в поставляемом продукте, чтобы разработчик мог подключить отладчик к работающему экземпляру клиента.
Причины
[ редактировать ]Недоступный код может существовать по многим причинам, например:
- ошибки программирования в сложных условных переходах
- следствие внутренних преобразований, выполняемых оптимизирующим компилятором ;
- неполное тестирование нового или измененного кода
- Устаревший код
- Код заменен другой реализацией
- Недоступный код, который программист решил не удалять, поскольку он перемешан с доступным кодом.
- Потенциально доступный код, который никогда не понадобится в текущих сценариях использования.
- Спящий код, который намеренно сохраняется на случай, если он понадобится позже.
- Код используется только для отладки.
Устаревший код — это тот, который когда-то был полезен, но больше не используется и не требуется. Но недостижимый код также может быть частью сложной библиотеки, модуля или процедуры, если он полезен другим или в условиях, которые не выполняются в конкретном сценарии.
Примером такого условно недостижимого кода может быть реализация общей функции форматирования строк в библиотеке времени выполнения компилятора, которая содержит сложный код для обработки всех возможных аргументов, из которых фактически используется только небольшое подмножество. Компиляторы обычно не смогут удалить неиспользуемые разделы кода во время компиляции, поскольку поведение во многом определяется значениями аргументов во время выполнения.
Примеры
[ редактировать ]В этом фрагменте кода C:
int foo (int X, int Y)
{
return X + Y;
int Z = X * Y;
}
определение интервал Z = X * Y; никогда не достигается, поскольку функция всегда возвращается раньше него. Таким образом, Z не нужно ни выделять память, ни инициализировать.
перейти к ошибке
[ редактировать ]Apple SSL/TLS от февраля 2014 года содержал серьезную ошибку безопасности, официально известную как CVE . 2014-1266 и неофициально как «ошибка перехода к сбою». [5] [6] Соответствующий фрагмент кода [7] является:
static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
uint8_t *signature, UInt16 signatureLen)
{
OSStatus err;
...
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
goto fail;
...
fail:
SSLFreeBuffer(&signedHashes);
SSLFreeBuffer(&hashCtx);
return err;
}
Здесь есть два последовательных goto fail
заявления. В синтаксисе языка C второй является безусловным и, следовательно, всегда пропускает вызов SSLHashSHA1.final
.
Как следствие, err
будет хранить статус операции обновления SHA1, и проверка подписи никогда не завершится неудачей. [5]
Здесь недостижимый код — это вызов final
функция. [6] Применение компилятора Clang с опцией -Weverything
включает анализ недостижимого кода, который может вызвать тревогу для этого кода. [6]
С++
[ редактировать ]В C++ некоторые конструкции имеют неопределенное поведение . Компилятор может реализовать любое поведение или не реализовывать его, и обычно оптимизирующий компилятор предполагает, что код недоступен. [8]
Анализ
[ редактировать ]Обнаружение недостижимого кода — это форма анализа потока управления для поиска кода, доступ к которому никогда невозможен ни в каком возможном состоянии программы. В некоторых языках (например, Java [9] ) некоторые формы недостижимого кода явно запрещены. Оптимизация, которая удаляет недоступный код, известна как устранение мертвого кода .
Код может стать недоступным в результате преобразований, выполняемых оптимизирующим компилятором (например, исключение общего подвыражения ).
На практике сложность анализа оказывает существенное влияние на количество обнаруживаемого недостижимого кода. Например, свертывание констант и простой анализ потока показывают, что внутренняя часть оператора if в следующем коде недоступна:
int N = 2 + 1;
if (N == 4)
{
/* unreachable */
}
Однако, чтобы понять, что соответствующий блок недостижим в следующем коде, требуется гораздо больше сложностей:
double X = sqrt(2);
if (X > 5)
{
/* unreachable */
}
Техника устранения недостижимого кода относится к тому же классу оптимизации, что и устранение мертвого кода и устранение избыточного кода .
Недоступность против профилирования
[ редактировать ]В некоторых случаях практическим подходом может быть сочетание простых критериев недостижимости и использования профилировщика для обработки более сложных случаев. Профилирование в целом не может ничего доказать о недоступности фрагмента кода, но может быть хорошей эвристикой для поиска потенциально недостижимого кода. Как только подозрительный фрагмент кода обнаружен, можно использовать другие методы, такие как более мощный инструмент анализа кода или даже ручной анализ, чтобы решить, действительно ли код недоступен.
См. также
[ редактировать ]- Покрытие кода
- Дублирующий код
- Мертвый код
- Кодекс старицы
- Проблема остановки - общая проблема определения того, недоступен ли фрагмент кода, по крайней мере так же сложна, как проблема остановки, и, следовательно, неразрешима.
Ссылки
[ редактировать ]- ^ Дебрэ, Саумья К.; Эванс, Уильям; Мут, Роберт; Де Саттер, Бьорн (1 марта 2000 г.). «Методы компилятора для сжатия кода». Транзакции ACM в языках и системах программирования . 22 (2): 378–415. CiteSeerX 10.1.1.43.7215 . дои : 10.1145/349214.349233 . S2CID 6129772 .
- ^ Аспекты программного обеспечения RTCA/DO-178C при сертификации бортовых систем и оборудования . RTCA, Inc. 2011. с. 112 . Проверено 11 июня 2019 г.
Мертвый код — исполняемый объектный код (или данные), который существует в результате ошибки разработки программного обеспечения, но не может быть выполнен (код) или использован (данные) в любой операционной конфигурации целевой компьютерной среды. Это не связано с системными или программными требованиями. Следующие исключения часто ошибочно относят к категории мертвого кода, но они необходимы для реализации требований/проектирования: встроенные идентификаторы, защитные программные структуры для повышения надежности и деактивированный код, такой как неиспользуемые библиотечные функции. [Поскольку проверка на основе требований должна идентифицировать такой код как неотслеживаемый по функциональным требованиям, статический анализ кода должен идентифицировать такой код как недостижимый, а анализ структурного покрытия результатов тестирования на основе требований должен идентифицировать такой код как недостижимый, наличие неоправданного мертвого кода в проект должен повысить эффективность процессов разработки и проверки организации.]
- ^ Джей Томас (24 января 2017 г.). «Прослеживаемость требований формирует основу для тщательного тестирования программного обеспечения» . Проверено 11 июня 2019 г.
Сочетание отслеживания требований с анализом покрытия также может выявить области «мертвого кода» или кода, который никогда не выполнялся. Этот код в большинстве случаев может доставлять неудобства, но он также может представлять угрозу безопасности, если хакер сможет получить доступ и оттуда получить контроль. Это код, который невозможно отследить, и поэтому его следует удалить.
- ^ Консорциум MISRA (март 2013 г.). MISRA C:2012 Рекомендации по использованию языка C в критически важных системах . МИРА Лимитед . п. 41 . Проверено 11 июня 2019 г.
быть не должно Правило 2.2: мертвого кода . Любая операция, которая выполняется, но удаление которой не влияет на поведение программы, представляет собой мертвый код .
- ^ Jump up to: а б Адам Лэнгли (2014). «Ошибка SSL/TLS Apple» .
- ^ Jump up to: а б с Арье ван Дёрсен (2014). «Уроки ошибки безопасности Apple #gotofail» .
- ^ «sslKeyExchange.c — Исходный код для поддержки обмена ключами и обмена ключами сервера» .
- ^ «MSC15-C. Не зависеть от неопределенного поведения» . Университет Карнеги-Меллон. 2020 . Проверено 28 сентября 2020 г.
Поскольку компиляторы не обязаны генерировать код для неопределенного поведения, такое поведение является кандидатом на оптимизацию.
- ^ «Спецификация языка Java» .
- Аппель, AW 1998. Современная реализация компилятора на Java. Издательство Кембриджского университета.
- Мучник С.С. 1997. Расширенное проектирование и реализация компилятора. Морган Кауфманн.