Ejecución especulativa
# Ejecución especulativa

En ciencias de la computación, la ejecución especulativa es una forma de optimización en la que un sistema informático realiza una tarea que podría no ser necesaria; la idea consiste en llevar a cabo un trabajo antes de saber si será realmente necesario con la intención de evitar el retraso que supondría realizarlo después de saber que sí es necesario. Si el trabajo en cuestión resulta ser innecesario, la mayoría de los cambios realizados por ese trabajo se revierten y los resultados se ignoran.

El objetivo de esta técnica es proporcionar una mayor concurrencia en caso de disponer de más recursos. Esta técnica se utiliza en una variedad de áreas informáticas, incluyendo la predicción de saltos en las CPU que soportan segmentación, la predicción de valores dirigida a explotar la localización de valores, la prelectura de memoria y archivos, y el control de concurrencia optimista en sistemas de bases de datos.

## Antecedentes

Los modernos microprocesadores que incluyen segmentación utilizan ejecución especulativa para reducir el coste de las instrucciones que dependen de la predicción de saltos, y lo hacen mediante técnicas que predicen la ruta de ejecución de un programa basándose en el historial de los saltos realizados anteriormente. Con el fin de mejorar el rendimiento y optimizar la utilización de los recursos del sistema informático, las instrucciones pueden planificarse cuando todavía no se ha determinado si será o no necesario ejecutarlas, antes de un salto.

## Variantes

### Ejecución ansiosa

La ejecución ansiosa es una forma de ejecución especulativa en la que se ejecutan ambas partes de la ramificación; sin embargo, los resultados sólo se tienen en cuenta si lo predicho se cumple. En el caso hipotético de disponer de recursos ilimitados, la ejecución ansiosa (también conocida como ejecución de oráculo) proporcionaría teóricamente el mismo rendimiento que una predicción de saltos perfecta. Al disponer de recursos limitados, la ejecución ansiosa debe utilizarse con cuidado puesto que los recursos necesarios crecen de forma exponencial con cada nivel de ramificación que se ejecuta de forma ansiosa.

### Ejecución predictiva

La ejecución predictiva es una forma de ejecución especulativa en la que se predice un resultado y la ejecución se lleva a cabo por la ruta predicha hasta que llega a conocerse el verdadero resultado. Si la predicción se cumplió, se permite que la ejecución predicha entregue su resultado; sin embargo, si la predicción no se ha cumplido, la ejecución tiene que volver atrás y volver a ejecutarse. Las formas comunes de esta técnica son los predictores de saltos y la predicción de dependencia de memoria. Existe una forma generalizada a la que a veces se denomina predicción de valores.

### Ejecución vaga

La ejecución vaga no emplea la especulación. La incorporación de ejecución especulativa en implementaciones del lenguaje de programación Haskell es un tema de investigación actual. Eager Haskell (Haskell Ansioso) está diseñado sobre la idea de la ejecución especulativa. Una tesis doctoral de 2003 hizo que el compilador de Haskell de la Universidad de Glasgow soportase cierto tipo de ejecución especulativa mediante un mecanismo de cancelación que daba marcha atrás en caso de tomar una mala decisión; se la denominó ejecución optimista. Sin embargo, esta forma de ejecución especulativa fue considerada demasiado complicada.

## Vulnerabilidades de seguridad

En junio de 2017, un equipo formado por investigadores independientes, laboratorios universitarios de investigación, algunos miembros del Proyecto Cero de Google y Cyberus Technology descubrió dos vulnerabilidades de seguridad que eran posibles gracias al uso ampliamente extendido de la ejecución especulativa. El problema fue descubierto también de forma independiente por otros investigadores más o menos al mismo tiempo. Las vulnerabilidades terminaron haciéndose públicas en enero de 2018 y fueron denominadas Meltdown y Spectre. Ambas tienen el potencial de permitir que software malicioso pueda leer de la memoria protegida de un sistema informático, obteniendo acceso a información sensible como pueden ser contraseñas y claves de encriptación.

Para hacer que la ejecución especulativa ofrezca la máxima eficacia, algunas combinaciones de hardware y sistemas operativos permiten que ésta toque datos de la memoria privada del sistema operativo antes de que sea necesario. Las vulnerabilidades parten de la capacidad que tiene el programa malicioso de deducir en qué consiste esa información que por lo demás le resulta inaccesible.

La vulnerabilidad más ampliamente comentada de las dos, Meltdown, se basa en ciertas decisiones del hardware de leer la información sensible del usuario, pero puede solventarse mediante actualizaciones del software que se ejecuta en el sistema en cuestión. Spectre, la vulnerabilidad menos conocida y también la más difícil de aplicar de las dos, hace posible que un programa acceda a información correspondiente a otro programa que se ejecuta en el chip, y es mucho más difícil de solventar con una única solución. Aunque no es lo ideal, las mejores defensas generales sugeridas hasta el momento incluyen por un lado la detección, y por el otro la separación de la actividad referente al caché de los procesos concurrentes. La mitigación del segundo caso puede acarrear una pérdida significativa de rendimiento en ciertas arquitecturas, dependiendo también del tipo de tarea que ejecute el sistema.

Ambas vulnerabilidades funcionan realizando una carga indexada desde la memoria. Durante esta carga se lee de la memoria un dato A (que supuestamente está "fuera de límites") y entonces este dato se emplea para calcular la dirección de otro dato $B_A$ (accesible) para ser leído también de la memoria. Puesto que A es inaccesible, el procesador termina cancelando cualquier efecto directo de la operación sobre los registros y la memoria una vez que se da cuenta de que la lectura no está autorizada. Sin embargo, $B_A$ sigue estando presente en el caché, y esta condición puede ser detectada por el atacante leyendo $B_A$ para todos los posibles valores de A y observando qué operación de lectura tarda bastante menos tiempo en completarse que las demás.

La diferencia entre ambas vulnerabilidades radica en la condición no permitido que se está soslayando y en los medios para conseguirlo:

* En Meltdown, el atacante hace que la ejecución especulativa rompa la barrera de protección situada entre el espacio de memoria de un programa de usuario y una página protegida del kernel, haciendo para ello que la ejecución especulativa lea una dirección de memoria y coloque su contenido en el caché. Los modernos procesadores de Intel ejecutan el código y luego toman y almacenan los resultados especulativos, dejándolos en el caché. Posteriormente, el código usado para explotar la vulnerabilidad puede tomar notas de lo ocurrido, ya sea después de un fallo, o después de preparar el código original como parte de una transacción de memoria que atenuará el fallo. Se cree que algunos procesadores, como los de AMD, son inmunes a esto, ya que realizan la comprobación de permiso de acceso a la página antes de ejecutar la lectura especulativa.
* En Spectre, el atacante no depende de estos mecanismos de fallo, sino que va a por otro proceso del usuario de una forma más generalista. Spectre depende de que un fallo en la predicción de saltos realice de forma especulativa la lectura de la celda de una matriz, aunque en el salto precedente se percibiese que la lectura iría más allá del final de la matriz. Empieza por entrenar la maquinaria de predicción de saltos del procesador para forzar un fallo en la predicción que se sale los límites de un proceso y entonces manipula al proceso objetivo para que ejecute parte de su propio código, que en realidad toca la rama especulativa. De nuevo, lo que ha tocado se revela mediante un canal lateral basado en medir los tiempos del caché. En este caso se muestra un ejemplo que combinado con un fragmento de código JavaScript pueden emplearse para leer todo el espacio de direcciones del proceso al que se ha engañado para ejecutar el código en cuestión.

Se cree que sólo los navegadores web que realizan compilación JIT de JavaScript son vulnerables a esta forma de usar Spectre. Sin embargo, todos los navegadores modernos –incluyendo Chrome, Firefox, Safari y Edge– utilizan un motor JIT. Es más, Spectre puede emplearse también para manipular el predictor de saltos de varias formas, aunque no intervenga un lenguaje de scripts.

En principio Google tenía prevista una publicación coordinada del informe al completo de Proyecto Cero el martes 9 de enero de 2018, y dijo que había estado trabajando con empresas tanto de hardware como de software a lo largo de varios meses para mitigar los riesgos. Sin embargo, la escalada de rumores al respecto parece haber forzado un adelanto en la revelación de ambas vulnerabilidades.

