Clear Sky Science · nl

Stackgebaseerde statische WebAssembly-binaire slicing en mutatie voor het genereren van geldige sub-binaries

· Terug naar het overzicht

Waarom het opdelen van code in kleinere stukken ertoe doet

Moderne webapps vertrouwen steeds vaker op WebAssembly, een compacte, machineachtige taal die programma’s geschreven in C, C++ of Rust in browsers, clouds en zelfs kleine ingebedde apparaten bijna native laat draaien. Nu steeds meer systemen WebAssembly uitvoeren, hebben we veilige en grondige methoden nodig om deze uitvoeringsmotoren op fouten en beveiligingslekken te testen. Maar echte WebAssembly-programma’s zijn groot, complex en moeilijk in voldoende variatie te verzamelen. Dit artikel introduceert een nieuwe manier om bestaande WebAssembly-programma’s in veel kleinere,zelfstandige mini‑programma’s te hakken en die vervolgens slim aan te passen. Het resultaat is een rijke bron van geldige, hanteerbare testgevallen die WebAssembly-engines veel effectiever kunnen stress‑testen dan de huidige hulpmiddelen.

Figure 1
Figuur 1.

Van één groot programma naar veel kleine

De auteurs vertrekken vanuit de observatie dat het willekeurig genereren van WebAssembly-code vrijwel nooit werkt: de taal wordt strikt gecontroleerd, vooral rond het gebruik van een stack die tijdelijke waarden bewaart. In plaats van code helemaal opnieuw te maken, beginnen zij met een bestaand "basis"-WebAssembly-binary en gebruiken ze een techniek die programmaslicing heet. Een slice is een gereduceerde versie van een programma die alleen de stukjes code bewaart die invloed hebben op een gekozen interessepunt, zoals een bepaalde instructie binnen een functie. Door terug te volgen via gegevensstroom, geheugengebruik en controleflow vanaf dat punt, extraheert de methode een compact fragment dat nog steeds “iets betekent” qua berekening, in plaats van willekeurige ruis te zijn.

De onzichtbare stack in balans houden

WebAssembly-programma’s vertrouwen op een strikte stackdiscipline: elke instructie popt en pusht waarden, en gestructureerde blokken en branches moeten de stack precies in de juiste vorm achterlaten wanneer ze eindigen. Het naïef wegsnijden van delen van een programma breekt deze discipline bijna altijd, waardoor het resultaat ongeldig wordt. Eerder werk repareerde slices door ze op te vullen met vaste patronen van dummy-instructies, wat ze uitvoerbaar hield maar hun variatie beperkte. Dit artikel introduceert in plaats daarvan een gespecialiseerde stack-balanscorrectie‑algoritme. Het analyseert de controleflow binnen elke slice, vindt plaatsen waar takken of block‑einden de verwachte stackvorm verstoren, en voegt dan op maat gemaakte instructiesequenties toe die ongewenste waarden verwijderen en nieuwe waarden van de juiste types synthetiseren. Deze kleine “gadgets” herstellen correctheid terwijl ze veel rijkere instructiepatronen toestaan dan eenvoudige opvulling.

Hele mini‑programma’s bouwen met gecontroleerde complexiteit

Een gecorrigeerde slice is nog steeds slechts een functielichaam, en die functie kan andere functies aanroepen of afhangen van module‑niveau kenmerken zoals globale variabelen en lineair geheugen. De auteurs breiden hun methode daarom uit om volledige, uitvoerbare mini‑modules samen te stellen. Beginnend bij een willekeurig gekozen entry‑functie slicen en repareren ze herhaaldelijk alle aangeroepen functies, tot een door de gebruiker gekozen "call depth" die bepaalt hoeveel aanroeplagen behouden blijven. Verder dan die diepte worden aanroepen vervangen door korte stand‑ins die het juiste aantal argumenten en resultaten nabootsen zonder meer code binnen te halen. Het systeem reconstrueert ook de omringende secties van een WebAssembly‑module—types, tabellen, geheugen enzovoort—zodat elk gegenereerd binary zelfvoorzienend is en direct door standaardtools en runtimes kan worden uitgevoerd.

Figure 2
Figuur 2.

Variatie toevoegen door zorgvuldige mutatie

Zelfs met slicing lijken veel gegenereerde binaries nog op hun ouderprogramma. Om de dekking te vergroten, introduceren de auteurs een mutatiefase die achterwaarts door elke functie werkt. Voor instructies die inputs consumeren kiest het algoritme willekeurig een nieuw resultaattype, vindt vervangende instructies die dat type produceren, en muteert vervolgens recursief de eerdere instructies die hun inputs leveren zodat de hele keten nog steeds samen past. Dit behoudt stackcorrectheid terwijl de berekening van vorm verandert. In vergelijking met een basisset van echte WebAssembly‑programma’s bevatten de gemuteerde binaries veel meer verschillende instructiepatronen en maken ze veel zwaarder gebruik van zelden geziene features zoals vectoroperaties, die belangrijk zijn om de volledige implementatie van WebAssembly‑engines te stimuleren.

Wat de experimenten aantonen

Om hun aanpak te testen implementeerden de auteurs ongeveer 17.000 regels Python die bijna de volledige WebAssembly 2.0‑standaard dekken. Met een grote publieke dataset van duizenden echte binaries toonden ze aan dat hun techniek programma’s routinematig kan verkleinen tot een fractie van hun oorspronkelijke instructietelling—vaak onder de 40% van de grootste baseline‑groottes—terwijl ze ze geldig houden in meer dan 99% van de gevallen wanneer mutatie is ingeschakeld. De methode draait snel, doorgaans ruim onder de seconde per gegenereerd binary van bescheiden grootte, en vergroot zowel het aanbod aan instructietypes als het aantal unieke instructiesequenties aanzienlijk. Praktisch gezien kunnen testers en beveiligingsonderzoekers met een bescheiden corpus van WebAssembly‑programma’s een grote, diverse suite van kleine, geldige binaries maken die ideaal zijn voor fuzzing, differentiële tests en regressietests van WebAssembly‑runtimes.

Hoe dit helpt WebAssembly veilig te houden

In gewone bewoordingen beschrijft het artikel een slimme “codeverfijner” voor WebAssembly: het hakt grote programma’s in veel kleinere, betekenisvolle brokken, repareert ze automatisch zodat ze nog steeds aan de strikte taalregels voldoen, en vormt ze vervolgens om om ongewone hoeken van de instructieset te verkennen. Omdat deze mini‑programma’s realistisch maar compact blijven, kunnen ze snel en in grote aantallen worden uitgevoerd, waardoor de kans vergroot dat subtiele bugs of beveiligingsproblemen in de software die WebAssembly uitvoert aan het licht komen. Terwijl WebAssembly zich van browsers naar clouds en apparaten verspreidt, biedt zo’n systematische generatie van geldige, diverse testbinaries een belangrijk instrument om dit ecosysteem robuust en veilig te houden.

Bronvermelding: 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

Trefwoorden: WebAssembly-testen, programma-slicing, binaire mutatie, softwarebeveiliging, runtime-verificatie