В состав Android 10 входит демон живой блокировки Android ( 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
в своем основном цикле. Функция возвращает период времени до следующего ожидаемого вызова этого обработчика.