Demonio Android Live-LocK (llkd)

Android 10 incluye Android Live-LocK Daemon ( llkd ), que está diseñado para detectar y mitigar los puntos muertos del kernel. El componente llkd proporciona una implementación independiente predeterminada, pero también puede integrar el código llkd en otro servicio, ya sea como parte del ciclo principal o como un subproceso separado.

Escenarios de detección

El llkd tiene dos escenarios de detección: estado D o Z persistente y firma de pila persistente.

Estado persistente D o Z

Si un subproceso se encuentra en estado D (suspensión ininterrumpida) o Z (zombie) sin avance durante más tiempo que ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms , llkd elimina el proceso (o el proceso principal). ). Si un escaneo posterior muestra que el mismo proceso continúa existiendo, el llkd confirma una condición de bloqueo en vivo y genera pánico en el kernel de una manera que proporciona el informe de error más detallado para la condición.

El llkd incluye un perro guardián que emite una alarma si llkd se bloquea; watchdog es el doble del tiempo esperado para fluir a través del bucle principal y el muestreo es cada ro.llk_sample_ms .

Firma de pila persistente

Para las versiones de depuración de usuarios, llkd puede detectar bloqueos en vivo del kernel mediante la verificación de firma de pila persistente. Si un subproceso en cualquier estado excepto Z tiene un símbolo de kernel ro.llk.stack enumerado persistentemente que se informa por más tiempo que ro.llk.timeout_ms o ro.llk.stack.timeout_ms , el llkd el proceso (incluso si hay reenvío progreso de la programación). Si un escaneo posterior muestra que el mismo proceso continúa existiendo, el llkd confirma una condición de bloqueo en vivo y genera pánico en el kernel de una manera que proporciona el informe de error más detallado para la condición.

La verificación lldk persiste continuamente cuando existe la condición de bloqueo activo y busca las cadenas compuestas " symbol+0x" o " symbol.cfi+0x" en el archivo /proc/pid/stack en Linux. La lista de símbolos está en ro.llk.stack y por defecto 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 ocurren cada ro.llk.check_ms ). Debido a la falta de protección ABA, esta es la única forma de evitar una activación falsa. La función de símbolo debe aparecer debajo de la función que llama al bloqueo que podría competir. Si el bloqueo está debajo o en la función de símbolo, el símbolo aparece en todos los procesos afectados, no solo en el que provocó el bloqueo.

Cobertura

La implementación predeterminada de llkd no supervisa las init , [kthreadd] o [kthreadd] . Para que llkd cubra [kthreadd] -subprocesos generados:

  • Los conductores no deben permanecer en un estado D persistente,

O

  • Los controladores deben tener mecanismos para recuperar el subproceso en caso de que se elimine externamente. Por ejemplo, use wait_event_interruptible() en lugar de wait_event() .

Si se cumple una de las condiciones anteriores, la lista negra de llkd se puede ajustar para cubrir los componentes del núcleo. La verificación de símbolos de pila implica una lista negra de procesos adicional para evitar violaciones 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 (enumeradas a continuación).

  • Las propiedades llamadas prop_ms están en milisegundos.
  • Las propiedades que usan el separador de coma (,) para las listas usan un separador inicial para conservar la entrada predeterminada, luego agregan o restan entradas con los prefijos opcionales más (+) y menos (-) respectivamente. Para estas listas, la cadena "falso" 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.depurable

El dispositivo está configurado para depuración de usuario o compilación de ingeniería.

ro.llk.sysrq_t

Si la propiedad es "eng", el valor predeterminado no es ro.config.low_ram ni ro.debuggable . Si es verdadero, vuelca todos los subprocesos ( sysrq t ).

ro.llk.habilitar

Permita que se habilite el demonio de bloqueo en vivo. El valor predeterminado es falso.

llk.habilitar

Evaluado para compilaciones de ing. El valor predeterminado es ro.llk.enable .

ro.khungtask.habilitar

Permita que se habilite el demonio [khungtask] . El valor predeterminado es falso.

khungtask.habilitar

Evaluado para compilaciones de ing. El valor predeterminado es ro.khungtask.enable .

ro.llk.mlockall

Habilite la llamada a mlockall() . El valor predeterminado es falso.

ro.khungtask.tiempo de espera

[khungtask] límite de tiempo máximo. El valor predeterminado es 12 minutos.

ro.llk.timeout_ms

Límite de tiempo máximo D o Z. El valor predeterminado es 10 minutos. Duplique este valor para establecer el mecanismo de vigilancia de alarmas 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 Z. El valor predeterminado es ro.llk.timeout_ms .

ro.llk.stack.timeout_ms

Comprueba el límite de tiempo máximo de los símbolos de pila persistentes. El valor predeterminado es ro.llk.timeout_ms . Activo solo en compilaciones de depuración de usuarios o eng .

ro.llk.check_ms

Muestras de subprocesos para D o Z. El valor predeterminado es dos minutos.

ro.llk.stack

Comprueba los símbolos de la pila del kernel que, si están persistentemente presentes, pueden indicar que un subsistema está bloqueado. El valor predeterminado es cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable lista de símbolos del kernel separados por comas. La verificación no programa ABA hacia adelante, excepto sondeando 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 muestras de la pila). Comprueba si hay una coincidencia para " symbol+0x" o " symbol.cfi+0x" en la expansión de la pila. Disponible solo en compilaciones de depuración de usuarios o eng ; las preocupaciones de seguridad en las compilaciones de los usuarios dan como resultado privilegios limitados que impiden esta verificación.

ro.llk.blacklist.proceso

El llkd no observa los procesos especificados. El valor predeterminado es 0,1,2 ( kernel , init y [kthreadd] ) más los nombres de proceso 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.padre

El llkd no observa los procesos que tienen los padres especificados. El valor predeterminado es 0,2,adbd&[setsid] ( kernel , [kthreadd] y adbd solo para zombie setsid ). Un separador de y comercial (&) especifica que el padre se ignora solo en combinación con el proceso hijo de destino. Se seleccionó Ampersand porque nunca forma parte del nombre de un proceso; sin embargo, un setprop en el shell requiere que el ampersand sea escapado o entre comillas, aunque el archivo init rc donde normalmente se especifica esto no tiene este problema. Un proceso principal o de destino puede ser una referencia comm , cmdline o pid .

ro.llk.lista negra.uid

El llkd no observa los procesos que coinciden con los uid especificados. Lista separada por comas de números uid o nombres. El valor predeterminado es vacío o falso.

ro.llk.lista negra.proceso.pila

El llkd no supervisa el subconjunto especificado de procesos para firmas de pila de bloqueo en vivo. El valor predeterminado es nombres de proceso init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd . Previene la violación de la política de seguridad asociada con procesos que bloquean ptrace (ya que estos no se pueden verificar). Activo solo en compilaciones de depuración de usuarios y eng . Para obtener detalles sobre los tipos de compilación, consulte Compilación de Android .

Preocupaciones arquitectónicas

  • Las propiedades están limitadas a 92 caracteres (sin embargo, esto se ignora para los valores predeterminados definidos en el archivo include/llkd.h en las fuentes).
  • El demonio [khungtask] es demasiado genérico y se dispara con el código del controlador que se encuentra demasiado en el estado D. Cambiar a S haría que la(s) tarea(s) se pudiera(n) eliminar (y resucitar por parte de los conductores si es necesario).

Interfaz de biblioteca (opcional)

Opcionalmente, puede incorporar el llkd en otro demonio privilegiado utilizando la siguiente interfaz 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 genera automáticamente un subproceso; de lo contrario, la persona que llama debe llamar a llkCheckMilliseconds en su ciclo principal. La función devuelve el período de tiempo antes de la próxima llamada esperada a este controlador.