viernes, enero 05, 2018

El caos que genera Metldown & Spectre con sus consecuencias.

Confieso que no tenía en mis planes ponerme a escribir durante estas vacaciones de invierno sobre ningún tema profundo, pero el caos que se ha generado con el descubrimiento de los bugs de Metldown y Spectre ha sido lo suficientemente interesante como para sentarse con ellos y dejar la visita a la exposición de Star Wars que hay en la Flag Store de Telefónica Gran Vía para el fin de semana, o para un poco más tarde.

Figura 1: El caos que genera Metldown & Spectre con sus consecuencias.

Si habéis leído sobre los fallos, supongo que ya estaréis más que al día, pero dejadme que haga un pequeño resumen para que sea fácil de entender para todo el mundo en qué se basa el fallo, y por qué es tan importante desde el punto de vista de la seguridad, y desde el punto de vista de - lo que para mí es crucial - el rendimiento.

Arquitecturas Avanzadas

Si estás estudiando en la Universidad, esto se suele explicar en las asignaturas de Arquitecturas Avanzadas, que es donde lo aprendí yo cuando estuve acabando la Ingeniería Informática. En esa parte de la formación es donde se analizan cómo los microprocesadores aplican técnicas para aprovechar al máximo el paralelismo que ofrece el hardware incluido en los chips. Técnicas como el adelanto de instrucciones, el cómputo de todas las ramas de un instrucción alternativa, o el paralelismo de estructuras secuenciales. Para que os hagáis una idea - resumiendo mucho - supongamos que una teneos una instrucción que dice algo como:
Si A> 10 entonces B=1 de lo contrario B=0
Esto, en un microprocesador sin utilizar toda la potencia del paralelismo llevaría a ejecutar primero la comparación del valor de A y después el movimiento de un valor al registro B.  ¿Sencillo, no? Ahora vamos a complicarlo un poco.
Si f(A)>0 entonces g(B) de lo contrario h(B)
Vale, supongamos ahora una instrucción en la que f(A) tarda 10 segundos, g(B) toma 5 segundos calcular y h(B) 7 segundos su ejecución. En este entorno, una arquitectura tradicional tardaría, como mínimo 15 segundos, y como máximo 17 segundos, luego en media tendríamos un procesador que ejecutaría este programa en 16 segundos.

Pero... ¿y si le añadimos al sistema un módulo de Ejecución Especulativa y una caché con el objeto de paralelizar esto? Es decir, supongamos que el microprocesador es capaz de ejecutar f(A), g(B) y h(B) totalmente en paralelo, es decir, que antes de que se termine f(A) ya sabemos el resultado de g(B) y de h(B). El resultado es que el tiempo de ejecución máximo en cualquiera de los casos sería de 10 segundos, lo que nos ha incrementado un 60 % el rendimiento del sistema.

Para conseguir esto, lo que se hace es aprovecharse de dos características del hardware de los microhips. En primer lugar la existencia de puertas lógicas en el chips duplicadas que permiten paralelizar acciones y la segunda de zonas de memoria caché que permiten almacenar los resultados de g(B) y h(B) hasta saber cuál es el resultado que pasa a la memoria principal del programa. 

Por supuesto, no siempre es necesaria una comparación, supongamos ahora que tenemos que ejecutar algo como:
R1=f(A) tarda 5 segundos 
R2=g(A) tarda 8 segundos 
R3=h(A) tarda 4 segundos 
Imprime R1+R2+R3 tarda 1 segundo
En este caso tenemos cuatro instrucciones serializadas, que si se ejecutan secuencialmente harán que el programa tarde 18 segundos en media. Pero... si tenemos hardware de sobra, y no hay dependencia de ninguna función (solo la última instrucción depende del cálculo de las anteriores), podríamos ejecutar en paralelo el cálculo de R1, R2 y R3, lo que haría que el tiempo máximo fuera 8 segundos y luego imprimirlo, con lo que de 18 segundos habríamos pasado a 9, justo la mitad.

¿Y la seguridad?

Aquí viene el problema que ha sido descubierto por Metldown (y en una variación por Spectre). Si cualquier programa pudiera leer cualquier zona de memoria del sistema tendríamos un serio problema. Esto implicaría que cualquier ejecución (por ejemplo un JavaScript que te muestra un anuncio en el navegador) podría ejecutar cualquier cosa en tu sistema - por ejemplo un malware - porque sabría qué direcciones de memoria se van a ejecutar con privilegios dentro del sistema.

Por ello, es necesario establecer mecanismos de protección de la memoria para que cada proceso pueda leer solo su memoria, y que los procesos con altos privilegios puedan guardar los datos sensibles sin que se vean afectados por programas de usuario - como el JavaScript que muestra los anuncios que contaba antes -. 

Para que esto sea posible, es necesario que la memoria de los procesos esté protegida desde el microprocesador, y ningún programa que se ejecute en el procesador acceda a zonas de memoria protegida o de otro proceso. Para ello hay varios mecanismos de protección, pero dos fundamentales. Uno que prohibe acceder a zonas de memoria y otro que aleatoriza la carga de direcciones en memoria, el famoso ASLR (Address Space Layout Randomization) que en el Kernel del sistema operativo se llama KASLR.

Perdonad por la simplificación al extremo de los mecanismos de protección, pero para entender cómo funciona Meltdown es suficiente. Si quieres saber mucho más, tus libros son, sin duda, Linux Exploiting y Máxima Seguridad en Windows. Imaginemos ahora un programa que hace algo como:
A=R1 
B=(A+0)*A
Como se puede ver, en este caso las instrucciones no "deberían" ser paralelizables, pero tal y como funciona el microprocesador sí que lo es, al menos parcialmente. Imaginemos que R1 es un registro protegido que va a hacer que salte una excepción, que va a ser tratada por un código de error que evitará que el programa se siga ejecutando por un intento de violación del espacio de memoria protegida. Eso debería implicar que la instrucción con el cómputo de B no se debería ejecutar, pero lo cierto es que el mecanismo de Ejecución Especulativa que paraleliza las instrucciones paraleliza también el tratamiento el error con el cálculo de B, por lo que en la caché del microprocesador se encuentra B calculado.

Esto es un grave problema de seguridad, ya que la caché del microprocesador no es una zona de memoria protegida contra ataques side-channel como ya se han visto en otros estudios por lo que es posible saber saber si un valor está allí o no, lo que haría que cualquier programa de usuario pudiera volcar valores protegidos como R1 de zonas de memoria que no debería ver.... y por eso ...Meltdown.

¿Cómo arreglarlo?

Hacer un buen arreglo de este fallo implica tomar muchas decisiones nuevas. Analizar los "corner cases" o poner nuevas medidas de protección a la caché del microprocesador. Esto se puede hacer a nivel de software - con el kernel de los sistemas operativos - o a nivel de hardware - lo que  implica el rediseño del funcionamiento de los microprocesadores y la nueva toma de decisiones -. Y nada es fácil ni gratis.

Si hablamos de que Intel (afectado por Meltdown y Spectre) o AMD y ARM (afectados por Spectre) tuvieran que cambiar el funcionamiento de la Ejecución Especulativa haciendo cambios para meter controles de seguridad en estos casos, implicaría que los chips en almacén, los chips en fábricas, los contratos firmados de entrega, etcétera, se verían afectados, por lo que costarían muchos dólares, y eso es malo para el negocio, eso es malo para la acción, eso es malo para tu dinero si tienes acciones de esas compañías.

Pero tendrán que arreglarlo en futuros diseños de sus chips.

Eso sí, mientras tanto, la solución es que lo arregle el SO, poniendo controles por encima. Controles que pueden ser o bien anular la Ejecución Especulativa (imposible desde el punto de vista de rendimiento) o bien empezar a poner controles de seguridad mucho más exhaustivos (rendimiento afectado también, pero se puede ir afinando).

Si no se ponen parches, cualquier programa podría acceder al Kernel del sistema en el core del microchip y ejecutar lo que le diera la gana. Imaginad que un JavaScript de un navegador llega al microprocesador hardware en un equipo en un datacenter de Cloud. Y desde allí puede ver todas las máquinas virtuales, todos los procesos que corren por encima e infectarlo con un software de características de gusano para que esos equipos hagan lo mismo. 

Lo que llevaría a que un entorno de Cloud se pudiera convertir en un problema para todas las máquinas, así que hay que parchear sí o sí. Y eso es malo para el negocio. Eso es malo para la acción. Eso es malo para tu dinero si tienes acciones allí.

Todos los fabricantes tienen que parchear. Google, Microsoft, Amazon, Apple, etcétera, y deben hacerlo para todos los sistemas operativos, y luego, todas las empresas deben actualizar el software..pero....

¿Qué va a suceder con el rendimiento?

Pues que todo va a ir un poco más lento - y a veces mucho más lento -. Al tener que ponerse medidas de seguridad en la Ejecución Especulativa, vamos a ejecutar más instrucciones, se van a paralelizar menos y cuando tengamos procesos complejos, pues la cosa puede ir muuucho más lenta.

Puede que los procesos de Machine Learning, los procesos de Inteligencia Artificial basados en Redes Neuronales en algoritmos de Deep Learning, cálculos en SGDB de alto rendimiento (Que aprovechan al máximo las optimizaciones del microprocesador), o muchos sistemas, vayan lentas.

Pero es que si no, la seguridad de un automóvil, o un sistema SCADA, o de un avión, se ven comprometidas por estos fallos de seguridad, que afectan al corazón de la seguridad de las arquitecturas tecnológicas que usamos hoy en día. Las próximas semanas, aún seguiremos viendo muuuchas noticias al respecto. Os iré contando.

Saludos Malignos!

15 comentarios:

  1. Buena explicación, la mejor que leí hasta el momento y eso que leí varias, incluso ví algunas demos https://www.youtube.com/watch?v=RbHbFkh6eeE


    Saludos.

    ResponderEliminar
  2. Chema,
    Estaba esperando tu post sobre este asunto para ver si arrojabas luz sobre este tema sobre el que hasta los ignorantes hablan como creyéndose que saben algo y me temo que con este post no has logrado estar a la altura de tu reputación.
    Puedo estar de acuerdo en que los hilos de ejecución especulativa pueden leer cualquier dirección de memoria y que hacen copias de esa información en RAM muy rápidas que están dentro del procesador (su caché). Sin embargo, esas copias de información solo están accesibles para el hilo de ejecución que originó los hilos especulativos ya que si los algoritmos que le dan la funcionalidad a la caché no impidieran a otros hilos a acceder a esos registros en la cache entonces tendríamos las conocidas RACE CONDITIONS que haría impredecible el funcionamiento de los programas. Por tanto las lecturas y escrituras que se hacen son solo alcanzables del hilo y una vez el hilo se termina ya no se puede alcanzar. Si los algoritmos de ejecución especulativa permitieran al hilo que los origina lecturas de su memoria cuando el hilo especulativo ha sido descartado de nuevo tendríamos comportamientos impredecibles del programa.
    Por tanto no hay ningún conjunto de instrucciones que no pueda ser optimizado haciendo uso de ejecución especulativa.
    Tu post además contribuye a la confusión porque mezclas la paralización que se da en un procesador (un núcleo), con técnicas de paralización que aprovechan que los sistemas que tienen varios procesadores (varios núcleos). No hay forma alguna de decirle a un procesador que deje de realizar sus paralizaciones de optimización, el procesador tienen una forma de estimar si le compensa o no esa ejecución especulativa y los programadores al trabajar a tan alto nivel tienen muy complicado prever esa ejecución especulativa para impedirla. Por eso me extraña enormemente que los parches no vayan saliendo poco a poco sino que parece que los sistemas operativos ya cuentan con parches para todas sus versiones.
    Sospecho que hay un cartel de toda la industria informática para realizar una obsolescencia programada. ¡Ojala alguien explique mejor lo que está sucediendo para quitarme la idea de la cabeza!.

    ResponderEliminar
  3. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  4. @Oscar, no es como tú dices Leete bien lo que he puesto y luego cuando me siente con el portátil te contexto con calma, que estoy fuera }:)

    ResponderEliminar
  5. @Oscar, te recomiendo leer los papers originales de Meltdown y Spectre, que puedes encontrar en https://spectreattack.com/ . Allí viene bastante bien explicado.

    Por lo que he entendido, todo el ataque consiste en usar "side channels". En lo referente a la caché del procesador, el ataque consiste en conseguir que la ejecución especulativa acceda a áreas de memoria que estén mapeadas en el espacio de direcciones virtuales tanto del proceso atacado como del proceso atacante. Algo así como y = ARRAY_COMUN[a+DATO_CRITICO]. El "ARRAY_COMUN" puede ser, por ejemplo, la "libc" en Linux o una DLL precargada por el SO. Así, se fuerza al procesador a cargar en la caché una variable cuya posición depende de "DATO_CRITICO". Y, como el "ARRAY_COMUN" puede ser accedido también a través del espacio de direcciones virtual del proceso atacante, midiendo los tiempos de carga de cada uno de los elementos del ARRAY_COMUN puede determinarse cuál ha sido precargado en la caché del procesador durante la ejecución especulativa y, por tanto, cuál es el valor de DATO_CRITICO.

    ResponderEliminar
  6. @Oscar Gras, vaya por delante que he pretendido simplificar la explicación como he dicho en el artículo desde el primer momento, contando el impacto en la ejecución de las instrucciones que hace el microprocesador. Pero sigue siendo más que válida la explicación. Es cierto que el SO toma decisiones en la optimización, pero también el microprocesador, eligiendo adelantar instrucciones, hacer ejecución especulativa, o paralelizando determinadas instrucciones.

    Dicho esto, el SO puede hacer reorganización de las instrucciones para evitar determinadas acciones del microprocesador, y elegir construir instrucciones "contra la optimización" del micropocesador, cargándose el rendimiento del sistema. Los parches a nivel de software van justo por estos laos - de aquí las preocupaciones de todo el mundo.

    Por otro lado, las protecciones a nivel de microprocesador van por esos lados, evitar la lectura de los valores de caché mediante los side-channels o evitar que la ejecución especulativa en determinados casos - lo que implica un análisis de seguridad y una replanificación compleja -. En cualquiera de los casos, de nuevo, el damnificado es el rendimiento.

    Si quieres los detalles concretos, el paper de Kaiser lo explica bien, y en la última actualización de la Wikipedia de Metldown lo han ampliado, pero la explicación que he hecho ha intentado ser didáctica para que la gente entienda por qué se habla tanto de la "ejecución especulativa" en los artículos.

    Saludos!

    ResponderEliminar
  7. Sabia que viniendo aqui encontraria una buena explicacion. Gracias Txema.

    ResponderEliminar
  8. Saludos.
    En algún momento leía tu blog desde Cuba, y ahora he tenido oportunidad de poder tener acceso a todo el material. Darte las gracias caballero, por las experiencias que compartes. Me he identificado contigo, por la filosofía con que trabajas, y por lo educativo y pedagógico que eres. Ahora estoy recién llegado a los dominios .es y espero poder seguirte con más frecuencia. Con una visión totalmente distinta, desde el punto de vista geográfico y todas las limitaciones naturales (o impuestas, depende del enfoque) que tenemos...pero, sin lugar a dudas: gracias por tus aportes.

    ResponderEliminar
  9. Google acaba de anunciar una solución que no afectaría el rendimiento de los equipos ¿Podrías explicarnos cómo funciona?

    ResponderEliminar
  10. Me encantaría saber tu opinión sobre el Realtime SIlicon Secured Memory que incluye Oracle en sus procesadores M7(2015), S7(2016) y M8 (2017).
    Aquí hay una introducción muy básica:
    https://swisdev.oracle.com/_files/What-Is-SSM.html
    Pero hay toda la documentación técnica adicional que se necesite para entenderlo en profundidad.

    ResponderEliminar
  11. "...Lo que llevaría a que un entorno de Cloud se pudiera convertir en un problema para todas las máquinas, así que hay que parchear sí o sí. Y eso es malo para el negocio. Eso es malo para la acción. Eso es malo para tu dinero si tienes acciones allí." Bueno, es una clara oportunidad de aumentar el coste por proceso a los clientes a costa del "nuevo" y "extraño" fenómeno.
    AWS users experiencing significantly increased CPU utilization
    https://forums.aws.amazon.com/thread.jspa?threadID=269858&tstart=0


    ResponderEliminar
  12. @Chema, gracias por tu respuesta. He ido a la wikipedia en Español y ya si que me parece creíble la vulnerabilidad. Muy resumidamente consiste en solicitar una dirección de memoria a la que si se puede acceder indicando tal dirección de memoria con una expresión que use el valor de una dirección a la que no se tiene acceso y aunque el hilo se descarta y no lees nada, la ejecución del hilo deja como huella que ese valor esta en cache. Un posterior lectura legitima de esa dirección sería muy rápida por estar en cache y con ello se puede deducir el valor que había en la dirección de memoria en la que no teníamos privilegios (tu blind sql inyection pero con cache).
    Quiero pedirte disculpas por mi comentario, el primero que necesita que seas lo mas sencillo posible para poderte entender soy yo, pero tal como lo contaste la vulnerabilidad no me resulto creíble y creía que estábamos cayendo todos en un engaño.
    @Rafael, gracias por tu comentario. Normalmente recurro al Blog de Chema para entender las noticias sobre vulnerabilidades de seguridad, de esa forma me puedo ahorrar leerme los paper técnicos que me resultan mas complicados de entender. Como finalmente con la wikipedia lo he entendido no me ha hecho falta seguir tu recomendación pero gracias de todas formas.

    ResponderEliminar
  13. Voces que piden una nueva arquitectura de CPU:

    http://www.zdnet.com/article/why-intel-x86-must-die-our-cloud-centric-future-depends-on-open-source-chips-meltdown/

    https://www.extremetech.com/computing/261678-spectre-meltdown-death-knell-x86-standard

    ResponderEliminar
  14. @Manuel, la vulnerabilidad no es un problema inherente a una arquitectura concreta por eso los chip ARM y no solo los x86 se han visto afectados. Aprovechar la vulnerabilidad para defender los chips open source es demagogia.
    La industria informática tiene un problema grave debido a que Microsoft se ha quedado estancado ofreciendo pocas novedades en su software y en su mayoría son superficiales.
    Los fabricantes de chips tienen pocas posibilidades de ofrecer funcionalidades nuevas debido a que poca gente que programa en lenguajes como c o c++ donde se puede tener acceso a hacer uso de ellas.
    La fabricantes de Chips son conscientes de que pocos son ya los que se preocupan por lo que sucede a bajo nivel y dedican todo su esfuerzo a mejorar rendimientos en programas que no están programados por personas muy cualificadas. Llamamos programador a alguien que conoce la sintaxis de un lenguaje y en lo libros de programación el 98 % de lo que se enseña es sintaxis de un lenguaje.
    Se ha puesto de moda las factorías de software donde el único parámetro que se valora es el coste del programa y por ello los programas son cada vez peores.
    EL consumidor no esta comprando open source, esta comprando "Jail products", sistemas cerrados hardware y software, porque es un ignorante en informática y quiere seguir siendolo, buscan productos que le permitan hacer lo que quieren y les importan muy poquito como lo hacen. Los Freakies de la informática de bajo nivel han desaparecido, ya no son consumidores relevantes.

    ResponderEliminar
  15. Intel sigue sin saber manejar el problema:
    https://www.theregister.co.uk/2018/01/22/intel_spectre_fix_linux/

    ResponderEliminar