Демон блокировки Android (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

Устройство настроено для пользовательской отладки или сборки на английском языке.

ro.llk.sysrq_t

Если свойство имеет eng , значением по умолчанию является не ro.config.low_ram или ro.debuggable . Если true , выгрузить все потоки ( sysrq t ).

ro.llk.enable

Разрешить включение демона live-lock. По умолчанию — false .

llk.enable

Оценивается для англоязычных сборок. По умолчанию — ro.llk.enable .

ro.khungtask.enable

Разрешите включение демона [khungtask] . По умолчанию — false .

khungtask.enable

Оценивается для англоязычных сборок. По умолчанию — ro.khungtask.enable .

ro.llk.mlockall

Включите вызов mlockall() . По умолчанию — false .

ro.khungtask.timeout

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

ro.llk.timeout_ms

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

ro.llk.D.timeout_ms

D максимальный срок. По умолчанию — 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.stack

Проверяет символы стека ядра, которые, если они постоянно присутствуют, могут указывать на то, что подсистема заблокирована. По умолчанию — 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.process

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] ( kernel , [kthreadd] и adbd только для зомби- setsid ). Разделитель амперсанд (&) указывает, что родительский процесс игнорируется только в сочетании с целевым дочерним процессом. Был выбран амперсанд, поскольку он никогда не является частью имени процесса; однако setprop в оболочке требует, чтобы амперсанд был экранирован или заключен в кавычки, хотя файл init rc , где это обычно указывается, не имеет этой проблемы. Родительский или целевой процесс может быть ссылкой comm , cmdline или pid .

ro.llk.blacklist.uid

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

ro.llk.blacklist.process.stack

llkd не отслеживает указанное подмножество процессов на наличие сигнатур стека действующих блокировок. По умолчанию используются имена процессов init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd . Предотвращает нарушение политики безопасности, связанное с процессами, блокирующими 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 в своем основном цикле. Функция возвращает период времени до следующего ожидаемого вызова этого обработчика.