Memory-bus contention covert channel¶
İşbirliği yapmayan iki kiracı, iki cache line'ı kapsayan atomic operasyonlarla (
LOCKprefix'li bus lock) global memory-bus contention'ı açıp kapatarak çekirdekler arası sinyalleşir.
Mechanism¶
Note
x86'da, operandı iki cache line'ı kapsayan bir atomic read-modify-write tek bir cache-line lock ile karşılanamaz, bu yüzden işlemci global bir bus lock öne sürmeye geri döner: atomic tamamlanana kadar tüm diğer çekirdeklerden gelen bellek trafiğini stall eder. Bu contention, herhangi bir paylaşılan bellek olmadan çekirdek ve hatta socket sınırlarını aşan, paylaşılan, gözlemlenebilir bir kaynaktır. Bir sender bit'leri bus lock üreterek (yavaşlama = 1) ya da sessiz kalarak (yavaşlama yok = 0) kodlar; bir receiver kendi bellek erişimlerini sürekli zamanlar ve bit'i latency'sinin sıçrayıp sıçramadığından okur.
Kanal, paylaşılan address space'e, paylaşılan cache set'e ihtiyaç duymaz ve çekirdek göçünden sağ çıkar — sistem genelindeki bus/interconnect üzerinde gider. Wu, Xu ve Wang ("Whispers in the Hyper-Space", USENIX Security 2012), memory bus'ı kilitlemek için atomic operasyonlar kullanan bir bus-contention covert kanalı gösterdi: laboratuvar kurulumunda ~746.8 bps, gerçek dünya Amazon EC2'de ise ~%0.39 hata ile ~66.1 bps transmission rate raporladı. Split-lock varyantı (bir cache line'ı geçen atomic) modern, yüksek-contention'lı tetikleyicidir.
Walkthrough¶
Sender: bus lock'u öne sürmek için bir split-lock atomic'i zorla. Bir buffer
allocate et ve lock-prefix'li bir atomic'i, operandı 64 byte'lık bir cache-line
sınırına yayılacak şekilde yerleştir:
// Place an 8-byte atomic so it crosses a cache line -> bus lock (split lock)
char *buf = aligned_alloc(4096, 4096);
uint64_t *split = (uint64_t *)(buf + 64 - 4); // 4 bytes in each of two lines
for (;;) {
if (send_bit_is_1())
for (int i = 0; i < N; i++)
__sync_fetch_and_add(split, 1); // emits LOCK; bus is locked
else
nanosleep(&quiet, NULL); // idle window encodes 0
wait_slot_boundary();
}
Receiver: her slot'ta sabit bir sıradan bellek erişimleri grubunu zamanla; bir latency sıçraması, sender'ın bus'ı tuttuğu anlamına gelir.
uint64_t t0 = rdtscp();
for (int i = 0; i < N; i++) victim_access(work); // ordinary loads
uint64_t dt = rdtscp() - t0;
int bit = (dt > THRESHOLD) ? 1 : 0; // contention -> 1
Beklenen davranış: receiver'ın ölçtüğü latency iki banda kümelenir; yüksek band, sender'ın bus-kilitleme slot'larıyla hizalanır ve çekirdekler arası güvenilir bir seri bit akışı üretir.
Detection¶
Split-lock / bus-lock olayları sayılabilir: modern Intel CPU'ları bunları açığa
çıkarır ve OS onları trap edebilir (Linux split_lock_detect). Ayrıcalıksız bir
process'ten sürekli bir bus lock akışı anormaldir ve performance counter'lar
üzerinden görünürdür.
Mitigation¶
- Cache-line-spanning atomic'ler issue eden process'leri trap etmek ya da
öldürmek için split-lock detection'ı (Linux'ta
split_lock_detect=fatal/warn) etkinleştir. - Bus-lock rate limiting / bus lock'ta VM-exit (hypervisor
bus-lockkontrolleri) kanalı bulut kiracıları için kısıtlar.