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 dewait_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.