Clear Sky Science · es
Corte y mutación estática de binarios WebAssembly basados en pila para generar sub-binaries válidos
Por qué importa dividir el código en piezas más pequeñas
Las aplicaciones web modernas dependen cada vez más de WebAssembly, un lenguaje compacto similar a la máquina que permite ejecutar programas escritos en C, C++ o Rust a velocidad casi nativa en navegadores, nubes e incluso en dispositivos embebidos diminutos. A medida que más sistemas ejecutan WebAssembly, necesitamos formas seguras y exhaustivas de probar estos motores de ejecución en busca de errores y fallos de seguridad. Pero los programas WebAssembly del mundo real son grandes, complejos y difíciles de recopilar con suficiente variedad. Este artículo presenta una nueva forma de trocear programas WebAssembly existentes en muchos mini-programas más pequeños y autocontenidos y luego modificarlos de manera inteligente. El resultado es un abundante suministro de casos de prueba válidos y manejables que pueden someter a estrés a los motores WebAssembly mucho más eficazmente que las herramientas actuales.

De un programa grande a muchos pequeños
Los autores parten de la observación de que generar código WebAssembly aleatorio casi nunca funciona: el lenguaje está fuertemente verificado, especialmente en lo relativo a su uso de una pila que contiene valores temporales. En lugar de crear código desde cero, comienzan con un binario WebAssembly "base" existente y usan una técnica llamada segmentación de programas (program slicing). Un slice es una versión reducida de un programa que conserva solo las porciones de código que influyen en un punto de interés elegido, como una instrucción particular dentro de una función. Siguiendo el flujo de datos, el uso de memoria y el flujo de control hacia atrás desde ese punto, el método extrae un fragmento compacto que aún “significa algo” en términos de cálculo, en lugar de ser ruido aleatorio.
Mantener la pila invisible en equilibrio
Los programas WebAssembly dependen de una estricta disciplina de pila: cada instrucción extrae y apila valores, y los bloques estructurados y las ramas deben dejar la pila exactamente con la forma esperada al terminar. Cortar partes de un programa de forma ingenua casi siempre rompe esa disciplina, haciendo que el resultado sea inválido. Trabajos anteriores reparaban los slices rellenándolos con patrones fijos de instrucciones ficticias, lo que los hacía ejecutables pero limitaba su variedad. Este artículo, en cambio, introduce un algoritmo dedicado de corrección del equilibrio de la pila. Analiza el flujo de control dentro de cada slice, encuentra lugares donde las ramas o los finales de bloques alteran la forma esperada de la pila e inserta secuencias de instrucciones a medida que descartan valores innecesarios y sintetizan nuevos del tipo correcto. Estos pequeños “gadgets” restauran la corrección mientras permiten patrones de instrucciones mucho más ricos que el simple relleno.
Construir mini‑programas completos con complejidad controlada
Un slice corregido sigue siendo solo el cuerpo de una función, y esa función puede llamar a otras o depender de características a nivel de módulo como variables globales y memoria lineal. Por ello, los autores amplían su método para ensamblar mini‑módulos completos y ejecutables. Partiendo de una función de entrada elegida al azar, aplican de forma repetida el slicing y corrigen cualquier función que sea llamada, hasta una "profundidad de llamada" elegida por el usuario que controla cuántas capas de llamadas se conservan. Más allá de esa profundidad, las llamadas se reemplazan por sustitutos breves que imitan el número correcto de argumentos y resultados sin arrastrar más código. El sistema también reconstruye las secciones circundantes de un módulo WebAssembly—tipos, tablas, memoria, y demás—de modo que cada binario generado es autocontenido y puede ejecutarse directamente con herramientas y entornos estándar.

Añadir variedad mediante mutación cuidadosa
Incluso con slicing, muchos binarios generados aún se parecen a su programa padre. Para ampliar la cobertura, los autores introducen una fase de mutación que trabaja hacia atrás a través de cada función. Para instrucciones que consumen entradas, el algoritmo elige aleatoriamente un nuevo tipo de resultado, encuentra instrucciones sustitutas que producen ese tipo y luego muta recursivamente las instrucciones anteriores que suministran sus entradas para que toda la cadena siga encajando. Esto preserva la corrección de la pila mientras remodela el cálculo. En comparación con un conjunto de referencia de programas WebAssembly del mundo real, los binarios mutados contienen muchos más patrones de instrucciones distintos y hacen un uso mucho mayor de características raramente vistas, como operaciones vectoriales, que son importantes para ejercitar la implementación completa de los motores WebAssembly.
Qué revelan los experimentos
Para evaluar su enfoque, los autores implementaron alrededor de 17 000 líneas de Python que cubren casi el estándar completo de WebAssembly 2.0. Usando un gran conjunto de datos público con miles de binarios del mundo real, demostraron que su técnica puede reducir rutinariamente programas a una fracción de su conteo original de instrucciones—con frecuencia por debajo del 40% de los tamaños máximos de la referencia—manteniéndolos válidos en más del 99% de los casos cuando la mutación está habilitada. El método es rápido, típicamente muy por debajo de un segundo por binario generado de tamaño moderado, y aumenta significativamente tanto la variedad de tipos de instrucciones como el número de secuencias de instrucciones únicas observadas. En términos prácticos, esto significa que probadores e investigadores de seguridad pueden convertir un corpus modesto de programas WebAssembly en una suite grande y diversa de binarios pequeños y válidos que son ideales para fuzzing, pruebas diferenciales y pruebas de regresión de entornos WebAssembly.
Cómo esto ayuda a mantener WebAssembly seguro
En términos cotidianos, el artículo describe un “refinador de código” inteligente para WebAssembly: corta programas grandes en muchos fragmentos más pequeños y con sentido, los repara automáticamente para que sigan las estrictas reglas del lenguaje y luego los remodela para explorar rincones inusuales del conjunto de instrucciones. Dado que estos mini‑programas siguen siendo realistas pero compactos, pueden ejecutarse rápida y masivamente, aumentando las probabilidades de descubrir errores sutiles o problemas de seguridad en el software que ejecuta WebAssembly. A medida que WebAssembly se extiende desde los navegadores hacia nubes y dispositivos, esta generación sistemática de binarios de prueba válidos y diversos ofrece una herramienta importante para mantener este ecosistema robusto y seguro.
Cita: Choi, G., Jeon, S. Stack-based static WebAssembly binary slicing and mutation for generating valid sub-binaries. Sci Rep 16, 10910 (2026). https://doi.org/10.1038/s41598-026-45837-y
Palabras clave: Pruebas de WebAssembly, segmentación de programas, mutación binaria, seguridad de software, verificación en tiempo de ejecución