Aracılığıyla paylaş


Kurgusal Yürütme Yan Kanalları için C++ Geliştirici Kılavuzu

Bu makale, geliştiricilerin C++ yazılımındaki kurgusal yürütme tarafı kanal donanım güvenlik açıklarını tanımlama ve azaltma konusunda yardımcı olması için rehberlik içerir. Bu güvenlik açıkları güven sınırları genelinde hassas bilgileri açığa çıkarır ve tahmine dayalı, sıra dışı yönergelerin yürütülmesini destekleyen işlemcilerde çalışan yazılımları etkileyebilir. Bu güvenlik açıkları sınıfı ilk olarak Ocak 2018'de açıklanmıştır ve Ek arka plan ve yönergeler Microsoft'un güvenlik danışmanlığında bulunabilir.

Bu makale tarafından sağlanan kılavuz, aşağıdakiler tarafından temsil edilen güvenlik açığı sınıfları ile ilgilidir:

  1. CVE-2017-5753, Spectre varyant 1 olarak da bilinir. Bu donanım güvenlik açığı sınıfı, koşullu dal yanlış gösterimi sonucunda oluşan tahmini yürütme nedeniyle ortaya çıkabilecek yan kanallarla ilgilidir. Visual Studio 2017'deki Microsoft C++ derleyicisi (sürüm 15.5.5'den başlayarak), CVE-2017-5753 ile ilgili sınırlı sayıda güvenlik açığı olabilecek kodlama deseni için derleme süresi azaltma sağlayan anahtar desteği /Qspectre içerir. Bu /Qspectre anahtar, Visual Studio 2015 Güncelleştirme 3 ile KB 4338871 arasında da kullanılabilir. Bayrağın /Qspectre belgeleri, etkileri ve kullanımı hakkında daha fazla bilgi sağlar.

  2. CVE-2018-3639, Tahmini Mağaza Atlama (SSB) olarak da bilinir. Bu donanım güvenlik açığı sınıfı, bir bellek erişimi yanlış gösterimi sonucunda bağımlı deponun önündeki bir yükün tahmini olarak yürütülmesi nedeniyle ortaya çıkabilecek yan kanallarla ilgilidir.

Kurgusal yürütme tarafı kanal güvenlik açıklarına erişilebilir bir giriş, bu sorunları keşfeden araştırma ekiplerinden biri tarafından The Case of Spectre and Meltdown başlıklı sunuda bulunabilir.

Kurgusal Yürütme Tarafı Kanalı donanım güvenlik açıkları nelerdir?

Modern CPU'lar, yönergelerin kurgusal ve sıra dışı yürütülmesini kullanarak daha yüksek performans dereceleri sağlar. Örneğin, bu genellikle CPU'nun tahmin edilen dal hedefinde yönergeleri tahmine dayalı olarak yürütmeye başlamasını sağlayan dalların hedefini tahmin ederek (koşullu ve dolaylı) gerçekleştirilir ve böylece gerçek dal hedefi çözümlenene kadar duraklamadan kaçınılır. CPU'nun daha sonra bir yanlış tahmin gerçekleştiğini bulması durumunda, tahmine dayalı olarak hesaplanan tüm makine durumu atılır. Bu, yanlış tahmin edilen spekülasyonun mimari olarak görünür bir etkisi olmamasını sağlar.

Kurgusal yürütme mimari olarak görünür durumu etkilemese de, CPU tarafından kullanılan çeşitli önbellekler gibi mimari olmayan durumda artık izlemeler bırakabilir. Yan kanal güvenlik açıklarına neden olabilecek, kurgusal yürütmenin bu artık izleridir. Bunu daha iyi anlamak için CVE-2017-5753 (Sınır Denetimi Atlama) örneğini sağlayan aşağıdaki kod parçasını göz önünde bulundurun:

// 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];
    }
}

Bu örnekte, ReadByte bu arabelleğe bir arabellek, arabellek boyutu ve dizin sağlanır. tarafından untrusted_indexbelirtilen dizin parametresi, yönetim dışı bir işlem gibi daha az ayrıcalıklı bir bağlam tarafından sağlanır. değerinden küçükseuntrusted_index, bu dizindeki karakter konumundan buffer okunur ve tarafından shared_bufferbaşvurulan paylaşılan bellek bölgesine dizin oluşturmak için buffer_sizekullanılır.

Mimari açıdan bakıldığında, her zaman değerinden buffer_sizeküçük olacağı garanti edilen untrusted_index bu kod dizisi son derece güvenlidir. Ancak, kurgusal yürütmenin varlığında, CPU'nun koşullu dalı yanlış tanıtıp , değerinden büyük veya eşit olsa untrusted_index bile if deyiminin gövdesini yürütmesi buffer_sizemümkündür. Bunun bir sonucu olarak CPU, sınırlarının ötesinden buffer (gizli dizi olabilir) tahmini olarak bir bayt okuyabilir ve ardından aracılığıyla shared_buffersonraki bir yükün adresini hesaplamak için bu bayt değerini kullanabilir.

CPU sonunda bu yanlış gösterimi algılasa da, artık yan etkiler, sınırlardan okunan bayt değeri hakkındaki bilgileri ortaya koyan CPU önbelleğinde bufferbırakılabilir. Bu yan etkiler, içindeki her önbellek satırına shared_buffer ne kadar hızlı erişilebileceğini araştırmak suretiyle sistemde çalışan daha az ayrıcalıklı bir bağlam tarafından algılanabilir. Bunu gerçekleştirmek için izleyebileceğiniz adımlar şunlardır:

  1. değerinden küçük olmasıyla untrusted_index birden çok kez çağırmaReadByte.buffer_size Saldırı bağlamı, dal tahmincisi küçük olduğu gibi alınmayacak şekilde eğitilecek şekilde untrusted_index buffer_sizekurban bağlamını çağırmasına ReadByte (örneğin RPC aracılığıyla) neden olabilir.

  2. içindeki shared_buffertüm önbellek satırlarını temizleyin. Saldırıya uğrayan bağlam, tarafından shared_bufferbaşvurulan belleğin paylaşılan bölgesindeki tüm önbellek satırlarını temizlemelidir. Bellek bölgesi paylaşıldığından bu basittir ve gibi _mm_clflushiç bilgileri kullanarak gerçekleştirilebilir.

  3. untrusted_index değerindenbuffer_size büyük olarak çağır.ReadByte Saldırı bağlamı, kurban bağlamın dalın alınmayacak olduğunu yanlış tahmin edecek şekilde çağırmasına ReadByte neden olur. Bu, işlemcinin , değerinden büyük buffer_sizeolan if bloğunun untrusted_index gövdesini tahmine dayalı olarak yürütmesine neden olur, böylece sınır dışı okumasına bufferneden olur. Sonuç olarak, shared_buffer sınır dışı olarak okunan olası bir gizli dizi değeri kullanılarak dizine alınır ve bu da ilgili önbellek satırının CPU tarafından yüklenmesine neden olur.

  4. Hangisine en hızlı şekilde erişildiğine bakmak için içindeki her önbellek satırını shared_buffer okuyun. Saldırı bağlamı içindeki shared_buffer her önbellek satırını okuyabilir ve diğerlerinden önemli ölçüde daha hızlı yüklenen önbellek satırını algılayabilir. Bu, 3. adıma kadar getirilmiş olma olasılığı olan önbellek satırıdır. Bu örnekte bayt değeri ile önbellek satırı arasında 1:1 ilişkisi olduğundan, bu, saldırganın sınırların dışında okunan bayt değerini çıkarmasını sağlar.

Yukarıdaki adımlar, CVE-2017-5753 örneğinden yararlanma ile birlikte FLUSH+RELOAD olarak bilinen bir tekniği kullanma örneği sağlar.

Hangi yazılım senaryoları etkilenebilir?

Güvenlik Geliştirme Yaşam Döngüsü (SDL) gibi bir işlem kullanarak güvenli yazılım geliştirmek için genellikle geliştiricilerin kendi uygulamalarındaki güven sınırlarını belirlemesi gerekir. Bir uygulamanın sistemdeki başka bir işlem veya çekirdek modu cihaz sürücüsü söz konusu olduğunda yönetici olmayan bir kullanıcı modu işlemi gibi daha az güvenilen bir bağlam tarafından sağlanan verilerle etkileşim kurabileceği yerlerde güven sınırı vardır. Kurgusal yürütme tarafı kanallarını içeren yeni güvenlik açıkları sınıfı, bir cihazdaki kodu ve verileri yalıtan mevcut yazılım güvenlik modellerindeki güven sınırlarının çoğuyla ilgilidir.

Aşağıdaki tabloda geliştiricilerin ortaya çıkan bu güvenlik açıklarıyla ilgilenmeleri gerekebilecek yazılım güvenlik modellerinin bir özeti yer alır:

Güven sınırı Açıklama
Sanal makine sınırı Başka bir sanal makineden güvenilmeyen veriler alan ayrı sanal makinelerdeki iş yüklerini yalıtan uygulamalar risk altında olabilir.
Çekirdek sınırı Yönetici olmayan bir kullanıcı modu işleminden güvenilmeyen veriler alan çekirdek modu cihaz sürücüsü risk altında olabilir.
İşlem sınırı Uzak Yordam Çağrısı (RPC), paylaşılan bellek veya diğer İşlemler Arası İletişim (IPC) mekanizmaları gibi yerel sistemde çalışan başka bir işlemden güvenilmeyen veriler alan bir uygulama risk altında olabilir.
Kapanım sınırı Güvenli bir kapanım içinde (Intel SGX gibi) yürütülen ve kapanım dışından güvenilmeyen veriler alan bir uygulama risk altında olabilir.
Dil sınırı Daha üst düzey bir dilde yazılmış güvenilmeyen kodu yorumlayan veya Tam Zamanında (JIT) derleyen ve yürüten bir uygulama risk altında olabilir.

Yukarıdaki güven sınırlarından herhangi birine maruz kalan saldırı yüzeyi olan uygulamalar, kurgusal yürütme tarafı kanal güvenlik açıklarının olası örneklerini belirlemek ve azaltmak için saldırı yüzeyindeki kodu gözden geçirmelidir. Uzak ağ protokolleri gibi uzak saldırı yüzeylerine maruz kalan güven sınırlarının kurgusal yürütme tarafı kanal güvenlik açıklarına karşı risk altında olduğu gösterilmediği belirtilmelidir.

Güvenlik açığı olabilecek kodlama desenleri

Birden çok kodlama deseninin sonucu olarak kurgusal yürütme tarafı kanal güvenlik açıkları ortaya çıkabilir. Bu bölümde, güvenlik açığı olabilecek kodlama desenleri açıklanır ve her biri için örnekler sağlanır, ancak bu temalardaki varyasyonların mevcut olabileceği kabul edilmelidir. Bu nedenle, geliştiricilerin bu desenleri örnek olarak alması ve güvenlik açığı olabilecek tüm kodlama desenlerinin kapsamlı bir listesi olarak almaları tavsiye edilir. Günümüzde yazılımlarda mevcut olabilecek bellek güvenliği güvenlik açıklarının aynı sınıfları, arabellek taşmaları, sınır dışı dizi erişimleri, başlatılmamış bellek kullanımı, tür karışıklığı vb. dahil ancak bunlarla sınırlı olmamak üzere kurgusal ve sıra dışı yürütme yollarında da bulunabilir. Saldırganların mimari yollarda bellek güvenliği güvenlik açıklarından yararlanmak için kullanabileceği temel öğeler, kurgusal yollar için de geçerli olabilir.

Genel olarak, koşullu dal yanlış kullanımla ilgili kurgusal yürütme tarafı kanalları, bir koşullu ifade daha az güvenilir bir bağlam tarafından denetlenebilen veya etkilenebilen veriler üzerinde çalıştığında ortaya çıkabilir. Örneğin, bu, , for, while, switchveya üçüncül deyimlerde ifkullanılan koşullu ifadeleri içerebilir. Bu deyimlerin her biri için derleyici, CPU'nun çalışma zamanında dal hedefini tahmin edebileceği bir koşullu dal oluşturabilir.

Her örnek için, bir geliştiricinin bir engeli azaltma olarak tanıtabileceği "SPEKÜLASYON ENGELİ" ifadesini içeren bir açıklama eklenir. Bu, risk azaltmalar bölümünde daha ayrıntılı olarak ele alınıyor.

Tahmini sınır dışı yük

Bu kodlama desenleri kategorisi, tahmini sınır dışı bellek erişimine yol açan bir koşullu dal yanlış kullanım içerir.

Bir yükü besleyen sınır dışı dizi yükü

Bu kodlama düzeni başlangıçta CVE-2017-5753 (Sınır Denetimi Atlama) için güvenlik açığı bulunan kodlama desenidir. Bu makalenin arka plan bölümünde bu desen ayrıntılı olarak açıklanmaktadır.

// 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];
    }
}

Benzer şekilde, bir dizi sınır dışı yükü, bir yanlış kullanım nedeniyle sonlandırıcı koşulunu aşan bir döngüyle birlikte oluşabilir. Bu örnekte, ifadeyle ilişkili koşullu dal, değerinden x < buffer_size büyük veya buna eşit buffer_sizeolduğunda x döngünün for gövdesini yanlış tanıtabilir ve tahmine dayalı olarak yürütebilir ve bu da tahmini sınır dışı yüke neden olur.

// 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];
    }
}

Dizi sınır dışı yük dolaylı dalı besliyor

Bu kodlama düzeni, bir koşullu dalın yanlış anlaşılmasının bir işlev işaretçisi dizisine sınır dışı erişime yol açması ve ardından sınır dışı okunan hedef adrese dolaylı bir dala yol açması durumunu içerir. Aşağıdaki kod parçacığı bunu gösteren bir örnek sağlar.

Bu örnekte DispatchMessage'a parametresi aracılığıyla güvenilmeyen bir ileti tanımlayıcısı untrusted_message_id sağlanır. değerinden MAX_MESSAGE_IDküçükseuntrusted_message_id, işlev işaretçileri dizisine dizin oluşturmak ve ilgili dal hedefine dal eklemek için kullanılır. Bu kod mimari açıdan güvenlidir, ancak CPU koşullu dalı yanlış ifade ederse değeri değerinden büyük veya buna eşit MAX_MESSAGE_IDolduğunda dizine eklenerek untrusted_message_id sınır dışı erişime neden olabilirDispatchTable. Bu, dizi sınırlarının ötesinde türetilen bir dal hedef adresinden tahmini yürütmeye neden olabilir ve bu da tahmini olarak yürütülen koda bağlı olarak bilgilerin açığa çıkmasına neden olabilir.

#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);
    }
}

Başka bir yükü besleyen sınır dışı dizi yükünde olduğu gibi, bu durum da yanlış bir ifade nedeniyle sonlandırıcı koşulunu aşan bir döngüyle birlikte ortaya çıkabilir.

Dizi sınır dışı depo dolaylı dalı besliyor

Önceki örnekte, tahmini sınır dışı yükün dolaylı dal hedefini nasıl etkilediği gösterilmiş olsa da, sınır dışı deponun işlev işaretçisi veya dönüş adresi gibi dolaylı dal hedefini değiştirmesi de mümkündür. Bu, saldırgan tarafından belirtilen bir adresten tahmini yürütmeye yol açabilir.

Bu örnekte, parametresi aracılığıyla untrusted_index güvenilmeyen bir dizin geçirilir. Dizinin öğe değerinden küçükse untrusted_index (256 öğe), içinde ptr sağlanan işaretçi değeri diziye pointers pointers yazılır. Bu kod mimari açıdan güvenlidir, ancak CPU koşullu dalı yanlış yorumlarsa yığın tarafından ayrılan pointers dizinin sınırlarının ötesinde tahmine dayalı olarak yazılmasıyla sonuçlanabilirptr. Bu, için dönüş adresinin WriteSlottahmini bozulmasına neden olabilir. Bir saldırgan değerini ptrdenetleyebilirse, tahmini yol boyunca döndürdüğünde WriteSlot rastgele bir adresten tahmini yürütmeye neden olabilir.

unsigned char WriteSlot(unsigned int untrusted_index, void *ptr) {
    void *pointers[256];
    if (untrusted_index < 256) {
        // SPECULATION BARRIER
        pointers[untrusted_index] = ptr;
    }
}

Benzer şekilde, adlı func bir işlev işaretçisi yerel değişkeni yığında ayrılmışsa, koşullu dal yanlış kullanım gerçekleştiğinde başvuruda bulunan adresi func tahmine dayalı olarak değiştirmek mümkün olabilir. Bu, işlev işaretçisi aracılığıyla çağrıldığında rastgele bir adresten tahmini yürütmeye neden olabilir.

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();
}

Bu örneklerin her ikisinin de yığına ayrılmış dolaylı dal işaretçilerinin tahmini olarak değiştirilmesini içerdiği belirtilmelidir. Genel değişkenler, yığınla ayrılan bellek ve hatta bazı CPU'larda salt okunur bellek için kurgusal değişiklikler de gerçekleşebilir. Yığına ayrılmış bellek için, Microsoft C++ derleyicisi, derleyici güvenlik özelliğinin bir parçası /GS olarak arabelleklerin bir güvenlik tanımlama bilgisinin bitişiğine yerleştirilmesi gibi yerel değişkenleri yeniden sıralayarak yığın ayrılmış dolaylı dal hedeflerini tahmine dayalı olarak değiştirmeyi zorlaştıracak adımları zaten gerçekleştirmektedir.

Kurgusal tür karışıklığı

Bu kategori, kurgusal tür karışıklığına neden olabilecek kodlama desenleriyle ilgilidir. Bu, kurgusal yürütme sırasında mimari olmayan bir yol boyunca yanlış bir tür kullanılarak belleğe erişildiğinde oluşur. Hem koşullu dal yanlış kullanım hem de tahmini depo atlamaları tahmini tür karışıklığına yol açabilir.

Tahmini depo atlama için bu durum, derleyicinin birden çok türdeki değişkenler için bir yığın konumunu yeniden kullanacağı senaryolarda ortaya çıkabilir. Bunun nedeni, türündeki A bir değişkenin mimari deposunun atlanması ve dolayısıyla türün A yükünün değişken atanmadan önce tahmini olarak yürütülmesine olanak sağlamasıdır. Önceden depolanan değişken farklı bir türdeyse, bu tahmine dayalı tür karışıklığı koşullarını oluşturabilir.

Koşullu dal yanlış kullanımı için, spekülatif tür karışıklığının neden olabileceği farklı koşulları açıklamak için aşağıdaki kod parçacığı kullanılır.

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;
    }
}

Sınır dışı yüke yol açan tahmini tür karışıklığı

Bu kodlama düzeni, tahmini tür karışıklığının, yüklenen değerin sonraki bir yük adresini beslediği sınırlar dışında veya tür karışıklığı alan erişimine neden olabileceği durumu içerir. Bu, sınır dışı dizi kodlama düzenine benzer, ancak yukarıda gösterildiği gibi alternatif bir kodlama dizisiyle gösterilir. Bu örnekte, saldırı bağlamı, kurban bağlamının türünde CType1 bir nesneyle birden çok kez yürütülmesine ProcessType neden olabilir (type alan eşittir Type1). Bu, ilk if deyimin alınmadığını tahmin etmek için koşullu dalı eğitme etkisine sahip olur. Daha sonra saldırı bağlamı, kurban bağlamının türünde CType2bir nesneyle yürütülmesine ProcessType neden olabilir. bu, ilk if deyimin koşullu dalının deyiminin gövdesini yanlış tanıtması ve yürütmesi if ve bu nedenle türünde bir nesnesini 'a CType1ataması durumunda tahmini tür CType2 karışıklığına neden olabilir. değerinden küçük CType1olduğundanCType2, 'a CType1::field2 bellek erişimi gizli olabilecek tahmini sınır dışı veri yüküne neden olur. Bu değer daha sonra daha önce açıklanan sınır dışı dizi örneğinde olduğu gibi gözlemlenebilir yan etkiler oluşturabilen bir yükte shared_buffer kullanılır.

Dolaylı dala yol açan kurgusal tür karışıklığı

Bu kodlama düzeni, kurgusal tür karışıklığının kurgusal yürütme sırasında güvenli olmayan bir dolaylı dala neden olabileceği durumu içerir. Bu örnekte, saldırı bağlamı, kurban bağlamının türünde CType2 bir nesneyle birden çok kez yürütülmesine ProcessType neden olabilir (type alan eşittir Type2). Bu, ilk if deyimin alınması ve else if deyiminin alınmaması için koşullu dalı eğitme etkisine sahiptir. Daha sonra saldırı bağlamı, kurban bağlamının türünde CType1bir nesneyle yürütülmesine ProcessType neden olabilir. bu, ilk if deyimin koşullu dalının alındığını ve deyiminin else if alınmadığını tahmin edip gövdesini else if yürütüp türündeki CType1 bir nesneyi 'ye CType2ataması durumunda tahmini tür karışıklığına neden olabilir. CType2::dispatch_routine Alan dizisiyle CType1::field1çakıştığı için char bu, istenmeyen dal hedefine kurgusal bir dolaylı dalla sonuçlanabilir. Saldırı bağlamı dizideki CType1::field1 bayt değerlerini denetleyebilirse, dal hedef adresini denetleyebilir.

Tahmini başlatılmamış kullanım

Bu kodlama desenleri kategorisi, kurgusal yürütmenin başlatılmamış belleğe erişebileceği ve bunu sonraki bir yükü veya dolaylı dalı beslemek için kullanabileceği senaryoları içerir. Bu kodlama düzenlerinin kötüye kullanılabilmesi için, saldırganın kullanıldığı bağlam tarafından başlatılmadan kullanılan belleğin içeriğini denetleyebilmesi veya anlamlı bir şekilde etkileyebilmesi gerekir.

Sınır dışı yüke yol açan tahmini başlatılmamış kullanım

Tahmini bir başlatılmamış kullanım, saldırgan tarafından denetlenen bir değer kullanılarak sınır dışı yüke yol açabilir. Aşağıdaki örnekte değeri index tüm mimari yollara atanır trusted_index ve trusted_index değerinden küçük veya eşit buffer_sizeolduğu varsayılır. Ancak, derleyici tarafından üretilen koda bağlı olarak, ve bağımlı ifadelerin atamadan buffer[index] indexönce yürütülmesine izin veren tahmini bir depo atlaması oluşabilir. Bu durumda, için başlatılmamış bir değerindex, bir saldırganın hassas bilgileri sınırların dışında okumasına ve bağımlı yükü shared_bufferaracılığıyla bir yan kanal üzerinden iletmesine olanak tanıyan uzaklık buffer olarak kullanılır.

// 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];
}

Dolaylı dala yol açan kurgusal başlatılmamış kullanım

Kurgusal bir başlatılmamış kullanım, dal hedefinin bir saldırgan tarafından denetlendiği dolaylı bir dala yol açabilir. Aşağıdaki örnekte, routine değerine veya değerine bağlı modeolarak atanır DefaultMessageRoutine1 DefaultMessageRoutine. Mimari yolda, bu durum her zaman dolaylı daldan önce başlatılmasına neden routine olur. Ancak, derleyici tarafından üretilen koda bağlı olarak, dolaylı dalın routine atamadan routineönce tahmini olarak yürütülmesini sağlayan tahmini bir depo atlama işlemi oluşabilir. Bu durumda, saldırganın başlatılmamış değerini routineetkileyebileceği veya denetleyebileceği varsayılarak, saldırgan rastgele bir adresten tahmine dayalı olarak yürütebilir.

#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);
}

Azaltma seçenekleri

Kaynak kodda değişiklik yapılarak kurgusal yürütme tarafı kanal güvenlik açıkları azaltılabilir. Bu değişiklikler, spekülasyon engeli eklemek veya hassas bilgilerin tahmini yürütmeye erişilemez hale getirilmesi için uygulamanın tasarımında değişiklikler yapmak gibi bir güvenlik açığının belirli örneklerini azaltmayı içerebilir.

El ile izleme yoluyla spekülasyon engeli

Kurgusal yürütmenin mimari olmayan bir yol boyunca ilerlemesini önlemek için bir geliştirici tarafından el ile bir spekülasyon engeli eklenebilir. Örneğin, bir geliştirici, koşullu bloğun gövdesinde tehlikeli bir kodlama düzeninden önce, bloğun başına (koşullu daldan sonra) veya endişe duyulan ilk yüklemeden önce bir spekülasyon engeli ekleyebilir. Bu, koşullu dal yanlış beyanlarının yürütmeyi seri hale getirerek mimari olmayan bir yolda tehlikeli kodu yürütmesini engeller. Spekülasyon engeli dizisi, aşağıdaki tabloda açıklandığı gibi donanım mimarisine göre farklılık gösterir:

Mimari CVE-2017-5753 için spekülasyon engeli CVE-2018-3639 için spekülasyon engeli
x86/x64 _mm_lfence() _mm_lfence()
ARM şu anda kullanılamıyor __dsb(0)
ARM64 şu anda kullanılamıyor __dsb(0)

Örneğin, aşağıdaki kod deseni aşağıda gösterildiği gibi iç kullanılarak azaltılabilir _mm_lfence .

// 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];
    }
}

Derleyici zaman izlemesi aracılığıyla spekülasyon engeli

Visual Studio 2017'deki Microsoft C++ derleyicisi (sürüm 15.5.5'den başlayarak) CVE-2017-5753 ile ilgili sınırlı sayıda güvenlik açığı olabilecek kodlama deseni için otomatik olarak bir spekülasyon engeli ekleyen anahtar için destek /Qspectre içerir. Bayrağın /Qspectre belgeleri, etkileri ve kullanımı hakkında daha fazla bilgi sağlar. Bu bayrağın tüm güvenlik açığı olabilecek kodlama desenlerini kapsamadığını ve bu tür geliştiricilerin bu güvenlik açıkları sınıfı için kapsamlı bir azaltma olarak buna güvenmemesi gerektiğini unutmayın.

Dizi dizinlerini maskeleme

Tahmini sınır dışı yükün oluşabileceği durumlarda dizi dizini, dizi dizinini açıkça bağlamaya yönelik mantık ekleyerek hem mimari hem de mimari olmayan yolda kesin olarak sınırlanabilir. Örneğin, bir dizi iki üssüne hizalanmış bir boyuta ayrılabiliyorsa basit bir maske eklenebilir. Bu, iki güçle hizalandığı buffer_size varsayıldığı aşağıdaki örnekte gösterilmiştir. Bu, bir koşullu dal yanlış işlemi gerçekleşse ve değerinden untrusted_index büyük veya buna eşit bir değerle geçirilse bile her zaman değerinden küçük buffer_sizeolmasını buffer_sizesağlar.untrusted_index

Burada gerçekleştirilen dizin maskelemenin, derleyici tarafından oluşturulan koda bağlı olarak tahmini depo atlamasına tabi olabileceği belirtilmelidir.

// 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];
    }
}

Bellekten hassas bilgileri kaldırma

Kurgusal yürütme tarafı kanal güvenlik açıklarını azaltmak için kullanılabilecek bir diğer teknik de hassas bilgileri bellekten kaldırmaktır. Yazılım geliştiricileri, kurgusal yürütme sırasında hassas bilgilere erişilmeyecek şekilde uygulamalarını yeniden düzenleme fırsatı arayabilir. Bu, hassas bilgileri ayrı işlemler halinde yalıtmak için bir uygulamanın tasarımını yeniden düzenleyerek gerçekleştirilebilir. Örneğin, bir web tarayıcısı uygulaması her web kaynağıyla ilişkili verileri ayrı işlemlere ayırmayı deneyerek bir işlemin tahmini yürütme yoluyla çıkış noktaları arası verilere erişmesini engelleyebilir.

Ayrıca bkz.

Kurgusal yürütme yan kanal güvenlik açıklarını azaltma kılavuzu
Kurgusal yürütme tarafı kanal donanım güvenlik açıklarını azaltma