Démon Android Live-LocK (llkd)

Android 10 inclut le démon Android Live-LocK ( llkd ), qui est conçu pour détecter et atténuer les blocages du noyau. Le composant llkd fournit une implémentation autonome par défaut, mais vous pouvez également intégrer le code llkd dans un autre service, soit dans le cadre de la boucle principale, soit en tant que thread séparé.

Scénarios de détection

Le llkd a deux scénarios de détection : état D ou Z persistant et signature de pile persistante.

État D ou Z persistant

Si un thread est dans l'état D (veille ininterrompue) ou Z (zombie) sans progression vers l'avant pendant plus longtemps que ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms , le llkd tue le processus (ou le processus parent ). Si une analyse ultérieure montre que le même processus continue d'exister, le llkd confirme une condition de verrouillage en direct et panique le noyau de manière à fournir le rapport de bogue le plus détaillé pour la condition.

Le llkd inclut un chien de garde automatique qui s'alarme si llkd verrouille ; le chien de garde est le double du temps prévu pour traverser la boucle principale et l'échantillonnage est chaque ro.llk_sample_ms .

Signature de pile persistante

Pour les versions userdebug, le llkd peut détecter les verrous en direct du noyau en utilisant la vérification persistante de la signature de la pile. Si un thread dans n'importe quel état sauf Z a un symbole de noyau ro.llk.stack répertorié persistant qui est signalé pendant plus longtemps que ro.llk.timeout_ms ou ro.llk.stack.timeout_ms , le llkd tue le processus (même s'il y a progression de la planification). Si une analyse ultérieure montre que le même processus continue d'exister, le llkd confirme une condition de verrouillage en direct et panique le noyau de manière à fournir le rapport de bogue le plus détaillé pour la condition.

La vérification lldk persiste en continu lorsque la condition de verrouillage en direct existe et recherche les chaînes composées " symbol+0x" ou " symbol.cfi+0x" dans le /proc/pid/stack sous Linux. La liste des symboles est dans ro.llk.stack et par défaut la liste séparée par des virgules de " cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable ".

Les symboles doivent être suffisamment rares et de courte durée pour que, sur un système typique, la fonction ne soit vue qu'une seule fois dans un échantillon sur la période de temporisation de ro.llk.stack.timeout_ms (les échantillons se produisent tous les ro.llk.check_ms ). En raison du manque de protection ABA, c'est le seul moyen d'empêcher un faux déclenchement. Le symbole fonction doit apparaître en dessous de la fonction appelant la serrure qui pourrait concourir. Si le verrou est en dessous ou dans la fonction de symbole, le symbole apparaît dans tous les processus concernés, pas seulement celui qui a provoqué le blocage.

Couverture

L'implémentation par défaut de llkd ne surveille pas les spawns init , [kthreadd] ou [kthreadd] . Pour que llkd couvre les threads générés par [kthreadd] :

  • Les conducteurs ne doivent pas rester dans un état D persistant,

OU

  • Les pilotes doivent avoir des mécanismes pour récupérer le thread s'il est tué en externe. Par exemple, utilisez wait_event_interruptible() au lieu de wait_event() .

Si l'une des conditions ci-dessus est remplie, la liste noire llkd peut être ajustée pour couvrir les composants du noyau. La vérification des symboles de pile implique une liste noire de processus supplémentaire pour empêcher les violations de sepolicy sur les services qui bloquent les opérations ptrace .

Propriétés Android

Le llkd répond à plusieurs propriétés Android (énumérées ci-dessous).

  • Les propriétés nommées prop_ms sont en millisecondes.
  • Les propriétés qui utilisent le séparateur virgule (,) pour les listes utilisent un séparateur de tête pour conserver l'entrée par défaut, puis ajoutent ou soustraient des entrées avec les préfixes facultatifs plus (+) et moins (-) respectivement. Pour ces listes, la chaîne "false" est synonyme d'une liste vide, et les entrées vides ou manquantes utilisent la valeur par défaut spécifiée.

ro.config.low_ram

L'appareil est configuré avec une mémoire limitée.

ro.débogable

L'appareil est configuré pour userdebug ou eng build.

ro.llk.sysrq_t

Si la propriété est "eng", la valeur par défaut n'est pas ro.config.low_ram ou ro.debuggable . Si vrai, vider tous les threads ( sysrq t ).

ro.llk.enable

Autoriser l'activation du démon live-lock. La valeur par défaut est false.

llk.enable

Évalué pour les versions eng. La valeur par défaut est ro.llk.enable .

ro.khungtask.enable

Autoriser l'activation du démon [khungtask] . La valeur par défaut est false.

khungtask.enable

Évalué pour les versions eng. La valeur par défaut est ro.khungtask.enable .

ro.llk.mlockall

Activer l'appel à mlockall() . La valeur par défaut est false.

ro.khungtask.timeout

[khungtask] limite de temps maximale. La valeur par défaut est 12 minutes.

ro.llk.timeout_ms

Limite de temps maximale D ou Z. La valeur par défaut est 10 minutes. Doublez cette valeur pour définir le chien de garde d'alarme pour llkd .

ro.llk.D.timeout_ms

D délai maximum. La valeur par défaut est ro.llk.timeout_ms .

ro.llk.Z.timeout_ms

Z limite de temps maximale. La valeur par défaut est ro.llk.timeout_ms .

ro.llk.stack.timeout_ms

Vérifie la limite de temps maximale des symboles de pile persistants. La valeur par défaut est ro.llk.timeout_ms . Actif uniquement sur les builds userdebug ou eng .

ro.llk.check_ms

Échantillons de fils pour D ou Z. La valeur par défaut est de deux minutes.

ro.llk.pile

Vérifie les symboles de la pile du noyau qui, s'ils sont présents de manière persistante, peuvent indiquer qu'un sous-système est verrouillé. La valeur par défaut est cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable liste de symboles du noyau séparés par des virgules. La vérification ne fait pas de planification ABA, sauf en interrogeant chaque ro.llk_check_ms sur la période ro.llk.stack.timeout_ms , donc les symboles de pile doivent être exceptionnellement rares et éphémères (il est très peu probable qu'un symbole apparaisse de manière persistante dans tous échantillons de la pile). Vérifie une correspondance pour " symbol+0x" ou " symbol.cfi+0x" dans l'expansion de la pile. Disponible uniquement sur les builds userdebug ou eng ; les problèmes de sécurité sur les builds utilisateur entraînent des privilèges limités qui empêchent cette vérification.

ro.llk.blacklist.process

Le llkd ne surveille pas les processus spécifiés. La valeur par défaut est 0,1,2 ( kernel , init et [kthreadd] ) plus les noms de processus init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1] . Un processus peut être une référence comm , cmdline ou pid . Une valeur par défaut automatisée peut être supérieure à la taille de propriété maximale actuelle de 92.

ro.llk.blacklist.parent

Le llkd ne surveille pas les processus qui ont le(s) parent(s) spécifié(s). La valeur par défaut est 0,2,adbd&[setsid] ( kernel , [kthreadd] et adbd uniquement pour zombie setsid ). Un séparateur esperluette (&) spécifie que le parent est ignoré uniquement en combinaison avec le processus enfant cible. L'esperluette a été sélectionnée car elle ne fait jamais partie d'un nom de processus ; cependant, un setprop dans le shell nécessite que l'esperluette soit échappée ou entre guillemets, bien que le fichier init rc où cela est normalement spécifié n'ait pas ce problème. Un processus parent ou cible peut être une référence comm , cmdline ou pid .

ro.llk.blacklist.uid

Le llkd ne surveille pas les processus qui correspondent aux uid spécifiés. Liste séparée par des virgules de numéros ou de noms uid. La valeur par défaut est vide ou faux.

ro.llk.blacklist.process.stack

Le llkd ne surveille pas le sous-ensemble de processus spécifié pour les signatures de pile de verrous en direct. La valeur par défaut est les noms de processus init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd . Empêche la violation de sepolicy associée aux processus qui bloquent ptrace (car ceux-ci ne peuvent pas être vérifiés). Actif uniquement sur les builds userdebug et eng . Pour plus de détails sur les types de build, reportez-vous à Création d'Android .

Préoccupations architecturales

  • Les propriétés sont limitées à 92 caractères (cependant, ceci est ignoré pour les valeurs par défaut définies dans le fichier include/llkd.h dans les sources).
  • Le démon [khungtask] est trop générique et se déclenche trop souvent sur le code du pilote qui se trouve dans l'état D. Passer à S rendrait la ou les tâches tuables (et ressuscitables par les pilotes si nécessaire).

Interface de la bibliothèque (facultatif)

Vous pouvez éventuellement incorporer le llkd dans un autre démon privilégié à l'aide de l'interface C suivante du composant libllkd :

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

Si un nom de thread est fourni, un thread est généré automatiquement, sinon l'appelant doit appeler llkCheckMilliseconds dans sa boucle principale. La fonction renvoie la période de temps avant le prochain appel attendu à ce gestionnaire.