Spectre-in-eBPF Spectre-PHT (v1) gadget¶
Verifier'ın onayladığı bir bounds check'in ardından speculatively bir out-of-bounds load yapan ve secret'ı cache'e encode eden ikinci bir load üzerinden sızdıran, unprivileged bir eBPF programı yükle — kernel içinde bir Spectre v1 (PHT / bounds-check-bypass) gadget'ı.
Mechanism¶
Note
Spectre v1 (Pattern History Table, namıdiğer bounds-check bypass), conditional-branch
predictor'ı istismar eder. if (index < bound) gibi bir check'ten sonra, CPU index
gerçekte out of bounds olsa bile in-bounds path'i speculatively alabilir, geçici olarak
attacker'ın seçtiği belleği dereference eder ve ölçülebilir bir cache footprint'i bırakır.
eBPF bunu tehlikeli yapar, çünkü bir unprivileged kullanıcı verifier'ın architecturally
güvenli olduğunu kanıtladığı bir programı yükleyebilir; ne var ki aynı program, verifier'ın
ayrıca akıl yürütmesi gereken bir speculative path boyunca hâlâ sızdırabilir. Verifier'ın
invariant'ı şudur: misspeculation altında bile hiçbir load veya pointer-ALU bir out-of-bounds
adrese ulaşamaz. Tarihsel olarak (commit 9183671af6db) verifier, speculative path'lerinin
güvenli olduğunu kanıtlayamadığı programları yalnızca reddetti — ki bu, LWN'e göre,
"programların %31 ile %54'ünü etkiler." Daha yeni yaklaşım, speculative path'leri doğrulamaya
devam eder ama güvenliği kanıtlayamadığında bir speculation barrier (BPF_NOSPEC) ekler.
Walkthrough¶
Canonical gadget, iki bağımlı array erişimidir; ilk load speculatively out-of-bounds, ikincisi ise sızdırılan byte'ı cache state'ine encode eder:
/* pseudo-BPF: arr1_base, arr2_base are map pointers; index is attacker-controlled */
if (index < arr1_size) { /* mispredicted as taken */
v = arr1[index]; /* speculative OOB read of secret byte */
tmp = arr2[v * 64]; /* secret -> cache line -> Flush+Reload */
}
Verifier bunu iki katmanda savunur:
- ALU sanitization / index masking — "ALU sanitization, speculation altında bile sonraki
bir ptr erişiminin asla OOB'ye gidememesini sağlamak için getirildi"; tarihsel olarak branch'e
güvenmek yerine pointer/index'in türetilmiş bir mask ile branchless bir
AND'i olarak uygulanmıştır. BPF_NOSPECbarrier — verifier'ın güvenli olduğunu kanıtlayamadığı speculative path'lerde emit edilir. JIT bunu bir architecture fence'ine indirir: x86_64'te birLFENCE(önceki instruction'lar retire olana kadar sonraki hiçbir instruction'ın, speculatively dahi, execute olmamasını sağlar), arm64'te iseSBspeculation-barrier instruction'ı.
Patch serisinden ilgili verifier tesisatı şunları içerir: sanitize_stack_spill'in
nospec_result olarak yeniden adlandırılması, BPF_NOSPEC eklenmesi, nospec-korumalı
variable-offset stack erişimine izin verilmesi ve per-arch opt-out'lar
bpf_jit_bypass_spec_v1() / bpf_jit_bypass_spec_v4(). Testler, barrier yerleşimini
__xlated_unpriv annotation'ı üzerinden assert eder (nospec, translate edilmiş unprivileged
programda görünür).
Detection¶
Bunlar memory corruption değil side-channel info leak'leridir, dolayısıyla yakalanacak bir crash yoktur. Pratik risk sinyali, etkilenen bir kernel'de unprivileged eBPF programları yükleyebilme yeteneğidir. Kernel Spectre dokümantasyonundaki ilgili ifade — "bilinen tek gerçek dünya BHB saldırı vektörünün unprivileged eBPF üzerinden olduğu" — aslında ayrı bir varyant olan BHB (Branch History Buffer, Spectre-v2 / branch-history ailesi) hakkındadır; buradaki Spectre-PHT (v1) gadget'ıyla aynı şey değildir. İki ifadeyi birleştirmemek gerekir: buradaki ilgi, her iki varyantın da paylaştığı aynı unprivileged-eBPF attack surface'inin gerçek dünyadaki önemidir.
Mitigation¶
kernel.unprivileged_bpf_disabled = 1(veya2), unprivileged BPF program yüklemelerini yasaklayarak attack surface'i tamamen kaldırır.- Mainline mitigation, verifier'ın index masking'i artı güvenli olduğunu kanıtlayamadığı
speculative path'lere otomatik
BPF_NOSPEC/LFENCEeklenmesidir.