Демон Android Live-Lock (llkd)

Android 10 включает демона Android Live-LocK ( llkd ), который предназначен для обнаружения и устранения взаимоблокировок ядра. Компонент llkd предоставляет автономную реализацию по умолчанию, но вы также можете интегрировать код llkd в другую службу либо как часть основного цикла, либо как отдельный поток.

Сценарии обнаружения

У llkd есть два сценария обнаружения: постоянное состояние D или Z и постоянная подпись стека.

Постоянное состояние D или Z

Если поток находится в состоянии D (непрерывный сон) или Z (зомби) без продвижения вперед дольше, чем ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms , llkd убивает процесс (или родительский процесс). ). Если последующее сканирование показывает, что тот же процесс продолжает существовать, llkd подтверждает состояние активной блокировки и вызывает панику ядра таким образом, чтобы предоставить наиболее подробный отчет об ошибке для этого состояния.

llkd включает в себя сторожевой таймер, который подает сигнал тревоги, если llkd блокируется; сторожевой таймер в два раза превышает ожидаемое время прохождения основного цикла, а выборка выполняется каждые ro.llk_sample_ms .

Постоянная подпись стека

Для выпусков llkd может обнаруживать живые блокировки ядра, используя постоянную проверку подписи стека. Если поток в любом состоянии, кроме Z, имеет постоянный символ ядра ro.llk.stack в списке, о котором сообщается дольше, чем ro.llk.timeout_ms или ro.llk.stack.timeout_ms , llkd уничтожает процесс (даже если ход планирования). Если последующее сканирование показывает, что тот же процесс продолжает существовать, llkd подтверждает состояние активной блокировки и вызывает панику ядра таким образом, чтобы предоставить наиболее подробный отчет об ошибке для этого состояния.

Проверка lldk постоянно, когда существует условие динамической блокировки, и ищет составленные строки " symbol+0x" или " symbol.cfi+0x" в файле /proc/pid/stack в Linux. Список символов находится в ro.llk.stack и по умолчанию представляет собой разделенный запятыми список « cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable ».

Символы должны быть редкими и недолговечными, чтобы в типичной системе функция отображалась только один раз в выборке за время ожидания ro.llk.stack.timeout_ms (выборки происходят каждые ro.llk.check_ms ). Из-за отсутствия защиты ABA это единственный способ предотвратить ложное срабатывание. Функция символа должна располагаться ниже функции, вызывающей блокировку, которая может соперничать. Если блокировка находится ниже или в функции символа, символ появляется во всех затронутых процессах, а не только в том, который вызвал зависание.

Покрытие

Реализация llkd по умолчанию не отслеживает запуски init , [kthreadd] или [kthreadd] . Чтобы llkd покрывал потоки, порожденные [kthreadd] :

  • Драйверы не должны оставаться в постоянном состоянии D,

ИЛИ

  • Драйверы должны иметь механизмы для восстановления потока, если он будет уничтожен извне. Например, используйте wait_event_interruptible() вместо wait_event() .

Если одно из вышеперечисленных условий выполнено, черный список llkd можно настроить так, чтобы он охватывал компоненты ядра. Проверка символов стека включает в себя дополнительный черный список процессов для предотвращения нарушений политики безопасности в службах, которые блокируют операции ptrace .

Свойства Android

llkd реагирует на несколько свойств Android (перечисленных ниже).

  • Свойства с именем prop_ms указаны в миллисекундах.
  • Свойства, которые используют разделитель-запятую (,) для списков, используют начальный разделитель для сохранения записи по умолчанию, а затем добавляют или вычитают записи с необязательными префиксами плюс (+) и минус (-) соответственно. Для этих списков строка «false» является синонимом пустого списка, а пустые или отсутствующие записи прибегают к указанному значению по умолчанию.

ro.config.low_ram

Устройство настроено с ограниченным объемом памяти.

ro.debuggable

Устройство настроено для userdebug или eng build.

ro.llk.sysrq_t

Если свойство равно "eng", по умолчанию не ro.config.low_ram или ro.debuggable . Если true, дамп всех потоков ( sysrq t ).

ro.llk.enable

Разрешить демону live-lock быть включенным. Значение по умолчанию — ложь.

llk.enable

Оценивается для eng билдов. По умолчанию используется ro.llk.enable .

ro.khungtask.enable

Разрешить [khungtask] быть включенным. Значение по умолчанию — ложь.

khungtask.enable

Оценивается для eng билдов. По умолчанию используется ro.khungtask.enable .

ro.llk.mlockall

Включить вызов mlockall() . Значение по умолчанию — ложь.

ro.khungtask.timeout

[khungtask] максимальный срок. По умолчанию 12 минут.

ro.llk.timeout_ms

Максимальный срок D или Z. По умолчанию 10 минут. Удвойте это значение, чтобы установить сторожевой таймер для llkd .

ro.llk.D.timeout_ms

Д максимальный срок. По умолчанию ro.llk.timeout_ms .

ro.llk.Z.timeout_ms

Z максимальный срок. По умолчанию ro.llk.timeout_ms .

ro.llk.stack.timeout_ms

Проверяет максимальный срок хранения постоянных символов стека. По умолчанию ro.llk.timeout_ms . Активен только на сборках userdebug или eng .

ro.llk.check_ms

Образцы потоков для D или Z. По умолчанию две минуты.

ro.llk.стек

Проверяет символы стека ядра, которые, если они постоянно присутствуют, могут указывать на то, что подсистема заблокирована. По умолчанию это cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable запятыми список символов ядра. Проверка не выполняет ABA-планирование вперед, за исключением опроса каждого ro.llk_check_ms в течение периода ro.llk.stack.timeout_ms , поэтому символы стека должны быть исключительно редкими и мимолетными (крайне маловероятно, что символ будет постоянно отображаться во всех случаях). образцы стека). Проверяет совпадение для " symbol+0x" или " symbol.cfi+0x" в расширении стека. Доступно только в сборках userdebug или eng ; проблемы безопасности в пользовательских сборках приводят к ограниченным привилегиям, препятствующим этой проверке.

ro.llk.blacklist.процесс

llkd не следит за указанными процессами. По умолчанию 0,1,2 ( kernel , init и [kthreadd] ) плюс имена процессов init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1] . Процесс может быть ссылкой на comm , cmdline или pid . Автоматическое значение по умолчанию может быть больше, чем текущий максимальный размер свойства, равный 92.

ro.llk.blacklist.parent

llkd не наблюдает за процессами, имеющими указанных родителей. По умолчанию 0,2,adbd&[setsid] setsid kernel , [kthreadd] и adbd только для наборов зомби). Разделитель амперсанд (&) указывает, что родительский процесс игнорируется только в сочетании с целевым дочерним процессом. Амперсанд был выбран потому, что он никогда не является частью имени процесса; однако setprop в оболочке требует, чтобы амперсанд был экранирован или заключен в кавычки, хотя в файле init rc , где это обычно указывается, этой проблемы нет. Родительский или целевой процесс может быть ссылкой на comm , cmdline или pid .

ro.llk.blacklist.uid

llkd не отслеживает процессы, соответствующие указанным uid. Разделенный запятыми список номеров или имен uid. По умолчанию пусто или ложно.

ro.llk.blacklist.process.stack

llkd не отслеживает указанное подмножество процессов на наличие сигнатур активных блокировок. По умолчанию это имена процессов init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd . Предотвращает нарушение sepolicy, связанное с процессами, блокирующими ptrace (поскольку их нельзя проверить). Активен только на сборках userdebug и eng . Дополнительные сведения о типах сборки см. в разделе Сборка Android .

Архитектурные проблемы

  • Свойства ограничены 92 символами (однако это игнорируется для значений по умолчанию, определенных в файле include/llkd.h в исходных текстах).
  • Встроенный демон [khungtask] слишком универсален и слишком часто срабатывает на коде драйвера, который находится в состоянии D. Переключение на S сделало бы задачу(и) убиваемой (и при необходимости восстанавливаемой драйверами).

Интерфейс библиотеки (опционально)

При желании вы можете включить llkd в другой привилегированный демон, используя следующий интерфейс C из компонента libllkd :

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

Если указано имя потока, поток создается автоматически, в противном случае вызывающая сторона должна вызвать llkCheckMilliseconds в своем основном цикле. Функция возвращает период времени до следующего ожидаемого вызова этого обработчика.