Skip to content

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_NOSPEC barrier — verifier'ın güvenli olduğunu kanıtlayamadığı speculative path'lerde emit edilir. JIT bunu bir architecture fence'ine indirir: x86_64'te bir LFENCE (önceki instruction'lar retire olana kadar sonraki hiçbir instruction'ın, speculatively dahi, execute olmamasını sağlar), arm64'te ise SB speculation-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 (veya 2), 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/LFENCE eklenmesidir.

References