Pada infrastruktur backend CI/CD yang menggunakan worker emulasi RISC-V (berbasis QEMU sistem atau custom VMM layer), salah satu masalah paling sulit diisolasi adalah crash non-deterministik. Gejala khas muncul ketika guest kernel menerima beban syscall tinggi dari kompilasi paralel: kernel panik secara acak dengan pesan Instruction page fault atau Kernel access to user space without uaccess flag pada privilege mode yang tidak sesuai.
Artikel ini membahas investigasi teknis dari akar masalah debug trap handling RISC-V, di mana kegagalan sinkronisasi status Control and Status Register (CSR) saat trap bersarang (nested traps) merusak transisi privilege level dan konteks eksekusi guest.
Gejala dan Karakteristik Kerusakan
Saat menjalankan suite kompilasi intensif, runner microVM mengeksekusi puluhan thread yang memicu clone, mmap, dan I/O syscall secara agresif. Dalam kondisi ini, guest kernel Linux (RISC-V 64-bit) sewaktu-waktu melempar panik:
Unable to handle kernel paging request at virtual address 0x0000002badc0ffee
Oops [#1] PREEMPT SMP
CPU: 0 PID: 412 Comm: rustc Not tainted 6.1.0 #1
epc : 0xffffffff800091a4 ra : 0xffffffff80009120 sp : 0xfffffff0018a3d40
sstatus: 0x0000000200000100 sbadaddr: 0x0000002badc0ffee scause: 0x000000000000000cAnalisis log crash menunjukkan inkonsistensi pada bit sstatus.SPP (Supervisor Previous Privilege) dan sstatus.SPIE (Supervisor Previous Interrupt Enable). Register sstatus mengindikasikan bahwa CPU kembali ke User mode (U-mode) padahal program counter (sepc) masih mengarah ke halaman memori kernel internal, atau sebaliknya: eksekusi di U-mode membawa hak akses kernel (S-mode).
Root Cause: Korupsi CSR sstatus dan mie pada Nested Trap
Pada arsitektur RISC-V, mekanisme trap entry ke Supervisor-mode otomatis memodifikasi field pada register sstatus:
- Bit
SPPdiisi dengan privilege mode sebelum trap terjadi (0 untuk U-mode, 1 untuk S-mode). - Bit
SPIEdiisi dengan nilaiSIEaktif saat ini. - Bit
SIEdinonaktifkan (di-set 0) untuk mencegah trap lanjutan secara default.
Masalah terjadi jika emulasi backend menangani asynchronous trap (seperti timer interrupt atau software interrupt IPI) tepat saat CPU sedang berada di dalam trap handler synchronous (misalnya penanganan ecall lingkungan syscall), sebelum kode perakitan kernel sempat menyimpan sstatus dan sepc ke trap frame di memori stack (pt_regs).
Jika backend hipervisor menyuntikkan virtual interrupt ke guest sebelum stack frame sepenuhnya terisolasi dan state register diamankan, modifikasi CSR oleh perangkat keras/emulatur menimpa nilai sstatus.SPP yang lama. Akibatnya, saat instruksi sret dieksekusi di akhir penanganan syscall, privilege CPU tidak kembali ke U-mode melainkan tetap di S-mode dengan address space yang korup.
Isolasi dan Tracing dengan QEMU
Langkah pertama isolasi adalah memetakan titik kegagalan menggunakan fitur tracing QEMU. Jalankan instance runner dengan flag instrumentasi CPU register dan trap execution:
qemu-system-riscv64 \
-M virt -m 4G -smp 4 \
-kernel Image \
-append "console=ttyS0 root=/dev/vda rw init=/init" \
-d int,cpu,exec \
-D ./qemu_trap_anomaly.logMenganalisis file qemu_trap_anomaly.log saat crash terjadi mengungkap rangkaian instruksi kritis berikut:
Trap: scause=0000000000000008 (Environment call from U-mode) epc=0x0000000010042a10
sstatus=0x0000000200000020 -> SPP=0, SPIE=1, SIE=0
[Virtual CPU Interruption Injected by Host]
Trap: scause=8000000000000005 (Supervisor timer interrupt) epc=0xffffffff80003004
sstatus overwrite: SPP=1, SPIE=0, SIE=0
... (Handler interrupt selesai, sret dipanggil)
Returning from trap: sstatus.SPP=1 -> CPU tetap berada di S-mode pada user context!Reproduksi Sederhana (Minimal Trigger)
Snippet assembly berikut mereproduksi racing condition pada trap frame saat penanganan interrupt terjadi sebelum penyimpanan state selesai:
# Assembly reproduksi: simulasi window kerentanan trap frame
.globl trigger_nested_trap_bug
trigger_nested_trap_bug:
// Masuk melalui syscall (ecall otomatis set SPP=0)
li a7, 64 # sys_write
ecall # Hardware set sstatus.SPP = 0, sepc = address berikut
// Kerentanan terjadi di trap handler entry:
// Jika host menginjeksi interrupt sebelum CSR dibaca:
csrr t0, sstatus # Mengambil nilai yang sudah tertimpa interrupt lain
csrr t1, sepc
sd t0, 0(sp) # Trap frame korup tersimpan ke memori
retSolusi: Perbaikan Sinkronisasi Trap Frame di Host Layer
Jika problem berada pada backend emulasi/virtualisasi (custom runner layer atau QEMU accelerator state injection), host tidak boleh menginjeksi trap interrupt jika state guest CPU saat ini memiliki interupsi Supervisor dimatikan (sstatus.SIE == 0) atau jika guest masih berada dalam critical section transisi CSR.
Patch pada logic injeksi interrupt backend emulasi harus memvalidasi bit penahan interrupt sebelum memaksa pengalihan context execution:
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -89,8 +89,14 @@ bool riscv_cpu_has_interrupt_pending(CPURISCVState *env)
uint64_t mie = env->mie;
uint64_t mstatus = env->mstatus;
- /* Cek apakah Supervisor Interrupt aktif */
- bool s_enabled = (env->priv < PRV_S) || (env->priv == PRV_S && (mstatus & MSTATUS_SIE));
+ /* Fix: Jangan izinkan pengiriman interrupt Supervisor jika trap entry
+ sedang berlangsung atau status SIE berada dalam kondisi terkunci */
+ if (env->priv == PRV_S && !(env->sstatus & SSTATUS_SIE)) {
+ return false;
+ }
+
+ uint64_t pending = env->mip & env->mie;
+ return pending != 0;
}Verifikasi dan Penanganan di Sisi Guest Kernel
Di level guest kernel, pastikan trap vector (stvec) mengarah ke handler yang memprioritaskan penyimpanan scratch register (sscratch) secara atomik tanpa mengaktifkan kembali interupsi lokal sebelum seluruh isi register volatile tersimpan sempurna pada stack frame:
ENTRY(handle_exception)
csrrw sp, CSR_SSCRATCH, sp
bnez sp, .Lsave_context
csrr sp, CSR_SSCRATCH
.Lsave_context:
addi sp, sp, -CONTEXT_SIZE
sd ra, 1*8(sp)
sd gp, 3*8(sp)
// Simpan CSR kritis secepat mungkin
csrr t0, CSR_SSTATUS
csrr t1, CSR_SEPC
sd t0, 32*8(sp)
sd t1, 33*8(sp)
// Baru izinkan interrupt nesting jika didukung
...
END(handle_exception)Kesimpulan
Root cause kernel panic pada microVM emulasi RISC-V di bawah beban syscall berat sering kali bukan berasal dari bug alokasi memori Linux, melainkan inkonsistensi isolasi state antara interrupt asynchronous dan exception synchronous pada layer emulasi host. Menjaga kepatuhan aturan arsitektur terkait masking CSR sstatus.SIE saat trap execution menjamin integritas transisi privilege CPU guest dan menstabilkan build runner backend.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!