Även om jag idag är mest känd för nätverk på applikationsnivå och distribuerade system, tillbringade jag den första delen av min karriär på operativsystem och hypervisorer. Jag upprätthåller en djup fascination med de låga detaljerna om hur moderna processorer och systemprogramvara fungerar. När den senaste tidens härdsmälta och Spectre sårbarheter tillkännagavs, jag grävde i den tillgängliga informationen och var angelägna om att lära sig mer.
sårbarheterna är häpnadsväckande; Jag skulle hävda att de är en av de viktigaste upptäckterna inom datavetenskap under de senaste 10-20 åren. Begränsningarna är också svåra att förstå och korrekt information om dem är svår att hitta. Detta är inte förvånande med tanke på deras kritiska natur. Att mildra sårbarheterna har krävt månader av hemligt arbete av alla de stora CPU -, operativsystems-och molnleverantörerna. Det faktum att frågorna hölls under wraps för 6 månader när bokstavligen hundratals människor sannolikt arbetar på dem är fantastiskt.
Även om mycket har skrivits om Meltdown Och Spectre sedan deras tillkännagivande, har jag inte sett en bra mid-level introduktion till sårbarheter och mildringar. I det här inlägget kommer jag att försöka korrigera det genom att ge en mild introduktion till hårdvaru-och mjukvarubakgrunden som krävs för att förstå sårbarheterna, en diskussion om sårbarheterna själva, samt en diskussion om de nuvarande begränsningarna.
viktig anmärkning: eftersom jag inte har arbetat direkt med begränsningarna och inte fungerar på Intel, Microsoft, Google, Amazon, Red Hat, etc. några av de detaljer som jag kommer att ge kanske inte är helt korrekta. Jag har sammanställt det här inlägget baserat på min kunskap om hur dessa system fungerar, offentligt tillgänglig dokumentation och patchar/diskussioner som skickas till LKML och xen-devel. Jag skulle gärna bli korrigerad om något av det här inlägget är felaktigt, men jag tvivlar på att det kommer att hända när som helst snart med tanke på hur mycket av detta ämne fortfarande omfattas av NDA.
i det här avsnittet kommer jag att ge lite bakgrund som krävs för att förstå sårbarheterna. Avsnittet glansar över en stor mängd detaljer och riktar sig till läsare med begränsad förståelse för datormaskinvara och systemprogramvara.
virtuellt minne
virtuellt minne är en teknik som används av alla operativsystem sedan 1970-talet. det ger ett lager av abstraktion mellan minnesadresslayouten som de flesta program ser och de fysiska enheterna som stöder det minnet (RAM, diskar etc.). På en hög nivå tillåter det applikationer att utnyttja mer minne än vad maskinen faktiskt har; detta ger en kraftfull abstraktion som gör många programmeringsuppgifter enklare.
figur 1 visar en förenklad dator med 400 byte minne som anges i ”sidor” på 100 byte (riktiga datorer använder två krafter, vanligtvis 4096). Datorn har två processer, var och en med 200 byte minne över 2 sidor vardera. Processerna kan köra samma kod med fasta adresser i 0-199 byte-intervallet, men de stöds av diskret fysiskt minne så att de inte påverkar varandra. Även om moderna operativsystem och datorer använder virtuellt minne på ett väsentligt mer komplicerat sätt än vad som presenteras i detta exempel, gäller den grundläggande förutsättningen som presenteras ovan i alla fall. Operativsystem abstraherar adresserna som applikationen ser från de fysiska resurserna som stöder dem.
att översätta virtuella till fysiska adresser är en så vanlig operation i moderna datorer att om operativsystemet måste vara involverat i alla fall skulle datorn vara otroligt långsam. Modern CPU-hårdvara tillhandahåller en enhet som kallas en Translation Lookaside Buffer (TLB) som cachar nyligen använda mappningar. Detta gör det möjligt för processorer att utföra adressöversättning direkt i hårdvara större delen av tiden.
Figur 2 visar adressöversättningsflödet:
ett program hämtar en virtuell adress.
CPU: n försöker översätta den med TLB. Om adressen hittas används översättningen.
Om adressen inte hittas konsulterar CPU en uppsättning ”sidtabeller” för att bestämma kartläggningen. Sidtabeller är en uppsättning fysiska minnessidor som tillhandahålls av operativsystemet på en plats som hårdvaran kan hitta dem (till exempel CR3-registret på x86-hårdvara). Sidtabeller kartlägger virtuella adresser till fysiska adresser och innehåller även metadata som behörigheter.
Om sidtabellen innehåller en mappning returneras den, cachas i TLB och används för sökning. Om sidtabellen inte innehåller en mappning höjs ett” sidfel ” till operativsystemet. Ett sidfel är en speciell typ av avbrott som gör att operativsystemet kan ta kontroll och bestämma vad man ska göra när det saknas eller ogiltig mappning. Till exempel kan operativsystemet avsluta programmet. Det kan också tilldela lite fysiskt minne och kartlägga det i processen. Om en sidfelhanterare fortsätter exekveringen kommer den nya mappningen att användas av TLB.
figur 3 visar en något mer realistisk bild av hur virtuellt minne ser ut i en modern dator (Pre-Meltdown — mer om detta nedan). I den här inställningen har vi följande funktioner:
kärnminne visas i rött. Den finns i fysiskt adressintervall 0-99. Kärnminne är speciellt minne som endast operativsystemet ska kunna komma åt. Användarprogram ska inte kunna komma åt det.
användarminnet visas i grått.
odelat fysiskt minne visas i blått.
i det här exemplet börjar vi se några av de användbara funktionerna i virtuellt minne. Primärt:
användarminne i varje process ligger i det virtuella intervallet 0-99, men stöds av olika fysiska minne.
kärnminne i varje process ligger i det virtuella intervallet 100-199, men stöds av samma fysiska minne.
som jag kort nämnde i föregående avsnitt har varje sida associerade behörighetsbitar. Även om kärnminnet mappas in i varje användarprocess, när processen körs i användarläge kan den inte komma åt kärnminnet. Om en process försöker göra det kommer det att utlösa ett sidfel vid vilken tidpunkt operativsystemet kommer att avsluta det. Men när processen körs i kärnläge (till exempel under ett systemanrop) tillåter processorn åtkomst.
Vid denna tidpunkt kommer jag att notera att denna typ av dubbelmappning (varje process som har kärnan mappad in i den direkt) har varit standardpraxis i operativsystemdesign i över trettio år av prestandaskäl (systemanrop är mycket vanliga och det skulle ta lång tid att omforma kärnan eller användarutrymmet vid varje övergång).