Недостижимый код - Unreachable code

В компьютерное программирование, недостижимый код является частью исходный код программы, которая никогда не может быть выполнена, потому что не существует поток управления путь к коду из остальной части программы.[1]

Недостижимый код иногда также называют мертвый код,[2][3] несмотря на то что мертвый код может также относиться к коду, который выполняется, но не влияет на вывод программы.[4]

Недоступный код обычно считается нежелательным по нескольким причинам:

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

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

Причины

Недостижимый код может существовать по многим причинам, например:

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

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

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

Примеры

В этом фрагменте кода C:

int фу (int Икс, int Y){    возвращаться Икс + Y;    int Z = Икс * Y;}

Определение int Z = X * Y; никогда не достигается, так как функция всегда возвращается перед ним. Следовательно Z не нужно ни выделять память, ни инициализировать.

ошибка goto fail

Apple SSL / TLS с февраля 2014 г. содержал серьезную уязвимость безопасности, официально известную как CVE -2014-1266 и неофициально как «ошибка перехода к неудачам».[5][6] Соответствующий фрагмент кода[7]является:

статический OSStatusSSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,                                 uint8_t *подпись, UInt16 подписьLen){    OSStatus        ошибаться;    ...     если ((ошибаться = SSLHashSHA1.Обновить(&hashCtx, &serverRandom)) != 0)        перейти к провал;    если ((ошибаться = SSLHashSHA1.Обновить(&hashCtx, &signedParams)) != 0)        перейти к провал;        перейти к провал;    если ((ошибаться = SSLHashSHA1.окончательный(&hashCtx, &hashOut)) != 0)        перейти к провал;    ... провал:    SSLFreeBuffer(&подписанные хеши);    SSLFreeBuffer(&hashCtx);    возвращаться ошибаться;}

Здесь два последовательных вызова перейти к неудаче. В синтаксисе языка C второй безусловен, а значит всегда пропускает звонок на SSLHashSHA1.final.Как следствие, ошибаться будет содержать статус операции обновления SHA1, а проверка подписи будет никогда провал.[5]

Здесь недостижимый код - это вызов окончательный функция. Существует несколько методов кодирования, которые могли бы предотвратить эту ошибку, например, проверка кода, правильное использование отступов или блочной структуры и анализ тестового покрытия.[6] Применяя Лязг компилятор с опцией -Все включает анализ недоступного кода, который вызовет тревогу для этого кода.[6]

C ++

В C ++, некоторые конструкции указаны как неопределенное поведение. Компилятор может реализовать любое поведение или ничего, и обычно оптимизирующий компилятор предполагает, что код недоступен.[8]

Анализ

Обнаружение недостижимого кода - это форма анализ потока управления чтобы найти код, который никогда не будет достигнут ни в одном из возможных состояний программы. На некоторых языках (например, Ява[9]) некоторые формы недостижимого кода явно запрещены. Оптимизация, удаляющая недоступный код, известна как устранение мертвого кода.

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

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

int N = 2 + 1;если (N == 4){   / * недоступен * /}

Однако требуется гораздо больше усилий, чтобы понять, что соответствующий блок недоступен в следующем коде:

двойной Икс = sqrt(2);если (Икс > 5){    / * недоступен * /}

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

Недоступность vs. профилирование

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

Смотрите также

Рекомендации

  1. ^ Debray, Saumya K .; Эванс, Уильям; Мут, Роберт; Де Суттер, Бьорн (1 марта 2000 г.). «Компиляторные методы для сжатия кода». Транзакции ACM по языкам и системам программирования. 22 (2): 378–415. CiteSeerX  10.1.1.43.7215. Дои:10.1145/349214.349233.
  2. ^ Рекомендации по программному обеспечению RTCA / DO-178C при сертификации бортовых систем и оборудования. RTCA, Inc. 2011. с. 112. Получено 2019-06-11. Мертвый код - исполняемый объектный код (или данные), который существует в результате ошибки разработки программного обеспечения, но не может быть выполнен (код) или использован (данные) в любой операционной конфигурации целевой компьютерной среды. Это не связано с требованиями к системе или программному обеспечению. Следующие исключения часто ошибочно классифицируются как мертвый код, но они необходимы для реализации требований / дизайна: встроенные идентификаторы, защитные программные структуры для повышения надежности и деактивированный код, например неиспользуемые библиотечные функции. [Поскольку проверка на основе требований должна идентифицировать такой код как неотслеживаемый по функциональным требованиям, статический анализ кода должен определять такой код как недоступный, а анализ структурного покрытия результатов тестирования на основе требований должен определять такой код как недоступный, наличие неоправданного мертвого кода в проект должен повышать внимание к эффективности процессов разработки и проверки организации.]
  3. ^ Джей Томас. «Отслеживание требований формирует основу для тщательного тестирования программного обеспечения». Получено 2019-06-11. Комбинация отслеживаемости требований с анализом покрытия также может выявить области «мертвого кода» или кода, который никогда не выполнялся. Этот код в основном может доставлять неудобства, но он также может быть угрозой безопасности, если хакер может получить доступ и оттуда получить контроль. Это код, который невозможно отследить, и поэтому его следует удалить.
  4. ^ Консорциум MISRA (март 2013 г.). MISRA C: 2012 Руководство по использованию языка C в критических системах. МИРА Лимитед. п. 41 год. Получено 2019-06-11. Правило 2.2 не допускается мертвый код. Любая выполняемая операция, удаление которой не повлияет на поведение программы, считается мертвый код.
  5. ^ а б Адам Лэнгли (2014). "Ошибка SSL / TLS от Apple".
  6. ^ а б c Ари ван Дерсен (2014). «Учимся на ошибке безопасности Apple #gotofail».
  7. ^ «sslKeyExchange.c - Исходный код для поддержки обмена ключами и серверными ключами».
  8. ^ «MSC15-C. Не зависеть от неопределенного поведения». Университет Карнеги Меллон. 2020 г.. Получено 28 сентября 2020. Поскольку компиляторы не обязаны генерировать код для неопределенного поведения, эти варианты поведения являются кандидатами на оптимизацию.
  9. ^ «Спецификация языка Java».
  • Аппель, А. В. 1998 Современная реализация компилятора на Java. Издательство Кембриджского университета.
  • Мучник С. С. 1997 Расширенный дизайн и реализация компилятора. Морган Кауфманн.