En informática , la ejecución simbólica (también conocida como evaluación simbólica o symbex ) es un método para analizar un programa y determinar qué entradas activan cada parte del mismo . Un intérprete sigue el programa, asumiendo valores simbólicos para las entradas en lugar de obtener las entradas reales, como ocurriría en la ejecución normal. De esta forma, obtiene expresiones en términos de esos símbolos para las expresiones y variables del programa, y restricciones en términos de esos símbolos para los posibles resultados de cada bifurcación condicional. Finalmente, las posibles entradas que activan una bifurcación se pueden determinar resolviendo las restricciones.
El campo de la simulación simbólica aplica el mismo concepto al hardware. La computación simbólica aplica el concepto al análisis de expresiones matemáticas.
Ejemplo
Considere el siguiente programa, que lee un valor y falla si la entrada es 6.
#include <stdio.h>// lee un número entero de algún lugar y lo devuelve int read ();int main () { int y = read (); int z = y * 2 ; if ( z == 12 ) { perror ( "¡El programa falló!" ); return 1 ; } else { printf ( "OK" ); return 0 ; } }Durante una ejecución normal (ejecución "concreta"), el programa leería un valor de entrada concreto (por ejemplo, 5) y lo asignaría a y. La ejecución continuaría entonces con la multiplicación y la bifurcación condicional, que se evaluaría como falsa e imprimiría OK.
Durante la ejecución simbólica, el programa lee un valor simbólico (por ejemplo, λ) y lo asigna a y. El programa procedería entonces con la multiplicación y asignaría λ * 2a z. Al llegar a la ifinstrucción, evaluaría λ * 2 == 12. En este punto del programa, λpodría tomar cualquier valor, y la ejecución simbólica puede, por lo tanto, continuar por ambas ramas, "bifurcando" dos caminos. A cada camino se le asigna una copia del estado del programa en la instrucción de bifurcación, así como una restricción de camino. En este ejemplo, la restricción de camino es λ * 2 == 12para la iframa y para la rama . Ambos caminos pueden ejecutarse simbólicamente de forma independiente. Cuando los caminos terminan (por ejemplo, como resultado de ejecutar o simplemente salir), la ejecución simbólica calcula un valor concreto para resolviendo las restricciones de camino acumuladas en cada camino. Estos valores concretos pueden considerarse como casos de prueba concretos que pueden, por ejemplo, ayudar a los desarrolladores a reproducir errores. En este ejemplo, el solucionador de restricciones determinaría que para alcanzar la instrucción, tendría que ser igual a 6 o (si es un entero de complemento a dos de 32 bits y se tiene en cuenta el desbordamiento de enteros ) -2147483642.λ * 2 != 12elsefail()λfail()λint
Limitaciones
Explosión de trayectoria
La ejecución simbólica de todas las rutas de programa factibles no es escalable para programas grandes. El número de rutas factibles en un programa crece exponencialmente con el aumento del tamaño del programa e incluso puede ser infinito en el caso de programas con iteraciones de bucle ilimitadas. [ 1 ] Las soluciones al problema de la explosión de rutas generalmente utilizan heurísticas para la búsqueda de rutas para aumentar la cobertura de código, [ 2 ] reducen el tiempo de ejecución paralelizando rutas independientes, [ 3 ] o fusionando rutas similares. [ 4 ] Un ejemplo de fusión es el veritesting , que "emplea la ejecución simbólica estática para amplificar el efecto de la ejecución simbólica dinámica". [ 5 ]
Eficiencia dependiente del programa
La ejecución simbólica se utiliza para analizar un programa paso a paso, lo cual supone una ventaja frente al análisis entrada por entrada, como hacen otros paradigmas de prueba (por ejemplo, el análisis dinámico de programas ). Sin embargo, si pocas entradas siguen la misma ruta a través del programa, el ahorro es mínimo en comparación con probar cada entrada por separado.
Alias de memoria
La ejecución simbólica es más difícil cuando se puede acceder a la misma ubicación de memoria mediante diferentes nombres ( alias ). Los alias no siempre se pueden reconocer de forma estática, por lo que el motor de ejecución simbólica no puede reconocer que un cambio en el valor de una variable también cambia la otra. [ 6 ]
Matrices
Dado que un array es una colección de muchos valores distintos, los ejecutores simbólicos deben tratar todo el array como un solo valor o tratar cada elemento del array como una ubicación separada. El problema de tratar cada elemento del array por separado es que una referencia como "A[i]" solo se puede especificar dinámicamente, cuando el valor de i tiene un valor concreto. [ 6 ]
interacciones con el medio ambiente
Los programas interactúan con su entorno mediante llamadas al sistema , recepción de señales, etc. Pueden surgir problemas de consistencia cuando la ejecución alcanza componentes que no están bajo el control de la herramienta de ejecución simbólica (por ejemplo, el núcleo o las bibliotecas). Considere el siguiente ejemplo:
int main () { FILE * fp = fopen ( "my_document.txt" , "w" ); char data [ 100 ]; bool cond = /* alguna condición aquí */ ; if ( cond ) { fputs ( "Algunos datos" , fp ); } else { fputs ( "Otros datos" , fp ); } fgets ( data , sizeof ( data ), fp ); }Este programa abre un archivo y, según una condición, escribe diferentes tipos de datos en él. Posteriormente, lee los datos escritos. En teoría, la ejecución simbólica bifurcaría dos rutas en la línea 5, y cada ruta a partir de ahí tendría su propia copia del archivo. Por lo tanto, la instrucción en la línea 11 devolvería datos consistentes con el valor de "condición" en la línea 5. En la práctica, las operaciones de archivo se implementan como llamadas al sistema en el núcleo y están fuera del control de la herramienta de ejecución simbólica. Los principales enfoques para abordar este desafío son:
Ejecutar llamadas al entorno directamente. La ventaja de este enfoque es su sencillez de implementación. La desventaja es que los efectos secundarios de dichas llamadas sobrescribirán todos los estados gestionados por el motor de ejecución simbólica. En el ejemplo anterior, la instrucción de la línea 11 devolvería "algunos datos, otros datos" o "otros datos, algunos datos" dependiendo del orden secuencial de los estados.
Modelado del entorno. En este caso, el motor instrumenta las llamadas al sistema con un modelo que simula sus efectos y que almacena todos los efectos secundarios en un almacenamiento por estado. La ventaja es que se obtienen resultados correctos al ejecutar simbólicamente programas que interactúan con el entorno. La desventaja es que se necesita implementar y mantener muchos modelos potencialmente complejos de llamadas al sistema. Herramientas como KLEE, [ 7 ] Cloud9 y Otter [ 8 ] adoptan este enfoque implementando modelos para operaciones del sistema de archivos, sockets, IPC , etc.
La bifurcación del estado completo del sistema. Las herramientas de ejecución simbólica basadas en máquinas virtuales resuelven el problema del entorno mediante la bifurcación del estado completo de la máquina virtual. Por ejemplo, en S2E [ 9 ], cada estado es una instantánea independiente de la máquina virtual que puede ejecutarse por separado. Este enfoque reduce la necesidad de escribir y mantener modelos complejos y permite ejecutar simbólicamente prácticamente cualquier binario de programa. Sin embargo, conlleva un mayor consumo de memoria (las instantáneas de la máquina virtual pueden ser grandes).
Herramientas
Versiones anteriores de las herramientas
- EXE [ 12 ] es una versión anterior de KLEE. El documento de EXE se puede encontrar aquí .
Historia
El concepto de ejecución simbólica se introdujo académicamente en la década de 1970 con descripciones de: el sistema Select, [ 13 ] el sistema EFFIGY, [ 14 ] el sistema DISSECT, [ 15 ] y el sistema de Clarke. [ 16 ]
Véase también
Referencias
- ↑ Anand, Saswat; Patrice Godefroid; Nikolai Tillmann (2008). «Ejecución simbólica compositiva impulsada por la demanda». Herramientas y algoritmos para la construcción y el análisis de sistemas . Lecture Notes in Computer Science. Vol. 4963. pp. 367–381 . doi : 10.1007/978-3-540-78800-3_28 . ISBN 978-3-540-78799-0.
- ↑ Ma, Kin-Keng; Khoo Yit Phang; Jeffrey S. Foster; Michael Hicks (2011). "Ejecución simbólica dirigida" . Actas de la 18.ª Conferencia Internacional sobre Análisis Estadístico . Springer. págs. 95–111 . ISBN 9783642237010. Consultado el 3 de abril de 2013 .
- ↑ Staats, Matt; Corina Pasareanu (2010). «Ejecución simbólica paralela para la generación de pruebas estructurales». Actas del 19.º Simposio Internacional sobre Pruebas y Análisis de Software . págs. 183–194 . doi : 10.1145/1831708.1831732 . hdl : 11299/217417 . ISBN 9781605588230. S2CID 9898522 .
- ↑ Kuznetsov, Volodymyr; Kinder, Johannes; Bucur, Stefan; Candea, George (1 de enero de 2012). «Fusión eficiente de estados en la ejecución simbólica». Actas de la 33.ª Conferencia ACM SIGPLAN sobre diseño e implementación de lenguajes de programación . Nueva York, NY, EE. UU.: ACM. págs. 193–204 . CiteSeerX 10.1.1.348.823 . doi : 10.1145/2254064.2254088 . ISBN 978-1-4503-1205-9. S2CID 135107 .
- ↑ "Mejora de la ejecución simbólica con Veritesting" . Junio de 2016.
- 1 2 DeMillo, Rich; Offutt, Jeff (1991-09-01). "Generación automática de datos de prueba basada en restricciones". IEEE Transactions on Software Engineering . 17 (9): 900– 910. doi : 10.1109/32.92910 .
- ↑ Cadar, Cristian; Dunbar, Daniel; Engler, Dawson (1 de enero de 2008). "KLEE: Generación automática y sin asistencia de pruebas de alta cobertura para programas de sistemas complejos" . Actas de la 8.ª Conferencia USENIX sobre Diseño e Implementación de Sistemas Operativos . OSDI'08: 209–224 .
- ↑ Turpie, Jonathan; Reisner, Elnatan; Foster, Jeffrey; Hicks, Michael. "MultiOtter: Ejecución simbólica multiproceso" (PDF) .
- ↑ Chipounov, Vitaly; Kuznetsov, Volodymyr; Candea, George (2012-02-01). "La plataforma S2E: diseño, implementación y aplicaciones" . ACM Trans. Comput. Syst . 30 (1): 2:1–2:49. doi : 10.1145/2110356.2110358 . ISSN 0734-2071 . S2CID 16905399 .
- ↑ Andrès, Léo (2024). "Owi: Performant Parallel Symbolic Execution Made Easy, an Application to WebAssembly" . The Art, Science, and Engineering of Programming . 9. arXiv : 2412.06391 . doi : 10.22152/programming-journal.org/2025/9/3 .
- ↑ Sharma, Asankhaya (2014). "Explotación de comportamientos indefinidos para una ejecución simbólica eficiente". ICSE Companion 2014: Actas complementarias de la 36.ª Conferencia Internacional sobre Ingeniería de Software . págs. 727–729 . doi : 10.1145/2591062.2594450 . ISBN 9781450327688. S2CID 10092664 .
- ↑ Cadar, Cristian; Ganesh, Vijay; Pawlowski, Peter M.; Dill, David L.; Engler, Dawson R. (2008). "EXE: Generación automática de entradas de muerte". ACM Trans. Inf. Syst. Secur . 12 : 10:1–10:38. doi : 10.1145/1455518.1455522 . S2CID 10905673 .
- ↑ Robert S. Boyer, Bernard Elspas y Karl N. Levitt, SELECT: un sistema formal para probar y depurar programas mediante ejecución simbólica, Actas de la Conferencia Internacional sobre Software Confiable, 1975, páginas 234-245, Los Ángeles, California.
- ↑ James C. King, Ejecución simbólica y pruebas de programas, Communications of the ACM, volumen 19, número 7, 1976, 385-394
- ↑ William E. Howden, Experimentos con un sistema de evaluación simbólica, Actas de la Conferencia Nacional de Computación, 1976.
- ↑ Lori A. Clarke, Un sistema de prueba de programas, ACM 76: Actas de la Conferencia Anual, 1976, páginas 488-491, Houston, Texas, Estados Unidos
Enlaces externos
- Ejecución simbólica para la detección de errores
- Presentación sobre ejecución simbólica y pruebas de software en NASA Ames.
- Ejecución simbólica para pruebas de software en la práctica: evaluación preliminar
- Una bibliografía de artículos relacionados con la ejecución simbólica
- Interpretación abstracta
- Análisis del programa