Diretrizes para desenvolvedores de C++ para canais do lado de execução especulativaC++ Developer Guidance for Speculative Execution Side Channels

Este artigo contém diretrizes para desenvolvedores para ajudá-lo a identificar e atenuar a execução especulativa lado canal hardware as vulnerabilidades no software de C++.This article contains guidance for developers to assist with identifying and mitigating speculative execution side channel hardware vulnerabilities in C++ software. Essas vulnerabilidades podem revelar informações confidenciais entre limites de confiança e podem afetar o software que é executado em processadores que dão suporte à execução especulativa, fora de ordem de instruções.These vulnerabilities can disclose sensitive information across trust boundaries and can affect software that runs on processors that support speculative, out-of-order execution of instructions. Essa classe de vulnerabilidades foi primeiro descrito em janeiro de 2018 e plano de fundo adicional e diretrizes que podem ser encontradas no comunicado de segurança da Microsoft.This class of vulnerabilities was first described in January, 2018 and additional background and guidance can be found in Microsoft's security advisory.

A orientação fornecida por este artigo está relacionada às classes de vulnerabilidades representadas por:The guidance provided by this article is related to the classes of vulnerabilities represented by:

  1. CVE-2017-5753, também conhecido como variante Spectre 1.CVE-2017-5753, also known as Spectre variant 1. Essa classe de vulnerabilidade de hardware está relacionado aos canais de lado que podem surgir devido a execução especulativa que ocorre como resultado um misprediction ramificação condicional.This hardware vulnerability class is related to side channels that can arise due to speculative execution that occurs as a result of a conditional branch misprediction. O Microsoft C++ compilador no Visual Studio 2017 (começando com a versão 15.5.5) inclui suporte para o /Qspectre comutador que fornece uma redução do tempo de compilação para um conjunto limitado de padrões de codificação potencialmente vulnerável relacionados para CVE 2017-5753.The Microsoft C++ compiler in Visual Studio 2017 (starting with version 15.5.5) includes support for the /Qspectre switch which provides a compile-time mitigation for a limited set of potentially vulnerable coding patterns related to CVE-2017-5753. O /Qspectre comutador também está disponível no Visual Studio 2015 atualização 3 por meio 4338871 KB.The /Qspectre switch is also available in Visual Studio 2015 Update 3 through KB 4338871. A documentação para o /Qspectre sinalizador fornece mais informações sobre o seu uso e efeitos.The documentation for the /Qspectre flag provides more information on its effects and usage.

  2. CVE-2018-3639, também conhecido como especulativa Store Bypass (SSB).CVE-2018-3639, also known as Speculative Store Bypass (SSB). Essa classe de vulnerabilidade de hardware está relacionado aos canais de lado que podem surgir devido à execução especulativa de uma carga à frente de um repositório dependente como resultado um misprediction de acesso de memória.This hardware vulnerability class is related to side channels that can arise due to speculative execution of a load ahead of a dependent store as a result of a memory access misprediction.

Uma introdução acessível à vulnerabilidades de canal lateral de execução especulativa pode ser encontrada na apresentação intitulada caso do Spectre e Meltdown por uma das equipes de pesquisa que esses problemas descobertos.An accessible introduction to speculative execution side channel vulnerabilities can be found in the presentation titled The Case of Spectre and Meltdown by one of the research teams that discovered these issues.

O que são vulnerabilidades de hardware especulativo execução lado canal?What are Speculative Execution Side Channel hardware vulnerabilities?

CPUs modernas fornecem níveis mais altos de desempenho, fazendo uso de execução especulativa e fora de ordem de instruções.Modern CPUs provide higher degrees of performance by making use of speculative and out-of-order execution of instructions. Por exemplo, isso geralmente é feito ao prever o destino de ramificações (condicionais e indiretos) que permite que a CPU começar a forma especulativa executar instruções em que o destino da ramificação previstos, evitando uma vaga de até que seja o destino da ramificação real resolvido.For example, this is often accomplished by predicting the target of branches (conditional and indirect) which enables the CPU to begin speculatively executing instructions at the predicted branch target, thus avoiding a stall until the actual branch target is resolved. No caso em que a CPU mais tarde descobre que um misprediction ocorreu, todos os estados de máquina que foi calculado de forma especulativa é descartado.In the event that the CPU later discovers that a misprediction occurred, all of the machine state that was computed speculatively is discarded. Isso garante que não há nenhum efeitos visíveis em termos de arquitetura da especulação mispredicted.This ensures that there are no architecturally visible effects of the mispredicted speculation.

Durante a execução especulativa não afeta o estado visível em termos de arquitetura, ele pode deixar rastreamentos residuais em estado não-arquitetura, como os vários caches que são usados pela CPU.While speculative execution does not affect the architecturally visible state, it can leave residual traces in non-architectural state, such as the various caches that are used by the CPU. Ele é esses rastreamentos residuais de execução especulativa que pode dar origem a vulnerabilidades de canal lateral.It is these residual traces of speculative execution that can give rise to side channel vulnerabilities. Para entender isso melhor, considere o seguinte fragmento de código que fornece um exemplo de CVE-2017-5753 (Bypass verificar limites):To better understand this, consider the following code fragment which provides an example of CVE-2017-5753 (Bounds Check Bypass):

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

Neste exemplo, ReadByte é fornecido um buffer, um tamanho de buffer e um índice nesse buffer.In this example, ReadByte is supplied a buffer, a buffer size, and an index into that buffer. O parâmetro de índice, conforme especificado pelo untrusted_index, é fornecido por um menor contexto privilegiado, como um processo não administrativas.The index parameter, as specified by untrusted_index, is supplied by a less privileged context, such as a non-administrative process. Se untrusted_index é menor que buffer_size, em seguida, o caractere daquele índice é lido a partir buffer e é usado para o índice em uma região compartilhada de memória referenciada por shared_buffer.If untrusted_index is less than buffer_size, then the character at that index is read from buffer and used to index into a shared region of memory referred to by shared_buffer.

De uma perspectiva arquitetônica, esta sequência de código é segura perfeitamente como que é garantido que ele untrusted_index sempre será menor que buffer_size.From an architectural perspective, this code sequence is perfectly safe as it is guaranteed that untrusted_index will always be less than buffer_size. No entanto, na presença de execução especulativa, é possível que a CPU previsão a ramificação condicional e executar o corpo da se instrução mesmo quando untrusted_index é maior que ou igual a buffer_size.However, in the presence of speculative execution, it is possible that the CPU will mispredict the conditional branch and execute the body of the if statement even when untrusted_index is greater than or equal to buffer_size. Como consequência disso, a CPU forma especulativa pode ler um byte fora dos limites da buffer (que poderia ser um segredo) e, em seguida, poderia usar esse valor de byte para calcular o endereço de uma carga subsequente por meio de shared_buffer.As a consequence of this, the CPU may speculatively read a byte from beyond the bounds of buffer (which could be a secret) and could then use that byte value to compute the address of a subsequent load through shared_buffer.

Enquanto a CPU será, eventualmente, detectar esse misprediction, efeito colateral residual poderão ser deixados no cache da CPU que revelam informações sobre o valor de byte foi lido fora dos limites de buffer.While the CPU will eventually detect this misprediction, residual side effects may be left in the CPU cache that reveal information about the byte value that was read out of bounds from buffer. Esses efeitos colaterais podem ser detectados por um menor contexto privilegiado, em execução no sistema, a rapidez de investigação de cada cache de linha no shared_buffer é acessado.These side effects can be detected by a less privileged context running on the system by probing how quickly each cache line in shared_buffer is accessed. As etapas que podem ser executadas para realizar isso são:The steps that can be taken to accomplish this are:

  1. Invocar ReadByte várias vezes com untrusted_index sendo menor que buffer_size .Invoke ReadByte multiple times with untrusted_index being less than buffer_size. O contexto de ataque pode fazer com que o contexto da vítima invocar ReadByte (por exemplo, via RPC), de modo que o indicador de ramificação é treinado para não ser-considerado untrusted_index é menor que buffer_size.The attacking context can cause the victim context to invoke ReadByte (e.g. via RPC) such that the branch predictor is trained to be not-taken as untrusted_index is less than buffer_size.

  2. Liberar todas as linhas de cache no shared_buffer .Flush all cache lines in shared_buffer. O contexto de ataque deve liberar todas as linhas de cache na região compartilhada de memória referenciada por shared_buffer.The attacking context must flush all of the cache lines in the shared region of memory referred to by shared_buffer. Como a região de memória for compartilhada, isso é simples e não pode ser feito usando intrínsecos, como _mm_clflush.Since the memory region is shared, this is straightforward and can be accomplished using intrinsics such as _mm_clflush.

  3. Invocar ReadByte com untrusted_index que é maior que buffer_size .Invoke ReadByte with untrusted_index being greater than buffer_size. O contexto de ataque faz com que o contexto da vítima invocar ReadByte , de modo que prevê incorretamente que a ramificação não será executada.The attacking context causes the victim context to invoke ReadByte such that it incorrectly predicts that the branch will not be taken. Isso faz com que o processador de forma especulativa, executar o corpo da se bloquear com untrusted_index que é maior que buffer_size, portanto, à esquerda para uma leitura fora dos limites de buffer.This causes the processor to speculatively execute the body of the if block with untrusted_index being greater than buffer_size, thus leading to an out-of-bounds read of buffer. Consequentemente, shared_buffer é indexado usando um valor possivelmente secreto que foi lido de fora dos limites, fazendo com que a linha do respectivo cache a ser carregado pela CPU.Consequently, shared_buffer is indexed using a potentially secret value that was read out-of-bounds, thus causing the respective cache line to be loaded by the CPU.

  4. Leia cada linha de cache no shared_buffer para ver o que é acessado mais rapidamente.Read each cache line in shared_buffer to see which is accessed most quickly. O contexto de ataque pode ler cada linha de cache no shared_buffer e detectar a linha de cache que carrega, significativamente mais rápido do que os outros.The attacking context can read each cache line in shared_buffer and detect the cache line that loads significantly faster than the others. Esta é a linha de cache que provavelmente foi levou a etapa 3.This is the cache line that is likely to have been brought in by step 3. Já que há uma relação de 1:1 entre a linha de cache e o valor de byte neste exemplo, isso permite que o invasor inferir o valor real do byte que foi lido de fora dos limites.Since there is a 1:1 relationship between byte value and cache line in this example, this allows the attacker to infer the actual value of the byte that was read out-of-bounds.

As etapas acima fornecem um exemplo de como usar uma técnica conhecida como FLUSH + RECARREGAR em conjunto com a exploração de uma instância de CVE 2017-5753.The above steps provide an example of using a technique known as FLUSH+RELOAD in conjunction with exploiting an instance of CVE-2017-5753.

Quais cenários de software podem ser afetados?What software scenarios can be impacted?

Desenvolvimento de software seguro usando um processo, como o Security Development Lifecycle (SDL) normalmente exige que os desenvolvedores identificar os limites de confiança que existem em seus aplicativos.Developing secure software using a process like the Security Development Lifecycle (SDL) typically requires developers to identify the trust boundaries that exist in their application. Existe um limite de confiança em lugares onde um aplicativo pode interagir com dados fornecidos por um contexto de menos confiáveis, como outro processo no sistema ou um processo de modo de usuário não administrativo no caso de um driver de dispositivo de modo kernel.A trust boundary exists in places where an application may interact with data provided by a less-trusted context, such as another process on the system or a non-administrative user mode process in the case of a kernel-mode device driver. A nova classe de vulnerabilidades que envolvem os canais do lado de execução especulativa é relevante para muitos dos limites de confiança nos modelos de segurança de software existentes que isolar o código e os dados em um dispositivo.The new class of vulnerabilities involving speculative execution side channels is relevant to many of the trust boundaries in existing software security models that isolate code and data on a device.

A tabela a seguir fornece um resumo dos modelos de segurança de software em que os desenvolvedores podem precisar se preocupar com essas vulnerabilidades ocorrendo:The following table provides a summary of the software security models where developers may need to be concerned about these vulnerabilities occurring:

Limite de confiançaTrust boundary DescriçãoDescription
Limites de máquina virtualVirtual machine boundary Aplicativos que isolar as cargas de trabalho em máquinas virtuais separadas que recebem dados não confiáveis de outra máquina virtual podem estar em risco.Applications that isolate workloads in separate virtual machines that receive untrusted data from another virtual machine may be at risk.
Limite de kernelKernel boundary Um driver de dispositivo de modo kernel que recebe dados não confiáveis de um processo do modo de usuário não administrativo pode estar em risco.A kernel-mode device driver that receives untrusted data from a non-administrative user mode process may be at risk.
Limite de processoProcess boundary Um aplicativo que recebe dados não confiáveis de outro processo em execução no sistema local, como por meio de um procedimento remoto (RPC), memória compartilhada ou outra comunicação entre processos (IPC) mecanismos podem estar em risco.An application that receives untrusted data from another process running on the local system, such as through a Remote Procedure Call (RPC), shared memory, or other Inter-Process Communication (IPC) mechanisms may be at risk.
Limite de enclaveEnclave boundary Um aplicativo que é executado dentro de um enclave seguro (como Intel SGX) que recebe dados não confiáveis de fora o enclave pode estar em risco.An application that executes within a secure enclave (such as Intel SGX) that receives untrusted data from outside of the enclave may be at risk.
Limites de linguagemLanguage boundary Um aplicativo que interpreta ou (JIT) just-in-compila e executa o código não confiável, escrito em uma linguagem de nível mais alto pode estar em risco.An application that interprets or Just-In-Time (JIT) compiles and executes untrusted code written in a higher-level language may be at risk.

Aplicativos que têm a superfície de ataque exposta a qualquer um dos itens acima dos limites devem examinar o código na superfície de ataque para identificar e atenuar possíveis ocorrências de vulnerabilidades de canal lateral de execução especulativa de confiança.Applications that have attack surface exposed to any of the above trust boundaries should review code on the attack surface to identify and mitigate possible instances of speculative execution side channel vulnerabilities. Deve-se observar que os limites de confiança expostos a superfícies de ataque remoto, tais como protocolos de rede remota, não foram demonstrados fiquem em risco a vulnerabilidades de canal lateral de execução especulativa.It should be noted that trust boundaries exposed to remote attack surfaces, such as remote network protocols, have not been demonstrated to be at risk to speculative execution side channel vulnerabilities.

Padrões de codificação potencialmente vulnerávelPotentially vulnerable coding patterns

Vulnerabilidades de canal lateral de execução especulativa podem surgir como consequência de vários padrões de codificação.Speculative execution side channel vulnerabilities can arise as a consequence of multiple coding patterns. Esta seção descreve os padrões de codificação potencialmente vulneráveis e fornece exemplos para cada um, mas devem ser reconhecido que variações esses temas podem existir.This section describes potentially vulnerable coding patterns and provides examples for each, but it should be recognized that variations on these themes may exist. Dessa forma, os desenvolvedores são aconselhados a levar esses padrões como exemplos e não como uma lista completa de todos os padrões de codificação potencialmente vulneráveis.As such, developers are advised to take these patterns as examples and not as an exhaustive list of all potentially vulnerable coding patterns. As mesmas classes de vulnerabilidades de segurança de memória que podem existir no software hoje em dia também podem existir ao longo de especulativo e os caminhos de fora de ordem de execução, incluindo mas não limitado a estouros de buffer, acessos, o uso de memória não inicializada, o tipo de matriz fora dos limites confusão e assim por diante.The same classes of memory safety vulnerabilities that can exist in software today may also exist along speculative and out-of-order paths of execution, including but not limited to buffer overruns, out-of-bounds array accesses, uninitialized memory use, type confusion, and so on. Os mesmos primitivos do que os invasores podem usar para explorar vulnerabilidades de segurança de memória ao longo de caminhos de arquiteturas também podem ser aplicáveis aos caminhos especulativos.The same primitives that attackers can use to exploit memory safety vulnerabilities along architectural paths may also apply to speculative paths.

Em geral, misprediction de ramificação relacionados a condicional a execução especulativa lado canais pode surgir quando uma expressão condicional opera em dados que podem ser controlados ou influenciados por um contexto de menos confiáveis.In general, speculative execution side channels related to conditional branch misprediction can arise when a conditional expression operates on data that can be controlled or influenced by a less-trusted context. Por exemplo, isso pode incluir expressões condicionais usadas na if, for, while, switch, ou instruções ternárias.For example, this can include conditional expressions used in if, for, while, switch, or ternary statements. Para cada uma dessas instruções, o compilador pode gerar uma ramificação condicional que a CPU, em seguida, pode prever o destino da ramificação para em tempo de execução.For each of these statements, the compiler may generate a conditional branch that the CPU may then predict the branch target for at runtime.

Para cada exemplo, um comentário com a frase "BARREIRA de ESPECULAÇÃO" é inserido em que um desenvolvedor poderia introduzir uma barreira como uma mitigação.For each example, a comment with the phrase "SPECULATION BARRIER" is inserted where a developer could introduce a barrier as a mitigation. Isso é discutido mais detalhadamente na seção reduções.This is discussed in more detail in the section on mitigations.

Especulativo fora dos limites de cargaSpeculative out-of-bounds load

Essa categoria de padrões de codificação envolve um misprediction branch condicional que leva a um especulativa fora dos limites acesso à memória.This category of coding patterns involves a conditional branch misprediction that leads to a speculative out-of-bounds memory access.

Alimentando uma carga de carga de matriz fora dos limitesArray out-of-bounds load feeding a load

Esse padrão de codificação é o padrão de codificação vulnerável originalmente descrito para CVE-2017-5753 (Bypass verificar limites).This coding pattern is the originally described vulnerable coding pattern for CVE-2017-5753 (Bounds Check Bypass). A seção de plano de fundo deste artigo explica esse padrão em detalhes.The background section of this article explains this pattern in detail.

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

Da mesma forma, uma matriz fora dos limites a carga pode ocorrer em conjunto com um loop que excede seu encerramento condição devido a um misprediction.Similarly, an array out-of-bounds load may occur in conjunction with a loop that exceeds its terminating condition due to a misprediction. Neste exemplo, a ramificação condicional associado com o x < buffer_size expressão pode de previsão e forma especulativa executa o corpo do for quando um loop x é maior que ou igual a buffer_size, portanto, resultando em um especulativo fora dos limites de carga.In this example, the conditional branch associated with the x < buffer_size expression may mispredict and speculatively execute the body of the for loop when x is greater than or equal to buffer_size, thus resulting in a speculative out-of-bounds load.

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

Matriz fora dos limites de carga alimentando uma ramificação indiretaArray out-of-bounds load feeding an indirect branch

Esse padrão de codificação envolve o caso em que um misprediction ramificação condicional pode levar a um fora dos limites acesso a uma matriz de ponteiros de função que, em seguida, leva a uma ramificação indireta para o destino de endereço que foi lido fora dos limites.This coding pattern involves the case where a conditional branch misprediction can lead to an out-of-bounds access to an array of function pointers which then leads to an indirect branch to the target address that was read out-of-bounds. O trecho a seguir fornece um exemplo que demonstra isso.The following snippet provides an example that demonstrates this.

Neste exemplo, um identificador de mensagem não confiáveis é fornecido para DispatchMessage por meio de untrusted_message_id parâmetro.In this example, an untrusted message identifier is provided to DispatchMessage through the untrusted_message_id parameter. Se untrusted_message_id é menor que MAX_MESSAGE_ID, em seguida, ele é usado para indexar uma matriz de ponteiros de função e ramificar para o destino de ramificação correspondente.If untrusted_message_id is less than MAX_MESSAGE_ID, then it is used to index into an array of function pointers and branch to the corresponding branch target. Esse código é seguro em termos de arquitetura, mas se a CPU previsões incorretas de branch condicional, ela pode resultar na DispatchTable estão sendo indexados pelo untrusted_message_id quando seu valor é maior que ou igual a MAX_MESSAGE_ID, gerando, portanto, um acesso fora dos limites.This code is safe architecturally, but if the CPU mispredicts the conditional branch, it could result in DispatchTable being indexed by untrusted_message_id when its value is greater than or equal to MAX_MESSAGE_ID, thus leading to an out-of-bounds access. Isso pode resultar na execução especulativa de um endereço de destino de ramificação que é derivada além dos limites da matriz que pode levar à divulgação de informações dependendo do código que é executado de forma especulativa.This could result in speculative execution from a branch target address that is derived beyond the bounds of the array which could lead to information disclosure depending on the code that is executed speculatively.

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

Como no caso de uma matriz fora dos limites de carga alimentando carga outro, essa condição também pode surgir em conjunto com um loop que excede sua condição de terminação devido a um misprediction.As with the case of an array out-of-bounds load feeding another load, this condition may also arise in conjunction with a loop that exceeds its terminating condition due to a misprediction.

Matriz fora dos limites armazenar alimentando uma ramificação indiretaArray out-of-bounds store feeding an indirect branch

Embora o exemplo anterior mostrou como um especulativa fora dos limites carga pode influenciar a um destino de ramificação indireta, também é possível que um repositório fora dos limites para modificar um destino de ramificação indireta, como um ponteiro de função ou um endereço de retorno.While the previous example showed how a speculative out-of-bounds load can influence an indirect branch target, it is also possible for an out-of-bounds store to modify an indirect branch target, such as a function pointer or a return address. Isso pode levar à execução especulativa de um endereço especificado pelo invasor.This can potentially lead to speculative execution from an attacker-specified address.

Neste exemplo, um índice não confiável é passado por meio de untrusted_index parâmetro.In this example, an untrusted index is passed through the untrusted_index parameter. Se untrusted_index é menor que a contagem de elemento do pointers array (256 elementos), em seguida, o valor de ponteiro fornecido na ptr é gravado o pointers matriz.If untrusted_index is less than the element count of the pointers array (256 elements), then the provided pointer value in ptr is written to the pointers array. Esse código é seguro em termos de arquitetura, mas se a CPU previsões incorretas de branch condicional, ela pode resultar na ptr que estão sendo gravados de forma especulativa além dos limites de alocados a pilha pointers matriz.This code is safe architecturally, but if the CPU mispredicts the conditional branch, it could result in ptr being speculatively written beyond the bounds of the stack-allocated pointers array. Isso pode levar à corrupção especulativa do endereço para devolução para WriteSlot.This could lead to speculative corruption of the return address for WriteSlot. Se um invasor pode controlar o valor de ptr, poderão causar a execução especulativa de um arbitrário de endereços quando WriteSlot retorna ao longo do caminho especulativo.If an attacker can control the value of ptr, they may be able to cause speculative execution from an arbitrary address when WriteSlot returns along the speculative path.

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

Da mesma forma, se uma variável local de ponteiro de função chamada func foram alocados na pilha, em seguida, talvez seja possível de forma especulativa, modifique o endereço que func refere-se a quando ocorre o misprediction ramificação condicional.Similarly, if a function pointer local variable named func were allocated on the stack, then it may be possible to speculatively modify the address that func refers to when the conditional branch misprediction occurs. Isso pode resultar na execução especulativa de um endereço arbitrário quando o ponteiro de função é chamado por meio do.This could result in speculative execution from an arbitrary address when the function pointer is called through.

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

Observe que ambos os exemplos envolvem modificação especulativa de ponteiros de branch indireta alocada na pilha.It should be noted that both of these examples involve speculative modification of stack-allocated indirect branch pointers. É possível que a modificação especulativa também pode ocorrer para variáveis globais, a memória alocada no heap e até mesma memória somente leitura em algumas CPUs.It is possible that speculative modification could also occur for global variables, heap-allocated memory, and even read-only memory on some CPUs. Para a memória alocada na pilha, o Microsoft C++ compilador já toma medidas para tornar mais difícil modificar forma especulativa destinos de ramificação indireta alocada na pilha, como a reordenação de variáveis locais, de modo que os buffers serão colocadas adjacentes a uma segurança cookie como parte do /GS recurso de segurança do compilador.For stack-allocated memory, the Microsoft C++ compiler already takes steps to make it more difficult to speculatively modify stack-allocated indirect branch targets, such as by reordering local variables such that buffers are placed adjacent to a security cookie as part of the /GS compiler security feature.

Confusão de tipo especulativoSpeculative type confusion

Esta categoria lida com os padrões que podem dar origem a uma confusão especulativa tipo de codificação.This category deals with coding patterns that can give rise to a speculative type confusion. Isso ocorre quando a memória é acessada por meio de um tipo incorreto ao longo de um caminho não arquitetura durante a execução especulativa.This occurs when memory is accessed using an incorrect type along a non-architectural path during speculative execution. Misprediction ramificação condicional e repositório especulativa bypass potencialmente podem gerar confusão um tipo especulativa.Both conditional branch misprediction and speculative store bypass can potentially lead to a speculative type confusion.

Para ignorar store especulativa, isso pode ocorrer em cenários em que um compilador reutiliza um local da pilha para variáveis de vários tipos.For speculative store bypass, this could occur in scenarios where a compiler reuses a stack location for variables of multiple types. Isso ocorre porque o repositório de arquitetura de uma variável do tipo A pode ser ignorado, permitindo assim que a carga do tipo A para executar de forma especulativa antes que a variável é atribuída.This is because the architectural store of a variable of type A may be bypassed, thus allowing the load of type A to speculatively execute before the variable is assigned. Se a variável armazenada anteriormente é de um tipo diferente, isso pode criar as condições para uma confusão de tipo especulativa.If the previously stored variable is of a different type, then this can create the conditions for a speculative type confusion.

Para misprediction ramificação condicional, o trecho de código a seguir será usado para descrever a enfrentar condições diferentes que pode dar a confusão de tipo especulativa.For conditional branch misprediction, the following code snippet will be used to describe different conditions that speculative type confusion can give rise to.

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

Confusão de tipo especulativa, levando a um fora dos limites de cargaSpeculative type confusion leading to an out-of-bounds load

Esse padrão de codificação envolve o caso em que uma confusão de tipo especulativa pode resultar em um fora dos limites ou acesso confundido por tipo de campo em que o valor carregado feeds de um endereço de carregamento subsequente.This coding pattern involves the case where a speculative type confusion can result in an out-of-bounds or type-confused field access where the loaded value feeds a subsequent load address. Isso é semelhante ao padrão de codificação fora dos limites de matriz, mas ela é manifestada por meio de uma alternativa a sequência de codificação, como mostrado acima.This is similar to the array out-of-bounds coding pattern but it is manifested through an alternative coding sequence as shown above. Neste exemplo, um contexto de ataque pode fazer com que o contexto da vítima para executar ProcessType várias vezes com um objeto do tipo CType1 (type campo é igual a Type1).In this example, an attacking context could cause the victim context to execute ProcessType multiple times with an object of type CType1 (type field is equal to Type1). Isso terá o efeito de treinar a ramificação condicional para o primeiro if instrução não prever feito.This will have the effect of training the conditional branch for the first if statement to predict not taken. O contexto de ataque pode fazer com que o contexto da vítima para executar ProcessType com um objeto do tipo CType2.The attacking context can then cause the victim context to execute ProcessType with an object of type CType2. Isso pode resultar em uma confusão de tipo especulativa se condicional ramificar para a primeira if previsões incorretas de instrução e executa o corpo do if instrução, portanto, um objeto do tipo de conversão CType2 para CType1.This can result in a speculative type confusion if the conditional branch for the first if statement mispredicts and executes the body of the if statement, thus casting an object of type CType2 to CType1. Uma vez que CType2 é menor do que CType1, o acesso de memória para CType1::field2 resultam em um especulativa fora dos limites carregará de dados que podem ser secretos.Since CType2 is smaller than CType1, the memory access to CType1::field2 will result in a speculative out-of-bounds load of data that may be secret. Esse valor é usado em uma carga de shared_buffer que pode criar um efeito colateral observável, assim como acontece com a matriz fora dos limites exemplo descrito anteriormente.This value is then used in a load from shared_buffer which can create observable side effects, as with the array out-of-bounds example described previously.

Confusão de tipo especulativa levando a uma ramificação indiretaSpeculative type confusion leading to an indirect branch

Esse padrão de codificação envolve o caso em que uma confusão de tipo especulativa pode resultar em uma ramificação indireta não segura durante a execução especulativa.This coding pattern involves the case where a speculative type confusion can result in an unsafe indirect branch during speculative execution. Neste exemplo, um contexto de ataque pode fazer com que o contexto da vítima para executar ProcessType várias vezes com um objeto do tipo CType2 (type campo é igual a Type2).In this example, an attacking context could cause the victim context to execute ProcessType multiple times with an object of type CType2 (type field is equal to Type2). Isso terá o efeito de treinar a ramificação condicional para o primeiro if instrução a ser executada e o else if instrução, a não ser executada.This will have the effect of training the conditional branch for the first if statement to be taken and the else if statement to be not taken. O contexto de ataque pode fazer com que o contexto da vítima para executar ProcessType com um objeto do tipo CType1.The attacking context can then cause the victim context to execute ProcessType with an object of type CType1. Isso pode resultar em uma confusão de tipo especulativa se condicional ramificar para a primeira if prevê a instrução executada e o else if prevê a instrução não é feito, em execução, portanto, o corpo do else if e conversão de um objeto do tipo CType1 para CType2.This can result in a speculative type confusion if the conditional branch for the first if statement predicts taken and the else if statement predicts not taken, thus executing the body of the else if and casting an object of type CType1 to CType2. Uma vez que o CType2::dispatch_routine campo se sobrepõe a char matriz CType1::field1, isso pode resultar em uma ramificação indireta especulativa a um destino de ramificação não intencionais.Since the CType2::dispatch_routine field overlaps with the char array CType1::field1, this could result in a speculative indirect branch to an unintended branch target. Se o contexto de ataque pode controlar os valores de byte no CType1::field1 matriz, eles podem ser capazes de controlar o endereço de destino de ramificação.If the attacking context can control the byte values in the CType1::field1 array, they may be able to control the branch target address.

Especulativo uso não inicializadoSpeculative uninitialized use

Essa categoria de padrões de codificação envolve a cenários em que a execução especulativa pode acessar a memória não inicializada e usá-lo para alimentar uma carga subsequente ou ramificação indireta.This category of coding patterns involves scenarios where speculative execution may access uninitialized memory and use it to feed a subsequent load or indirect branch. Para esses padrões de codificação a ser explorada, um invasor precisa ser capaz de controlar ou influenciar significativamente o conteúdo da memória que é usado sem ser inicializado pelo que está sendo usado no contexto.For these coding patterns to be exploitable, an attacker needs to be able to control or meaningfully influence the contents of the memory that is used without being initialized by the context that it is being used in.

Especulativo uso não inicializado, levando a um fora dos limites de cargaSpeculative uninitialized use leading to an out-of-bounds load

Um uso não inicializado especulativo potencialmente pode levar a um fora dos limites de carga usando um valor controlado pelo invasor.A speculative uninitialized use can potentially lead to an out-of-bounds load using an attacker controlled value. No exemplo a seguir, o valor de index é atribuída trusted_index em todos os caminhos de arquiteturas e trusted_index deve para ser menor ou igual a buffer_size.In the example below, the value of index is assigned trusted_index on all architectural paths and trusted_index is assumed to be less than or equal to buffer_size. No entanto, dependendo do código produzido pelo compilador, é possível que um repositório especulativa bypass pode ocorrer que permite que a carga de buffer[index] e as expressões dependentes executado à frente da atribuição a index.However, depending on the code produced by the compiler, it is possible that a speculative store bypass may occur that allows the load from buffer[index] and dependent expressions to execute ahead of the assignment to index. Se isso ocorrer, um valor não inicializado para index será usado como o deslocamento na buffer que permite que um invasor leia informações confidenciais fora dos limites e transmitir isso por meio de um canal de lado por meio de carregamento dependente de shared_buffer .If this occurs, an uninitialized value for index will be used as the offset into buffer which could enable an attacker to read sensitive information out-of-bounds and convey this through a side channel through the dependent load of 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];
}

Especulativo uso não inicializado, levando a uma ramificação indiretaSpeculative uninitialized use leading to an indirect branch

Um uso não inicializado especulativo potencialmente pode levar a uma ramificação indireta em que o destino da ramificação é controlado por um invasor.A speculative uninitialized use can potentially lead to an indirect branch where the branch target is controlled by an attacker. No exemplo a seguir, routine for atribuído a uma DefaultMessageRoutine1 ou DefaultMessageRoutine dependendo do valor de mode.In the example below, routine is assigned to either DefaultMessageRoutine1 or DefaultMessageRoutine depending on the value of mode. No caminho de arquitetura, isso resultará em routine que está sendo inicializado sempre à frente na ramificação indireta.On the architectural path, this will result in routine always being initialized ahead of the indirect branch. No entanto, dependendo do código produzido pelo compilador, um repositório especulativa bypass pode ocorrer que permite que a ramificação indireta por meio routine a ser executado de forma especulativa à frente da atribuição a routine.However, depending on the code produced by the compiler, a speculative store bypass may occur that allows the indirect branch through routine to be speculatively executed ahead of the assignment to routine. Se isso ocorrer, um invasor pode ser capaz de executar de forma especulativa de um endereço arbitrário, supondo que o invasor pode influenciar ou controlar o valor não inicializado do routine.If this occurs, an attacker may be able to speculatively execute from an arbitrary address, assuming the attacker can influence or control the uninitialized value of 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);
}

Opções de atenuaçãoMitigation options

Vulnerabilidades de canal lateral de execução especulativa podem ser minimizadas, tornando as alterações ao código-fonte.Speculative execution side channel vulnerabilities can be mitigated by making changes to source code. Essas alterações podem envolver a redução de instâncias específicas de uma vulnerabilidade, como com a adição de um barreira de especulação, ou fazendo alterações no design de um aplicativo para tornar informações confidenciais inacessível para especulativo execução.These changes can involve mitigating specific instances of a vulnerability, such as by adding a speculation barrier, or by making changes to the design of an application to make sensitive information inaccessible to speculative execution.

Barreira de especulação por meio da instrumentação manualSpeculation barrier via manual instrumentation

Um barreira de especulação podem ser inseridos manualmente por um desenvolvedor para impedir a execução especulativa continuação ao longo de um caminho não arquitetura.A speculation barrier can be manually inserted by a developer to prevent speculative execution from proceeding along a non-architectural path. Por exemplo, um desenvolvedor pode inserir uma barreira de especulação antes de um padrão de codificação perigoso no corpo de um bloco condicional, no início do bloco (após a ramificação condicional) ou antes da primeira carga é uma preocupação.For example, a developer can insert a speculation barrier before a dangerous coding pattern in the body of a conditional block, either at the beginning of the block (after the conditional branch) or before the first load that is of concern. Isso impedirá que um misprediction ramificação condicional da execução do código de perigoso em um caminho não arquitetura serializando a execução.This will prevent a conditional branch misprediction from executing the dangerous code on a non-architectural path by serializing execution. A sequência de barreira de especulação difere pela arquitetura de hardware, conforme descrito pela tabela a seguir:The speculation barrier sequence differs by hardware architecture as described by the following table:

ArquiteturaArchitecture Barreira de especulação intrínseca para CVE 2017-5753Speculation barrier intrinsic for CVE-2017-5753 Barreira de especulação intrínseca para CVE-2018-3639Speculation barrier intrinsic for CVE-2018-3639
x86/x64x86/x64 _mm_lfence()_mm_lfence() _mm_lfence()_mm_lfence()
ARMARM não disponível atualmentenot currently available __dsb(0)__dsb(0)
ARM64ARM64 não disponível atualmentenot currently available __dsb(0)__dsb(0)

Por exemplo, o padrão de código a seguir pode ser abrandado utilizando o _mm_lfence intrínseco, conforme mostrado abaixo.For example, the following code pattern can be mitigated by using the _mm_lfence intrinsic as shown below.

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

Barreira de especulação por meio da instrumentação de tempo do compiladorSpeculation barrier via compiler-time instrumentation

O Microsoft C++ compilador no Visual Studio 2017 (começando com a versão 15.5.5) inclui suporte para o /Qspectre comutador que automaticamente insere uma barreira de especulação para um conjunto limitado de padrões de codificação potencialmente vulnerável relacionados a CVE-2017-5753.The Microsoft C++ compiler in Visual Studio 2017 (starting with version 15.5.5) includes support for the /Qspectre switch which automatically inserts a speculation barrier for a limited set of potentially vulnerable coding patterns related to CVE-2017-5753. A documentação para o /Qspectre sinalizador fornece mais informações sobre o seu uso e efeitos.The documentation for the /Qspectre flag provides more information on its effects and usage. É importante observar que esse sinalizador não abrange todos os padrões de codificação potencialmente vulneráveis e como tal, os desenvolvedores não devem confiar nele como uma mitigação abrangente para esta classe de vulnerabilidades.It is important to note that this flag does not cover all of the potentially vulnerable coding patterns and as such developers should not rely on it as a comprehensive mitigation for this class of vulnerabilities.

Índices da matriz de mascaramentoMasking array indices

Em casos onde um especulativa fora dos limites de carga podem ocorrer, o índice de matriz pode ser fortemente limitado no caminho não arquitetônicas e de arquitetura, adicionando a lógica para associado explicitamente o índice de matriz.In cases where a speculative out-of-bounds load may occur, the array index can be strongly bounded on both the architectural and non-architectural path by adding logic to explicitly bound the array index. Por exemplo, se uma matriz pode ser alocada para um tamanho que está alinhado a uma potência de dois, em seguida, uma máscara simple pode ser introduzida.For example, if an array can be allocated to a size that is aligned to a power of two, then a simple mask can be introduced. Isso é ilustrado no exemplo a seguir em que ele é presumido que buffer_size está alinhado a uma potência de dois.This is illustrated in the sample below where it is assumed that buffer_size is aligned to a power of two. Isso garante que untrusted_index é sempre menor que buffer_size, mesmo se ocorre um misprediction ramificação condicional e untrusted_index foi transmitido com um valor maior que ou igual a buffer_size.This ensures that untrusted_index is always less than buffer_size, even if a conditional branch misprediction occurs and untrusted_index was passed in with a value greater than or equal to buffer_size.

Deve-se observar que o mascaramento de índice executado aqui pode estar sujeitos a bypass de repositório especulativa dependendo do código gerado pelo compilador.It should be noted that the index masking performed here could be subject to speculative store bypass depending on the code that is generated by the compiler.

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

Remover informações confidenciais da memóriaRemoving sensitive information from memory

Outra técnica que pode ser usada para atenuar as vulnerabilidades de canal lateral de execução especulativa é remover informações confidenciais da memória.Another technique that can be used to mitigate speculative execution side channel vulnerabilities is to remove sensitive information from memory. Os desenvolvedores de software podem buscar oportunidades para refatorar seu aplicativo, de modo que informações confidenciais não estão acessíveis durante a execução especulativa.Software developers can look for opportunities to refactor their application such that sensitive information is not accessible during speculative execution. Isso pode ser feito pela refatoração o design de um aplicativo para isolar as informações confidenciais em processos separados.This can be accomplished by refactoring the design of an application to isolate sensitive information into separate processes. Por exemplo, um aplicativo de navegador da web pode tentar isolar os dados associados a cada origem da web em processos separados, evitando que um processo que está sendo capaz de acessar os dados entre origens por meio de execução especulativa.For example, a web browser application can attempt to isolate the data associated with each web origin into separate processes, thus preventing one process from being able to access cross-origin data through speculative execution.

Consulte tambémSee also

Orientações para atenuar as vulnerabilidades de canal lateral de execução especulativaGuidance to mitigate speculative execution side-channel vulnerabilities
Atenuar as vulnerabilidades de execução especulativa lado canal hardwareMitigating speculative execution side channel hardware vulnerabilities