Visual walkthrough — Spectre - Meltdown speculative side channels
5.3.15 · D2· Hardware › Advanced Microarchitecture › Spectre - Meltdown speculative side channels
Yeh Spectre - Meltdown speculative side channels ka picture-walkthrough companion hai. Agar koi word yahan unexplained lagta hai, toh woh wahan explain kiya gaya hai — hum kuch bhi assume nahi karte.
Shuru karne se pehle, teen plain-word ideas jinpar hum rely karenge (har ek ka apna vault note hai agar aap aur padhna chahein):
- Cache — CPU ke paas rakhi ek choti, fast memory. Haal hi mein access kiya hua data yahan rehta hai. Dekho Cache Memory Hierarchy.
- Out-of-order execution — CPU instructions strictly upar se neeche nahi chalata; jo ho sakta hai abhi wahi chalata hai, aur baad mein decide karta hai "kya yeh allowed / zaruri tha?". Dekho Out-of-Order Execution.
- Privilege levels — memory ko "user" ya "kernel (supervisor)" tag hota hai. User code ko kernel-tagged memory padhne ki mana hai. Dekho Privilege Levels and Protection Rings.
Step 1 — Do ghariyan: fast cache vs. slow memory
KYA. Pehle hum apne aap ko prove karte hain ki data padhne mein alag kitna time lagta hai is baat par depend karta hai ki woh data cache mein already hai ya nahi.
KYO. Meltdown jo bhi karta hai akhir mein woh sab time measure karna hai. Agar time cache state par depend nahi karta, toh leak karne ka koi channel hi nahi hota. Isliye bilkul foundation yeh hai: cache = fast, main memory = slow, aur yeh gap bahut bada aur measurable hai.
PICTURE. Do bars, same kaam ("jao ek byte fetch karo"), lekin lengths bilkul alag.

vs ke figures yahi wajah hain ki hum kehte hain cache leaks karta hai: CPU ne kabhi bataya nahi secret, lekin read ki duration bata deti hai.
Step 2 — Forbidden read aur woh darwaza jo bahut der se band hota hai
KYA. Hum CPU se kehte hain ki ek kernel address se ek byte padho — woh memory jise hum touch karne ki permission nahi.
KYO. Normally memory-protection hardware (page tables) us page ko supervisor bit se mark karta hai, aur user access ko ek page fault () raise karna padta hai — ek exception jo hamari instruction abort kar deta hai.
Term by term padhein: ek user-mode access jo supervisor-tagged page ko aim kare woh supposed to hai ki page fault mein khatam ho, read marne se pehle ki hum kuch dekh sakein.
PICTURE. Ek user arrow jo ek locked kernel door se takrata hai jispar "supervisor bit" likha hai.

Pakad — jo agli step visible karti hai — yeh hai ki woh check kab hota hai.
Step 3 — Gap: abhi execute karo, baad mein check karo
KYA. Hum split karte hain ki "ek load instruction" time mein actually do stages mein kya karta hai.
KYO. Ek out-of-order CPU mein, execute stage (data fetch karne jao) jaldi aur eagerly chalta hai. Retire stage (result official banao, aur permission check karo) baad mein hota hai. Unke beech ek window hai jahan kernel data cache mein already pull ho chuka hota hai lekin fault abhi fire nahi hua.
PICTURE. Teen ticks wala ek timeline; danger zone Time 1 aur Time 2 ke beech shaded hai.

Time 0: load kernel_addr is issued
Time 1: load EXECUTES out-of-order → kernel byte now sits in cache
Time 2: load RETIRES → permission check → #PF → instruction aborted
└──────────── the gap: data is in cache, no fault yet ─────────┘
- Architectural state — woh jo ek program dekhne ki permission rakhta hai: registers, visible memory. Fault par cleanly undo ho jaata hai.
- Microarchitectural state — hidden speedup machinery: caches, TLBs, predictors. Undo nahi hota.
Step 4 — Encoding: secret value ko cache address mein badlo
KYA. Usi speculative window mein, hum forbidden byte ko ek index ke roop mein use karte hain us array mein jo hamara apna hai.
KYO. Fault fire hone par secret byte gayab ho jaayega — isliye hamein iske marne se pehle ise spend karna hoga. Hum ise spend karte hain isko use karke choose karte hain ki kaun si cache line load ho. Woh choice survive karta hai, kyunki caching roll back nahi hoti.
char secret = *kernel_addr; // forbidden; executes speculatively
char dummy = probe_array[secret * 4096]; // uses secret before the faultTerm by term:
secret— woh byte jo hum chura rahe hain, se .probe_array— ek badi array jo hamari apni hai (allowed memory), har possible byte value ke liye ek slot.secret * 4096— woh multiply jo value → location map karta hai.
4096 se multiply kyun? Ek cache line ~64 bytes hoti hai; agar do candidate values ek hi line mein land karte, toh ek ko touch karna doosre ko touch karne jaisa lagta (false sharing). page size hai, ek line se aaram se bada, isliye 256 possible values mein se har ek apni alag cache line mein land karta hai. Koi collision nahi, koi ambiguity nahi.
PICTURE. Ek dial (secret 0–255) exactly 256 well-separated cache lines mein se ek se wired, woh light up ho rahi hai.

Fault ke baad, secret registers se chala jaata hai — lekin ek cache line, woh index secret par, ab hot hai.
Step 5 — Read hone ko ensure karna: branch ko mistrain karna
KYA. Hum forbidden read ko ek branch mein wrap karte hain jo architecturally hamesha false hai, phir predictor ko "true" guess karne par train karte hain.
KYO. Hume CPU ki zarurat hai ki woh speculatively illegal load chalaaye. Branch predictor if(...) ke outcome ka guess pehle se kar leta hai jab condition actually compute ho rahi hoti hai, aur CPU guessed path par aage daud leta hai. Agar hum pehle branch ko kai baar chalayen jab woh liya jaata hai, toh predictor ki history kehti hai "taken", isliye malicious call par woh speculatively forbidden block mein ghus jaata hai — Steps 2–4 chalaate hue — branch false tha yeh discover karne aur unwind karne se pehle.
if (untrusted_condition) { // trained to look "true"
char secret = *kernel_addr;
probe_array[secret * 4096] = 1; // encode (Step 4)
}PICTURE. Ek predictor "history register" past outcomes ka jo fork ko speculative (galat) path ki taraf tilt kar raha hai.

Step 6 — Fingerprint padhna: sabhi 256 lines ko time karo
KYA. Hum sabhi 256 candidate byte values par loop karte hain aur time karte hain ki har probe_array[i * 4096] padhne mein kitna waqt lagta hai.
KYO. Step 1 se, in reads mein se ek fast aayegi (woh line Step 4 se hot hai); baaki 255 slow aayengi. Fast index hi secret byte hai.
for (int i = 0; i < 256; i++) {
t0 = rdtsc();
dummy = probe_array[i * 4096];
dt = rdtsc() - t0;
if (dt < CACHE_THRESHOLD) recovered = i; // the hot line
}rdtsc()— CPU ka cycle counter padhta hai (hamari stopwatch).CACHE_THRESHOLD— aur ke beech ek cutoff (maan lo ~100 cycles); isse neeche = hot.recovered— winning ; woh byte hi secret hai.
PICTURE. 256 timing bars, 255 lamba (slow), exactly ek chota (fast) — chota wala answer ke roop mein flag kiya hua.

Poori chain, ab symbol by symbol samajhi hui:
Step 7 — Edge cases: agar yeh cleanly kaam na kare toh?
Real attacks ko messy cases mein survive karna hota hai. Har ek neeche handle kiya gaya hai, taaki reader ko koi surprise na mile.
Case A — secret byte 0 hai. Toh value ki line probe_array[0] par hot hai. Theek hai — jab tak kisi ne pehle se line 0 ko unrelated reasons se touch na kiya ho (noise). Fix: pehle sabhi 256 lines flush karo clflush se taaki har line cold start kare; sirf speculative load hi ek ko warm kar sake.
Case B — noise / koi line clearly hot nahi. Speculation probabilistic hai; kabhi kabhi window close ho jaati hai encode land hone se pehle. Fix: poora attack kai baar repeat karo aur woh value lo jo zyada baar fast dikhe. Ek trial unreliable hai; hazaron par vote solid hota hai.
Case C — do lines hot lagti hain. Prefetchers ya neighbour access ne extra line warm kar di. -byte spacing (Step 4) pehle se yeh minimize karta hai; voting ke saath combine karke (Case B), sahi secret frequency se jeet jaata hai.
Case D — mitigation on hai. Agar KPTI (Kernel Page Table Isolation) active hai, toh kernel pages user page tables mein simply map hi nahi hote — isliye Step 2 ke address ka koi translation hi nahi hoga, speculative load ke paas kuch fetch karne ko nahi, aur channel band ho jaata hai. (Iska cost: har kernel entry par ek TLB flush.)
PICTURE. Chaar mini-panels: all-cold start, ek noisy vote histogram ek value par converge karta hua, ek double-hot majority se resolve hoti hui, aur ek "KPTI: page unmapped" blocked path.

Ek-picture summary

Pipeline compressed: flush (sabhi lines cold) → predictor ko mistrain karo → forbidden read speculate karo → value ko ek cache line mein encode karo → fault & rollback (registers erase, cache footprint survive) → sabhi lines time karo → byte recover karo.
Recall Feynman retelling — bina jargon ke dost ko explain karo
Socho ek librarian (CPU) hai jise ek secret file padhne ki mana hai, lekin jo bahut eager hai aur kaam shuru kar deti hai check karne se pehle ki allowed hai ya nahi. Tum use dare karte ho secret padho. Woh use grab karti hai, aur — boss ke rokne se pehle — woh secret number use karke ek huge shelf se ek specific book uthaa ke apni desk par rakh deti hai. Phir boss chillata hai "tumhe permission nahi thi!" aur woh secret number bhool jaati hai aur sab wapas rakh deti hai… lekin woh ek book desk par abhi bhi warm hai. Tum aate ho aur har book touch karte ho: 255 cold aur slow hain, ek warm aur fast hai. Us warm wali ki shelf position tumhe woh secret number bata deti hai jo use reveal karna mana tha. Reliable banane ke liye tum (1) har book cold start karo, (2) use eager banao pehle kai legitimate baar dare karke, aur (3) repeat karo aur vote karo taaki noise average out ho jaaye. Ek defence jo actually kaam karta hai: secret file ko kahi bhi aisi jagah mat rakho jahan woh pahunch sake (KPTI use unmapped kar deta hai).
Recall Quick self-check
Multiply 4096 use kyun karna padta hai, 64 nahi? ::: 4096 (page size) ek cache line se bada hai, isliye 256 possible byte values mein se har ek apni distinct cache line mein land karta hai — candidate values ke beech koi false sharing nahi.
Fault ke baad CPU kaunsi state roll back karna bhool jaata hai? ::: Microarchitectural state — specifically woh cache line jo speculative encode ne warm ki. Registers (architectural state) correctly roll back ho jaate hain.
Timing loop mein kaun si value secret hai? ::: Woh index jiska probe_array[i*4096] read fast aata hai (CACHE_THRESHOLD se neeche) — woh ek hot line.
Malicious call se pehle branch train kyun karo? ::: Taaki branch predictor "taken" guess kare aur CPU speculatively forbidden read chalaye mispredict pakde jaane se pehle.