Daemon Android Live-LocK (llkd)

Android 10 bao gồm Android Live-LocK Daemon ( llkd ), được thiết kế để phát hiện và giảm thiểu tình trạng bế tắc trong kernel. Thành phần llkd cung cấp cách triển khai độc lập mặc định, nhưng bạn có thể tích hợp mã llkd vào một dịch vụ khác theo cách khác, như một phần của vòng lặp chính hoặc dưới dạng một luồng riêng biệt.

Kịch bản phát hiện

llkd có hai tình huống phát hiện: Trạng thái D hoặc Z liên tục và chữ ký ngăn xếp liên tục.

Trạng thái D hoặc Z liên tục

Nếu một luồng ở trạng thái D (ngủ liên tục) hoặc Z (zombie) không có tiến trình chuyển tiếp lâu hơn ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms , thì llkd sẽ giết tiến trình (hoặc tiến trình gốc) ). Nếu lần quét tiếp theo cho thấy quy trình tương tự vẫn tiếp tục tồn tại, thì llkd sẽ xác nhận tình trạng khóa trực tiếp và xử lý kernel theo cách cung cấp báo cáo lỗi chi tiết nhất cho tình trạng đó.

llkd bao gồm một cơ quan giám sát tự động cảnh báo nếu llkd bị khóa; cơ quan giám sát tăng gấp đôi thời gian dự kiến ​​để truyền qua vòng lặp chính và lấy mẫu là mỗi ro.llk_sample_ms .

Chữ ký ngăn xếp liên tục

Đối với các bản phát hành userdebug, llkd có thể phát hiện các khóa trực tiếp của kernel bằng cách sử dụng tính năng kiểm tra chữ ký ngăn xếp liên tục. Nếu một luồng ở bất kỳ trạng thái nào ngoại trừ Z có biểu tượng hạt nhân ro.llk.stack được liệt kê liên tục được báo cáo lâu hơn ro.llk.timeout_ms hoặc ro.llk.stack.timeout_ms , thì llkd sẽ giết tiến trình (ngay cả khi có chuyển tiếp tiến độ lập kế hoạch). Nếu lần quét tiếp theo cho thấy quy trình tương tự vẫn tiếp tục tồn tại, thì llkd sẽ xác nhận tình trạng khóa trực tiếp và xử lý kernel theo cách cung cấp báo cáo lỗi chi tiết nhất cho tình trạng đó.

Quá trình kiểm tra lldk vẫn tiếp tục diễn ra khi điều kiện khóa trực tiếp tồn tại và tìm kiếm các chuỗi được soạn thảo " symbol+0x" hoặc " symbol.cfi+0x" trong tệp /proc/pid/stack trên Linux. Danh sách các ký hiệu nằm trong ro.llk.stack và mặc định là danh sách được phân tách bằng dấu phẩy của " cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable ".

Các ký hiệu phải hiếm và tồn tại trong thời gian ngắn đủ để trên một hệ thống thông thường, hàm này chỉ được nhìn thấy một lần trong một mẫu trong khoảng thời gian chờ ro.llk.stack.timeout_ms (các mẫu xuất hiện mỗi ro.llk.check_ms ). Do thiếu sự bảo vệ ABA, đây là cách duy nhất để ngăn chặn kích hoạt sai. Hàm ký hiệu phải xuất hiện bên dưới hàm gọi ổ khóa có thể tranh chấp. Nếu khóa ở bên dưới hoặc trong chức năng biểu tượng, biểu tượng sẽ xuất hiện trong tất cả các quy trình bị ảnh hưởng chứ không chỉ quy trình gây ra tình trạng khóa.

Phủ sóng

Việc triển khai mặc định của llkd không giám sát việc sinh sản init , [kthreadd] hoặc [kthreadd] . Để llkd bao gồm các chủ đề được tạo ra [kthreadd] :

  • Trình điều khiển không được duy trì trạng thái D liên tục,

HOẶC

  • Trình điều khiển phải có cơ chế để khôi phục luồng nếu nó bị tắt từ bên ngoài. Ví dụ: sử dụng wait_event_interruptible() thay vì wait_event() .

Nếu đáp ứng một trong các điều kiện trên, danh sách đen llkd có thể được điều chỉnh để bao gồm các thành phần kernel. Kiểm tra biểu tượng ngăn xếp bao gồm một danh sách đen quy trình bổ sung để ngăn chặn các vi phạm chính sách trên các dịch vụ chặn hoạt động ptrace .

Thuộc tính Android

llkd phản hồi một số thuộc tính Android (được liệt kê bên dưới).

  • Các thuộc tính có tên prop_ms được tính bằng mili giây.
  • Các thuộc tính sử dụng dấu phân cách bằng dấu phẩy (,) cho danh sách sử dụng dấu phân cách ở đầu để giữ nguyên mục nhập mặc định, sau đó cộng hoặc trừ các mục nhập có tiền tố cộng (+) và trừ (-) tùy chọn tương ứng. Đối với những danh sách này, chuỗi "false" đồng nghĩa với danh sách trống và các mục trống hoặc bị thiếu sẽ sử dụng giá trị mặc định được chỉ định.

ro.config.low_ram

Thiết bị được cấu hình với bộ nhớ hạn chế.

ro.debuggable

Thiết bị được định cấu hình cho userdebug hoặc eng build.

ro.llk.sysrq_t

Nếu thuộc tính là "eng", giá trị mặc định không phải là ro.config.low_ram hoặc ro.debuggable . Nếu đúng, hãy kết xuất tất cả các chủ đề ( sysrq t ).

ro.llk.enable

Cho phép bật daemon khóa trực tiếp. Mặc định là sai.

llk.enable

Được đánh giá cho các bản dựng eng. Mặc định là ro.llk.enable .

ro.khungtask.enable

Cho phép kích hoạt daemon [khungtask] . Mặc định là sai.

khungtask.enable

Được đánh giá cho các bản dựng eng. Mặc định là ro.khungtask.enable .

ro.llk.mlockall

Cho phép gọi tới mlockall() . Mặc định là sai.

ro.khungtask.timeout

[khungtask] giới hạn thời gian tối đa. Mặc định là 12 phút.

ro.llk.timeout_ms

D hoặc Z giới hạn thời gian tối đa. Mặc định là 10 phút. Nhân đôi giá trị này để đặt cơ quan giám sát cảnh báo cho llkd .

ro.llk.D.timeout_ms

D giới hạn thời gian tối đa. Mặc định là ro.llk.timeout_ms .

ro.llk.Z.timeout_ms

Giới hạn thời gian tối đa Z. Mặc định là ro.llk.timeout_ms .

ro.llk.stack.timeout_ms

Kiểm tra giới hạn thời gian tối đa của các biểu tượng ngăn xếp liên tục. Mặc định là ro.llk.timeout_ms . Chỉ hoạt động trên bản dựng userdebug hoặc eng .

ro.llk.check_ms

Mẫu chủ đề cho D hoặc Z. Mặc định là hai phút.

ro.llk.stack

Kiểm tra các ký hiệu ngăn xếp hạt nhân mà nếu xuất hiện liên tục có thể cho biết hệ thống con đã bị khóa. Mặc định là cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable danh sách các ký hiệu hạt nhân được phân tách bằng dấu phẩy. Việc kiểm tra không thực hiện chuyển tiếp lập lịch ABA ngoại trừ bằng cách thăm dò mọi ro.llk_check_ms trong khoảng thời gian ro.llk.stack.timeout_ms , vì vậy các biểu tượng ngăn xếp phải cực kỳ hiếm và thoáng qua (rất khó có khả năng một biểu tượng xuất hiện liên tục trong tất cả mẫu của ngăn xếp). Kiểm tra sự trùng khớp cho " symbol+0x" hoặc " symbol.cfi+0x" trong mở rộng ngăn xếp. Chỉ khả dụng trên các bản dựng userdebug hoặc eng ; những lo ngại về bảo mật trên các bản dựng của người dùng dẫn đến các đặc quyền hạn chế ngăn chặn việc kiểm tra này.

ro.llk.blacklist.process

llkd không xem các quy trình được chỉ định. Mặc định là 0,1,2 ( kernel , init[kthreadd] ) cộng với tên tiến trình init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1] . Một tiến trình có thể là một tham chiếu comm , cmdline hoặc pid . Giá trị mặc định tự động có thể lớn hơn kích thước thuộc tính tối đa hiện tại là 92.

ro.llk.blacklist.parent

llkd không xem các tiến trình có (các) tiến trình gốc được chỉ định. Mặc định là 0,2,adbd&[setsid] ( kernel , [kthreadd]adbd chỉ dành cho zombie setsid ). Dấu phân cách ký hiệu (&) chỉ định rằng tiến trình cha chỉ bị bỏ qua khi kết hợp với tiến trình con đích. Ampersand được chọn vì nó không bao giờ là một phần của tên quy trình; tuy nhiên, một setprop trong shell yêu cầu ký hiệu và phải được thoát hoặc trích dẫn, mặc dù tệp init rc nơi điều này thường được chỉ định không gặp phải vấn đề này. Quá trình cha hoặc quá trình đích có thể là tham chiếu comm , cmdline hoặc pid .

ro.llk.blacklist.uid

llkd không xem các quy trình khớp với (các) uid được chỉ định. Danh sách số hoặc tên uid được phân tách bằng dấu phẩy. Mặc định là trống hoặc sai.

ro.llk.blacklist.process.stack

llkd không giám sát tập hợp con quy trình được chỉ định cho chữ ký ngăn xếp khóa trực tiếp. Mặc định là tên quy trình init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd . Ngăn chặn vi phạm chính sách liên quan đến các quy trình chặn ptrace (vì không thể kiểm tra những quy trình này). Chỉ hoạt động trên bản dựng userdebug và eng . Để biết chi tiết về các loại bản dựng, hãy tham khảo Xây dựng Android .

Mối quan tâm về kiến ​​trúc

  • Các thuộc tính được giới hạn ở 92 ký tự (tuy nhiên, điều này bị bỏ qua đối với các giá trị mặc định được xác định trong tệp include/llkd.h trong nguồn).
  • Trình nền [khungtask] tích hợp quá chung chung và vấp phải mã trình điều khiển nằm ở trạng thái D quá nhiều. Việc chuyển sang S sẽ làm cho (các) tác vụ có thể bị hủy (và trình điều khiển có thể phục hồi lại nếu cần).

Giao diện thư viện (tùy chọn)

Bạn có thể tùy ý kết hợp llkd vào một daemon đặc quyền khác bằng giao diện C sau từ thành phần libllkd :

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

Nếu tên luồng được cung cấp, thì luồng sẽ tự động xuất hiện, nếu không thì người gọi phải gọi llkCheckMilliseconds trong vòng lặp chính của nó. Hàm trả về khoảng thời gian trước cuộc gọi dự kiến ​​tiếp theo tới trình xử lý này.