fbpx

Lazy FPU – A nova vulnerabilidade da Intel

Após o Meltdown e o Specter, que foram divulgados publicamente em janeiro, em seguida as vulnerabilidades do Spectre V3a e V4 ocorreram em maio, e esta última pode ser parte das 8 novas vulnerabilidades no total que serão divulgadas no decorrer do ano.

No início deste ano, Julian Stecklina (Amazon) e Thomas Prescher (Cyberus Technology) descobriram em conjunto e divulgaram com responsabilidade outra vulnerabilidade  e chamaram de LazyFP . O LazyFP (CVE-2018-3665) é um ataque direcionado a sistemas operacionais que usam comutação lenta de FPU. Este artigo descreve o que esse ataque significa, descreve como ele pode ser atenuado e como ele realmente funciona.

Resumo e Implicações

A divulgação pública desta vulnerabilidade foi inicialmente adiada por um embargo típico de informações de divulgação responsável até agosto, mas os primeiros rumores levaram a que essa data fosse descartada.

O estado do registro da unidade de ponto flutuante (FPU), que consiste nos conjuntos de registros AVX, MMX e SSE, pode ser vazado nos limites do domínio de proteção. Isso inclui vazamentos nos limites de processos e máquinas virtuais.

O estado da FPU pode conter informações confidenciais, como chaves criptográficas. Como exemplo, o conjunto de instruções Intel AES (AES-NI) usa registradores FPU para armazenar chaves redondas. Só é possível explorar quando o sistema operacional ou hypervisor subjacente usa a comutação de FPU lenta.

Outro caso de uso interessante dessa vulnerabilidade é a criação de canais ocultos de alto desempenho entre as VMs. Ao usar todos os conjuntos de registros e controlar a frequência de programação do núcleo, é possível maximizar a taxa de transferência até o intervalo de MB / s.

Os usuários são afetados quando executam uma combinação de processador afetado e sistemas operacionais afetados.

  • Sistemas Operacionais Afetados:
    • Linux:
      • versões do kernel <4.9 com parâmetros de inicialização não padrão ( eagerfpu=off ) são afetados
      • versões do kernel <4.6 rodando nos processadores Intel afetados antes do Haswell ou com parâmetros de inicialização personalizados ( eagerfpu=off ou noxsave )
      • versões do kernel 4.4.y com y <138 tem um bug em sua ansiosa troca de FPU
      • versões do kernel <3.7 em todas as CPUs afetadas
      • As versões do kernel Linux começando com 4.9 não são afetadas, independentemente da CPU.
    • FreeBSD
    • Microsoft Windows, consulte o aviso ADV180016
  • Hipervisores afetados:
    • KVM quando executado em versões de kernel do Linux afetadas
    • Todas as versões do Xen e geralmente todos os hipervisores que empregam comutação preguiçosa do FPU
  • CPUs afetadas quando o sistema operacional afetado ou o hipervisor é usado:
    • Verificamos o problema na microarquitetura Intel Core de Sandy Bridge para Skylake
    • Estado de outros processadores não é claro neste momento

A atenuação requer que o software do sistema use a comutação ansiosa FPU em vez da comutação lenta do FPU.

Antecedentes Técnicos

Esta vulnerabilidade é semelhante ao Meltdown (CVE-2017-5754) . Enquanto o Meltdown permitia ler o conteúdo da memória protegida de um programa de espaço do usuário, esse novo ataque permite ler determinados conteúdos de registro nos limites do domínio de proteção.

Para explorar a vulnerabilidade, o invasor acessa os registros FPU que ele não tem permissão para acessar ainda. Esse valor proibido é alimentado em um cálculo de endereço que é usado para um acesso de memória subseqüente. A CPU executará esta série de instruções especulativamente, ou seja, o conteúdo do registro correspondente é esmagado quando detecta seu erro.

Em seguida, ele tenta ler / gravar de / para um endereço de memória que contém o valor de registro proibido como parte de seu deslocamento de endereço. A CPU executará especulativamente essa série de cargas e armazenamentos, mas detectará e corrigirá seu erro.No entanto, essa execução especulativa deixa rastros no cache que podem vazar informações ocultas para o invasor.

Para entender melhor como esse ataque realmente funciona, é necessário aprofundar o funcionamento interno do x86 FPU e como ele é usado pelos sistemas operacionais.

A unidade de ponto flutuante (FPU)

Nos primórdios do x86, o FPU (também chamado de coprocessador matemático) era um coprocessador externo que poderia ser adicionado à arquitetura de processador x86 adotada agora pela Intel. Intel 8087 foi o primeiro co-processador matemático de ponto flutuante deste tipo. O objetivo dessa extensão era acelerar as operações matemáticas em números de ponto flutuante, como divisão e multiplicação. Com o modelo de CPU Intel 486DX (lançado em 1989), o FPU foi integrado ao próprio microchip. Dessa forma, nenhum coprocessador adicional era necessário.

Ao longo dos anos, o processador foi estendido para suportar conjuntos de instruções de instrução única, dados múltiplos (SIMD), ou seja, MMX / SSE / AVX. As instruções SIMD executam a mesma operação matemática em vários pares de operandos ao mesmo tempo para melhorar o desempenho. Cada uma dessas extensões de conjunto de instruções introduziu novos conjuntos de registradores que continuam sendo gerenciados como parte do estado do registro da FPU. Em processadores Intel recentes, o estado do registrador FPU pode conter mais de 2 kB de dados (o AVX2 oferece 32 registradores de 512 bits cada, o que se traduz em um estado de processador adicional de 2 kB). Devido à utilidade destas instruções, este conjunto de registros pode conter não apenas valores de ponto flutuante, mas também outros dados como, por exemplo, valores inteiros.

Carregando e armazenando o estado do FPU

Para habilitar a multitarefa, os sistemas operacionais interrompem periodicamente os processos para dar a outros processos a chance de serem executados. Caso contrário, um único processo que faz um loop para sempre poderia parar o sistema.

Quando o sistema operacional alterna de um processo para outro, ele precisa salvar o estado do processo anterior e restaurar o estado do processo que está prestes a ser executado. Este estado consiste nos valores armazenados em registradores de propósito geral e registros FPU.

A arquitetura de instruções x86 fornece várias instruções para carregar / armazenar o estado do registrador da FPU de / para a memória. Já sabemos sobre o tamanho grande do estado da FPU, portanto, é bastante óbvio que não se deseja ler e gravar tais quantidades de dados em todas as mudanças de contexto, a menos que seja realmente necessário, porque nem todos os processos usam a FPU.

Comutação FPU ansiosa e lenta

O comutador FPU ansioso é comparável a salvar o estado de registrador de propósito geral em um comutador de contexto. Para cada processo, o sistema operacional reserva uma área na memória para salvar o estado da FPU. Ao alternar de um processo para outro, ele executa uma instrução de armazenamento de FPU para transferir o conteúdo da FPU atual para a área de salvamento do estado. Em seguida, ele carrega o novo estado FPU da área de salvamento do estado do processo que está prestes a ser planejado.

A comutação FPU preguiçosa otimiza este procedimento para o caso em que nem todo processo usa o FPU o tempo todo.

Após uma troca de contexto, a FPU é desativada até ser usada pela primeira vez. Só então, o antigo estado FPU será salvo e o correto será restaurado da memória. Até este ponto, o FPU mantém o estado do registro do processo ou da VM que o usou por último.

Para implementar essa otimização, o kernel do sistema operacional desabilita temporariamente o FPU definindo um certo bit de registro de controle ( CR0.TS ). Se este bit estiver definido, qualquer tentativa de acessar o FPU causará uma exceção ( #NM , Dispositivo não disponível, Nenhum coprocessador matemático ). Quando a exceção ocorre, o kernel pode salvar o estado antigo na respectiva área de estado e restaurar o estado do processo atual. Os processos que simplesmente não usam o FPU nunca acionam essa exceção e, portanto, nunca acionam armazenamentos e cargas de estado do FPU.

Começando com a introdução da arquitetura x86-64 , a presença de pelo menos algumas extensões de conjuntos de instruções SIMD é obrigatória e seu uso se tornou mais difundido.Como tal, a suposição subjacente de comutação de FPU preguiçosa não é mais válida. O ganho de desempenho pela comutação preguiçosa do FPU tornou-se insignificante, e alguns núcleos já o removeram em favor de troca rápida.

O ataque

O ataque funciona lendo o conteúdo do registro FPU enquanto o FPU está desabilitado e divulgando o conteúdo através de um canal lateral baseado em cache. Neste ponto, o FPU contém o conteúdo do registro do último processo que o utilizou.

Para tornar esse ataque prático, é necessário ocultar a exceção que normalmente ocorre ao acessar o FPU. Encontramos três maneiras diferentes de fazer isso:

Executando o ataque na sombra de um pagefault

Todas as três versões funcionam de forma semelhante. Uma versão simplificada do pagefault poderia ser assim:

mov dword [0], 0         ; Aciona um pagefault
movq rax, xmm0           ; Lê o conteúdo de xmm0, acionando #NM
and rax, 1               ; máscara bit 0
shl rax, 6               ; alinhando à linha de cache
mov dword [mem + rax], 0 ; acessando "mem" com offset dependendo do conteúdo de xmm0

O ataque

O truque aqui é que a exceção #NM nunca ocorre, porque a instrução que toca no registrador FPU xmm0 é executada apenas especulativamente. No entanto, o local da memória tocado depende do valor de xmm0 e esse valor pode ser recuperado usando o mesmo ataque flush-and-reload que também é usado no Meltdown e no Specter. mem é apenas um buffer de memória comum que é acessível ao aplicativo. Neste ponto, estamos de volta ao estado inicial e podemos tentar novamente o ataque para vazar todo o estado do registro, bit por bit (sempre mascarando um bit diferente na etapa and do snippet de código).

OBS:  Note que o código de exemplo neste artigo apenas lê um único bit por uma questão de simplicidade. Mas a prova de conceito dos descobridores da vulnerabilidade lê vários bytes ao mesmo tempo, a fim de reduzir ainda mais o tamanho da janela de tempo necessária.

Existem outros 2 métodos de ataque que a pedido da Intel não foram publicados pelos descobridores da falha.

Praticabilidade

A praticabilidade do ataque varia de acordo com os diferentes métodos para empregá-lo: O atacante precisa de uma janela de tempo em que nenhuma interrupção pelo sistema operacional ocorra para ler todos os bits do registro, porque toda preempção tem a chance de alterar o estado do registrador e tornar o resultado inútil. Assim, quanto mais rápido o método de ataque, maior a probabilidade de sucesso.

Mitigação

Usando servidores Linux com versões de kernel> = 3.7, este ataque pode ser mitigado adicionando “ eagerfpu=on ” aos parâmetros de inicialização do kernel. Não temos conhecimento de uma solução alternativa para versões mais antigas. Para todos os outros sistemas operacionais: instale as atualizações oficiais do fornecedor.

Não estamos cientes de uma penalidade de desempenho causada pelo uso de uma rápida troca de FPU. mensagem de commit do patch do kernel do Linux que fez com que o FPU ansioso mudasse o padrão dos sistemas Linux até mesmo aponta que as suposições que justificaram a comutação lenta do FPU no passado não se sustentam mais na maioria dos sistemas modernos.

 

Pesquisa e tradução: Marcio Mariano Galvão – Team Linux Force.

Fonte: blog.cyberus-technology.de









21 de setembro de 2019

0 responses on "Lazy FPU - A nova vulnerabilidade da Intel"

    Deixe sua mensagem

    O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

    Sobre nós

    A Linux Force Brasil é uma empresa que ama a arte de ensinar. Nossa missão é criar talentos para a área de tecnologia e atender com excelência nossos clientes.

    CNPJ: 13.299.207/0001-50
    SAC:         0800 721 7901

    Comercial  Comercial: (11) 3796-5900

    Suporte:    (11) 3796-5900

    Copyright © Linux Force Security  - Desde 2011.