Daemon de bloqueio em tempo real do Android (llkd, na sigla em inglês)

O Android 10 inclui o daemon de bloqueio em tempo real do Android (llkd), que foi projetado para detectar e mitigar os impasses do kernel. O llkd fornece uma implementação independente padrão, mas é possível integrar o código llkd a outro serviço, seja como parte do loop principal ou como uma linha de execução separada.

Cenários de detecção

O llkd tem dois cenários de detecção: estado D ou Z persistente e pilha de assinaturas.

Estado D ou Z persistente

Se uma linha de execução estiver no estado D (suspensão ininterrupta) ou Z (zumbi) sem sinal de encaminhamento progresso por mais de ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms, o llkd encerra o processo (ou processo pai). Se uma verificação posterior mostrar o mesmo processo continuar existindo, o llkd vai confirmar uma condição de bloqueio em tempo real e entra em pânico o kernel de forma a fornecer o relatório mais detalhado do bug para o condição.

O llkd inclui um monitoramento automático que envia um alarme caso o llkd seja bloqueado. vigilante é o dobro do tempo esperado para fluir pelo loop principal, e a amostragem é a cada ro.llk_sample_ms.

Assinatura de pilha persistente

Para versões userdebug, o llkd pode detectar live-locks do kernel usando na verificação de assinaturas de pilha. Se uma linha de execução em qualquer estado, exceto Z, tiver uma símbolo de kernel ro.llk.stack listado que é informado por mais de ro.llk.timeout_ms ou ro.llk.stack.timeout_ms, o llkd encerra o processo mesmo que haja progresso na programação. Se uma verificação posterior mostrar o mesmo processo continuar existindo, o llkd vai confirmar uma condição de bloqueio em tempo real e entra em pânico o kernel de forma a fornecer o relatório mais detalhado do bug para o condição.

A verificação de lldk persiste continuamente quando a condição de bloqueio ativo existir e procura pelas strings compostas symbol+0x ou symbol.cfi+0x no /proc/pid/stack no Linux. A lista de símbolos está em ro.llk.stack e o padrão é uma lista separada por vírgulas de cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable.

Os símbolos devem ser raros e de curta duração o suficiente para que, em um sistema típico, é vista apenas uma vez em uma amostra durante o período de tempo limite de ro.llk.stack.timeout_ms (as amostras ocorrem a cada ro.llk.check_ms). Devido à falta da proteção ABA, essa é a única maneira de evitar um falso acionamento. O símbolo função deve aparecer abaixo da função que chama o bloqueio que pode conter. Se o cadeado estiver abaixo ou na função de símbolo, o símbolo aparecerá em todas as processos, não apenas aquele que causou o logotipo completo.

Cobertura

A implementação padrão de llkd não monitora init, [kthreadd] nem [kthreadd] gerações. Para que o llkd cubra linhas de execução geradas com [kthreadd]:

  • Os drivers não podem permanecer no estado D persistente.

OU

  • Os drivers precisam ter mecanismos para recuperar a linha de execução caso ela seja encerrada externamente. Por exemplo, use wait_event_interruptible() em vez de wait_event().

Se uma das condições acima for atendida, a lista de bloqueio llkd poderá ser ajustada para os componentes do kernel. A verificação de símbolos de pilha envolve um processo adicional lista de bloqueio para evitar violações de sepolicy nos serviços que bloqueiam ptrace as operações.

Propriedades do Android

O llkd responde a várias propriedades do Android (listadas abaixo).

  • As propriedades com o nome prop_ms ficam em milissegundos.
  • As propriedades que usam vírgula (,) como separador para listas usam um separador à esquerda para preservar a entrada padrão e, em seguida, adicionar ou subtrair entradas com o sinal de adição (+) e menos (-) respectivamente. Para essas listas, a string false é sinônimo de uma lista vazia, e entradas em branco ou ausentes recorrem à o valor padrão especificado.

ro.config.low_ram

O dispositivo está configurado com memória limitada.

ro.debuggable

O dispositivo está configurado para o build userdebug ou eng.

ro.llk.sysrq_t

Se a propriedade for eng, o padrão não será ro.config.low_ram nem ro.debuggable. Se true, despeja todas as linhas de execução (sysrq t).

ro.llk.enable

Permitir que o daemon de bloqueio em tempo real seja ativado. O padrão é false.

llk.ativar

Avaliado para builds eng. O padrão é ro.llk.enable.

ro.khungtask.enable

Permitir que o daemon [khungtask] seja ativado. O padrão é false.

khungtask.enable

Avaliado para builds eng. O padrão é ro.khungtask.enable.

ro.llk.mlockall

Ativar a chamada para mlockall(). O padrão é false.

ro.khungtask.timeout

Limite de tempo máximo de [khungtask]. O padrão é 12 minutos.

ro.llk.timeout_ms

D ou Z. O padrão é 10 minutos. Dobre esse valor para definir a monitoramento do alarme para llkd.

ro.llk.D.timeout_ms

D. O padrão é ro.llk.timeout_ms.

ro.llk.Z.timeout_ms

Z. O padrão é ro.llk.timeout_ms.

ro.llk.stack.timeout_ms

Verifica o limite de tempo máximo para símbolos de pilha persistentes. O padrão é ro.llk.timeout_ms: Ativo apenas em builds userdebug ou eng.

ro.llk.check_ms

Exemplos de linhas de execução para D ou Z. O padrão é dois minutos.

ro.llk.stack

Verifica se há símbolos de pilha do kernel que, se persistentemente presentes, podem indicar uma está bloqueado. O padrão é cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable lista separada por vírgulas de símbolos kernel. A verificação não faz agendamento de encaminhamento ABA, exceto pela pesquisa a cada ro.llk_check_ms durante o período ro.llk.stack.timeout_ms, portanto, os símbolos de pilha devem ser excepcionalmente raros e passageiro (é muito improvável que um símbolo apareça persistentemente em todos amostras da pilha). Verifica se há uma correspondência para symbol+0x ou symbol.cfi+0x na expansão de pilha. Disponível apenas no userdebug ou eng builds questões de segurança em builds do usuário resultam em privilégios limitados evitar essa verificação.

ro.llk.lista negra.process

O llkd não monitora os processos especificados. O padrão é 0,1,2 (kernel, init e [kthreadd]), além de nomes de processos init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]. Um processo pode ser uma referência comm, cmdline ou pid. Um padrão automatizado pode ser maior do que o tamanho máximo atual de propriedade, 92.

ro.llk.lista negra.parente

O llkd não monitora processos que tenham os pais especificados. Padrão é 0,2,adbd&[setsid] (kernel, [kthreadd] e adbd somente para zumbis setsid). Um separador "e" comercial (&) especifica que o pai é apenas ignorado. em combinação com o processo filho de destino. "E" comercial foi selecionado porque nunca faz parte do nome de um processo; No entanto, um setprop no shell requer a "e" comercial tenha escape ou esteja entre aspas, embora o arquivo init rc em que seja especificado normalmente não tem esse problema. Um processo pai ou destino pode ser um Referência comm, cmdline ou pid.

ro.llk.lista negra.uid

O llkd não monitora processos que correspondem aos UIDs especificados. Lista separada por vírgulas de números ou nomes UIS. O padrão é vazio ou false.

ro.llk.checklist.process.stack

O llkd não monitora o subconjunto especificado de processos para a pilha de bloqueio ativa assinaturas. O padrão são nomes de processos init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd: Impede a sepolicy violação associada a processos que bloqueiam ptrace (já que não podem ser está marcada). Ativo apenas em builds userdebug e eng. Para detalhes sobre o build consulte Como criar para o Android.

Preocupações com a arquitetura

  • As propriedades são limitadas a 92 caracteres (no entanto, isso é ignorado para os padrões definidos no arquivo include/llkd.h nas origens).
  • O daemon [khungtask] integrado é muito genérico e gera erros no código do driver que fica muito tempo no estado D. Mudar para S tornaria as tarefas elimináveis (e pode ser recuperada pelos motoristas, se necessário).

Interface "Library" (opcional)

Como opção, é possível incorporar o llkd em outro daemon com privilégios usando esta interface C do componente libllkd:

#include "llkd.h"
bool llkInit(const char* threadname) /* return true if enabled */
unsigned llkCheckMillseconds(void)   /* ms to sleep for next check */

Se um nome de thread for fornecido, um thread será gerado automaticamente. Caso contrário, o autor da chamada precisa chamar llkCheckMilliseconds no loop principal. A função retorna o período de tempo antes da próxima chamada esperada a esse manipulador.