Ataques Spectre: Explotación de la Ejecución Especulativa Paul Kocher1, Jann Horn2, Anders Fogh3, Daniel Genkin4, Daniel Gruss5, Werner Haas6, Mike Hamburg7, Moritz Lipp5, Stefan Mangard5, Thomas Prescher6, Michael Schwarz5, Yuval Yarom8 1 Independent 3G (www.paulkocher.com), 2 Google Project Zero, DATA Advanced Analytics, 4 University of Pennsylvania and University of Maryland, 5 Graz 7 Rambus, University of Technology, 6 Cyberus Technology, Cryptography Research Division, 8 University of Adelaide and Data61 Resumen Los procesadores modernos utilizan la predicción y la ejecución especulativa para maximizar el rendimiento. Por ejemplo, si el destino de una ejecución depende de un valor de memoria que está en proceso de lectura, las CPU intentarán adivinar el destino e intentarán ejecutarlo con anticipación. Cuando finalmente llega el valor de la memoria, la CPU descarta o confirma el cálculo especulativo. La lógica especulativa es no confiable en cómo se ejecuta, ya que puede acceder a la memoria y los registros de la víctima, y puede realizar operaciones con efectos secundarios medibles. Los ataques de Spectre implican inducir a una víctima a realizar operaciones especulativas que no ocurrirían durante la ejecución correcta del programa y que filtran la información confidencial de la víctima a través de un canal lateral al adversario. Este documento describe los ataques prácticos que combinan la metodología de los ataques de canal lateral, los ataques de fallas y la programación orientada al retorno que puede reactivar la memoria arbitraria del proceso de la víctima. En términos más generales, el documento muestra que las implementaciones de ejecución especulativa violan los supuestos de seguridad que sustentan numerosos mecanismos de seguridad de software, incluida la separación de procesos del sistema operativo, el uso de contenedores, la compilación justo a tiempo (JIT) y las contramedidas para el tiempo de caché y los ataques de canal lateral. Estos ataques representan una seria amenaza para los sistemas reales, ya que se encuentran capacidades de ejecución especulativa vulnerables en microprocesadores de Intel, AMD y ARM que se utilizan en miles de millones de dispositivos. Si bien en algunos casos son posibles contramedidas improvisadas específicas sobre el procesador, las soluciones requerirán correcciones a los diseños del procesador, así como actualizaciones a las arquitecturas del conjunto de instrucciones (ISA) para dar a los arquitectos de hardware y desarrolladores de software una comprensión común sobre que estado de implementaciones de computo de CPU son (y cuáles no son) permitidas las fugas. 1. Introducción Los cálculos realizados por dispositivos físicos a menudo dejan efectos secundarios observables más allá de los resultados nominales del cálculo. Los ataques de canal lateral se centran en explotar estos efectos secundarios para extraer información secreta que de otro modo no estaría disponible. Desde su 1 introducción a finales de los años 90 [43], muchos efectos físicos como el consumo de energía [41, 42], la radiación electromagnética [58] o el ruido acústico [20] se han aprovechado para extraer claves criptográficas, así como otros secretos. Los ataques físicos de canal lateral también se pueden utilizar para extraer información secreta de dispositivos complejos como PC y teléfonos móviles [21, 22]. Sin embargo, debido a que estos dispositivos a menudo ejecutan código de un origen potencialmente desconocido, enfrentan amenazas adicionales en forma de ataques basados en software, que no requieren equipos de medición externos. Mientras que algunos ataques aprovechan las vulnerabilidades del software (como desbordamientos del búfer [5] o errores de doble libre [12]), otros ataques de software aprovechan las vulnerabilidades del hardware para filtrar información confidencial. Los ataques de este último tipo incluyen ataques de microarquitectura que aprovechan el tiempo de caché [8, 30, 48, 52, 55, 69, 74], historial de predicción de bifurcacion [1, 2], búferes de objetivo de bifurcacion [14, 44] o filas de DRAM abiertas [56 ] Las técnicas basadas en software también se han utilizado para montar ataques de falla que alteran la memoria física [39] o los valores internos de la CPU. Varias técnicas de diseño microarquitectura han facilitado el aumento de la velocidad del procesador en las últimas décadas. Uno de estos avances es la ejecución especulativa, que se usa ampliamente para aumentar el rendimiento e implica que la CPU adivine las posibles direcciones de ejecución futuras y ejecute prematuramente instrucciones en estos caminos. Más específicamente, considere un ejemplo en el que el flujo de control del programa depende de un valor no almacenado en caché ubicado en la memoria física externa. Como esta memoria es mucho más lenta que la CPU, a menudo toma varios cientos de ciclos de reloj antes de que se conozca el valor. En lugar de desperdiciar estos ciclos al estar sin uso, la CPU intenta adivinar la dirección del flujo de control, guarda un punto de control de su estado de registro y procede a ejecutar especulativamente el programa en la ruta adivinada. Cuando el valor finalmente llega de la memoria, la CPU verifica la exactitud de su suposición inicial. Si la suposición era incorrecta, la CPU descarta la ejecución especulativa incorrecta al revertir el estado del registro al punto de control almacenado, lo que resulta en un rendimiento comparable a la inactividad. Sin embargo, si la suposición fue correcta, los resultados de la ejecución especulativa se comprometen, produciendo una ganancia de rendimiento significativa ya que se realizó un trabajo útil durante el retraso. Desde una perspectiva de seguridad, la ejecución especulativa implica ejecutar un programa de formas posiblemente incorrectas. Sin embargo, debido a que las CPU están diseñadas para mantener la corrección funcional al revertir los resultados de ejecuciones especulativas incorrectas a sus estados anteriores, se suponía anteriormente que estos errores eran seguros. 1.1 Nuestros resultados En este artículo, analizamos las implicaciones de seguridad de tal ejecución especulativa incorrecta. Presentamos una clase de ataques microarquitectónicos que llamamos ataques Spectre. A un alto nivel, los ataques de Spectre engañan al procesador para que ejecute secuencias de instrucciones especulativas que no deberían haberse ejecutado con la ejecución correcta del programa. Como los efectos de estas instrucciones en el estado nominal de la CPU se revierten, los llamamos instrucciones transitorias. Al influir en qué instrucciones transitorias se ejecutan especulativamente, podemos filtrar información desde el espacio de direcciones de memoria de la víctima. Demostramos empíricamente la viabilidad de los ataques de Spectre mediante la explotación de secuencias de instrucciones transitorias para filtrar información a través de dominios de seguridad tanto del código nativo no privilegiado como del código JavaScript portátil Ataques con código nativo. Como prueba de concepto, creamos un programa simple para víctimas que contiene datos secretos dentro de su espacio de direcciones de memoria. A continuación, buscamos en el binario compilado de la víctima y en las bibliotecas compartidas del sistema operativo secuencias de 2 instrucciones que se pueden usar para filtrar información del espacio de direcciones de la víctima. Finalmente, escribimos un programa atacante que explota la función de ejecución especulativa de la CPU para ejecutar las secuencias encontradas previamente como instrucciones transitorias. Con esta técnica, podemos leer la memoria del espacio de direcciones de la víctima, incluidos los secretos almacenados en ella. Ataques con JavaScript y eBPF. Además de violar los límites de aislamiento del proceso usando código nativo, los ataques Spectre también se pueden usar para violar el área de prueba, por ejemplo, montándolos a través de un código JavaScript portátil. Demostrando esto empíricamente, mostramos un programa JavaScript que lee con éxito los datos del espacio de direcciones del proceso del navegador que lo ejecuta. Además, demostramos ataques aprovechando el intérprete eBPF y JIT en Linux 1.2 Nuestras técnicas En un nivel alto, los ataques de Spectre violan los límites de aislamiento de la memoria al combinar la ejecución especulativa con la exfiltración de datos a través de canales encubiertos microarquitectónicos. Más específicamente, para montar un ataque Spectre, un atacante comienza localizando o introduciendo una secuencia de instrucciones dentro del espacio de direcciones del proceso que, cuando se ejecuta, actúa como un transmisor de canal secreto que pierde la memoria de la víctima o registra el contenido. Luego, el atacante engaña a la CPU para que ejecute especulativamente y erróneamente esta secuencia de instrucciones, filtrando así la información de la víctima por el canal secreto. Finalmente, el atacante recupera la información de la víctima a través del canal secreto. Si bien los cambios en el estado nominal de la CPU como resultado de esta ejecución especulativa errónea se revierten eventualmente, la información previamente filtrada o los cambios en otros estados microarquitectónicos de la CPU, por ejemplo, el contenido de la memoria caché, pueden sobrevivir a la reversión del estado nominal. La descripción anterior de los ataques de Spectre es general, y necesita ser instanciada concretamente con una forma de inducir la ejecución especulativa errónea, así como con un canal secreto microarquitectónico. Si bien son posibles muchas opciones para el componente de canal secreto, las implementaciones descritas en este trabajo utilizan canales secretos basados en caché [64], es decir, Flush + Reload [74] y Evict + Reload [25, 45]. Ahora procedemos a describir nuestras técnicas para inducir e influir en la ejecución especulativa errónea. Variante 1: Explotación de bifurcaciones condicionales. En esta variante de los ataques de Spectre, el atacante entrena mal al predictor de bifurcación de la CPU para predecir erróneamente la dirección de una bifurcación, lo que hace que la CPU viole temporalmente la semántica del programa al ejecutar código que de otro modo no se habría ejecutado. Como mostramos, esta ejecución especulativa incorrecta permite a un atacante leer información secreta almacenada en el espacio de direcciones del programa. De hecho, considere el siguiente ejemplo de código: if (x < array1_size) y = array2[array1[x] * 4096]; En el ejemplo anterior, suponga que la variable x contiene datos controlados por el atacante. Para garantizar la validez del acceso a la memoria de array1, el código anterior contiene una declaración if cuyo propósito es verificar que el valor de x esté dentro de un rango legal. Mostramos cómo un atacante puede omitir esta declaración if, leyendo así datos potencialmente secretos del espacio de direcciones del proceso. Primero, durante una fase inicial de entrenamiento incorrecto, el atacante invoca el código anterior con entradas válidas, entrenando así al predictor de bifurcacion para que espere que el if sea verdadero. Luego, durante la fase de explotación, el atacante invoca el código con un valor de x fuera de los límites de array1. En lugar de esperar la determinación del resultado de la bifurcacion, la CPU adivina que la verificación de límites será verdadera y ya ejecuta especulativamente instrucciones que evalúan array2 3 [array1 [x] * 4096] utilizando la x maliciosa. Tenga en cuenta que la lectura de array2 carga datos en el caché en una dirección que depende de array1 [x] usando la x maliciosa, escalada para que los accesos vayan a diferentes líneas de caché y para evitar efectos de captación previa de hardware. Cuando finalmente se determina el resultado de la verificación de límites, la CPU descubre su error y revierte cualquier cambio realizado a su estado microarquitectura nominal. Sin embargo, los cambios realizados en el estado de la memoria caché no se revierten, por lo que el atacante puede analizar el contenido de la memoria caché y encontrar el valor del byte potencialmente secreto recuperado en la lectura fuera de los límites de la memoria de la víctima. Variante 2: Explotación de bifurcaciones indirectas. A partir de la programación orientada al retorno (ROP) [63], en esta variante el atacante elige un dispositivo del espacio de direcciones de la víctima e influye en la víctima para que ejecute el dispositivo de forma especulativa. A diferencia de ROP, el atacante no se basa en una vulnerabilidad en el código de la víctima. En su lugar, el atacante entrena el Branch Target Buffer (BTB) para predecir erróneamente una bifurcacion de una instrucción indirecta de bifurcación a la dirección del dispositivo, lo que resulta en la ejecución especulativa del dispositivo. Como antes, si bien los efectos de la ejecución especulativa incorrecta en el estado nominal de la CPU finalmente se revierten, sus efectos en el caché no lo son, lo que permite que el dispositivo filtre información confidencial a través de un canal lateral de caché. Demostramos esto empíricamente y mostramos cómo la selección cuidadosa de dispositivos permite que este método lea la memoria arbitraria de la víctima Para forzar el BTB, el atacante encuentra la dirección virtual del gadget en el espacio de direcciones de la víctima, luego realiza ramificaciones indirectas a esta dirección. Este entrenamiento se realiza desde el espacio de direcciones del atacante. No importa qué reside en la dirección del gadget en el espacio de direcciones del atacante; todo lo que se requiere es que las direcciones virtuales del atacante durante el entrenamiento coincidan (o alias con) las de la víctima. De hecho, siempre que el atacante maneje las excepciones, el ataque puede funcionar incluso si no hay un código asignado en la dirección virtual del gadget en el espacio de direcciones del atacante. Otras variantes. Se pueden diseñar más ataques variando tanto el método para lograr la ejecución especulativa como el método utilizado para filtrar la información. Los ejemplos incluyen instrucciones de devolución mal formadas, información de fugas a través de variaciones de tiempo y contención en unidades aritméticas 1.3. Hardware objetivo y Estado Actual Hardware. Hemos verificado empíricamente la vulnerabilidad de varios procesadores Intel a los ataques de Spectre, incluidos los procesadores Ivy Bridge, Haswell, Broadwell, Skylake y Kaby Lake. También hemos verificado la aplicabilidad del ataque a las CPU AMD Ryzen. Finalmente, también hemos montado con éxito los ataques Specter en varios procesadores Samsung y Qualcomm basados en ARM que se encuentran en teléfonos móviles populares. Estado Actual. Utilizando la práctica de la divulgación responsable, grupos disjuntos de autores de este documento proporcionaron versiones preliminares de nuestros resultados a grupos de proveedores de CPU y otras compañías afectadas que se superponen parcialmente. En coordinación con la industria, los autores también participaron en una difusion de los resultados. La familia de ataques Spectre está documentada bajo CVE-2017-5753 y CVE-2017-5715. 1.4. Meltdown Meltdown [47] es un ataque microarquitectural relacionado que explota la ejecución fuera de orden para perder la memoria del núcleo. Meltdown es distinto de los ataques de Spectre en dos formas principales. 4 Primero, a diferencia de Spectre, Meltdown no usa predicción de bifurcación. En cambio, se basa en la observación de que cuando una instrucción causa una trampa, las instrucciones siguientes se ejecutan fuera de orden antes de ser terminadas. En segundo lugar, Meltdown explota una vulnerabilidad específica para muchos procesadores Intel y algunos procesadores ARM que permite que ciertas instrucciones ejecutadas especulativamente eviten la protección de la memoria. Combinando estos problemas, Meltdown accede a la memoria del núcleo desde el espacio del usuario. Este acceso provoca una trampa, pero antes de que se emita la trampa, las instrucciones que siguen al acceso filtran el contenido de la memoria accedida a través de un canal oculto de caché. En contraste, Specter ataca a una gama más amplia de procesadores, incluida la mayoría de los procesadores AMD y ARM. Además, el mecanismo KAISER [29], que se ha aplicado ampliamente como mitigación del ataque Meltdown, no protege contra Spectre. 2. Antecedentes En esta sección, describimos algunos de los componentes microarquitectónicos de los procesadores modernos de alta velocidad, cómo mejoran el rendimiento y cómo pueden filtrar información de los programas en ejecución. También describimos la programación orientada al retorno (ROP) y los gadgets. 2.1. Ejecución fuera de orden. Un paradigma de ejecución fuera de orden aumenta la utilización de los componentes del procesador al permitir que las instrucciones más abajo en el flujo de instrucciones de un programa se ejecuten en paralelo y, a veces, antes de las instrucciones anteriores. Los procesadores modernos trabajan internamente con microoperaciones, emulando el conjunto de instrucciones de la arquitectura, es decir, las instrucciones se descodifican en microoperaciones [15]. Una vez que se han completado todos los microops correspondientes a una instrucción, así como todas las instrucciones anteriores, las instrucciones pueden retirarse, confirmando sus cambios en los registros y otros estados arquitectónicos y liberando el espacio de almacenamiento intermedio de reordenamiento. Como resultado, las instrucciones se retiran en orden de ejecución del programa. 2.2. Ejecución especulativa. A menudo, el procesador no conoce el flujo de instrucciones futuro de un programa. Por ejemplo, esto ocurre cuando la ejecución fuera de orden alcanza una instrucción de bifurcación condicional cuya dirección depende de instrucciones anteriores cuya ejecución aún no se ha completado. En tales casos, el procesador puede preservar su estado de registro actual, hacer una predicción sobre la ruta que seguirá el programa y ejecutar instrucciones especulativas a lo largo de la ruta. Si la predicción resulta ser correcta, los resultados de la ejecución especulativa se confirman (es decir, se guardan), lo que proporciona una ventaja de rendimiento sobre el ralentí durante la espera. De lo contrario, cuando el procesador determina que siguió el camino incorrecto, abandona el trabajo que realizó especulativamente al revertir su estado de registro y reanudar a lo largo del camino correcto. Nos referimos a instrucciones que se realizan erróneamente (es decir, como resultado de una predicción errónea), pero pueden dejar rastros microarquitectónicos, como instrucciones transitorias. Aunque la ejecución especulativa mantiene el estado arquitectónico del programa como si la ejecución siguiera el camino correcto, los elementos microarquitectónicos pueden estar en un estado diferente (pero válido) que antes de la ejecución transitoria. La ejecución especulativa en CPU modernas puede ejecutar varios cientos de instrucciones por delante. El límite generalmente se rige por el tamaño del búfer de reordenamiento en la CPU. Por ejemplo, en la microarquitectura de Haswell, el búfer de reordenamiento tiene espacio suficiente para 5 192 microoperaciones [15]. Como no existe una relación uno a uno entre el número de microoperaciones e instrucciones, el límite depende de las instrucciones que se utilicen. 2.3. Predicción de bifurcación. Durante la ejecución especulativa, el procesador adivina el resultado probable de las instrucciones de bifurcación. Las mejores predicciones mejoran el rendimiento al aumentar el número de operaciones ejecutadas especulativamente que pueden confirmarse con éxito. Los predictores de bifurcación de los procesadores Intel modernos, por ejemplo, los procesadores Haswell Xeon, tienen múltiples mecanismos de predicción para bifurcaciones directas e indirectas. Las instrucciones de ramificación indirecta pueden saltar a direcciones de destino arbitrarias calculadas en tiempo de ejecución. Por ejemplo, las instrucciones x86 pueden saltar a una dirección en un registro, ubicación de memoria o en la pila, por ejemplo, "jmp eax "," jmp [eax] "y" ret ". Las bifurcaciones indirectas también son compatibles con ARM (por ejemplo, "MOV pc, r14"), MIPS (por ejemplo, "jr $ ra"), RISC-V (por ejemplo, "jalr x0, x1,0") y otros procesadores. Para compensar la flexibilidad adicional en comparación con las bifurcaciones directas, los saltos indirectos y las llamadas se optimizan utilizando al menos dos mecanismos de predicción diferentes [35]. Intel [35] describe que el procesador predice: “Llamadas y saltos directos” de manera estática o monotónica “Llamadas y saltos indirectos” de manera monotónica o de manera variable, que depende del comportamiento reciente del programa, y para “bifurcaciones condicionales”, el objetivo de la bifurcación y si se tomará la bifurcación. En consecuencia, se utilizan varios componentes del procesador para predecir el resultado de las bifurcaciones. El Branch Target Buffer (BTB) mantiene una asignación de direcciones de instrucciones de bifurcación ejecutadas recientemente a direcciones de destino [44]. Los procesadores pueden usar el BTB para predecir futuras direcciones de código incluso antes de decodificar las instrucciones de bifurcación. Evtyushkin et al. [14] analizó el BTB de un procesador Intel Haswell y concluyó que solo los 31 bits menos significativos de la dirección de la sucursal se utilizan para indexar el BTB. Para las bifurcaciones condicionales, no es necesario registrar la dirección de destino para predecir el resultado de la bifurcación, ya que el destino generalmente se codifica en la instrucción mientras la condición se determina en tiempo de ejecución. Para mejorar las predicciones, el procesador mantiene un registro de los resultados de las bifurcaciones, tanto para las bifurcaciones directas como indirectas recientes. Bhattacharya et al. [9] analizó la estructura de predicción del historial de sucursales en procesadores Intel recientes. Aunque las instrucciones de retorno son un tipo de bifurcación indirecta, a menudo se usa un mecanismo separado para predecir la dirección de destino en las CPU modernas. El Buffer de Pila de Devolución (RSB) mantiene una copia de la porción utilizada más recientemente de la pila de llamadas [15]. Si no hay datos disponibles en el RSB, diferentes procesadores detendrán la ejecución o utilizarán el BTB como respaldo [15]. La lógica de predicción de bifurcación, por ejemplo, BTB y RSB, generalmente no se comparte entre los núcleos físicos [19]. Por lo tanto, el procesador aprende solo de las bifurcaciones anteriores ejecutadas en el mismo núcleo. 2.4. La jerarquía de la memoria. Para cerrar la brecha de velocidad entre el procesador más rápido y la memoria más lenta, los procesadores usan una jerarquía de cachés sucesivamente más pequeños pero más rápidos. Los cachés dividen la memoria en fragmentos de tamaño fijo llamados líneas, con tamaños de línea típicos de 64 o 128 bytes. Cuando el procesador necesita datos de la memoria, primero verifica si el caché L1, en la parte superior de la jerarquía, contiene una copia. En el caso de un acierto en la memoria caché, es decir, los 6 datos se encuentran en la memoria caché, los datos se recuperan de la memoria caché L1 y se utilizan. De lo contrario, en el caso de una falta de memoria caché, el procedimiento se repite para intentar recuperar los datos de los siguientes niveles de memoria caché y, finalmente, la memoria externa. Una vez que se completa una lectura, los datos generalmente se almacenan en la memoria caché (y se desaloja un valor previamente almacenado en caché para hacer espacio) en caso de que se necesite nuevamente en el futuro cercano. Procesadores Intel modernos típicamente tienen tres niveles de caché, cada núcleo tiene cachés L1 y L2 dedicados y todos los núcleos comparten un caché L3 común, también conocido como caché de último nivel (LLC). Un procesador debe asegurarse de que los cachés L1 y L2 por núcleo sean coherentes utilizando un protocolo de coherencia de caché, a menudo basado en el protocolo MESI [35]. En particular, el uso del protocolo MESI o algunas de sus variantes implica que una operación de escritura de memoria en un núcleo hará que las copias de los mismos datos en los cachés L1 y L2 de otros núcleos se marquen como no válidos, lo que significa que los futuros accesos a estos datos en otros núcleos no podrán cargar rápidamente los datos del caché L1 o L2 [53, 68]. Cuando esto sucede repetidamente en una ubicación de memoria específica, esto se denomina informalmente rebote de línea de caché. Debido a que la memoria se almacena en caché con una granularidad de línea, esto puede suceder incluso si dos núcleos acceden a diferentes ubicaciones de memoria cercanas que se asignan a la misma línea de caché. Este comportamiento se denomina intercambio falso y es conocido como una fuente de problemas de rendimiento [33]. Estas propiedades del protocolo de coherencia de caché a veces se pueden abusar como un reemplazo para el desalojo de caché utilizando la instrucción clflush o patrones de desalojo [27]. Este comportamiento fue previamente explorado como un mecanismo potencial para facilitar los ataques de Rowhammer [16]. 2.5. Ataques de canal lateral microarquitectónicos Todos los componentes de microarquitectura que discutimos anteriormente mejoran el rendimiento del procesador al predecir el comportamiento futuro del programa. Con ese objetivo, mantienen un estado que depende del comportamiento del programa pasado y asumen que el comportamiento futuro es similar o relacionado con el comportamiento pasado. Cuando varios programas se ejecutan en el mismo hardware, ya sea simultáneamente o por tiempo compartido, los cambios en el estado microarquitectura causado por el comportamiento de un programa pueden afectar a otros programas. Esto, a su vez, puede dar lugar a filtraciones involuntarias de información de un programa a otro [19]. Los ataques iniciales del canal lateral microarquitectónico explotaron la variabilidad del tiempo [43] y la fuga a través del caché de datos L1 para extraer claves de primitivas criptográficas [52, 55, 69]. A lo largo de los años, se han demostrado canales en múltiples componentes microarquitectónicos, incluidos el caché de instrucciones [3], los cachés de nivel inferior [30, 38, 48, 74], el BTB [14, 44] y el historial de ramificaciones [1, 2] . Los objetivos de los ataques se han ampliado para abarcar la detección de ubicación conjunta [59], la ruptura de ASLR [14, 26, 72], el control de pulsaciones de teclas [25], la impresión de dedos en el sitio web [51] y el procesamiento del genoma [10]. Los resultados recientes incluyen ataques entre núcleos y CPU cruzados [37, 75], ataques basados en la nube [32, 76], ataques en y desde entornos de ejecución confiables [10, 44, 61], ataques desde código móvil [23, 46 , 51], y nuevas técnicas de ataque [11, 28, 44]. En este trabajo, utilizamos la técnica Flush + Reload [30, 74], y su variante Evict + Reload [25], para filtrar información confidencial. Usando estas técnicas, el atacante comienza desalojando a una línea de caché que comparte con la víctima. Después de que la víctima ejecuta durante un tiempo, el atacante mide el tiempo que lleva realizar una lectura de memoria en la dirección correspondiente a la línea de caché desalojada. Si la víctima accedió a la línea de caché monitoreada, los datos estarán en el caché y el acceso será rápido. De lo contrario, si la víctima no ha accedido a la línea, la lectura será lenta. Por lo tanto, al medir el tiempo de acceso, el atacante aprende si la víctima accedió a la línea de caché monitoreada entre los pasos de desalojo y sondeo. La principal diferencia entre las dos técnicas es el mecanismo utilizado 7 para desalojar la línea de caché monitoreada de la caché. En la técnica Flush + Reload, el atacante usa una instrucción de máquina dedicada, por ejemplo, clflush de x86, para desalojar la línea. Usando Evict + Reload, el desalojo se logra forzando la contención en el conjunto de caché que almacena la línea, por ejemplo, accediendo a otras ubicaciones de memoria que se cargan en el caché y (debido al tamaño limitado del caché) hace que el procesador descarte ( desalojar) la línea que se sondea posteriormente. 2.6. Programación orientada al retorno La programación orientada al retorno (ROP) [63] es una técnica que permite a un atacante que secuestra el flujo de control hacer que una víctima realice operaciones complejas encadenando fragmentos de código de máquina, llamados gadgets, que se encuentran en el código del víctima vulnerable Más específicamente, el atacante primero encuentra artilugios utilizables en el binario de la víctima. Cada gadget realiza algunos cálculos antes de ejecutar una instrucción de devolución. Un atacante que puede modificar el puntero de la pila, por ejemplo, para apuntar para devolver direcciones escritas en un búfer de escritura externa, o sobrescribir el contenido de la pila, por ejemplo, usando un desbordamiento de búfer, puede hacer que el puntero de la pila apunte al comienzo de una serie de Direcciones de gadget elegidas de forma malintencionada. Cuando se ejecuta, cada instrucción de retorno salta a una dirección de destino desde la pila. Debido a que el atacante controla esta serie de direcciones, cada retorno salta efectivamente al siguiente gadget de la cadena. 8