Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln innehåller vägledning för utvecklare att identifiera och mildra sårbarheter i spekulativa exekveringssidokanaler i C++-programvara. Dessa sårbarheter kan avslöja känslig information över förtroendegränser och kan påverka programvara som körs på processorer som stöder spekulativ, out-of-order-körning av instruktioner. Den här klassen av sårbarheter beskrevs först i januari 2018 och ytterligare bakgrund och vägledning finns i Microsofts säkerhetsrådgivning.
Vägledningen i den här artikeln gäller de klasser av sårbarheter som representeras av:
- CVE-2017-5753, även känd som Spectre variant 1. Den här klassen av sårbarheter i hårdvara är relaterad till sidokanaler som kan uppstå på grund av spekulativ körning som inträffar till följd av en felaktig förutsägelse av villkorsgren. Microsoft C++-kompilatorn i Visual Studio 2017 (från och med version 15.5.5) har stöd för växeln - /Qspectresom ger en kompileringstidsreducering för en begränsad uppsättning potentiellt sårbara kodmönster relaterade till CVE-2017-5753. Växeln- /Qspectreär också tillgänglig i Visual Studio 2015 Update 3 till KB 4338871. Dokumentationen- /Qspectreför flaggan innehåller mer information om dess effekter och användning.
- CVE-2018-3639, även kallat SSB (Speculative Store Bypass). Den här sårbarhetsklassen för maskinvara är relaterad till sidokanaler som kan uppstå på grund av spekulativ exekvering av en läsning före en beroende lagring till följd av en felaktig förutsägelse av minnesåtkomst. 
En tillgänglig introduktion till sidokanalssårbarheter i spekulativ exekvering finns i presentationen med titeln The Case of Spectre and Meltdown av ett av de forskarteam som upptäckte dessa problem.
Vad är hårdvarusårbarheter i spekulativ exekvering i sidokanalen?
Moderna processorer ger en högre prestandanivå genom användning av spekulativ och omordnad körning av instruktioner. Detta uppnås ofta genom att till exempel förutsäga målet för grenar (indirekta och villkorsstyrda), vilket gör det möjligt för processorn att påbörja spekulativ körning av instruktioner vid det förutsagda grenmålet och därmed undvika ett stillastånd tills det faktiska grenmålet har lösts. Om processorn senare upptäcker att en felprediction inträffade ignoreras alla datortillstånd som beräknades spekulativt. Detta säkerställer att det inte finns några arkitektoniskt synliga effekter av den felaktiga spekulationen.
Spekulativ körning påverkar inte det arkitektoniskt synliga tillståndet, men det kan lämna kvar restspårningar i icke-arkitektoniskt tillstånd, till exempel de olika cacheminnen som används av processorn. Det är dessa restspårningar av spekulativ körning som kan ge upphov till sårbarheter i sidokanalen. För att bättre förstå detta bör du överväga följande kodfragment som ger ett exempel på CVE-2017-5753 (Förbikoppling av gränskontroll):
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
    if (untrusted_index < buffer_size) {
        unsigned char value = buffer[untrusted_index];
        return shared_buffer[value * 4096];
    }
}
I det här exemplet ReadByte tillhandahålls en buffert, en buffertstorlek och ett index i bufferten. Indexparametern, som anges av untrusted_index, tillhandahålls av en mindre privilegierad kontext, till exempel en icke-administrativ process. Om untrusted_index är mindre än buffer_size, läss tecknet i indexet från buffer och används för att indexera till en delad minnesregion som refereras till av shared_buffer.
Ur ett arkitekturperspektiv är den här kodsekvensen helt säker eftersom den garanteras att untrusted_index den alltid är mindre än buffer_size. I närvaro av spekulativ körning är det dock möjligt att processorn felbedömer den villkorsstyrda grenen och kör if-instruktionens brödtext även när untrusted_index är större än eller lika med buffer_size. Som en följd av detta kan processorn spekulativt läsa en byte från utanför gränserna buffer för (vilket kan vara en hemlighet) och kan sedan använda bytevärdet för att beräkna adressen för en efterföljande belastning via shared_buffer.
Även om processorn så småningom kommer att upptäcka den här felpredictionen kan kvarvarande biverkningar finnas kvar i CPU-cachen som avslöjar information om bytevärdet som lästes utanför gränserna från buffer. Dessa biverkningar kan identifieras av en mindre privilegierad kontext som körs på systemet genom att undersöka hur snabbt varje cacherad i shared_buffer nås. De steg som kan vidtas för att åstadkomma detta är:
- Anropa - ReadByteflera gånger med- untrusted_indexmindre än- buffer_size. Den attackerande kontexten kan orsaka att offrets kontext anropar- ReadByte(t.ex. via RPC) så att grenförutsägaren tränas att inte tas, eftersom- untrusted_indexär mindre än- buffer_size.
- Töm alla cacherader i - shared_buffer. Den attackerande kontexten måste tömma alla cacherader i den delade minnesregion som anges av- shared_buffer. Eftersom minnesregionen delas är detta enkelt och kan utföras med hjälp av inbyggda funktioner som- _mm_clflush.
- Anropa - ReadBytemed- untrusted_indexstörre än- buffer_size. Den attackerande kontexten gör att offerkontexten anropar- ReadByteså att den felaktigt förutsäger att grenen inte kommer att tas. Detta driver processorn till att spekulativt köra innehållet i if-blocket med- untrusted_indexstörre än- buffer_size, vilket leder till en out-of-bounds-läsning av- buffer. Därför indexeras- shared_buffermed ett potentiellt hemligt värde som lästes utanför de tilldelade gränserna, vilket gör att den respektive cachelinjen laddas in av processorn.
- Läs varje cacherad i - shared_bufferför att se vilken som nås snabbast. Den attackerande kontexten kan läsa varje cacherad i- shared_bufferoch identifiera cacheraden som läses in betydligt snabbare än de andra. Det här är den cachelinje som sannolikt har förts in i steg 3. Eftersom det finns en 1:1-relation mellan bytevärde och cacherad i det här exemplet kan angriparen härleda det faktiska värdet för bytet som lästes utanför gränserna.
Stegen ovan är ett exempel på hur du använder en teknik som kallas FLUSH+RELOAD tillsammans med att utnyttja en instans av CVE-2017-5753.
Vilka programvaruscenarier kan påverkas?
Utveckling av säker programvara med en process som SDL (Security Development Lifecycle ) kräver vanligtvis att utvecklare identifierar de förtroendegränser som finns i deras program. Det finns en förtroendegräns på platser där ett program kan interagera med data som tillhandahålls av en mindre betrodd kontext, till exempel en annan process i systemet eller en icke-administrativ användarlägesprocess när det gäller en enhetsdrivrutin i kernelläge. Den nya klassen av sårbarheter som involverar spekulativa exekveringskanaler påverkar många av tillitsspärrarna i befintliga programvarusäkerhetsmodeller som isolerar kod och data på en apparat.
Följande tabell innehåller en sammanfattning av programvarusäkerhetsmodellerna där utvecklare kan behöva oroa sig för att dessa sårbarheter inträffar:
| Förtroendegräns | Beskrivning | 
|---|---|
| Gräns för virtuell dator | Program som isolerar arbetsbelastningar på separata virtuella datorer som tar emot ej betrodda data från en annan virtuell dator kan vara i fara. | 
| Kernelgräns | En enhetsdrivrutin i kernelläge som tar emot data som inte är betrodda från en icke-administrativ användarlägesprocess kan vara i fara. | 
| Processgräns | Ett program som tar emot data som inte är betrodda från en annan process som körs i det lokala systemet, till exempel via ett fjärrproceduranrop (RPC), delat minne eller andra IPC-mekanismer (Inter-Process Communication) kan vara i riskzonen. | 
| Enklavgräns | Ett program som körs inom en säker enklav (till exempel Intel SGX) som tar emot ej betrodda data utanför enklaven kan vara i fara. | 
| Språkgräns | Ett program som tolkar eller JUST-In-Time (JIT) kompilerar och kör obetrodd kod som skrivits på ett högre språk kan vara i fara. | 
Programmen som har en attackyta som utsätts för någon av ovanstående tillitsgränser bör granska koden på attackytan för att identifiera och begränsa möjliga instanser av sidokanalsårbarheter vid spekulativ körning. Det bör noteras att förtroendegränser som exponeras för fjärrangreppsytor, till exempel fjärrnätverksprotokoll, inte har visat sig vara utsatta för sårbarheter i sidokanaler på grund av spekulativ exekvering.
Potentiellt sårbara kodningsmönster
Sidokanal- sårbarheter vid spekulativ exekvering kan uppstå till följd av flera kodmönster. Det här avsnittet beskriver potentiellt sårbara kodningsmönster och innehåller exempel för var och en, men det bör erkännas att variationer i dessa teman kan finnas. Därför rekommenderas utvecklare att ta dessa mönster som exempel och inte som en fullständig lista över alla potentiellt sårbara kodningsmönster. Samma klasser av säkerhetsrisker för minnet som kan finnas i programvara i dag kan också finnas längs spekulativa och oordnade körningsvägar, inklusive men inte begränsat till buffertöverskridanden, out-of-bounds-matrisåtkomster, oinitierad minnesanvändning, typförvirring och så vidare. Samma primitiver som angripare kan använda för att utnyttja säkerhetsrisker i minnet längs arkitekturvägar kan också gälla för spekulativa sökvägar.
I allmänhet kan spekulativa exekveringssidokanaler relaterade till felaktig förutsägelse av villkorsgrenar uppstå när ett villkorsuttryck fungerar på data som kan styras eller påverkas av en mindre pålitlig kontext. Detta kan till exempel innehålla villkorsuttryck som används i if, for, while, switcheller ternary-instruktioner. För var och en av dessa instruktioner kan kompilatorn generera en villkorsstyrd gren som processorn sedan kan förutsäga grenmålet för vid körning.
För varje exempel infogas en kommentar med frasen "SPECULATION BARRIER" där en utvecklare kan införa en barriär som en åtgärd. Detta beskrivs mer detaljerat i avsnittet om åtgärder.
Spekulativ out-of-bounds-belastning
Denna typ av kodningsmönster omfattar en felaktig förutsägelse för en villkorlig gren som leder till en spekulativ minnesåtkomst utanför gränser.
Matrisens out-of-bounds-belastning matar in en belastning
Det här kodningsmönstret är det ursprungligen beskrivna sårbara kodningsmönstret för CVE-2017-5753 (Förbikoppling av gränskontroll). I bakgrundsavsnittet i den här artikeln beskrivs det här mönstret i detalj.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
    if (untrusted_index < buffer_size) {
        // SPECULATION BARRIER
        unsigned char value = buffer[untrusted_index];
        return shared_buffer[value * 4096];
    }
}
På samma sätt kan en out-of-bounds-matrisbelastning uppstå tillsammans med en loop som överskrider dess avslutande villkor på grund av en felprediction. I det här exemplet kan den villkorsstyrda grenen som är associerad med uttrycket x < buffer_size felbedömas och spekulativt köra loopens brödtext for när x är större än eller lika med buffer_size, vilket resulterar i en spekulativ belastning utanför gränserna.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadBytes(unsigned char *buffer, unsigned int buffer_size) {
    for (unsigned int x = 0; x < buffer_size; x++) {
        // SPECULATION BARRIER
        unsigned char value = buffer[x];
        return shared_buffer[value * 4096];
    }
}
Matrisens out-of-bounds-belastning matar in en indirekt gren
Det här kodningsmönstret omfattar ett fall där en felaktig förutsägelse av villkorsgren kan leda till en out-of-bounds-åtkomst till en array av funktionspekare, vilket sedan leder till en indirekt gren till måladressen som lästes utanför dess gränser. Följande kodfragment innehåller ett exempel som visar detta.
I det här exemplet tillhandahålls en ej betrodd meddelandeidentifierare till DispatchMessage via parametern untrusted_message_id . Om untrusted_message_id är mindre än MAX_MESSAGE_ID, används den för att indexera en matris med funktionspekare och gå till motsvarande mål för grenen. Den här koden är säker arkitektoniskt, men om processorn felbedömer den villkorsstyrda grenen kan det resultera i att DispatchTable indexeras med untrusted_message_id när dess värde är större än eller lika med MAX_MESSAGE_ID, vilket leder till en out-of-bounds-åtkomst. Detta kan leda till spekulativ körning från en grenadress som härleds utanför matrisens gränser, vilket kan leda till informationsläckage beroende på vilken kod som körs spekulativt.
#define MAX_MESSAGE_ID 16
typedef void (*MESSAGE_ROUTINE)(unsigned char *buffer, unsigned int buffer_size);
const MESSAGE_ROUTINE DispatchTable[MAX_MESSAGE_ID];
void DispatchMessage(unsigned int untrusted_message_id, unsigned char *buffer, unsigned int buffer_size) {
    if (untrusted_message_id < MAX_MESSAGE_ID) {
        // SPECULATION BARRIER
        DispatchTable[untrusted_message_id](buffer, buffer_size);
    }
}
Precis som när det gäller en matris som matar in en annan belastning utanför gränserna, kan det här villkoret också uppstå tillsammans med en loop som överskrider dess avslutande villkor på grund av en felprediction.
Matris utanför gränserna lagrar matning av en indirekt gren
Det föregående exemplet visade hur en spekulativ belastning utanför gränserna kan påverka ett indirekt grenmål, men det är också möjligt för ett out-of-bounds-lager att ändra ett indirekt grenmål, till exempel en funktionspekare eller en returadress. Detta kan möjligtvis leda till spekulativ körning från en av angriparen specificerad adress.
I det här exemplet skickas ett obetrott index genom parametern untrusted_index . Om untrusted_index är mindre än antalet element i matrisen pointers (256 element) skrivs det angivna pekarvärdet i ptr till matrisen pointers . Den här koden är säker arkitektoniskt, men om processorn felbedömer den villkorsstyrda grenen kan ptr spekulativt skrivas utanför gränserna för den stackallokerade pointers arrayen. Detta kan leda till spekulativ förvrängning av returadressen till WriteSlot. Om en angripare kan styra värdet för ptr, kan de orsaka spekulativ körning från en godtycklig adress när WriteSlot returneras längs den spekulativa sökvägen.
unsigned char WriteSlot(unsigned int untrusted_index, void *ptr) {
    void *pointers[256];
    if (untrusted_index < 256) {
        // SPECULATION BARRIER
        pointers[untrusted_index] = ptr;
    }
}
Om en lokal funktionspekare med namnet func allokerades på stacken kan det också vara möjligt att spekulativt ändra den adress func refererar till när felprediktionen för villkorsgrenen inträffar. Detta kan resultera i spekulativ exekvering från en godtycklig adress när funktionspekaren anropas via.
unsigned char WriteSlot(unsigned int untrusted_index, void *ptr) {
    void *pointers[256];
    void (*func)() = &callback;
    if (untrusted_index < 256) {
        // SPECULATION BARRIER
        pointers[untrusted_index] = ptr;
    }
    func();
}
Det bör noteras att båda dessa exempel omfattar spekulativ ändring av stackallokerade indirekta grenpekare. Det är möjligt att spekulativ ändring också kan ske för globala variabler, heap-allokerat minne och till och med skrivskyddat minne på vissa processorer. För stackallokerat minne vidtar Microsoft C++-kompilatorn redan åtgärder för att göra det svårare att spekulativt ändra stackallokerade indirekta grenmål, till exempel genom att ordna om lokala variabler så att buffertar placeras intill en säkerhetscookie som en del av /GS kompilatorsäkerhetsfunktionen.
Spekulativ typförvirring
Den här kategorin behandlar kodningsmönster som kan ge upphov till en spekulativ typförvirring. Detta inträffar när minnet används med en felaktig typ längs en icke-arkitektonisk sökväg under spekulativ körning. Både felprediction för villkorsstyrd gren och förbikoppling av spekulativt lager kan potentiellt leda till en förvirring av spekulativ typ.
För förbikoppling av spekulativt lager kan detta inträffa i scenarier där en kompilator återanvänder en stackplats för variabler av flera typer. Det beror på att arkitekturarkivet för en variabel av typen A kan kringgås, vilket gör att belastningen av typen A kan köras spekulativt innan variabeln tilldelas. Om den tidigare lagrade variabeln är av en annan typ kan detta skapa förutsättningar för en spekulativ typförvirring.
För felaktig förutsägelse av villkorsstyrd gren kommer följande kodfragment att användas för att beskriva olika villkor som spekulativ typkonfusion kan ge upphov till.
enum TypeName {
    Type1,
    Type2
};
class CBaseType {
public:
    CBaseType(TypeName type) : type(type) {}
    TypeName type;
};
class CType1 : public CBaseType {
public:
    CType1() : CBaseType(Type1) {}
    char field1[256];
    unsigned char field2;
};
class CType2 : public CBaseType {
public:
    CType2() : CBaseType(Type2) {}
    void (*dispatch_routine)();
    unsigned char field2;
};
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ProcessType(CBaseType *obj)
{
    if (obj->type == Type1) {
        // SPECULATION BARRIER
        CType1 *obj1 = static_cast<CType1 *>(obj);
        unsigned char value = obj1->field2;
        return shared_buffer[value * 4096];
    }
    else if (obj->type == Type2) {
        // SPECULATION BARRIER
        CType2 *obj2 = static_cast<CType2 *>(obj);
        obj2->dispatch_routine();
        return obj2->field2;
    }
}
Förvirring av spekulativ typ som leder till en laddning utanför gränserna
Det här kodningsmönstret omfattar ett fall där en spekulativ typförvirring kan resultera i en åtkomst utanför gränserna eller en typförvirrad fältåtkomst där det inlästa värdet matar in en efterföljande laddningsadress. Detta liknar matrisens kodningsmönster utanför gränserna, men det manifesteras genom en alternativ kodningssekvens som visas ovan. I det här exemplet kan en angreppskontext göra att offerkontexten kör ProcessType flera gånger med ett objekt av typen CType1 (det type fältet är lika med Type1). Detta kommer att få effekten att träna den villkorsstyrda förgreningen för den första if instruktionen att förutsäga att den inte tas. Den attackerande kontexten kan sedan orsaka att offerkontexten utför ProcessType med hjälp av ett objekt av typen CType2. Detta kan leda till en spekulativ typförvirring om den villkorsstyrda grenen för den första if instruktionen felbeskriver och kör instruktionens if brödtext och därmed genererar ett objekt av typen CType2 till CType1. Eftersom CType2 är mindre än CType1 resulterar minnesåtkomsten till CType1::field2 i en spekulativ out-of-bounds-laddning av data som kan innehålla hemlig information. Det här värdet används sedan vid inläsning från shared_buffer vilket kan skapa observerbara sidoeffekter, som med exemplet med arrayen utanför gränserna som beskrevs tidigare.
Förvirring av spekulativ typ som leder till en indirekt gren
Det här kodningsmönstret omfattar ett fall där en spekulativ typförväxling kan leda till en osäker indirekt övergång under spekulativ exekvering. I det här exemplet kan en angreppskontext göra att offerkontexten kör ProcessType flera gånger med ett objekt av typen CType2 (det type fältet är lika med Type2). Detta kommer att ha effekten att träna den villkorsstyrda grenen så att den första if instruktionen tas och else if instruktionen inte tas. Den attackerande kontexten kan sedan orsaka att offerkontexten utför ProcessType med hjälp av ett objekt av typen CType1. Detta kan leda till en spekulativ typförvirring om den villkorsstyrda grenen för den första if-instruktionen förutsäger taget och else if-instruktionen förutsäger inte tas, vilket leder till att kroppen av else if körs och kastar ett objekt av typen CType1 till CType2. Eftersom fältet CType2::dispatch_routine överlappar matrisen charCType1::field1kan det resultera i en spekulativ indirekt gren till ett oavsiktligt grenmål. Om den attackerande kontexten kan styra bytevärdena i matrisen CType1::field1 kanske de kan styra grenens måladress.
Spekulativ onitialiserad användning
Den här kategorin av kodningsmönster omfattar scenarier där spekulativ körning kan komma åt oinitierat minne och använda det för att mata in en efterföljande belastning eller indirekt gren. För att dessa kodningsmönster ska vara exploaterbara måste en angripare kunna styra eller påverka innehållet i det minne som används utan att initieras av kontexten som den används i.
Spekulativ onitialiserad användning som leder till en out-of-bounds-belastning
En spekulativ oinitialiserad användning kan potentiellt leda till en out of bounds-belastning med hjälp av ett värde kontrollerat av angriparen. I exemplet nedan tilldelas index värdet trusted_index på alla arkitektursökvägar, och trusted_index antas vara mindre än eller lika med buffer_size. Beroende på vilken kod kompilatorn har skapat är det dock möjligt att en spekulativ förbikoppling av lagring kan inträffa, vilket gör att hämtning från buffer[index] och beroende uttryck kan köras före tilldelningen till index. Om detta inträffar kommer ett oinitialiserat värde för index att användas som förskjutning i buffer, vilket kan göra det möjligt för en angripare att läsa känslig information utanför gränserna och förmedla detta via en sidokanal genom den beroende inläsningen av shared_buffer.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
void InitializeIndex(unsigned int trusted_index, unsigned int *index) {
    *index = trusted_index;
}
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int trusted_index) {
    unsigned int index;
    InitializeIndex(trusted_index, &index); // not inlined
    // SPECULATION BARRIER
    unsigned char value = buffer[index];
    return shared_buffer[value * 4096];
}
Spekulativ onitialiserad användning som leder till en indirekt gren
En spekulativ onitialiserad användning kan potentiellt leda till en indirekt gren där grenmålet styrs av en angripare. I exemplet nedan tilldelas routine antingen DefaultMessageRoutine1 eller DefaultMessageRoutine beroende på värdet för mode. På arkitekturvägen kommer detta att resultera i att routine alltid initieras före den indirekta grenen. Beroende på vilken kod som kompilatorn producerar kan det dock uppstå en spekulativ förbikoppling av lagring som gör att den indirekta grenen genom routine kan köras spekulativt före tilldelningen till routine. Om detta inträffar kan en angripare spekulativt köra från en godtycklig adress, förutsatt att angriparen kan påverka eller kontrollera det oinitialiserade värdet för routine.
#define MAX_MESSAGE_ID 16
typedef void (*MESSAGE_ROUTINE)(unsigned char *buffer, unsigned int buffer_size);
const MESSAGE_ROUTINE DispatchTable[MAX_MESSAGE_ID];
extern unsigned int mode;
void InitializeRoutine(MESSAGE_ROUTINE *routine) {
    if (mode == 1) {
        *routine = &DefaultMessageRoutine1;
    }
    else {
        *routine = &DefaultMessageRoutine;
    }
}
void DispatchMessage(unsigned int untrusted_message_id, unsigned char *buffer, unsigned int buffer_size) {
    MESSAGE_ROUTINE routine;
    InitializeRoutine(&routine); // not inlined
    // SPECULATION BARRIER
    routine(buffer, buffer_size);
}
Alternativ för minskning
Sidokanalssårbarheter i spekulativ exekvering kan mitigeras genom att göra ändringar i källkoden. Dessa ändringar kan omfatta att mildra specifika instanser av en säkerhetsrisk, till exempel genom att lägga till en spekulationsbarriär, eller genom att göra ändringar i designen av en applikation för att göra känslig information otillgänglig för spekulativ exekvering.
Spekulationsspärr genom manuell instrumentering
En spekulationsbarriär kan infogas manuellt av en utvecklare för att förhindra att spekulativ körning fortsätter längs en väg som inte är arkitektonisk. En utvecklare kan till exempel infoga en spekulationsbarriär före ett farligt kodningsmönster i brödtexten i ett villkorsblock, antingen i början av blocket (efter den villkorsstyrda grenen) eller före den första belastningen som är viktig. Detta förhindrar att en felaktig förutsägelse av en villkorsgren kör den farliga koden på en icke-arkitektonisk bana genom att serialisera körningen. Spekulationsbarriärsekvensen skiljer sig åt efter maskinvaruarkitektur enligt beskrivningen i följande tabell:
| Arkitektur | Inbyggd spekulationsbarriär för CVE-2017-5753 | Inneboende spekulationsbarriär för CVE-2018-3639 | 
|---|---|---|
| x86/x64 | _mm_lfence() | _mm_lfence() | 
| ARM | inte tillgänglig för närvarande | __dsb(0) | 
| ARM64 | inte tillgänglig för närvarande | __dsb(0) | 
Följande kodmönster kan till exempel minimeras med hjälp av det _mm_lfence inbyggda som visas nedan.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
    if (untrusted_index < buffer_size) {
        _mm_lfence();
        unsigned char value = buffer[untrusted_index];
        return shared_buffer[value * 4096];
    }
}
Spekulationsbarriär genom instrumentering vid kompileringstid
Microsoft C++-kompilatorn i Visual Studio 2017 (från och med version 15.5.5) har stöd för växeln /Qspectre som automatiskt infogar en spekulationsbarriär för en begränsad uppsättning potentiellt sårbara kodmönster relaterade till CVE-2017-5753. Dokumentationen /Qspectre för flaggan innehåller mer information om dess effekter och användning. Det är viktigt att observera att den här flaggan inte täcker alla potentiellt sårbara kodningsmönster och att utvecklare därför inte bör förlita sig på den som en omfattande åtgärd för den här klassen av säkerhetsrisker.
Maskera matrisindex
I de fall där en spekulativ gränsöverskridande belastning kan inträffa kan matrisindexet begränsas starkt på både de arkitektoniska och icke-arkitektoniska sökvägarna genom att lägga till logik för att uttryckligen begränsa matrisindexet. Till exempel, om en matris kan tilldelas en storlek som är justerad till en potens av två, kan en enkel mask introduceras. Detta illustreras i exemplet nedan där det antas att buffer_size är justerat till en tvåpotens. Detta säkerställer att untrusted_index alltid är mindre än buffer_size, även om ett förutsägelsefel i villkorsgren inträffar och untrusted_index skickades in med ett värde som är större än eller lika med buffer_size.
Det bör noteras att indexmaskeringen som utförs här kan bli föremål för förbikoppling av spekulativt lager beroende på vilken kod som genereras av kompilatorn.
// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;
unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
    if (untrusted_index < buffer_size) {
        untrusted_index &= (buffer_size - 1);
        unsigned char value = buffer[untrusted_index];
        return shared_buffer[value * 4096];
    }
}
Ta bort känslig information från minnet
En annan teknik som kan användas för att minimera spekulativa exekveringssidokanalens sårbarheter är att ta bort känslig information från minnet. Programvaruutvecklare kan söka efter möjligheter att omstrukturera sitt program så att känslig information inte är tillgänglig under spekulativ körning. Detta kan åstadkommas genom att omstrukturera utformningen av ett program för att isolera känslig information i separata processer. En webbläsarapplikation kan till exempel försöka isolera data som är associerade med varje webbursprung i separata processer, vilket förhindrar att en process kan komma åt ursprungsöverskridande data genom spekulativ körning.
Se även
              Vägledning för att minska sårbarheter i sidokanal för spekulativ exekvering
              Mildra sidokanalsårbarheter vid spekulativ körning i maskinvara