В компьютерном программировании недостижимый код — это часть исходного кода программы, которая никогда не может быть выполнена, поскольку не существует пути управления потоком выполнения к коду из остальной части программы. [1]
Недостижимый код иногда также называют мертвым кодом , [2] [3] хотя мертвый код может также относиться к коду, который выполняется, но не оказывает никакого влияния на вывод программы. [4]
Недостижимый код обычно считается нежелательным по нескольким причинам:
Недостижимый код может иметь некоторые законные применения, например, предоставление библиотеки функций для вызова или перехода вручную через отладчик , пока программа остановлена после точки останова . Это особенно полезно для изучения и наглядного отображения внутреннего состояния программы. Может иметь смысл иметь такой код в поставляемом продукте, чтобы разработчик мог прикрепить отладчик к работающему экземпляру клиента.
Недоступный код может существовать по многим причинам, например:
Устаревший код — это тот, который когда-то был полезен, но больше не используется или не требуется. Но недоступный код может также быть частью сложной библиотеки, модуля или процедуры, где он полезен для других или в условиях, которые не выполняются в определенном сценарии.
Примером такого условно недостижимого кода может быть реализация общей функции форматирования строк в библиотеке времени выполнения компилятора, которая содержит сложный код для обработки всех возможных аргументов, из которых фактически используется только небольшое подмножество. Компиляторы, как правило, не могут удалить неиспользуемые разделы кода во время компиляции, поскольку поведение во многом определяется значениями аргументов во время выполнения.
В этом фрагменте кода на языке C:
int foo ( int X , int Y ) { return X + Y ; int Z = X * Y ; }
определение int Z = X * Y; никогда не достигается, так как функция всегда возвращается до него. Поэтому Z не нужно ни выделять память, ни инициализировать.
В SSL/TLS от Apple с февраля 2014 года содержалась серьезная уязвимость безопасности, официально известная как CVE - 2014-1266 и неофициально как «ошибка goto fail». [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 ) перейти к сбою ; if (( err = SSLHashSHA1.update ( & hashCtx , & signedParams )) != 0 ) перейти к сбою ; перейти к сбою ; if ( ( err = SSLHashSHA1.final ( & hashCtx , & hashOut )) != 0 ) перейти к сбою ; ... сбой : SSLFreeBuffer ( & signedHashes ) ; SSLFreeBuffer ( & hashCtx ); возврат ошибки ; }
Здесь есть два последовательных goto fail
оператора. В синтаксисе языка C второй является безусловным, и поэтому всегда пропускает вызов SSLHashSHA1.final
. Как следствие, err
будет удерживать статус операции обновления SHA1, и проверка подписи никогда не будет неудачной. [5]
Здесь недостижимый код — это вызов функции final
. [6] Применение компилятора Clang с опцией -Weverything
включает анализ недостижимого кода, который вызовет сигнал тревоги для этого кода. [6]
В C++ некоторые конструкции определены как имеющие неопределенное поведение . Компилятор может реализовать любое поведение или не реализовать никакого, и обычно оптимизирующий компилятор предполагает, что код недостижим. [8]
Обнаружение недостижимого кода — это форма анализа потока управления для поиска кода, который никогда не может быть достигнут ни в одном возможном состоянии программы. В некоторых языках (например, Java [9] ) некоторые формы недостижимого кода явно запрещены. Оптимизация, которая удаляет недостижимый код, известна как устранение мертвого кода .
Код может стать недоступным в результате преобразований, выполняемых оптимизирующим компилятором (например, исключение общих подвыражений ).
На практике сложность анализа оказывает значительное влияние на количество обнаруженного недостижимого кода. Например, сворачивание констант и простой анализ потока показывают, что внутренняя часть оператора if в следующем коде недостижима:
целое N = 2 + 1 ; если ( N == 4 ) { /* недостижимо */ }
Однако требуется гораздо больше сложности, чтобы определить, что соответствующий блок недоступен в следующем коде:
двойной X = sqrt ( 2 ); если ( X > 5 ) { /* недостижимо */ }
Метод устранения недостижимого кода относится к тому же классу оптимизаций, что и устранение мертвого кода и устранение избыточного кода .
В некоторых случаях практическим подходом может быть сочетание простых критериев недостижимости и использование профилировщика для обработки более сложных случаев. Профилирование в целом не может ничего доказать о недостижимости фрагмента кода, но может быть хорошей эвристикой для поиска потенциально недостижимого кода. После обнаружения подозрительного фрагмента кода можно использовать другие методы, такие как более мощный инструмент анализа кода или даже ручной анализ, чтобы решить, действительно ли код недостижим.
код — исполняемый объектный код (или данные), который существует в результате ошибки разработки программного обеспечения, но не может быть выполнен (код) или использован (данные) в какой-либо рабочей конфигурации целевой компьютерной среды. Он не прослеживается до системного или программного требования. Следующие исключения часто ошибочно классифицируются как мертвый код, но они необходимы для реализации требований/проекта: встроенные идентификаторы, защитные программные структуры для повышения надежности и деактивированный код, такой как неиспользуемые библиотечные функции. [Поскольку обзор на основе требований должен идентифицировать такой код как не прослеживаемый к функциональным требованиям, статический анализ кода должен идентифицировать такой код как недостижимый, а структурный анализ покрытия результатов тестирования на основе требований должен идентифицировать такой код как недостижимый, наличие неоправданного мертвого кода в проекте должно заставить задуматься об эффективности процессов разработки и проверки организации.]
трассируемости требований с анализом покрытия также может выявить области "мертвого кода" или кода, который никогда не выполняется. Этот код может быть в основном неудобным, но он также может представлять угрозу безопасности, если хакер может получить доступ и оттуда получить контроль. Это код, который невозможно отследить, и поэтому его следует удалить.
Правило 2.2. Не должно быть
мертвого кода
. Любая операция, которая выполняется, но удаление которой не повлияет на поведение программы, представляет собой
мертвый код
.
Поскольку компиляторы не обязаны генерировать код для неопределенного поведения, эти поведения являются кандидатами на оптимизацию.