Skip to content

Branch History Buffer fingerprinting

Bir indirect branch'in nasıl mispredict ettiğini probe'layarak global Branch History Buffer (BHB)'ı — CPU'nun son ~29 taken branch'in kayan fingerprint'ini — okuyup, yakın geçmişteki control flow'u her seferinde 2 bit sızdır.

Mechanism

Neden çalışır

Modern çekirdekler indirect branch'leri global branch history kullanarak predict eder: küçük bir register olan Branch History Buffer (BHB), taken branch'lerin son dizisini katlayıp branch target predictor'a index'ler. Aynı indirect branch'e giden iki farklı path farklı BHB state'leri üretir ve dolayısıyla farklı predicted target'lara çözülebilir — history tabanlı prediction'ı doğru kılan budur.

Saldırganın istismar ettiği invariant: BHB, deterministik, kayıplı bir control flow accumulator'ıdır. Intel'de (Project Zero'nun reverse engineering'i) her taken branch onu, source/destination adres bitlerinin left-shift-by-2 artı XOR-fold'u olarak günceller:

bhb_state <<= 2;
bhb_state ^= (dst & 0x3f);
bhb_state ^= (src & 0xc0) >> 6;
... (further folding of higher src/dst bits)

Branch başına yalnızca 2 bit yeni bilgi girdiği ve buffer yaklaşık son 29 taken branch'ten sonra doyduğu için BHB, programın az önce nerede olduğunun kompakt, ölçülebilir bir özetidir. Saldırgan bir probe branch'i kontrol edip onun mispredict edip etmediğini izleyerek BHB state'ini geri kurtarır — yani herhangi bir belleği okumadan yakın geçmişteki control flow'u fingerprint'ler.

Bu, indirect branch predictor mistraining ve Spectre-BTB'nin zehirlediği aynı global-history zeminidir; burada yazılmak yerine okunuyor.

Walkthrough

1. İki hedefli bir probe branch kur. History'nin target A ile B arasında ayrıştırdığı bir indirect branch yerleştir. Onun prediction'ı mevcut BHB'ye bağlıdır.

// Two architectural paths reach the same indirect call site; which target
// is predicted depends on the BHB accumulated along the path taken.
void (*fp)(void) = choose_target();   // indirect branch under test
fp();                                  // mispredict => slow (measure with rdtscp)

2. History'de yürü. Probe'tan önce bilinen bir taken/not-taken conditional branch zinciri ekle, bir history slot'unu değiştir ve probe'u zamanla. Misprediction oranındaki bir değişim, kontrol edilen branch slot'u başına BHB state'inin 2 bitini açığa çıkarır.

$ ./bhb_probe --history-len 29
slot  0: predicted=A  (history bit-pair 0b01)
slot  1: predicted=B  (history bit-pair 0b10)
...
recovered 29-branch BHB fingerprint: 3a 9c 17 ... (58 bits)

3. Caller'ları / path'leri ayırt et. Ayrı control-flow path'leri ayrı fingerprint'ler ürettiği için, geri kurtarılan BHB bir tanımlayıcı gibi davranır: bir probe, victim'in hangi code path'i izlediğini söyleyebilir ya da sandbox/JIT ile native caller'ı tespit edebilir.

Ampirik BHB derinliği (Project Zero)
25 conditional branches before the probe: paths still distinguishable
26 branches: processor can no longer disambiguate
=> effective history horizon ~ last 29 taken branches
Note: some BHB bits are folded again via XOR for the BTB access;
      the exact final folding was not fully recovered.

Microarch'a özgülük

Yukarıdaki shift/fold pseudocode'u Intel'e özgüdür (Haswell dönemi reverse engineering); Arm ve AMD farklı history genişlikleri ve folding kullanır. SMT kardeşleri ve OS scheduler BHB'yi bozar — thread'i pin'le ve çekirdeği sakinleştir, aksi halde fingerprint alakasız branch'lerle kirlenir.

Mitigation

  • BHB temizleyen barrier'lar (örn. Intel'in BHI_DIS_S / IBRS sınıfı kontrolleri, yazılımsal BHB-flush dizileri) privilege sınırları boyunca global history'yi sıfırlar; hem fingerprinting'i hem de BHI mistraining'i kırar.
  • Secret'a bağlı control flow'dan kaçın ki fingerprint hassas bir şey taşımasın.

References