Android Live-LocK 데몬(llkd)

Android 10에는 커널 교착 상태를 포착하고 완화하도록 설계된 Android Live-LocK 데몬(llkd)이 포함되어 있습니다. llkd 구성요소에서 기본 독립형 구현을 제공하지만 llkd 코드를 기본 루프의 일부 또는 별도의 스레드로 다른 서비스에 통합할 수도 있습니다.

감지 시나리오

llkd에는 영구 D 또는 Z 상태와 영구 스택 서명, 두 가지 감지 시나리오가 있습니다.

영구 D 또는 Z 상태

스레드가 ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms보다 더 오랫동안 진행되지 않고 D(무중단 절전 모드) 또는 Z(좀비) 상태인 경우 llkd에서 프로세스(또는 상위 프로세스)를 종료합니다. 후속 스캔 결과 동일한 프로세스가 계속 존재하는 것으로 확인되면 llkd에서 라이브 잠금 조건을 확인하고 이 조건에 관해 가장 자세한 버그 신고를 제공하는 방식으로 커널을 패닉 상태로 만듭니다.

llkd에는 llkd가 잠길 경우 경고를 보내는 자체 워치독이 포함되어 있습니다. 워치독은 메인 루프를 통과하는 데 걸리는 예상 시간의 두 배이며 샘플링은 ro.llk_sample_ms마다 이루어집니다.

영구 스택 서명

userdebug 출시의 경우 llkd에서 영구 스택 서명 검사를 사용하여 커널 라이브 잠금을 감지할 수 있습니다. Z를 제외한 모든 상태의 스레드에 ro.llk.timeout_ms 또는 ro.llk.stack.timeout_ms보다 더 오랫동안 보고된 영구 ro.llk.stack 커널 기호가 있는 경우 (순행 스케쥴링에 따라 진행되고 있더라도) llkd에서 프로세스를 종료합니다. 후속 스캔 결과 동일한 프로세스가 계속 존재하는 것으로 확인되면 llkd에서 라이브 잠금 조건을 확인하고 이 조건에 관해 가장 자세한 버그 신고를 제공하는 방식으로 커널을 패닉 상태로 만듭니다.

라이브 잠금 조건이 존재하면 lldk 검사가 계속되며 Linux의 /proc/pid/stack 파일에서 작성된 문자열 " symbol+0x" 또는 " symbol.cfi+0x"를 찾습니다. 기호 목록은 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() 대신 wait_event_interruptible()를 사용합니다.

위 조건 중 하나가 충족되면 커널 구성요소를 포함하도록 llkd 블랙리스트를 조정할 수 있습니다. 스택 기호 검사에는 서비스에서 ptrace 작업을 차단하는 sepolicy 위반을 방지하기 위한 추가 프로세스 블랙리스트가 포함됩니다.

Android 속성

llkd는 아래에 나열된 Android의 여러 가지 속성에 응답합니다.

  • 이름이 prop_ms인 속성은 밀리초 단위입니다.
  • 목록에 쉼표(,) 구분자를 사용하는 속성에서는 선행 구분자를 사용하여 기본 항목을 보존한 다음 각각 선택적 더하기(+) 및 빼기(-) 접두어를 사용하여 항목을 추가하거나 삭제합니다. 이러한 목록에서 'false' 문자열은 빈 목록를 의미하며, 비어 있거나 누락된 항목은 지정된 기본값으로 설정됩니다.

ro.config.low_ram

기기가 제한된 메모리를 사용하여 구성되어 있습니다.

ro.debuggable

기기가 userdebug 또는 eng 빌드용으로 구성되어 있습니다.

ro.llk.sysrq_t

속성이 'eng'인 경우 기본값은 ro.config.low_ram 또는 ro.debuggable이 아닙니다. true이면 모든 스레드를 덤프합니다(sysrq t).

ro.llk.enable

라이브 잠금 데몬을 사용 설정할 수 있도록 허용합니다. 기본값은 false입니다.

llk.enable

eng 빌드에 대해 평가됩니다. 기본값은 ro.llk.enable입니다.

ro.khungtask.enable

[khungtask] 데몬을 사용 설정할 수 있도록 허용합니다. 기본값은 false입니다.

khungtask.enable

eng 빌드에 대해 평가됩니다. 기본값은 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의 스레드 샘플입니다. 기본값은 2분입니다.

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를 하지 않으므로 스택 기호는 극히 드물고 순식간에 사라집니다(기호가 스택의 모든 샘플에 지속적으로 표시될 가능성은 매우 낮습니다). 스택 확장에서 " 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](좀비 setsid의 경우에만 kernel, [kthreadd], adbd)입니다. 앰퍼샌드(&) 구분자는 상위 요소가 타겟 하위 프로세스와 조합되는 경우에만 무시된다는 것을 지정합니다. 앰퍼샌드는 프로세스 이름의 일부가 아니기 때문에 선택되었습니다. 그러나 셸의 setprop에서는 앰퍼샌드를 이스케이프 처리하거나 따옴표로 묶어야 합니다. 하지만 일반적으로 이 항목이 지정되는 init rc 파일에는 이 문제가 없습니다. 상위 또는 타겟 프로세스는 comm, cmdline 또는 pid 참조일 수 있습니다.

ro.llk.blacklist.uid

llkd는 지정된 uid와 일치하는 프로세스를 감시하지 않습니다. 쉼표로 구분된 uid 번호 또는 이름 목록입니다. 기본값은 비어 있거나 false입니다.

ro.llk.blacklist.process.stack

llkd는 라이브 잠금 스택 서명의 지정된 프로세스 하위 집합을 모니터링하지 않습니다. 기본값은 프로세스 이름 init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd입니다. ptrace를 차단하는 프로세스와 관련된 sepolicy 위반을 방지합니다(이 프로세스는 확인할 수 없기 때문입니다). userdebug 및 eng 빌드에서만 활성화됩니다. 빌드 유형에 관한 자세한 내용은 Android 빌드를 참조하세요.

아키텍처 문제

  • 속성은 영문 92자로 제한됩니다(단, 소스의 include/llkd.h 파일에 정의된 기본값의 경우 무시됩니다).
  • 기본으로 제공되는 [khungtask] 데몬은 너무 일반적이며 지나치게 D 상태로 있는 드라이버 코드에서 자주 멈춥니다. S로 전환하면 작업을 종료할 수 있습니다(필요한 경우 드라이버에서 복원할 수 있습니다).

라이브러리 인터페이스(선택사항)

선택사항으로 libllkd 구성요소에서 다음 C 인터페이스를 사용하여 llkd를 권한이 있는 다른 데몬에 통합할 수도 있습니다.

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

스레드 이름이 제공되면 스레드가 자동으로 생성됩니다. 그렇지 않으면 호출자가 기본 루프에서 llkCheckMilliseconds를 호출해야 합니다. 함수는 이 핸들러를 다음에 호출할 때까지 예상되는 기간을 반환합니다.