ASA-2019-00082 – Linux: Binder use-after-free via otimização fdget()


For the English version of this alert, click here.

Allele Security Alert

ASA-2019-00082

Identificador(es)

ASA-2019-00082, A-120025789, CVE-2019-2000

Título

Binder use-after-free via otimização fdget()

Fabricante(s)

The Linux foundation
Google

Produto(s)

Linux
Android

Versão(ões) afetada(s)

Linux kernel desde commit:

binder: use standard functions to allocate fds
https://github.com/torvalds/linux/commit/44d8047f1d87adc2fd7eccc88533794f6d88c15e

Android Security Patch Levels anteriores a February 2019

Versão(ões) corrigida(s)

Linux kernel com o seguinte commit:

binder: fix use-after-free due to ksys_close() during fdget()
https://github.com/torvalds/linux/commit/80cd795630d6526ba729a089a435bf74a57af927

Android Security Patch Level February 2019

Prova de conceito

Sim

Descrição

O kernel do Linux contém um use-after-free no mecanismo de IPC Binder via otimização fdget().

Detalhes técnicos

No kernel do Linux, normalmente, quando uma `struct file *` é lido da tabela de descritores de arquivos, o contador de referência do `struct file` é incrementado para considerar a referência extra; isso acontece em fget(). Posteriormente, se a referência extra não for mais necessária, o contador de referência será decrementado por meio de fput(). Um efeito negativo disso é que, se o `struct file` é acessado freqüentemente, a cacheline contendo o contador de referência está constantemente suja; e se o `struct file` é usado por várias tarefas em paralelo, ocorre cache line bouncing.

O Linux fornece os helpers fdget() e fdput() para evitar essa sobrecarga. fdget() verifica se o contador de referência da tabela do descritor de arquivos é 1, implicando que a tarefa atual possui propriedade exclusiva da tabela do descritor de arquivos e que nenhuma modificação concorrente da tabela do descritor de arquivo pode ocorrer. Se esta verificação for bem-sucedida, fdget() então omite o incremento do contador de referência no `struct file`. fdget() define uma flag em seu valor de retorno que sinaliza para fdput() se um contador de referência foi tomado. Se assim for, fdput() usa a lógica normal fput(); se não, fdput() não faz nada.

Essa otimização depende de algumas regras, incluindo:

A) Uma referência feita via fdget() deve ser descartada com fdput() antes do final da syscall.
B) A referência de uma tarefa à sua tabela de descritores de arquivos só pode ser duplicada para escrita se essa tarefa for conhecida não estar entre fdget() e fdput().
C) Uma tarefa que pode estar entre um fdget() e fdput() elididos não deve usar ksys_close() no mesmo número de descritor de arquivo usado para fdget().

O código upstream atual viola a regra C. A seqüência de eventos a seguir pode fazer com que fput() deixe cair o contador de referência em uso de um arquivo binder para zero:

A tarefa A e a tarefa B estão conectadas via binder; A tarefa A tem /dev/binder aberto no descritor de arquivo número X. Ambas as tarefas são de thread única.

– a tarefa B envia uma mensagem binder com um array de descritor de arquivo (BINDER_TYPE_FDA) contendo um descritor de arquivo para a tarefa A
– a tarefa A lê a mensagem binder com o descritor de arquivo traduzido número Y
– A tarefa A usa dup2(X, Y) para sobrescrever o descritor de arquivo Y com o arquivo /dev/binder
– tarefa A desmapeia o mapeamento de memória do binder do espaço do usuário; o contador de referência na tarefa A /dev/binder é agora 2
– A tarefa A fecha o descritor de arquivo X; o contador de referência na tarefa A /dev/binder é agora 1
– A tarefa A chama o comando BC_FREE_BUFFER no descritor de arquivo X para liberar a mensagem de entrada do binder
– fdget() elimina o incremento do contador de referência, já que a tabela descritora de arquivos não é compartilhada – o manipulador BC_FREE_BUFFER remove a entrada da tabela descritor de arquivos para X e decrementa o contador de referência do arquivo /dev/binder da tarefa A para zero

Como o fput() usa o mecanismo de trabalho da tarefa para liberar o arquivo, isso não causa imediatamente um use-after-free que o KASAN possa detectar; para isso, a seguinte sequência de eventos funciona:

– A tarefa A fecha o descritor de arquivo X; o contador de referência na tarefa A /dev/binder é agora 1
– tarefa A cria uma nova tarefa, tarefa C, duplicando a tabela do descritor de arquivos; o contador de referência na tarefa A /dev/binder é agora 2
– A tarefa A chama o comando BC_FREE_BUFFER no descritor de arquivo X para liberar a mensagem binder de entrada
– fdget() em ksys_ioctl() elimina o incremento do contador de referência, já que a tabela de descritores de arquivos não é compartilhada
– o manipulador BC_FREE_BUFFER remove a entrada da tabela do descritor de arquivo para X e decrementa o contador de referência do arquivo /dev/binder da tarefa A para 1
– a tarefa C chama close(X), o que reduz o contador de referência da tarefa A /dev/binder para 0 e a libera
– a tarefa A continua o processamento do ioctl e acessa alguma propriedade de, por exemplo, o binder_proc => UAF detectável no KASAN

Créditos

Jann Horn (Google Project Zero)

Referência(s)

1719 – Android: binder use-after-free via fdget() optimization –  project-zero – Monorail
https://bugs.chromium.org/p/project-zero/issues/detail?id=1719

Android Security Bulletin — February 2019
https://source.android.com/security/bulletin/2019-02-01.html

binder: fix proc->files use-after-free
https://github.com/torvalds/linux/commit/7f3dc0088b98

binder: fix use-after-free due to ksys_close() during fdget()
https://github.com/torvalds/linux/commit/80cd795630d6526ba729a089a435bf74a57af927

binder: use standard functions to allocate fds
https://github.com/torvalds/linux/commit/44d8047f1d87adc2fd7eccc88533794f6d88c15e

FROMLIST: binder: fix proc->files use-after-free
https://android.googlesource.com/kernel/msm/+/1b652c7c29b7

CVE-2019-2000
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-2000

CVE-2019-2000
https://nvd.nist.gov/vuln/detail/CVE-2019-2000

Se encontrou algum erro neste alerta ou deseja uma análise compreensiva, entre em contato.

Última modificação: 19 fevereiro 2019