En la programación de computadoras , el código inalcanzable es parte del código fuente de un programa que nunca se puede ejecutar porque no existe una ruta de flujo de control al código del resto del programa. [1]
El código inaccesible a veces también se denomina código muerto , [2] [3] aunque el código inactivo también puede referirse al código que se ejecuta pero no tiene ningún efecto en la salida de un programa. [4]
El código inalcanzable generalmente se considera indeseable por varias razones:
- Utiliza memoria innecesariamente
- Puede causar un uso innecesario de la caché de instrucciones de la CPU.
- Esto también puede disminuir la localidad de los datos.
- Se puede dedicar tiempo y esfuerzo a probar, mantener y documentar el código que nunca se utiliza
- A veces, una prueba automatizada es lo único que usa el código.
Sin embargo, el código inalcanzable 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 e imprimir 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.
Causas
El código inalcanzable puede existir por muchas razones, como:
- errores de programación en ramas condicionales complejas
- una consecuencia de las transformaciones internas realizadas por un compilador optimizador ;
- prueba incompleta de código nuevo o modificado
- Código heredado
- Código reemplazado por otra implementación
- Código inalcanzable que un programador decidió no eliminar porque está mezclado con código accesible
- Código potencialmente accesible que los casos de uso actuales nunca necesitan
- Código inactivo que se mantiene intencionalmente en caso de que se necesite más adelante
- Código utilizado solo para depuración.
El código heredado es el que alguna vez fue útil pero que ya no se usa ni se requiere. Pero el código inalcanzable también puede ser parte de una biblioteca, módulo o rutina compleja donde es útil para otros o en condiciones que no se cumplen en un escenario particular.
Un ejemplo de un 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 usa un pequeño subconjunto. Normalmente, los compiladores 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.
Ejemplos de
En este fragmento de código C:
int foo ( int X , int Y ) { return X + Y ; int Z = X * Y ; }
la definición int Z = X * Y; nunca se alcanza ya que la función siempre regresa antes. Por lo tanto, el Z no necesita ser asignado ni inicializado.
Ir a fallar error
El SSL / TLS de Apple de febrero de 2014 contenía una falla de seguridad importante conocida formalmente como CVE - 2014-1266 e informalmente como el "error goto fail". [5] [6] El fragmento de código pertinente [7] es:
estática OSStatus SSLVerifySignedServerKeyExchange ( SSLContext * CTX , bool isRsa , SSLBuffer signedParams , uint8_t * firma , UInt16 signatureLen ) { OSStatus err ; ... if (( err = SSLHashSHA1 . update ( & hashCtx , & serverRandom )) ! = 0 ) goto fail ; if (( err = SSLHashSHA1 . update ( & hashCtx , & ignedParams )) ! = 0 ) goto fail ; ir a fallar ; if (( err = SSLHashSHA1 . final ( & hashCtx , & hashOut )) ! = 0 ) goto fail ; ... falla : SSLFreeBuffer ( & ignedHashes ); SSLFreeBuffer ( & hashCtx ); return err ; }
Aquí, hay dos llamadas sucesivas a goto fail
. En la sintaxis del lenguaje C, el segundo es incondicional y, por lo tanto, siempre omite la llamada a SSLHashSHA1.final
. Como 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. Hay varias prácticas de codificación que podrían haber evitado esta falla, como la revisión del código, el uso adecuado de la sangría o la estructura de bloques y el análisis de cobertura de prueba. [6] La aplicación del compilador Clang con la opción -Weverything
incluye un análisis de código inalcanzable, lo que dispararía una alarma para este código. [6]
C ++
En C ++ , se especifica que algunas construcciones tienen un comportamiento indefinido . Un compilador es libre de implementar cualquier comportamiento o ninguno, y típicamente un compilador optimizador asumirá que el código es inalcanzable. [8]
Análisis
La detección de código inalcanzable es una forma de análisis de flujo de control para encontrar código que nunca se puede alcanzar 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 las transformaciones realizadas por un compilador de optimización (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 ;if ( 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 = raíz cuadrada ( 2 );if ( 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 .
Inalcanzabilidad frente a elaboración de perfiles
En algunos casos, un enfoque práctico puede ser una combinación de criterios simples de inalcanzabilidad y el uso de un generador de perfiles para manejar los casos más complejos. La creación de perfiles en general no puede probar nada sobre la inalcanzabilidad de un fragmento de código, pero puede ser una buena heurística para encontrar código potencialmente inalcanzable. Una vez que se encuentra un fragmento de código sospechoso, se pueden usar otros métodos, como una herramienta de análisis de código más poderosa, o incluso el análisis manual, para decidir si el código es realmente inalcanzable.
Ver también
Referencias
- ^ Debray, Saumya K .; Evans, William; Muth, Robert; De Sutter, Bjorn (1 de marzo de 2000). "Técnicas de compilación para la compactación de código". Transacciones ACM sobre lenguajes y sistemas de programación . 22 (2): 378–415. CiteSeerX 10.1.1.43.7215 . doi : 10.1145 / 349214.349233 . S2CID 6129772 .
- ^ Consideraciones sobre el software RTCA / DO-178C en la certificación de equipos y sistemas de a bordo . RTCA, Inc. 2011. pág. 112 . Consultado el 11 de junio de 2019 .
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 utilizar (datos) en ninguna configuración operativa del entorno informático de destino. No se puede rastrear a un requisito de sistema o software. Las siguientes excepciones a menudo se clasifican erróneamente como código muerto, pero son necesarias para la implementación de los requisitos / diseño: identificadores integrados, estructuras de programación defensivas 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 imposible de rastrear a 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 las pruebas basadas en requisitos debe identificar dicho código como inalcanzable, presencia de código inactivo injustificado en un el proyecto debe plantear la consideración de la eficacia de los procesos de desarrollo y verificación de la organización.]
- ^ Jay Thomas. "La trazabilidad de requisitos constituye la base para pruebas exhaustivas de software" . Consultado el 11 de junio de 2019 .
La combinación de la trazabilidad de los requisitos con el análisis de cobertura también puede generar á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.
- ^ Consorcio MISRA (marzo de 2013). MISRA C: 2012 Directrices para el uso del lenguaje C en sistemas críticos . MIRA Limited . pag. 41 . Consultado el 11 de junio de 2019 .
Regla 2.2 no habrá código muerto . Cualquier operación que se ejecute pero cuya eliminación no afectaría el comportamiento del programa constituye código muerto .
- ^ a b Adam Langley (2014). "Error de SSL / TLS de Apple" .
- ^ a b c Arie van Deursen (2014). "Aprendiendo del error de seguridad #gotofail de Apple" .
- ^ "sslKeyExchange.c - Código fuente para soporte para intercambio de claves e intercambio de claves de servidor" .
- ^ "MSC15-C. No dependa de comportamientos indefinidos" . Universidad de Carnegie mellon. 2020 . Consultado el 28 de septiembre de 2020 .
Debido a que los compiladores no están obligados a generar código para comportamientos indefinidos, estos comportamientos son candidatos para la optimización.
- ^ "Especificación del lenguaje Java" .
- Appel, AW 1998 Implementación del compilador moderno en Java. Prensa de la Universidad de Cambridge.
- Muchnick SS 1997 Diseño e implementación avanzados del compilador. Morgan Kaufmann.