Clear Sky Science · pl

Statyczne cięcie i mutacja binarnego WebAssembly oparte na stosie w celu generowania prawidłowych pod‑binarek

· Powrót do spisu

Dlaczego ważne jest dzielenie kodu na mniejsze części

Nowoczesne aplikacje webowe coraz częściej polegają na WebAssembly — zwartej, maszynopodobnej reprezentacji, która pozwala programom napisanym w C, C++ czy Rust uruchamiać się w przeglądarkach, chmurach, a nawet małych urządzeniach w tempie zbliżonym do natywnego. Wraz z rosnącą liczbą systemów wykonujących WebAssembly potrzebujemy bezpiecznych i rzetelnych metod testowania tych silników wykonawczych pod kątem błędów i luk bezpieczeństwa. Jednak programy WebAssembly z rzeczywistych zastosowań są duże, złożone i trudno zgromadzić ich wystarczająco zróżnicowany zestaw. Niniejszy artykuł przedstawia nowy sposób wyodrębniania z istniejących programów WebAssembly wielu mniejszych, samodzielnych mini‑programów, które następnie są sprytnie modyfikowane. Efektem jest bogate źródło prawidłowych, kompaktowych przypadków testowych, które mogą znacznie skuteczniej obciążać silniki WebAssembly niż obecne narzędzia.

Figure 1
Figure 1.

Z jednego dużego programu do wielu małych

Autorzy wychodzą z obserwacji, że losowe generowanie kodu WebAssembly niemal nigdy się nie sprawdza: język jest silnie sprawdzany, szczególnie w zakresie użycia stosu przechowującego wartości tymczasowe. Zamiast tworzyć kod od zera, zaczynają od istniejącego „bazowego” binarnego WebAssembly i stosują technikę zwaną cięciem programu (program slicing). Slice to zredukowana wersja programu zawierająca tylko fragmenty kodu wpływające na wybrany punkt zainteresowania, na przykład konkretną instrukcję wewnątrz funkcji. Podążając wstecz za przepływem danych, użyciem pamięci i przepływem sterowania od tego punktu, metoda wyodrębnia kompaktowy fragment, który nadal „coś oblicza”, zamiast być losowym szumem.

Utrzymanie niewidzialnego stosu w równowadze

Programy WebAssembly opierają się na ścisłej dyscyplinie stosu: każda instrukcja pobiera i umieszcza wartości, a struktury bloków i rozgałęzień muszą kończyć się z dokładnie oczekiwanym kształtem stosu. Naiwne wycinanie części programu niemal zawsze narusza tę dyscyplinę, czyniąc wynik nieprawidłowym. Wcześniejsze prace naprawiały slice’y przez dopasowywanie ich przy użyciu stałych wzorców instrukcji‑wypełniaczy, co utrzymywało je uruchamialnymi, ale ograniczało ich różnorodność. W tym artykule wprowadza się dedykowany algorytm korekcji równowagi stosu. Analizuje on przepływ sterowania wewnątrz każdego slice’a, znajduje miejsca, gdzie rozgałęzienia lub zakończenia bloków zaburzają oczekiwany kształt stosu, i wstawia dopasowane sekwencje instrukcji, które usuwają niepotrzebne wartości i syntetyzują nowe odpowiednich typów. Te małe „gadżety” przywracają poprawność, jednocześnie pozwalając na znacznie bogatsze wzorce instrukcji niż proste wypełnianie.

Budowanie całych mini‑programów o kontrolowanej złożoności

Poprawiony slice to wciąż jedynie ciało funkcji, a dana funkcja może wywoływać inne lub zależeć od elementów na poziomie modułu, takich jak zmienne globalne czy pamięć liniowa. Dlatego autorzy rozszerzają metodę tak, aby składała kompletne, wykonywalne mini‑moduły. Zaczynając od losowo wybranej funkcji wejściowej, wielokrotnie tną i naprawiają funkcje, które są wywoływane, aż do określonej przez użytkownika „głębokości wywołań”, która kontroluje, ile warstw wywołań jest zachowanych. Po przekroczeniu tej głębokości wywołania, wywołania są zastępowane krótkimi substytutami naśladującymi odpowiednią liczbę argumentów i wyników bez wciągania dodatkowego kodu. System rekonstruuje również otaczające sekcje modułu WebAssembly — typy, tablice, pamięć itd. — tak aby każdy wygenerowany binarny plik był samodzielny i mógł być uruchomiony bezpośrednio przez standardowe narzędzia i środowiska wykonawcze.

Figure 2
Figure 2.

Dodawanie różnorodności przez uważną mutację

Nawet po cięciu wiele wygenerowanych binarek nadal przypomina program macierzysty. Aby poszerzyć pokrycie, autorzy wprowadzają fazę mutacji, która działa wstecz w obrębie każdej funkcji. Dla instrukcji konsumujących dane algorytm losowo wybiera nowy typ wyniku, znajduje zastępcze instrukcje produkujące ten typ, a następnie rekurencyjnie mutuje wcześniejsze instrukcje dostarczające ich wejścia, tak by cały łańcuch nadal do siebie pasował. To zachowuje poprawność stosu przy jednoczesnym przekształceniu obliczenia. W porównaniu z zestawem bazowych programów WebAssembly z rzeczywistych zastosowań, zmodyfikowane binarki zawierają znacznie więcej odmiennych wzorców instrukcji i częściej wykorzystują rzadko spotykane cechy, takie jak operacje wektorowe, które są istotne do testowania pełnej implementacji silników WebAssembly.

Co pokazują eksperymenty

Aby przetestować swoje podejście, autorzy zaimplementowali około 17 000 linii Pythona obejmującego niemal pełen standard WebAssembly 2.0. Wykorzystując duży publiczny zbiór tysięcy binarek z realnego świata, wykazali, że technika rutynowo skraca programy do ułamka ich pierwotnej liczby instrukcji — często poniżej 40% rozmiarów największych programów bazowych — przy jednoczesnym zachowaniu ich poprawności w ponad 99% przypadków, gdy włączona jest mutacja. Metoda działa szybko, zazwyczaj znacznie poniżej sekundy na wygenerowaną binarkę o umiarkowanym rozmiarze, i znacząco zwiększa zarówno zakres typów instrukcji, jak i liczbę unikalnych sekwencji instrukcji obserwowanych. W praktyce oznacza to, że testerzy i badacze bezpieczeństwa mogą przekształcić umiarkowany korpus programów WebAssembly w duży, zróżnicowany zestaw małych, prawidłowych binarek idealnych do fuzzingu, testów różnicowych i regresyjnych środowisk wykonawczych WebAssembly.

Jak to pomaga utrzymać WebAssembly bezpiecznym

Mówiąc prostymi słowami, artykuł opisuje inteligentny „oczyszczacz kodu” dla WebAssembly: rozbija duże programy na wiele mniejszych, znaczących kawałków, automatycznie je naprawia, tak aby nadal przestrzegały surowych reguł języka, a następnie przekształca je, by eksplorować nietypowe zakamarki zestawu instrukcji. Ponieważ te mini‑programy pozostają realistyczne, a jednocześnie kompaktowe, można je uruchamiać szybko i w dużej liczbie, co zwiększa szansę wykrycia subtelnych błędów lub problemów bezpieczeństwa w oprogramowaniu wykonującym WebAssembly. W miarę jak WebAssembly rozprzestrzenia się z przeglądarek do chmur i urządzeń, takie systematyczne generowanie prawidłowych, zróżnicowanych binarek testowych stanowi ważne narzędzie do utrzymania tego ekosystemu w dobrej kondycji i bezpiecznego stanu.

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

Słowa kluczowe: testowanie WebAssembly, cięcie programu, mutacja binarna, bezpieczeństwo oprogramowania, weryfikacja w czasie wykonywania