Trình nền khoá trực tiếp của Android (llkd)

Android 10 có trình nền khoá trực tiếp (llkd) của Android, được thiết kế để phát hiện và giảm thiểu các tắc nghẽn trong hạt nhân. Thành phần llkd cung cấp phương thức triển khai độc lập mặc định, nhưng bạn cũng có thể tích hợp mã llkd vào một dịch vụ khác, dưới dạng 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.

Tình huống phát hiện

llkd có hai trường hợp 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 ổn định

Nếu một luồng ở trạng thái D (ngủ không gián đoạn) hoặc Z (thây ma) không có tiến trình chuyển tiếp trong thời gian dài hơn ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms, thì llkd sẽ dừng quy trình (hoặc quy trình mẹ). Nếu quá trình quét tiếp theo cho thấy cùng một quy trình vẫn tiếp tục tồn tại, thì llkd sẽ xác nhận tình trạng khoá trực tiếp và làm kernel hoảng loạn theo cách cung cấp báo cáo lỗi chi tiết nhất cho tình trạng này.

llkd bao gồm một trình giám sát tự động sẽ báo động nếu llkd bị khoá; trình giám sát gấp đôi thời gian dự kiến để luân chuyể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 ổn định

Đối với các bản phát hành userdebug, llkd có thể phát hiện các khoá trực tiếp của hạt nhân bằng cách kiểm tra chữ ký ngăn xếp ổn định. 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ẽ chấm dứt quá trình (ngay cả khi có tiến trình lên lịch trước). Nếu quá trình quét tiếp theo cho thấy cùng một quy trình vẫn tiếp tục tồn tại, thì llkd sẽ xác nhận tình trạng khoá trực tiếp và làm kernel hoảng loạn theo cách cung cấp báo cáo lỗi chi tiết nhất cho tình trạng này.

Quá trình kiểm tra lldk sẽ liên tục diễn ra khi có điều kiện khoá trực tiếp và tìm kiếm các chuỗi đã kết hợp symbol+0x hoặc symbol.cfi+0x trong tệp /proc/pid/stack trên Linux. Danh sách biểu tượng 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 đủ ngắn để trên một hệ thống thông thường, hàm chỉ xuất hiện một lần trong một mẫu trong khoảng thời gian chờ của ro.llk.stack.timeout_ms (các mẫu xảy ra mỗi ro.llk.check_ms). Do thiếu biện pháp bảo vệ ABA, đây là cách duy nhất để ngăn chặn một trình kích hoạt giả. Hàm biểu tượng phải xuất hiện bên dưới hàm gọi khoá có thể tranh chấp. Nếu khoá nằm bên dưới hoặc trong hàm biểu tượng, thì 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 sự cố khoá.

Độ bao phủ

Phương thức triển khai mặc định của llkd không giám sát các lần tạo init, [kthreadd] hoặc [kthreadd]. Để llkd bao gồm các luồng do [kthreadd] tạo ra:

  • Trình điều khiển không được ở 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 luồng đó bị loại bỏ 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, bạn có thể điều chỉnh danh sách từ chối llkd để bao gồm các thành phần hạt nhân. Việc kiểm tra biểu tượng ngăn xếp liên quan đến một danh sách từ chối quy trình bổ sung để ngăn chặn các lỗi vi phạm chính sách bảo mật trên các dịch vụ chặn thao tác ptrace.

Thuộc tính Android

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

  • 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 dấu phẩy (,) cho danh sách sử dụng dấu phân cách ở đầu để giữ lại mục nhập mặc định, sau đó thêm hoặc trừ các mục nhập bằng tiền tố dấu cộng (+) và dấu trừ (-) không bắt buộc tương ứng. Đối với các 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ẽ dùng giá trị mặc định được chỉ định.

ro.config.low_ram

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

ro.debuggable

Thiết bị được định cấu hình cho bản dựng userdebug hoặc eng.

ro.llk.sysrq_t

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

ro.llk.enable

Cho phép bật trình nền khoá trực tiếp. Giá trị mặc định là false.

llk.enable

Được đánh giá theo bản dựng eng. Giá trị mặc định là ro.llk.enable.

ro.khungtask.enable

Cho phép bật trình nền [khungtask]. Mặc định là false.

khungtask.enable

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

ro.llk.mlockall

Bật lệnh gọi đến mlockall(). Giá trị mặc định là false.

ro.khungtask.timeout

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

ro.llk.timeout_ms

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

ro.llk.D.timeout_ms

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

ro.llk.Z.timeout_ms

Giới hạn thời gian tối đa củ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 biểu tượng ngăn xếp cố định. Giá trị 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 luồng cho D hoặc Z. Giá trị mặc định là 2 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 một hệ thống con bị khoá. Mặc định là danh sách các ký hiệu hạt nhân được phân tách bằng dấu phẩy cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable. Quy trình kiểm tra không lên lịch trước ABA ngoại trừ việc thăm dò ý kiến 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 sẽ rất hiếm và thoáng qua (rất khó để một biểu tượng xuất hiện liên tục trong tất cả các mẫu của ngăn xếp). Kiểm tra xem có khớp với symbol+0x hoặc symbol.cfi+0x trong quá trình mở rộng ngăn xếp hay không. Chỉ có trên các bản dựng userdebug hoặc bản dựng eng; các vấn đề bảo mật trên các bản dựng người dùng dẫn đến các đặc quyền bị hạn chế khiến quy trình kiểm tra này không thực hiện được.

ro.llk.blacklist.process

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

ro.llk.black.parent

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

ro.llk.blacklist.uid

llkd không theo dõi các quy trình khớp với UID được chỉ định. Danh sách số hoặc tên UIS được phân tách bằng dấu phẩy. Giá trị mặc định là trống hoặc false.

ro.llk.black.process.stack

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

Mối lo ngại về cấu trúc

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

Giao diện thư viện (không bắt buộc)

Bạn có thể tuỳ ý kết hợp llkd vào một trình nền đặc quyền khác bằng cách sử dụng giao diện C sau đây 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 bạn cung cấp tên luồng, thì luồng sẽ tự động tạo, nếu không, phương thức gọi phải gọi llkCheckMilliseconds trong vòng lặp chính. Hàm này trả về khoảng thời gian trước khi lệnh gọi dự kiến tiếp theo đến trình xử lý này.