En programación informática , el código inaccesible es parte del código fuente de un programa que nunca se puede ejecutar porque no existe una ruta de flujo de control hacia ese 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 se considera generalmente indeseable por varias razones:
- Utiliza memoria innecesariamente.
- Puede provocar un uso innecesario de la caché de instrucciones de la CPU.
- Esto también puede disminuir la localidad de los datos.
- Se puede invertir tiempo y esfuerzo en probar, mantener y documentar código que nunca se utiliza.
- A veces, una prueba automatizada es lo único que utiliza el código.
El código inaccesible puede tener usos legítimos, como proporcionar una biblioteca de funciones para llamar o acceder manualmente a ellas mediante un depurador mientras el programa se detiene tras un punto de interrupción . Esto resulta especialmente útil para examinar y visualizar de forma legible el estado interno del programa. Podría ser conveniente incluir dicho código en el producto final, de modo que un desarrollador pueda conectar un depurador a la instancia en ejecución del cliente.
Causas
El código inaccesible puede existir por muchas razones, como por ejemplo:
- Errores de programación en bifurcaciones condicionales complejas
- una consecuencia de las transformaciones internas realizadas por un compilador optimizador ;
- Pruebas incompletas de código nuevo o modificado
- Código heredado
- Código reemplazado por otra implementación
- Código inaccesible 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 conserva intencionalmente por si se necesita más adelante.
- Código utilizado únicamente para depuración.
El código heredado es aquel que alguna vez fue útil pero que ya no se usa ni se requiere. Sin embargo, el código inaccesible también puede formar parte de una biblioteca, módulo o rutina compleja, donde resulta útil para otros o bajo condiciones que no se cumplen en un escenario particular.
Un ejemplo de código condicionalmente inaccesible podría ser la implementación de una función general de formato de cadena 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. Por lo general, los compiladores no pueden 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
En este fragmento de código C:
int foo ( int x , int y ) { return x + y ; int z = x + y ; }La definición nunca se alcanza, ya que la función siempre regresa antes. Por lo tanto, no es necesario asignar ni inicializar espacio de almacenamiento para la variable.intz=x+y;z
error de ir a fallar
El protocolo SSL/TLS de Apple de febrero de 2014 contenía una importante vulnerabilidad de seguridad conocida formalmente como CVE - 2014-1266 e informalmente como el "error goto fail". [ 5 ] [ 6 ] El fragmento de código relevante [ 7 ] es:
static OSStatus SSLVerifySignedServerKeyExchange ( SSLContext * ctx , bool isRsa , SSLBuffer signedParams , uint8_t * signature , uint16_t 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 ;// ... fallar : SSLFreeBuffer ( & signedHashes ); SSLFreeBuffer ( & hashCtx ); return err ; }Aquí hay dos goto failinstrucciones sucesivas. En la sintaxis del lenguaje C , solo la primera instrucción después de una instrucción sin llaves ifes condicional. La segunda goto failes, por lo tanto, incondicional y, en consecuencia, siempre omite la llamada a SSLHashSHA1.final. Como resultado, errmantendrá el estado de la operación de actualización SHA1 y, siempre que ambas llamadas a SSLHashSHA1.updatetengan éxito, la verificación de la firma nunca fallará. [ 5 ]
Aquí, el código inalcanzable es la llamada a la finalfunción. [ 6 ] Aplicar el compilador Clang con la opción -Weverythingincluye análisis de código inalcanzable, lo que activaría una alarma para este código. [ 6 ]
C++
En C++ , algunas construcciones se especifican con comportamiento indefinido . Un compilador es libre de implementar cualquier comportamiento o ninguno, y normalmente 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 del flujo de control que busca código que nunca se puede ejecutar en ningún estado posible del programa. En algunos lenguajes (por ejemplo, Java [ 9 ] ), ciertas 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 inaccesible como consecuencia de las transformaciones realizadas por un compilador optimizador (por ejemplo, la 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 de constantes y el análisis de flujo simple muestran que el interior de la instrucción if en el siguiente código es inalcanzable:
entero n = 2 + 1 ;if ( n == 4 ) { // inalcanzable }Sin embargo, se necesita mucha más sofisticación para determinar que el bloque correspondiente es inaccesible en el siguiente código:
#include <math.h>doble x = raíz cuadrada ( 2 );Si ( x > 5 ) { // inalcanzable }La técnica de eliminación de código inaccesible pertenece a la misma clase de optimizaciones que la eliminación de código muerto y la eliminación de código redundante .
Algunos lenguajes permiten marcar explícitamente el código como inaccesible:
- C : a través de la macro (en ) [ 10 ]
unreachable()<stddef.h> - C++ : a través de la función (en ), que es [ 11 ]
std::unreachable()<utility>[[noreturn]] - C# : se puede indicar usando la clase, usando el método [ 12 ]
System.Diagnostics.DebugDebug.Fail() - Java : generalmente se marca lanzando la excepción [ 13 ]
java.lang.AssertionError - Rust : mediante la macro [ 14 ]
unreachable!() - Swift : vía , o funciones que devuelven [ 15 ]
fatalError()Never - Zig : a través de la
unreachablepalabra clave [ 16 ]
Inaccesibilidad frente a elaboración de perfiles
En algunos casos, un enfoque práctico puede consistir en combinar criterios sencillos de inaccesibilidad con el uso de un analizador de rendimiento para gestionar los casos más complejos. El análisis de rendimiento, en general, no puede demostrar la inaccesibilidad de un fragmento de código, pero puede ser una buena heurística para encontrar código potencialmente inaccesible. Una vez identificado 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 determinar si el código es realmente inaccesible.
Véase también
- Cobertura de código
- Código redundante
- Código muerto
- Código de Oxbow
- Problema de la parada : el problema general de determinar si una parte del código es inalcanzable es al menos tan difícil como el problema de la parada y, por lo tanto, indecidible.
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". ACM Transactions on Programming Languages and Systems . 22 (2): 378– 415. CiteSeerX 10.1.1.43.7215 . doi : 10.1145/349214.349233 . S2CID 6129772 .
- ↑ RTCA/DO-178C Consideraciones de software en la certificación de sistemas y equipos aerotransportados . RTCA, Inc. 2011. pág. 112. Consultado el 11 de junio de 2019.
Código muerto: Código objeto ejecutable (o datos) que existe como resultado de un error de desarrollo de software, pero que no puede ejecutarse (código) ni utilizarse (datos) en ninguna configuración operativa del entorno informático de destino. No es rastreable 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 robustez y código desactivado, como funciones de biblioteca no utilizadas. [Dado que la revisión basada en requisitos debería identificar dicho código como no rastreable a los requisitos funcionales, el análisis de código estático debería identificar dicho código como inaccesible, y el análisis de cobertura estructural de los resultados de las pruebas basadas en requisitos debería identificar dicho código como inaccesible, la presencia de código muerto injustificado en un proyecto debería plantear la consideración de la eficacia de los procesos de desarrollo y verificación de la organización.]
- ↑ Jay Thomas (24 de enero de 2017). "La trazabilidad de los requisitos constituye la base de las pruebas de software exhaustivas" . Recuperado el 11 de junio de 2019. La
combinación de la trazabilidad de los requisitos con el análisis de cobertura también puede revelar áreas de "código muerto", es decir, código que nunca se ejecuta. Este código suele ser un inconveniente, pero también puede representar una amenaza para la seguridad si un hacker logra acceder a él y, desde allí, obtener el control. Es código que no se puede rastrear y, por lo tanto, debe eliminarse.
- ↑ Consorcio MISRA (marzo de 2013). Directrices MISRA C:2012 para el uso del lenguaje C en sistemas críticos . MIRA Limited . pág. 41. Archivado del original el 3 de abril de 2019. Recuperado el 11 de junio de 2019.
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
.
- 1 2 Adam Langley (2014). "El error SSL/TLS de Apple" .
- 1 2 3 Arie van Deursen (2014). "Aprendiendo del error de seguridad #gotofail de Apple" .
- ↑ "sslKeyExchange.c - Código fuente para soporte de intercambio de claves e intercambio de claves de servidor" . Archivado del original el 23/04/2015 . Recuperado el 02/04/2014 .
- ↑ "MSC15-C. No depender de comportamientos indefinidos" . Universidad Carnegie Mellon. 2020. Consultado el 28 de septiembre de 2020.
Dado 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" .
- ↑ cppreference.com. "inalcanzable" . cppreference.com . cppreference.com . Consultado el 1 de mayo de 2026 .
- ↑ cppreference.com. "std::unreachable" . cppreference.com . cppreference.com . Consultado el 1 de mayo de 2026 .
- ↑ Microsoft Learn. "Método Debug.Fail" . learn.microsoft.com . Microsoft Learn . Consultado el 1 de mayo de 2026 .
- ↑ Oracle Corporation. "Class AssertionError" . docs.oracle.com . Oracle Corporation . Consultado el 1 de mayo de 2026 .
- ↑ El equipo de Rust (14 de abril de 2026). "Macro inalcanzable" . doc.rust-lang.org . El equipo de Rust.
- ↑ Apple Developer. "fatalError()" . developer.apple.com . Apple Developer . Consultado el 1 de mayo de 2026 .
- ↑ ziglang.org (14 de abril de 2026). "Referencia del lenguaje Zig - inaccesible" . ziglang.org . ziglang.org.
- Appel, AW 1998 Implementación moderna de compiladores en Java. Cambridge University Press.
- Muchnick SS 1997 Diseño e implementación avanzados de compiladores. Morgan Kaufmann.
- Optimizaciones del compilador
- Anomalías de software
- Código fuente