V8 Sandbox dirancang untuk mengisolasi memori engine JavaScript dari heap proses utama guna membatasi dampak exploitasi berbasis pointer corruption. Celah logika isolasi dan race condition pada subsystem V8 dapat merusak integritas memori sandbox. Mengintegrasikan automated fuzzing langsung ke dalam continuous integration (CI) memungkinkan pendeteksian dini regresi memory safety sebelum kode masuk ke branch utama.
Arsitektur V8 Sandbox dan Vektor Kerentanan
V8 Sandbox membatasi akses memori JavaScript engine ke dalam ruang virtual memory address terisolasi (biasanya 1 TB). Pointer internal direpresentasikan sebagai sandbox pointer table index atau offset relatif, bukan raw 64-bit virtual address. Kerusakan timbul jika operasi dereferensi bypass table validation atau runtime API native secara tidak sengaja menerima raw pointer yang berada di luar address space sandbox.
Integrasi LibFuzzer bersama AddressSanitizer (ASan) memungkinkan pemantauan akses out-of-bounds, use-after-free, dan uninitialized memory access pada heap V8 selama proses evaluasi bytecode acak berlangsung.
Membuat Target Harness LibFuzzer untuk V8
Harness yang efisien menghindari inisialisasi engine berulang di dalam inner loop. Alokasi v8::Isolate dan v8::Platform dilakukan sekali, sementara scope eksekusi di-reset setiap iterasi data baru.
#include <stddef.h>
#include <stdint.h>
#include <string>
#include "libplatform/libplatform.h"
#include "v8.h"
static std::unique_ptr<v8::Platform> g_platform;
static v8::Isolate* g_isolate = nullptr;
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
v8::V8::InitializeICUDefaultLocation((*argv)[0]);
v8::V8::InitializeExternalStartupData((*argv)[0]);
g_platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(g_platform.get());
v8::V8::Initialize();
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
g_isolate = v8::Isolate::New(create_params);
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size == 0) return 0;
v8::Isolate::Scope isolate_scope(g_isolate);
v8::HandleScope handle_scope(g_isolate);
v8::Local<v8::Context> context = v8::Context::New(g_isolate);
v8::Context::Scope context_scope(context);
std::string js_code(reinterpret_cast<const char*>(data), size);
v8::Local<v8::String> source;
if (!v8::String::NewFromUtf8(g_isolate, js_code.c_str(), v8::NewStringType::kNormal).ToLocal(&source)) {
return 0;
}
v8::TryCatch try_catch(g_isolate);
v8::Local<v8::Script> script;
if (v8::Script::Compile(context, source).ToLocal(&script)) {
v8::MaybeLocal<v8::Value> result = script->Run(context);
(void)result;
}
return 0;
}Pipeline GitHub Actions dengan LibFuzzer dan AddressSanitizer
Eksekusi fuzzing pada CI memerlukan trade-off antara coverage dan waktu build. Setup pipeline melibatkan kompilasi binary menggunakan clang++ dengan flag sanitasi, manajemen corpus via cache, serta upload crash artifact jika LibFuzzer mendeteksi pelanggaran memori.
name: V8 Sandbox Fuzzing
on:
pull_request:
paths:
- 'src/**'
- 'test/fuzz/**'
jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Fuzz Corpus
uses: actions/cache@v4
with:
path: corpus/
key: fuzz-corpus-${{ runner.os }}-${{ hashFiles('test/fuzz/**') }}
restore-keys: |
fuzz-corpus-${{ runner.os }}-
- name: Build Harness with ASan
run: |
mkdir -p build corpus crashes
clang++ -O1 -g -fsanitize=fuzzer,address \
-fno-omit-frame-pointer \
-I./include \
test/fuzz/v8_fuzzer.cc \
-L./out/x64.release/obj -lv8_monolith \
-lpthread -ldl \
-o build/v8_fuzzer
- name: Minimize Corpus
run: |
mkdir -p minimized_corpus
./build/v8_fuzzer -merge=1 minimized_corpus corpus || true
rm -rf corpus
mv minimized_corpus corpus
- name: Run LibFuzzer
run: |
./build/v8_fuzzer corpus \
-max_total_time=600 \
-artifact_prefix=crashes/ \
-print_final_stats=1
- name: Upload Crash Artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: crash-artifacts
path: crashes/*Corpus Minimization dan Manajemen Artefak
Seiring waktu, dataset input fuzzing bertambah besar dan berulang. Flag -merge=1 pada LibFuzzer mengeliminasi input duplikat yang memicu execution path yang sama, mengurangi disk space serta waktu warm-up pipeline. Bila crash terdeteksi, LibFuzzer menulis payload penyebab crash ke direktori crashes/. File ini diunggah sebagai build artifact GitHub Actions untuk debugging lokal.
Reproduksi dan Validasi Crash Lokal
Pengembang dapat mereproduksi laporan bug dari artifact CI secara deterministik tanpa perlu menjalankan kembali seluruh fuzzing engine. Jalankan harness target dengan path file artifact sebagai argumen tunggal:
./build/v8_fuzzer crashes/crash-9d8a3f892c5a01b7AddressSanitizer mencetak stack trace lengkap yang memetakan baris kode spesifik tempat terjadinya alokasi liar, invalid read/write, atau escape dari sandbox boundary.
Optimasi Runtime untuk CI Pipeline
- Persistent Process Mode: Jangan memuat platform V8 di dalam loop
LLVMFuzzerTestOneInput. Inisialisasi platform dan isolate padaLLVMFuzzerInitializemenekan overhead runtime hingga 90%. - Batasi Eksekusi per Input: Gunakan parameter
-timeout=2untuk menghentikan input yang memicu loop JavaScript tak terbatas. - Bypass JIT Optimization saat Fuzzing Parser: Gunakan flag runtime engine
--no-optatau--jitlessjika fokus pengujian berada pada syntax parsing atau AST manipulation untuk mempercepat eksekusi per iterasi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!