Android 10 incluye el daemon de bloqueo activo de Android (llkd
), que está diseñado para detectar y mitigar los interbloqueos del kernel. El componente llkd
proporciona una implementación independiente predeterminada, pero también puedes integrar el código llkd
en otro servicio, ya sea como parte del bucle principal o como un subproceso independiente.
Situaciones de detección
llkd
tiene dos situaciones de detección: el estado D o Z persistente y la firma de pila
persistente.
Estado D o Z persistente
Si un subproceso está en el estado D (suspensión ininterrumpida) o Z (zombificado) sin progreso durante más de ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms
, llkd
finaliza el proceso (o el proceso superior). Si un análisis posterior muestra que el mismo proceso sigue existiendo, llkd
confirma una condición de bloqueo activo y genera un pánico en el kernel de una manera que proporciona el informe de errores más detallado para la condición.
llkd
incluye un supervisor de eventos que genera una alarma si llkd
se bloquea. El supervisor es el doble del tiempo esperado para fluir a través del bucle principal y el muestreo se realiza cada ro.llk_sample_ms
.
Firma de pila persistente
En el caso de las versiones de userdebug, llkd
puede detectar bloqueos en vivo del kernel con la verificación de firma de pila persistente. Si un subproceso en cualquier estado, excepto Z, tiene un símbolo del kernel ro.llk.stack
enumerado de forma persistente que se informa durante más tiempo que ro.llk.timeout_ms
o ro.llk.stack.timeout_ms
, llkd
finaliza el proceso (incluso si hay un progreso de programación hacia adelante). Si un análisis posterior muestra que el mismo proceso sigue existiendo, llkd
confirma una condición de bloqueo activo y genera un pánico en el kernel de una manera que proporciona el informe de errores más detallado para la condición.
La verificación de lldk
persiste de forma continua cuando existe la condición de bloqueo en tiempo real y busca las cadenas compuestas symbol+0x
o symbol.cfi+0x
en el archivo /proc/pid/stack
de Linux. La lista de símbolos está en ro.llk.stack
y, de forma predeterminada, es la lista separada por comas de cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
.
Los símbolos deben ser raros y de corta duración para que, en un sistema típico, la función se vea solo una vez en una muestra durante el período de tiempo de espera de ro.llk.stack.timeout_ms
(las muestras se producen cada ro.llk.check_ms
). Debido a la falta de protección de ABA, esta es la única forma de evitar un activador falso. La función de símbolo debe aparecer debajo de la función que llama al bloqueo que podría estar en conflicto. Si el bloqueo está debajo o en la función del símbolo, el símbolo aparece en todos los procesos afectados, no solo en el que causó el bloqueo.
Cobertura
La implementación predeterminada de llkd
no supervisa los inicios de init
, [kthreadd]
ni [kthreadd]
. Para que llkd
cubra los subprocesos generados por [kthreadd]
, haz lo siguiente:
- Los controladores no deben permanecer en un estado D persistente,
O
- Los controladores deben tener mecanismos para recuperar el subproceso en caso de que se cancele de forma externa. Por ejemplo, usa
wait_event_interruptible()
en lugar dewait_event()
.
Si se cumple una de las condiciones anteriores, la lista de bloqueo llkd
se puede ajustar para cubrir los componentes del kernel. La verificación de símbolos de pila implica una lista de entidades prohibidas de procesos adicional para evitar incumplimientos de la política de seguridad en los servicios que bloquean las operaciones de ptrace
.
Propiedades de Android
El llkd
responde a varias propiedades de Android (que se indican a continuación).
- Las propiedades
prop_ms
se expresan en milisegundos. - Las propiedades que usan el separador de coma (,) para las listas usan un separador inicial para preservar la entrada predeterminada y, luego, agregar o quitar entradas con los prefijos opcionales de signo más (+) y signo menos (−), respectivamente. Para estas listas, la cadena
false
es sinónimo de una lista vacía, y las entradas en blanco o faltantes recurren al valor predeterminado especificado.
ro.config.low_ram
El dispositivo está configurado con memoria limitada.
ro.debuggable
El dispositivo está configurado para la compilación userdebug o eng.
ro.llk.sysrq_t
Si la propiedad es eng
, el valor predeterminado no es ro.config.low_ram
ni ro.debuggable
.
Si es true
, volca todos los subprocesos (sysrq t
).
ro.llk.enable
Permite que se habilite el daemon de bloqueo en vivo. El valor predeterminado es false
.
llk.enable
Se evaluó para compilaciones de eng. El valor predeterminado es ro.llk.enable
.
ro.khungtask.enable
Permite que se habilite el daemon [khungtask]
. El valor predeterminado es false
.
khungtask.enable
Se evaluó para compilaciones de eng. El valor predeterminado es ro.khungtask.enable
.
ro.llk.mlockall
Habilitar llamada a mlockall()
. El valor predeterminado es false
.
ro.khungtask.timeout
[khungtask]
límite de tiempo máximo. El valor predeterminado es de 12 minutos.
ro.llk.timeout_ms
Límite de tiempo máximo D o Z. El valor predeterminado es de 10 minutos. Duplica este valor para configurar el vigilante de alarma para llkd
.
ro.llk.D.timeout_ms
D límite de tiempo máximo. El valor predeterminado es ro.llk.timeout_ms
.
ro.llk.Z.timeout_ms
Límite de tiempo máximo de Z. El valor predeterminado es ro.llk.timeout_ms
.
ro.llk.stack.timeout_ms
Verifica el límite de tiempo máximo de los símbolos de pila persistentes. El valor predeterminado es ro.llk.timeout_ms
. Solo está activo en compilaciones userdebug o eng.
ro.llk.check_ms
Muestras de subprocesos para D o Z. El valor predeterminado es de dos minutos.
ro.llk.stack
Busca símbolos de pila de kernel que, si están presentes de forma persistente, pueden indicar que un subsistema está bloqueado. El valor predeterminado es una lista de símbolos del kernel separados por comas cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
. La verificación no realiza la programación anticipada de ABA, excepto mediante sondeos cada ro.llk_check_ms
durante el período ro.llk.stack.timeout_ms
, por lo que los símbolos de pila deben ser excepcionalmente raros y fugaces (es muy poco probable que un símbolo aparezca de forma persistente en todos los ejemplos de la pila). Busca una coincidencia para symbol+0x
o symbol.cfi+0x
en la expansión de pila. Disponible solo en compilaciones userdebug o eng. Los problemas de seguridad en las compilaciones de usuario generan privilegios limitados que impiden esta verificación.
ro.llk.blacklist.process
llkd
no supervisa los procesos especificados. El valor predeterminado es 0,1,2
(kernel
, init
y [kthreadd]
) más los nombres de los procesos init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]
.
Un proceso puede ser una referencia comm
, cmdline
o pid
. Un valor predeterminado automatizado puede ser mayor que el tamaño máximo actual de la propiedad de 92.
ro.llk.blacklist.parent
llkd
no observa los procesos que tienen los elementos superiores especificados. El valor predeterminado es 0,2,adbd&[setsid]
(kernel
, [kthreadd]
y adbd
solo para setsid
zombi). Un separador de signo & especifica que se ignora el elemento superior solo en combinación con el proceso secundario de destino. Se seleccionó el signo & porque nunca forma parte del nombre de un proceso. Sin embargo, un setprop
en la shell requiere que se escape o entrecomille el signo &, aunque el archivo init rc
en el que se especifica normalmente no tiene este problema. Un proceso superior o de destino puede ser una referencia comm
, cmdline
o pid
.
ro.llk.get.uid.uid
llkd
no supervisa los procesos que coinciden con los UIDs especificados.
Es una lista de números o nombres de UIS separados por comas. El valor predeterminado es vacío o false
.
ro.llk.blacklist.process.stack
llkd
no supervisa el subconjunto especificado de procesos para las firmas de pila de bloqueo en vivo. El valor predeterminado es el nombre de los procesos init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd
. Evita la violación de sepolicy asociada con los procesos que bloquean ptrace
(ya que no se pueden verificar). Solo está activo en compilaciones de userdebug y eng. Para obtener detalles sobre los tipos de compilación, consulta Cómo compilar Android.
Preocupaciones arquitectónicas
- Las propiedades se limitan a 92 caracteres (sin embargo, esto se ignora para los valores predeterminados definidos en el archivo
include/llkd.h
de las fuentes). - El daemon
[khungtask]
integrado es demasiado genérico y pasa demasiado el código del controlador que está en estado D. Si se cambia a S, se podrían finalizar las tareas (y los controladores podrían resucitarlas si fuera necesario).
Interfaz de la biblioteca (opcional)
De manera opcional, puedes incorporar el llkd
a otro daemon con privilegios mediante la siguiente interfaz de C del componente libllkd
:
#include "llkd.h"
bool llkInit(const char* threadname) /* return true if enabled */
unsigned llkCheckMillseconds(void) /* ms to sleep for next check */
Si se proporciona un nombre de subproceso, se crea uno automáticamente. De lo contrario, el llamador debe llamar a llkCheckMilliseconds
en su bucle principal. La función muestra el período de tiempo antes de la próxima llamada esperada a este controlador.