Android 10 inclut le daemon de blocage en direct Android (llkd
), conçu pour détecter et atténuer les interblocages 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, dans la boucle principale ou en tant que thread distinct.
Scénarios de détection
llkd
propose deux scénarios de détection: l'état D ou Z persistant et la signature de pile persistante.
État D ou Z persistant
Si un thread est dans l'état D (sommeil ininterruptible) ou Z (zombie) sans progression vers l'avant pendant plus de ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms
, llkd
arrête le processus (ou le processus parent). Si une analyse ultérieure montre que le même processus continue d'exister, llkd
confirme une condition de verrouillage et provoque une panique du noyau de manière à fournir le rapport de bug le plus détaillé pour la condition.
llkd
inclut un chien de garde automatique qui déclenche une alarme si llkd
se bloque. Le chien de garde est le double du temps prévu pour passer par la boucle principale, et l'échantillonnage est effectué toutes les ro.llk_sample_ms
.
Signature de pile persistante
Pour les versions userdebug, llkd
peut détecter les verrous actifs du noyau à l'aide d'une vérification de signature de pile persistante. Si un thread dans n'importe quel état, sauf Z, possède un symbole de kernel ro.llk.stack
listé de manière persistante qui est signalé pendant plus de temps que ro.llk.timeout_ms
ou ro.llk.stack.timeout_ms
, llkd
arrête le processus (même s'il y a une progression de la planification en avant). Si une analyse ultérieure montre que le même processus continue d'exister, llkd
confirme une condition de verrouillage et provoque une panique du noyau de manière à fournir le rapport de bug le plus détaillé pour la condition.
La vérification lldk
persiste en permanence lorsque la condition de verrouillage actif existe et recherche les chaînes composées symbol+0x
ou symbol.cfi+0x
dans le fichier /proc/pid/stack
sous Linux. La liste des symboles se trouve dans ro.llk.stack
et est par défaut la liste cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
séparée par une virgule.
Les symboles doivent être suffisamment rares et éphémères pour qu'un système typique ne voie la fonction qu'une seule fois dans un échantillon au cours de la période de délai avant expiration de ro.llk.stack.timeout_ms
(les échantillons se produisent toutes les ro.llk.check_ms
). En raison de l'absence de protection ABA, il s'agit du seul moyen d'éviter un déclenchement incorrect. La fonction de symbole doit apparaître sous la fonction qui appelle le verrouillage susceptible de se produire. Si le cadenas se trouve en dessous ou dans la fonction de symbole, le symbole apparaît dans tous les processus concernés, et pas seulement dans celui qui a causé le blocage.
Couverture
L'implémentation par défaut de llkd
ne surveille pas les apparitions de init
, [kthreadd]
ou [kthreadd]
. Pour que llkd
couvre les threads créés par [kthreadd]
:
- Les pilotes ne doivent pas rester dans un état D persistant.
OU
- Les pilotes doivent disposer de mécanismes permettant de récupérer le thread s'il est arrêté en externe. Par exemple, utilisez
wait_event_interruptible()
au lieu dewait_event()
.
Si l'une des conditions ci-dessus est remplie, la liste de blocage llkd
peut être ajustée pour couvrir les composants du noyau. La vérification des symboles de la pile implique une liste de blocage de processus supplémentaire pour éviter les cas de non-respect de la stratégie de sécurité sur les services qui bloquent les opérations ptrace
.
Propriétés Android
llkd
répond à plusieurs propriétés Android (listées ci-dessous).
- Les propriétés nommées
prop_ms
sont exprimées en millisecondes. - Les propriétés qui utilisent la virgule (,) comme séparateur pour les listes utilisent un séparateur initial pour conserver l'entrée par défaut, puis ajoutent ou soustraient des entrées avec des 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 reviennent à la valeur par défaut spécifiée.
ro.config.low_ram
La mémoire de l'appareil est limitée.
ro.debuggable
L'appareil est configuré pour la version userdebug ou eng.
ro.llk.sysrq_t
Si la propriété est eng
, la valeur par défaut n'est pas ro.config.low_ram
ni ro.debuggable
.
Si la valeur est true
, videz tous les threads (sysrq t
).
ro.llk.enable
Autorisez l'activation du daemon de verrouillage en direct. La valeur par défaut est false
.
llk.enable
Évalué pour les builds en anglais. La valeur par défaut est ro.llk.enable
.
ro.kskutask.enable
Autorisez l'activation du démon [khungtask]
. La valeur par défaut est false
.
kskutask.enable
Évalué pour les builds en anglais. La valeur par défaut est ro.khungtask.enable
.
ro.llk.mlockall
Activez l'appel à mlockall()
. La valeur par défaut est false
.
ro.khungtask.timeout
Limite de temps maximale [khungtask]
. La valeur par défaut est de 12 minutes.
ro.llk.timeout_ms
D ou Z : limite de temps maximale. La valeur par défaut est de 10 minutes. Doublez cette valeur pour définir le moniteur d'alarme pour llkd
.
ro.llk.D.timeout_ms
D : durée maximale. La valeur par défaut est ro.llk.timeout_ms
.
ro.llk.Z.timeout_ms
Limite de temps maximale Z. La valeur par défaut est ro.llk.timeout_ms
.
ro.llk.stack.timeout_ms
Vérifie la limite de temps maximale pour les symboles de pile persistants. La valeur par défaut est ro.llk.timeout_ms
. Active uniquement sur les builds userdebug ou eng.
ro.llk.check_ms
Exemples de threads pour D ou Z. La valeur par défaut est de deux minutes.
ro.llk.stack
Recherche des symboles de 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 une liste de symboles de kernel séparés par des virgules cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
. La vérification n'effectue pas de planification ABA en avant, sauf en interrogeant tous les ro.llk_check_ms
sur la période ro.llk.stack.timeout_ms
. Par conséquent, les symboles de pile doivent être exceptionnellement rares et éphémères (il est très peu probable qu'un symbole s'affiche de manière persistante dans tous les échantillons de la pile). Recherche une correspondance pour symbol+0x
ou symbol.cfi+0x
lors du développement de la pile. Disponible uniquement sur les builds userdebug ou eng ; les problèmes de sécurité sur les builds utilisateur entraînent des droits limités qui empêchent cette vérification.
ro.llk.blacklist.process
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 automatique peut être supérieure à la taille maximale actuelle de la propriété (92).
ro.llk.noir.parent
llkd
ne surveille pas les processus dont les parents sont spécifiés. La valeur par défaut est 0,2,adbd&[setsid]
(kernel
, [kthreadd]
et adbd
uniquement pour les setsid
zombies). L'esperluette (&) indique que le parent n'est ignoré qu'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. Toutefois, un setprop
dans le shell nécessite que l'esperluette soit échappée ou mise entre guillemets, bien que le fichier init rc
où cela est normalement spécifié ne présente pas ce problème. Un processus parent ou cible peut être une référence comm
, cmdline
ou pid
.
ro.llk.blacklist.uid
llkd
ne surveille pas les processus correspondant aux UID spécifiés.
Liste de numéros ou de noms UIS séparés par une virgule. La valeur par défaut est vide ou false
.
ro.llk.black.process.stack
llkd
ne surveille pas le sous-ensemble de processus spécifié pour les signatures de pile de verrouillage 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 la stratégie de sécurité associée aux processus qui bloquent ptrace
(car ils ne peuvent pas être vérifiés). Active uniquement sur les builds userdebug et eng. Pour en savoir plus sur les types de compilation, consultez Compilation d'Android.
Problèmes d'architecture
- Les propriétés sont limitées à 92 caractères (cependant, cette limite est ignorée pour les valeurs par défaut définies dans le fichier
include/llkd.h
dans les sources). - Le daemon
[khungtask]
intégré est trop générique et se déclenche sur le code du pilote qui reste trop longtemps en état D. Le passage à S rendrait les tâches tuables (et ressuscitables par les pilotes si nécessaire).
Interface de la bibliothèque (facultatif)
Vous pouvez éventuellement intégrer llkd
à un autre daemon 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 automatiquement créé. Sinon, l'appelant doit appeler llkCheckMilliseconds
dans sa boucle principale. La fonction renvoie la période avant le prochain appel attendu à ce gestionnaire.