En programación informática , el código inalcanzable es parte del código fuente de un programa que nunca puede ejecutarse porque no existe una ruta de flujo de control hacia el código desde el resto del programa. [1]
El código inalcanzable a veces también se denomina código muerto , [2] [3] aunque el código muerto también puede referirse al código que se ejecuta pero no tiene efecto en la salida de un programa. [4]
El código inaccesible generalmente se considera indeseable por varias razones:
El código inaccesible puede tener algunos usos legítimos, como proporcionar una biblioteca de funciones para llamar o saltar manualmente a través de un depurador mientras el programa se detiene después de un punto de interrupción . Esto es particularmente útil para examinar y escribir en formato de impresión el estado interno del programa. Puede tener sentido tener dicho código en el producto enviado, de modo que un desarrollador pueda adjuntar un depurador a la instancia en ejecución de un cliente.
El código inalcanzable puede existir por muchos motivos, como:
El código heredado es aquel que alguna vez fue útil pero que ya no se usa ni se requiere. Pero el código inaccesible también puede ser parte de una biblioteca, un módulo o una rutina complejos que son útiles para otros o en condiciones que no se cumplen en un escenario particular.
Un ejemplo de código condicionalmente inalcanzable puede ser la implementación de una función de formato de cadena general en la biblioteca de tiempo de ejecución de un compilador, que contiene código complejo para procesar todos los argumentos posibles, de los cuales solo se utiliza un pequeño subconjunto. Los compiladores normalmente no podrán eliminar las secciones de código no utilizadas en tiempo de compilación, ya que el comportamiento está determinado en gran medida por los valores de los argumentos en tiempo de ejecución.
En este fragmento de código C:
int foo ( int X , int Y ) { devolver X + Y ; int Z = X * Y ; }
La definición int Z = X * Y; nunca se alcanza porque la función siempre retorna antes que ella. Por lo tanto, no es necesario asignar espacio de almacenamiento a Z ni inicializarlo.
El SSL/TLS de Apple de febrero de 2014 contenía una importante falla de seguridad conocida formalmente como CVE - 2014-1266 e informalmente como el "error de error de Goto". [5] [6] El fragmento de código relevante [7] es:
estático OSStatus SSLVerifySignedServerKeyExchange ( SSLContext * ctx , bool isRsa , SSLBuffer signedParams , uint8_t * signature , UInt16 signatureLen ) { OSStatus err ; ... si (( err = SSLHashSHA1.update ( & hashCtx , & serverRandom )) != 0 ) ir a error ; si (( err = SSLHashSHA1.update ( & hashCtx , & signedParams )) != 0 ) ir a error ; ir a error ; si (( err = SSLHashSHA1.final ( & hashCtx , & hashOut ) ) ! = 0 ) ir a error ; ... error : SSLFreeBuffer ( & signedHashes ) ; SSLFreeBuffer ( & hashCtx ) ; devolver err ; }
Aquí hay dos goto fail
declaraciones sucesivas. En la sintaxis del lenguaje C, la segunda es incondicional y, por lo tanto, siempre omite la llamada a SSLHashSHA1.final
. En consecuencia, err
mantendrá el estado de la operación de actualización SHA1 y la verificación de la firma nunca fallará. [5]
Aquí, el código inalcanzable es la llamada a la final
función. [6] Al aplicar el compilador Clang-Weverything
con la opción se incluye un análisis de código inalcanzable, lo que activaría una alarma para este código. [6]
En C++ , se especifica que algunas construcciones tienen un comportamiento indefinido . Un compilador tiene la libertad de implementar cualquier comportamiento o ninguno, y normalmente un compilador optimizador asumirá que el código es inalcanzable. [8]
La detección de código inalcanzable es una forma de análisis del flujo de control para encontrar código al que nunca se puede acceder en ningún estado posible del programa. En algunos lenguajes (por ejemplo, Java [9] ) algunas formas de código inalcanzable están explícitamente prohibidas. La optimización que elimina el código inalcanzable se conoce como eliminación de código muerto .
El código puede volverse inalcanzable como consecuencia de transformaciones realizadas por un compilador optimizador (por ejemplo, eliminación de subexpresiones comunes ).
En la práctica, la sofisticación del análisis tiene un impacto significativo en la cantidad de código inalcanzable que se detecta. Por ejemplo, el plegado constante y el análisis de flujo simple muestran que el interior de la instrucción if en el siguiente código es inalcanzable:
int N = 2 + 1 ; si ( N == 4 ) { /* inalcanzable */ }
Sin embargo, se necesita mucha más sofisticación para determinar que el bloque correspondiente es inalcanzable en el siguiente código:
doble X = sqrt ( 2 ); si ( X > 5 ) { /* inalcanzable */ }
La técnica de eliminación de código inalcanzable pertenece a la misma clase de optimizaciones que la eliminación de código muerto y la eliminación de código redundante .
En algunos casos, un enfoque práctico puede ser una combinación de criterios de inaccesibilidad simples y el uso de un generador de perfiles para manejar los casos más complejos. El análisis de perfiles en general no puede probar nada sobre la inaccesibilidad de un fragmento de código, pero puede ser una buena heurística para encontrar código potencialmente inaccesible. Una vez que se encuentra un fragmento de código sospechoso, se pueden utilizar otros métodos, como una herramienta de análisis de código más potente, o incluso un análisis manual, para decidir si el código es realmente inaccesible.
Código muerto: código de objeto ejecutable (o datos) que existe como resultado de un error de desarrollo de software pero que no se puede ejecutar (código) ni usar (datos) en ninguna configuración operativa del entorno informático de destino. No se puede rastrear hasta un requisito de sistema o software. Las siguientes excepciones a menudo se clasifican por error como código muerto, pero son necesarias para la implementación de los requisitos/diseño: identificadores integrados, estructuras de programación defensiva para mejorar la solidez y código desactivado, como funciones de biblioteca no utilizadas. [Dado que la revisión basada en requisitos debe identificar dicho código como no rastreable hasta los requisitos funcionales, el análisis de código estático debe identificar dicho código como inalcanzable y el análisis de cobertura estructural de los resultados de pruebas basadas en requisitos debe identificar dicho código como inalcanzable, la presencia de código muerto injustificado en un proyecto debe plantear la consideración de la eficacia de los procesos de desarrollo y verificación de la organización.]
La combinación de la trazabilidad de requisitos con el análisis de cobertura también puede revelar áreas de "código muerto", o código que nunca se ejecuta. Este código puede ser principalmente un inconveniente, pero también puede ser una amenaza para la seguridad si un pirata informático puede obtener acceso y, desde allí, obtener el control. Es un código que no se puede rastrear y, por lo tanto, debe eliminarse.
Regla 2.2 no debe haber
código muerto
. Cualquier operación que se ejecute pero cuya eliminación no afecte el comportamiento del programa constituye
código muerto
.
Debido a que los compiladores no están obligados a generar código para comportamientos indefinidos, estos comportamientos son candidatos para la optimización.