Clear Sky Science · pt
Fatiamento estático baseado em pilha de binários WebAssembly e mutação para gerar sub‑binários válidos
Por que dividir o código em pedaços menores importa
Aplicações web modernas dependem cada vez mais do WebAssembly, uma linguagem compacta semelhante a código de máquina que permite que programas escritos em C, C++ ou Rust rodem em velocidade quase nativa em navegadores, nuvens e até dispositivos embarcados pequenos. À medida que mais sistemas executam WebAssembly, precisamos de maneiras seguras e abrangentes para testar esses motores de execução em busca de bugs e falhas de segurança. Mas programas WebAssembly do mundo real são grandes, complexos e difíceis de coletar em variedade suficiente. Este artigo apresenta uma nova forma de cortar programas WebAssembly existentes em muitos mini‑programas menores e autocontidos e então ajustá‑los de maneira inteligente. O resultado é um abastecimento rico de casos de teste válidos e de tamanho reduzido que pode exercitar motores WebAssembly com muito mais eficácia do que as ferramentas atuais.

De um programa grande para muitos pequenos
Os autores partem da observação de que simplesmente gerar código WebAssembly aleatoriamente quase nunca funciona: a linguagem é rigidamente verificada, especialmente quanto ao uso de uma pilha que sustenta valores temporários. Em vez de criar código do zero, eles começam com um binário WebAssembly “base” existente e utilizam uma técnica chamada fatiamento de programa. Um slice é uma versão reduzida de um programa que mantém apenas os trechos de código que influenciam um ponto de interesse escolhido, como uma instrução particular dentro de uma função. Seguindo o fluxo de dados, o uso de memória e o fluxo de controle para trás a partir desse ponto, o método extrai um fragmento compacto que ainda “significa algo” em termos de computação, em vez de ser ruído aleatório.
Mantendo a pilha invisível em equilíbrio
Programas WebAssembly dependem de uma disciplina de pilha estrita: cada instrução consome (pop) e produz (push) valores, e blocos estruturados e ramos devem deixar a pilha exatamente na forma esperada quando terminam. Cortar partes de um programa de forma ingênua quase sempre rompe essa disciplina, tornando o resultado inválido. Trabalhos anteriores reparavam slices preenchendo‑os com padrões fixos de instruções fictícias, o que os mantinha executáveis mas limitava sua variedade. Este artigo, em vez disso, introduz um algoritmo dedicado de correção do equilíbrio da pilha. Ele analisa o fluxo de controle dentro de cada slice, encontra pontos onde ramos ou finais de bloco perturbam a forma esperada da pilha e então insere sequências de instruções sob medida que descartam valores desnecessários e sintetizam novos valores dos tipos corretos. Esses pequenos “gadgets” restauram a correção enquanto permitem padrões de instrução muito mais ricos do que o simples preenchimento.
Construindo mini‑programas completos com complexidade controlada
Um slice corrigido ainda é apenas o corpo de uma função, e essa função pode chamar outras ou depender de recursos no nível do módulo, como variáveis globais e memória linear. Os autores, portanto, estendem seu método para montar mini‑módulos completos e executáveis. Partindo de uma função de entrada escolhida aleatoriamente, eles fatiam e consertam repetidamente quaisquer funções chamadas, até uma “profundidade de chamada” escolhida pelo usuário que controla quantas camadas de chamadas são preservadas. Além dessa profundidade, as chamadas são substituídas por substitutos curtos que imitam o número certo de argumentos e resultados sem trazer mais código. O sistema também reconstrói as seções circundantes de um módulo WebAssembly — tipos, tabelas, memória e assim por diante — de modo que cada binário gerado seja autocontido e possa ser executado diretamente por ferramentas e runtimes padrão.

Adicionando variedade por meio de mutação cuidadosa
Mesmo com fatiamento, muitos binários gerados ainda se assemelham ao programa pai. Para ampliar a cobertura, os autores introduzem uma fase de mutação que trabalha retroativamente por cada função. Para instruções que consomem entradas, o algoritmo escolhe aleatoriamente um novo tipo de resultado, identifica instruções substitutas que produzam esse tipo e então muta recursivamente as instruções anteriores que fornecem suas entradas, de modo que toda a cadeia continue a se encaixar. Isso preserva a correção da pilha enquanto remodela a computação. Comparados com um conjunto base de programas WebAssembly do mundo real, os binários mutados contêm muito mais padrões de instrução distintos e fazem uso muito maior de recursos raramente vistos, como operações vetoriais, que são importantes para exercitar a implementação completa dos motores WebAssembly.
O que os experimentos revelam
Para testar sua abordagem, os autores implementaram cerca de 17.000 linhas de Python cobrindo quase todo o padrão WebAssembly 2.0. Usando um grande conjunto público de milhares de binários do mundo real, eles mostraram que sua técnica pode rotineiramente reduzir programas para uma fração da contagem original de instruções — frequentemente abaixo de 40% dos maiores tamanhos de referência — mantendo‑os válidos em mais de 99% dos casos quando a mutação está ativada. O método é rápido, tipicamente bem abaixo de um segundo por binário gerado de tamanho moderado, e aumenta significativamente tanto a variedade de tipos de instrução quanto o número de sequências de instruções únicas observadas. Em termos práticos, isso significa que testadores e pesquisadores de segurança podem transformar um corpus modesto de programas WebAssembly em um conjunto grande e diversificado de binários pequenos e válidos, ideais para fuzzing, testes diferenciais e testes de regressão de runtimes WebAssembly.
Como isso ajuda a manter o WebAssembly seguro
Em termos cotidianos, o artigo descreve um “refinador de código” inteligente para WebAssembly: ele divide programas grandes em muitos pedaços menores e significativos, repara‑os automaticamente para que ainda sigam as regras estritas da linguagem e depois os reconfigura para explorar cantos incomuns do conjunto de instruções. Como esses mini‑programas permanecem realistas e compactos, eles podem ser executados rapidamente e em grande número, aumentando as chances de descobrir bugs sutis ou problemas de segurança no software que executa WebAssembly. À medida que o WebAssembly se espalha dos navegadores para nuvens e dispositivos, essa geração sistemática de binários de teste válidos e diversos oferece uma ferramenta importante para manter esse ecossistema robusto e seguro.
Citação: 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
Palavras-chave: teste de WebAssembly, fatiamento de programas, mutação binária, segurança de software, verificação em tempo de execução