Instrucciones para desarrolladores de C++ para los canales del lado de ejecución especulativaC++ Developer Guidance for Speculative Execution Side Channels

En este artículo contiene instrucciones para desarrolladores ayudar a identificar y mitigar las vulnerabilidades de hardware de canal de lado de ejecución especulativa en software de C++.This article contains guidance for developers to assist with identifying and mitigating speculative execution side channel hardware vulnerabilities in C++ software. Estas vulnerabilidades pueden revelar información confidencial a través de límites de confianza y pueden afectar al software que se ejecuta en procesadores que admiten la ejecución especulativa, fuera de secuencia de instrucciones.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. Esta clase de vulnerabilidades fue el primero se describe en enero de 2018 y obtener información general adicional y pueden encontrar instrucciones en aviso de seguridad de Microsoft.This class of vulnerabilities was first described in January, 2018 and additional background and guidance can be found in Microsoft's security advisory.

Las instrucciones proporcionadas en este artículo está relacionado con las clases de vulnerabilidades representadas por:The guidance provided by this article is related to the classes of vulnerabilities represented by:

  1. CVE-2017-5753, también conocido como variante 1.CVE-2017-5753, also known as Spectre variant 1. Esta clase de vulnerabilidad de hardware está relacionado con los canales de lado que pueden surgir debido a la ejecución especulativa que se produce como resultado una predicción de bifurcación 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. Microsoft C++ compilador en Visual Studio 2017 (empezando por la versión 15.5.5) incluye compatibilidad con la /Qspectre relacionados con CVE-2017-5753 conmutador que proporciona una mitigación de tiempo de compilación para un conjunto limitado de modelos de codificación potencialmente vulnerables.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. El /Qspectre también está disponible en Visual Studio 2015 Update 3 a través de conmutador 4338871 KB.The /Qspectre switch is also available in Visual Studio 2015 Update 3 through KB 4338871. La documentación de la /qspectre marca proporciona más información sobre sus efectos y uso.The documentation for the /Qspectre flag provides more information on its effects and usage.

  2. CVE-2018-3639, también conocido como especulativa Store omisión (SSB).CVE-2018-3639, also known as Speculative Store Bypass (SSB). Esta clase de vulnerabilidad de hardware está relacionado con los canales de lado que pueden surgir debido a la ejecución especulativa de una carga por delante de un almacén dependiente como resultado de una predicción de acceso de memoria.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.

Encontrará una introducción accesible sobre vulnerabilidades de canal lateral de ejecución especulativa en la presentación titulada el caso de Spectre y Meltdown por uno de los equipos de investigación que detectan estos problemas.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.

¿Cuáles son las vulnerabilidades de hardware de canal de lado de ejecución especulativa?What are Speculative Execution Side Channel hardware vulnerabilities?

Las CPU actuales ofrecen niveles superiores de rendimiento haciendo uso de la ejecución especulativa y de desorden de instrucciones.Modern CPUs provide higher degrees of performance by making use of speculative and out-of-order execution of instructions. Por ejemplo, esto se logra con frecuencia al predecir el destino de ramas (condicionales e indirectas) que permite que la CPU empezar a anticipa ejecutando instrucciones en el destino de bifurcación previstos, lo que evita una pausa hasta que el destino de bifurcación real puede resolver.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. En caso de que la CPU más tarde descubre que se ha producido una predicción, todo el estado de la máquina que se calculó anticipa se descartan.In the event that the CPU later discovers that a misprediction occurred, all of the machine state that was computed speculatively is discarded. Esto garantiza que no hay ningún efecto arquitectónicamente visible de la especulación mispredicted.This ensures that there are no architecturally visible effects of the mispredicted speculation.

Aunque la ejecución especulativa no afecta el estado de visibilidad de su arquitectura, puede dejar residuos en estado no arquitectónicos, como las memorias caché distintos que se usan por la 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. Resulta que estos seguimientos residuales de ejecución especulativa que puede dar lugar a vulnerabilidades de canal lateral.It is these residual traces of speculative execution that can give rise to side channel vulnerabilities. Para comprender mejor esto, considere el siguiente fragmento de código que proporciona un ejemplo de CVE-2017-5753 (omisión de comprobación de límites):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];
    }
}

En este ejemplo, ReadByte es proporcionar un búfer, un tamaño de búfer y un índice en ese búfer.In this example, ReadByte is supplied a buffer, a buffer size, and an index into that buffer. El parámetro de índice, según lo especificado por untrusted_index, proporcionado por una menor contexto con privilegios, como un proceso sin derechos administrativos.The index parameter, as specified by untrusted_index, is supplied by a less privileged context, such as a non-administrative process. Si untrusted_index es menor que buffer_size, a continuación, se lee el carácter que ocupa ese índice de buffer y se utiliza para el índice en una región compartida de memoria que hace referencia 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.

Desde una perspectiva arquitectónica, esta secuencia de código es perfectamente segura tal como se garantiza que untrusted_index siempre 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. Sin embargo, en la presencia de ejecución especulativa, es posible que la CPU se errores de predicción de la rama condicional y ejecutará el cuerpo de if instrucción incluso cuando untrusted_index es mayor o igual que 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. Por consiguiente, la CPU anticipa puede leer un byte más allá de los límites de buffer (que puede ser un secreto) y, a continuación, se podría usar ese valor de byte para calcular la dirección de una carga posterior a través 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.

Mientras que la CPU finalmente detectará esta predicción, pueden quedar residuales efectos secundarios en la memoria caché de CPU que ofrecen información sobre el valor de bytes que se leyó fuera de los límites 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. Estos efectos pueden ser detectados por una menor contexto privilegiado que se ejecuta en el sistema por sondeo rapidez cada caché línea shared_buffer se tiene acceso.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. Los pasos que se pueden realizar para lograr esto son:The steps that can be taken to accomplish this are:

  1. Invocar ReadByte varias veces con untrusted_index que se va a menos de buffer_size .Invoke ReadByte multiple times with untrusted_index being less than buffer_size. El contexto de ataque puede hacer que el contexto de la víctima invocar ReadByte (por ejemplo, a través de RPC), que es la predicción de ramas capacitado para no ser tomada como untrusted_index es 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. Vaciar todas las líneas de caché en shared_buffer .Flush all cache lines in shared_buffer. El contexto del atacante debe vaciar todas las líneas de caché en la región de memoria que hace referencia compartida shared_buffer.The attacking context must flush all of the cache lines in the shared region of memory referred to by shared_buffer. Dado que la región de memoria se comparte, esto es sencillo y puede realizarse mediante las funciones intrínsecas, 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 con untrusted_index es mayor que buffer_size .Invoke ReadByte with untrusted_index being greater than buffer_size. El contexto ataca provoca que el contexto de la víctima invocar ReadByte tal que predice incorrectamente que no se realizará la rama.The attacking context causes the victim context to invoke ReadByte such that it incorrectly predicts that the branch will not be taken. Esta hace que el procesador para ejecutar anticipa el cuerpo de if bloque con untrusted_index es mayor que buffer_size, por tanto, provocando una lectura fuera de límites 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. Por lo tanto, shared_buffer está indizada con un valor potencialmente confidencial que se leyó fuera de los límites, lo cual provoca la línea de caché correspondiente que va a cargar la 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. Leer cada línea de caché en shared_buffer para ver lo que se tiene acceso a más rápidamente.Read each cache line in shared_buffer to see which is accessed most quickly. El contexto del atacante puede leer cada línea de caché en shared_buffer y detectar la línea de caché que se carga mucho más rápido que los demás.The attacking context can read each cache line in shared_buffer and detect the cache line that loads significantly faster than the others. Se trata de la línea de caché que es probable que se han incorporado el paso 3.This is the cache line that is likely to have been brought in by step 3. Dado que hay una relación 1:1 entre la línea de valor y la memoria caché de bytes en este ejemplo, esto permite que el atacante puede deducir el valor real del byte que se leyó fuera de los límites.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.

Los pasos anteriores proporcionan un ejemplo del uso de una técnica conocida como VACIADO + RECARGA junto con el aprovechamiento de una instancia 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.

¿Qué escenarios de software pueden verse afectados?What software scenarios can be impacted?

Desarrollo de software seguro mediante un proceso como el Security Development Lifecycle (SDL) normalmente requiere que los desarrolladores a identificar los límites de confianza que existen en su aplicación.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 un límite de confianza en lugares donde una aplicación puede interactuar con los datos proporcionados por un contexto de menor confianza, por ejemplo, otro proceso en el sistema o un proceso en modo usuario sin derechos administrativos en el caso de un controlador 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. La nueva clase de vulnerabilidades relacionadas con canales de lado de la ejecución especulativa es relevante para muchos de los límites de confianza en los modelos de seguridad de software existente que aislar código y los datos en un 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.

En la tabla siguiente proporciona un resumen de los modelos de seguridad de software donde los desarrolladores que deba preocuparse acerca de estas vulnerabilidades que se producen:The following table provides a summary of the software security models where developers may need to be concerned about these vulnerabilities occurring:

Límite de confianzaTrust boundary DescripciónDescription
Límites de máquina virtualVirtual machine boundary Las aplicaciones que aislar las cargas de trabajo en máquinas virtuales independientes que reciben datos de confianza desde otra máquina virtual pueden estar en peligro.Applications that isolate workloads in separate virtual machines that receive untrusted data from another virtual machine may be at risk.
Límite del núcleoKernel boundary Un controlador de dispositivo de modo kernel que recibe los datos de confianza de un proceso en modo usuario no administrativo puede estar en peligro.A kernel-mode device driver that receives untrusted data from a non-administrative user mode process may be at risk.
Límite de procesoProcess boundary Una aplicación que recibe los datos de confianza de otro proceso que se ejecuta en el sistema local, como a través de una llamada a procedimiento remoto (RPC), memoria compartida u otras comunicaciones entre procesos (IPC) mecanismos pueden estar en peligro.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.
Límite de enclaveEnclave boundary Una aplicación que se ejecuta dentro de un enclave seguro (por ejemplo, Intel SGX) que recibe datos de confianza desde fuera el enclave puede estar en peligro.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.
Límites de los lenguajesLanguage boundary Una aplicación que interpreta o Just In Time (JIT) compila y ejecuta el código no seguro que se escriben un lenguaje de nivel superior puede estar en peligro.An application that interprets or Just-In-Time (JIT) compiles and executes untrusted code written in a higher-level language may be at risk.

Aplicaciones que tienen la superficie expuesta a ataques a cualquiera de los anteriores límites deben revisar el código en la superficie de ataque para identificar y mitigar los posibles casos de vulnerabilidades de canal lateral de ejecución especulativa de confianza.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. Debe tenerse en cuenta que los límites de confianza expuestos a las superficies de ataque remoto, como los protocolos de red remota, que no han demostrado poner en riesgo a vulnerabilidades de canal lateral de ejecución 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.

Potencialmente vulnerables patrones de codificaciónPotentially vulnerable coding patterns

Pueden surgir vulnerabilidades de canal lateral de ejecución especulativa como consecuencia de varios patrones de codificación.Speculative execution side channel vulnerabilities can arise as a consequence of multiple coding patterns. En esta sección se describe los patrones de codificación potencialmente vulnerables y proporciona ejemplos para cada uno, pero debería reconocer que pueden existir variaciones en estos temas.This section describes potentially vulnerable coding patterns and provides examples for each, but it should be recognized that variations on these themes may exist. Por lo tanto, se aconseja a los desarrolladores para aprovechar estos patrones como ejemplos y no como una lista exhaustiva de todos los modelos de codificación potencialmente vulnerables.As such, developers are advised to take these patterns as examples and not as an exhaustive list of all potentially vulnerable coding patterns. Las mismas clases de vulnerabilidades de seguridad de memoria que pueden existir en el software hoy en día pueden existir a lo largo de especulativa y rutas de acceso fuera de orden de ejecución, incluyendo pero sin limitarse a saturaciones del búfer, fuera de los límites de matriz accesos, uso de memoria no inicializada, tipo confusión y así sucesivamente.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. También se pueden aplicar los mismos tipos primitivos que los atacantes pueden utilizar para aprovechar las vulnerabilidades de seguridad de memoria a lo largo de las rutas de acceso de arquitectura a especulativas rutas de acceso.The same primitives that attackers can use to exploit memory safety vulnerabilities along architectural paths may also apply to speculative paths.

En general, predicción de ejecución especulativa lado canales rama relacionados a condicional puede presentar cuando una expresión condicional opera en datos que se pueden controlar o influidos por un contexto de menor confianza.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 ejemplo, esto puede incluir las expresiones condicionales usadas en if, for, while, switch, o las instrucciones ternarias.For example, this can include conditional expressions used in if, for, while, switch, or ternary statements. Para cada una de estas instrucciones, el compilador puede generar una rama condicional que la CPU, a continuación, puede predecir el destino de bifurcación para en tiempo de ejecución.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 ejemplo, se inserta un comentario con la frase "Barrera de especulación" donde un desarrollador podría suponer una barrera como mitigación.For each example, a comment with the phrase "SPECULATION BARRIER" is inserted where a developer could introduce a barrier as a mitigation. Esto se explica con más detalle en la sección en las mitigaciones.This is discussed in more detail in the section on mitigations.

Especulativa fuera de los límites de cargaSpeculative out-of-bounds load

Esta categoría de patrones de codificación implica una predicción de rama condicional que conduce a un especulativa fuera de los límites acceso a la memoria.This category of coding patterns involves a conditional branch misprediction that leads to a speculative out-of-bounds memory access.

Matriz fuera de los límites de carga proporcionando una cargaArray out-of-bounds load feeding a load

Este patrón de codificación es el patrón de codificación vulnerable descrito originalmente para CVE-2017-5753 (omisión de comprobación de límites).This coding pattern is the originally described vulnerable coding pattern for CVE-2017-5753 (Bounds Check Bypass). La sección de fondo de este artículo explica este patrón en detalle.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];
    }
}

De forma similar, una matriz fuera de los límites puede producirse carga junto con un bucle que supera su terminación condición debido a una predicción.Similarly, an array out-of-bounds load may occur in conjunction with a loop that exceeds its terminating condition due to a misprediction. En este ejemplo, la bifurcación condicional asociado con el x < buffer_size puede errores de predicción y anticipa ejecutar el cuerpo de expresión la for bucle cuando x es mayor o igual que buffer_size, por lo tanto lo que provocará un especulativa fuera de los límites 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 fuera de los límites de carga alimentar una bifurcaciónArray out-of-bounds load feeding an indirect branch

Este patrón de codificación implica el caso donde una predicción de bifurcación condicional puede dar lugar a un fuera de los límites acceso a una matriz de punteros de función, lo que, a continuación, lleva a una rama en el destino indirecta de direcciones que se leyó fuera de los límites.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. El siguiente fragmento de código proporciona un ejemplo que se muestra cómo hacerlo.The following snippet provides an example that demonstrates this.

En este ejemplo, se proporciona un identificador de mensaje que no se confía para DispatchMessage a través de la untrusted_message_id parámetro.In this example, an untrusted message identifier is provided to DispatchMessage through the untrusted_message_id parameter. Si untrusted_message_id es menor que MAX_MESSAGE_ID, a continuación, se usa para indexar una matriz de punteros de función y la rama para el destino de bifurcación correspondiente.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. Este código es seguro su arquitectura, pero si la CPU errores de predicción de la rama condicional, podría causar DispatchTable indexando untrusted_message_id cuando su valor es mayor o igual que MAX_MESSAGE_ID, provocando así un acceso fuera de los límites.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. Esto podría dar lugar a ejecución especulativa desde una dirección de destino de rama que se deriva más allá de los límites de la matriz que podría provocar la divulgación de información según el código que se ejecuta anticipa.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 con el caso de una matriz fuera de los límites de carga alimentar carga otro, esta condición también puede surgir junto con un bucle que supera su condición de finalización debido a una predicción.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.

Fuera de los límites de matriz almacenar alimentar una bifurcaciónArray out-of-bounds store feeding an indirect branch

Mientras que el ejemplo anterior se mostró cómo un especulativa fuera de los límites carga puede influir en un destino de bifurcación indirecta, también es posible que un almacén fuera de los límites para modificar un destino de bifurcación, como un puntero de función o una dirección 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. Esto puede provocar la ejecución especulativa desde una dirección especificada por el atacante.This can potentially lead to speculative execution from an attacker-specified address.

En este ejemplo, un índice de confianza se pasa a través de la untrusted_index parámetro.In this example, an untrusted index is passed through the untrusted_index parameter. Si untrusted_index es menor que el número de elementos de la pointers matriz (256 elementos) y, a continuación, el valor de puntero proporcionado en ptr se escribe en el 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. Este código es seguro su arquitectura, pero si la CPU errores de predicción de la rama condicional, podría causar ptr anticipa que se escribe más allá de los límites de la pila asignado 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. Esto podría provocar daños especulativo de la dirección de devolución para WriteSlot.This could lead to speculative corruption of the return address for WriteSlot. Si un atacante puede controlar el valor de ptr, es posible puedan provocar la ejecución especulativa de arbitrario de direcciones cuando WriteSlot devuelve a lo largo de la ruta de acceso especulativa.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;
    }
}

De forma similar, si una variable local de puntero de función denominada func asignados en la pila, a continuación, es posible modificar anticipa la dirección que func hace referencia a cuando se produce la predicción de bifurcación 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. Esto podría provocar la ejecución especulativa desde una dirección arbitraria cuando el puntero de función se llama a través.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();
}

Debe tenerse en cuenta que en ambos ejemplos implican la modificación especulativa de punteros de rama indirecta asignada a la pila.It should be noted that both of these examples involve speculative modification of stack-allocated indirect branch pointers. Es posible que también se puede producir especulativa modificación para incluso memoria de sólo lectura en algunas CPU, memoria asignados por montón y variables globales.It is possible that speculative modification could also occur for global variables, heap-allocated memory, and even read-only memory on some CPUs. Memoria asignada a la pila, Microsoft C++ compilador ya realiza los pasos para que sea más difícil de modificar anticipa destinos asignada a la pila de bifurcación indirecta, como reordenando las variables locales de forma que los búferes se colocan junto a la seguridad cookie como parte de la /GS característica de seguridad del 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.

Confusión de tipo especulativaSpeculative type confusion

Esta categoría se ocupa de patrones que pueden dar lugar a una confusión especulativa de tipo de codificación.This category deals with coding patterns that can give rise to a speculative type confusion. Esto se produce cuando se tiene acceso a memoria con un tipo incorrecto a lo largo de una ruta de acceso que no es arquitectura durante la ejecución especulativa.This occurs when memory is accessed using an incorrect type along a non-architectural path during speculative execution. Predicción de bifurcación condicional y omisión de la tienda especulativa pueden provocar confusión de un tipo especulativa.Both conditional branch misprediction and speculative store bypass can potentially lead to a speculative type confusion.

Para la omisión de la tienda especulativas, esto puede ocurrir en escenarios donde un compilador vuelve a usar una ubicación de la pila para las variables de varios tipos.For speculative store bypass, this could occur in scenarios where a compiler reuses a stack location for variables of multiple types. Esto es porque el almacén de arquitectura de una variable de tipo A puede omitirse, lo que permite la carga de tipo A anticipa ejecutar antes de que se asigna a la variable.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. Si la variable anteriormente almacenada es de un tipo diferente, esto puede crear las condiciones de una confusión especulativa de tipo.If the previously stored variable is of a different type, then this can create the conditions for a speculative type confusion.

Para la predicción de bifurcación condicional, se usará el siguiente fragmento de código para describir diferentes condiciones confusión especulativa tipo puede dar lugar a.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;
    }
}

Confusión de tipo especulativa dan lugar a un fuera de los límites de cargaSpeculative type confusion leading to an out-of-bounds load

Este patrón de codificación implica el caso donde puede dar lugar a una confusión especulativa de tipo en un fuera de los límites o acceso confundido por el tipo de campo donde el valor cargado fuentes de distribución de una dirección de carga posteriores.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. Esto es similar al patrón de codificación fuera de los límites de matriz, pero se manifiesta a través de una alternativa a la secuencia de codificación, como se indicó anteriormente.This is similar to the array out-of-bounds coding pattern but it is manifested through an alternative coding sequence as shown above. En este ejemplo, podría provocar que el contexto de la víctima para ejecutar un contexto ataca ProcessType varias veces con un objeto de tipo CType1 (type es igual al campo 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). Esto tendrá el efecto del entrenamiento de la rama condicional para la primera if instrucción no predecir tomada.This will have the effect of training the conditional branch for the first if statement to predict not taken. El contexto de ataque, a continuación, puede provocar el contexto de la víctima para ejecutar ProcessType con un objeto de tipo CType2.The attacking context can then cause the victim context to execute ProcessType with an object of type CType2. Esto puede provocar confusión de un tipo especulativa si el condicional de bifurcación para el primer if instrucción errores de predicción y ejecuta el cuerpo de la if instrucción, la conversión, por tanto, un objeto de tipo CType2 a 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. Puesto que CType2 es menor que CType1, el acceso a la memoria para CType1::field2 resultado en un especulativa fuera de los límites cargará de datos que pueden ser secretas.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. Este valor se utiliza en una carga de shared_buffer que puede crear efectos observables, igual que con la matriz fuera de los límites en el ejemplo se describe 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.

Confusión de tipo especulativa dan lugar a una rama indirectaSpeculative type confusion leading to an indirect branch

Este patrón de codificación implica el caso donde una confusión especulativa tipo puede dar lugar a una rama indirecta no segura durante la ejecución especulativa.This coding pattern involves the case where a speculative type confusion can result in an unsafe indirect branch during speculative execution. En este ejemplo, podría provocar que el contexto de la víctima para ejecutar un contexto ataca ProcessType varias veces con un objeto de tipo CType2 (type es igual al campo 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). Esto tendrá el efecto del entrenamiento de la rama condicional para la primera if instrucción que se debe realizar y el else if instrucción que no se debe realizar.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. El contexto de ataque, a continuación, puede provocar el contexto de la víctima para ejecutar ProcessType con un objeto de tipo CType1.The attacking context can then cause the victim context to execute ProcessType with an object of type CType1. Esto puede provocar confusión de un tipo especulativa si el condicional de bifurcación para el primer if predice la instrucción realizadas y el else if instrucción predice no tomada, ejecutar, por tanto, el cuerpo de la else if y la conversión de un objeto de tipo CType1 a 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. Puesto que la CType2::dispatch_routine campo se superpone con el char matriz CType1::field1, esto podría resultar en una rama especulativa indirecta a un destino de bifurcación no deseados.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. Si el contexto de ataque puede controlar los valores de byte en el CType1::field1 matriz, pueden ser capaces de controlar la dirección de destino de bifurcación.If the attacking context can control the byte values in the CType1::field1 array, they may be able to control the branch target address.

Uso sin inicializar especulativaSpeculative uninitialized use

Esta categoría de patrones de codificación implica escenarios donde puede obtener acceso a memoria no inicializada y usarlo para alimentar una carga posterior o rama indirecta ejecución especulativa.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 que estos modelos de codificación que se pueda usar, debe ser capaz de controlar o influyen significativamente en el contenido de la memoria que se usa sin inicializar el contexto que se está usando en un atacante.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.

Uso sin inicializar especulativa dan lugar a un fuera de los límites de cargaSpeculative uninitialized use leading to an out-of-bounds load

Puede provocar un uso sin inicializar especulativo un fuera de los límites de carga con un valor controlada por el atacante.A speculative uninitialized use can potentially lead to an out-of-bounds load using an attacker controlled value. En el ejemplo siguiente, el valor de index se asigna trusted_index en todas las rutas de arquitectura y trusted_index se supone que es menor o 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. Sin embargo, según el código generado por el compilador, es posible que se puede producir una omisión de la tienda especulativas que permite la carga de buffer[index] y expresiones dependientes ejecutará antes de la asignación 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. Si esto ocurre, un valor no inicializado para index se usará como el desplazamiento de buffer que podría permitir que un atacante leer la información confidencial fuera de los límites y se transmiten a través de un canal de lado a través de la carga dependiente 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];
}

Uso sin inicializar especulativa dan lugar a una rama indirectaSpeculative uninitialized use leading to an indirect branch

Un uso sin inicializar especulativo puede provocar una rama indirecta donde el destino de bifurcación está controlado por un atacante.A speculative uninitialized use can potentially lead to an indirect branch where the branch target is controlled by an attacker. En el ejemplo siguiente, routine está asignado tanto DefaultMessageRoutine1 o DefaultMessageRoutine dependiendo del valor de mode.In the example below, routine is assigned to either DefaultMessageRoutine1 or DefaultMessageRoutine depending on the value of mode. En la ruta de acceso de arquitectura, esto dará como resultado routine siempre que se está inicializando por delante de la rama indirecta.On the architectural path, this will result in routine always being initialized ahead of the indirect branch. Sin embargo, dependiendo del código generado por el compilador, una omisión especulativa store se puede producir que permite la rama indirecta a través de routine ejecutará anticipa por delante de la asignación 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. Si esto ocurre, un atacante puede ser capaz de ejecutar especulativo desde una dirección arbitraria, suponiendo que el atacante puede influir en o controlar el valor inicializado de 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);
}

Opciones de mitigaciónMitigation options

Al realizar cambios en el código fuente se pueden mitigar vulnerabilidades de canal lateral de ejecución especulativa.Speculative execution side channel vulnerabilities can be mitigated by making changes to source code. Estos cambios pueden implicar mitigar instancias específicas de una vulnerabilidad, por ejemplo, agregando un barrera de especulación, o al realizar cambios en el diseño de una aplicación para que información confidencial inaccesible para especulativa ejecución.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.

Barrera de especulación a través de la instrumentación manualSpeculation barrier via manual instrumentation

Un barrera de especulación se pueden insertar manualmente por un desarrollador para evitar la ejecución especulativa de continuar a lo largo de una ruta de acceso que no es arquitectura.A speculation barrier can be manually inserted by a developer to prevent speculative execution from proceeding along a non-architectural path. Por ejemplo, un desarrollador puede insertar una barrera de especulación antes de un patrón de codificación peligroso en el cuerpo de un bloque condicional, ya sea al principio del bloque (después de la rama condicional) o antes de la primera carga constituye un problema.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. Esto evitará que una predicción de bifurcación condicional, ejecutar el código peligroso en una ruta de acceso que no es arquitectura serializando la ejecución.This will prevent a conditional branch misprediction from executing the dangerous code on a non-architectural path by serializing execution. La secuencia de barrera de especulación difiere por arquitectura de hardware, como se describe en la tabla siguiente:The speculation barrier sequence differs by hardware architecture as described by the following table:

ArquitecturaArchitecture Barrera de especulación intrínseco para CVE-2017-5753Speculation barrier intrinsic for CVE-2017-5753 Barrera de especulación intrínseco para CVE-2018-3639Speculation barrier intrinsic for CVE-2018-3639
x86/x64x86/x64 _mm_lfence()_mm_lfence() _mm_lfence()_mm_lfence()
ARMARM actualmente, no disponiblenot currently available __dsb(0)__dsb(0)
ARM64ARM64 actualmente, no disponiblenot currently available __dsb(0)__dsb(0)

Por ejemplo, el modelo de código siguiente se puede mitigar mediante el uso de la _mm_lfence intrínsecos, como se muestra a continuación.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];
    }
}

Barrera de especulación a través de la instrumentación en tiempo de compiladorSpeculation barrier via compiler-time instrumentation

Microsoft C++ compilador en Visual Studio 2017 (empezando por la versión 15.5.5) incluye compatibilidad con la /Qspectre relacionados con el conmutador que inserta automáticamente una barrera de especulación para un conjunto limitado de modelos de codificación potencialmente vulnerables 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. La documentación de la /qspectre marca proporciona más información sobre sus efectos y uso.The documentation for the /Qspectre flag provides more information on its effects and usage. Es importante tener en cuenta que esta marca no cubre todos los modelos de codificación potencialmente vulnerables y por lo tanto los desarrolladores no deben confiar en ella como una solución completa para esta clase 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.

Los índices de matriz de enmascaramientoMasking array indices

En casos donde un especulativa fuera de los límites de carga se puede producir, el índice de matriz puede ser limitado fuertemente en la ruta de acceso no arquitectónicas y de arquitectura mediante la adición de lógica para enlazar explícitamente el í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 ejemplo, si una matriz se puede asignar a un tamaño que se alinea a la potencia de dos, puede introducir una máscara simple.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. Esto se muestra en el ejemplo siguiente, donde se supone que buffer_size se alinea a la potencia de dos.This is illustrated in the sample below where it is assumed that buffer_size is aligned to a power of two. Esto garantiza que untrusted_index es siempre menor que buffer_size, aunque se produzca una predicción de bifurcación condicional y untrusted_index se ha pasado con un valor mayor o igual que 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.

Debe tenerse en cuenta que el enmascaramiento de índice que se realiza aquí podrían experimentar almacén especulativa omisión según el código generado por el 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];
    }
}

Quitar información confidencial de la memoriaRemoving sensitive information from memory

Otra técnica que puede utilizarse para mitigar vulnerabilidades de canal lateral de ejecución especulativa es quitar información confidencial de la memoria.Another technique that can be used to mitigate speculative execution side channel vulnerabilities is to remove sensitive information from memory. Los desarrolladores de software pueden buscar oportunidades para refactorizar su aplicación que información confidencial no sea accesible durante la ejecución especulativa.Software developers can look for opportunities to refactor their application such that sensitive information is not accessible during speculative execution. Esto puede realizarse mediante la refactorización del diseño de una aplicación para aislar la información confidencial en procesos independientes.This can be accomplished by refactoring the design of an application to isolate sensitive information into separate processes. Por ejemplo, una aplicación de explorador web puede intentar aislar los datos asociados con cada origen de web en procesos independientes, lo que evita un proceso que se puede tener acceso a datos entre orígenes a través de la ejecución 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.

Vea tambiénSee also

Instrucciones para mitigar las vulnerabilidades de canal lateral de ejecución especulativaGuidance to mitigate speculative execution side-channel vulnerabilities
Mitigar vulnerabilidades de hardware de canal de lado de ejecución especulativaMitigating speculative execution side channel hardware vulnerabilities