Daemon live-lock Android (llkd)

Android 10 menyertakan daemon live-lock Android (llkd), yang dirancang untuk menangkap dan mengurangi deadlock kernel. Komponen llkd menyediakan implementasi mandiri default, tetapi Anda juga dapat mengintegrasikan kode llkd ke dalam layanan lain, baik sebagai bagian dari loop utama atau sebagai thread terpisah.

Skenario deteksi

llkd memiliki dua skenario deteksi: Status D atau Z persisten, dan tanda tangan stack persisten.

Status D atau Z persisten

Jika thread berada dalam status D (uninterruptible sleep) atau Z (zombie) tanpa progres maju selama lebih dari ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms, llkd akan menghentikan proses (atau proses induk). Jika pemindaian berikutnya menunjukkan bahwa proses yang sama terus ada, llkd akan mengonfirmasi kondisi live lock dan membuat kernel panik sedemikian rupa sehingga memberikan laporan bug paling mendetail untuk kondisi tersebut.

llkd menyertakan watchdog mandiri yang berbunyi jika llkd terkunci; watchdog menggandakan waktu yang diharapkan untuk mengalir melalui mainloop dan pengambilan sampelnya adalah setiap ro.llk_sample_ms.

Tanda tangan stack persisten

Untuk rilis userdebug, llkd dapat mendeteksi live-lock kernel menggunakan pemeriksaan tanda tangan stack persisten. Jika thread dalam status apa pun kecuali Z memiliki simbol kernel ro.llk.stack yang tercantum dan persisten yang dilaporkan lebih lama dari ro.llk.timeout_ms atau ro.llk.stack.timeout_ms, llkd akan menghentikan proses (meskipun ada progres penjadwalan maju). Jika pemindaian berikutnya menunjukkan proses yang sama terus ada, llkd akan mengonfirmasi kondisi live-lock dan membuatkan kernel panik dengan cara yang memberikan laporan bug paling mendetail untuk kondisi tersebut.

Pemeriksaan lldk terus berlanjut saat kondisi kunci aktif ada dan mencari string gabungan symbol+0x atau symbol.cfi+0x dalam file /proc/pid/stack di Linux. Daftar simbol ada di ro.llk.stack dan secara default menggunakan daftar cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable yang dipisahkan koma.

Simbol harus jarang dan cukup singkat sehingga pada sistem biasa, fungsi hanya terlihat sekali dalam sampel selama periode waktu tunggu ro.llk.stack.timeout_ms (sampel terjadi setiap ro.llk.check_ms). Karena tidak adanya perlindungan ABA, ini adalah satu-satunya cara untuk mencegah pemicu palsu. Fungsi simbol harus muncul di bawah fungsi yang memanggil kunci yang dapat bersaing. Jika kunci berada di bawah atau dalam fungsi simbol, simbol akan muncul di semua proses yang terpengaruh, bukan hanya proses yang menyebabkan logo lengkap.

Cakupan

Implementasi default llkd tidak memantau pembuatan init, [kthreadd], atau [kthreadd]. Agar llkd mencakup thread yang dihasilkan [kthreadd]:

  • Driver tidak boleh tetap dalam status D persisten,

ATAU

  • Driver harus memiliki mekanisme untuk memulihkan thread jika dimatikan secara eksternal. Misalnya, gunakan wait_event_interruptible(), bukan wait_event().

Jika salah satu kondisi di atas terpenuhi, daftar tolak llkd dapat disesuaikan untuk mencakup komponen kernel. Pemeriksaan simbol stack melibatkan daftar tolak proses tambahan untuk mencegah pelanggaran sepolicy pada layanan yang memblokir operasi ptrace.

Properti Android

llkd merespons beberapa properti Android (tercantum di bawah).

  • Properti yang bernama prop_ms dalam milidetik.
  • Properti yang menggunakan pemisah koma (,) untuk daftar menggunakan pemisah awal untuk mempertahankan entri default, lalu menambahkan atau mengurangi entri dengan awalan plus (+) dan minus (-) opsional. Untuk daftar ini, string false sinonim dengan daftar kosong, dan entri kosong atau yang tidak ada akan menggunakan nilai default yang ditentukan.

{i>ro.config.low_ram<i}

Perangkat dikonfigurasi dengan memori terbatas.

ro.debuggable

Perangkat dikonfigurasi untuk build userdebug atau eng.

{i>ro.llk.sysrq_t<i}

Jika properti adalah eng, defaultnya bukan ro.config.low_ram atau ro.debuggable. Jika true, dump semua thread (sysrq t).

ro.llk.enable

Mengizinkan daemon live-lock diaktifkan. Default-nya adalah false.

llk.enable

Dievaluasi untuk build eng. Default-nya adalah ro.llk.enable.

ro.khungtask.enable

Mengizinkan daemon [khungtask] diaktifkan. Default-nya adalah false.

khungtask.enable

Dievaluasi untuk build teknis. Default-nya adalah ro.khungtask.enable.

{i>ro.llk.mlockall<i}

Aktifkan panggilan ke mlockall(). Default-nya adalah false.

ro.khungtask.timeout

Batas waktu maksimum [khungtask]. Waktu defaultnya adalah 12 menit.

ro.llk.timeout_ms

Batas waktu maksimum D atau Z. Waktu defaultnya adalah 10 menit. Gandakan nilai ini untuk menetapkan watchdog alarm untuk llkd.

ro.llk.D.timeout_ms

Batas waktu maksimum D. Defaultnya adalah ro.llk.timeout_ms.

ro.llk.Z.timeout_ms

Batas waktu maksimum Z. Defaultnya adalah ro.llk.timeout_ms.

{i>ro.llk.stack.timeout_ms<i}

Memeriksa batas waktu maksimum simbol stack persisten. Defaultnya adalah ro.llk.timeout_ms. Hanya aktif di build userdebug atau eng.

ro.llk.check_ms

Contoh thread untuk D atau Z. Defaultnya adalah dua menit.

ro.llk.stack

Memeriksa simbol stack kernel yang jika terus-menerus ada dapat menunjukkan subsistem terkunci. Defaultnya adalah daftar simbol kernel yang dipisahkan koma cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable. Pemeriksaan tidak melakukan penjadwalan maju ABA kecuali dengan melakukan polling setiap ro.llk_check_ms selama periode ro.llk.stack.timeout_ms, sehingga simbol stack seharusnya sangat jarang dan selintas (simbol sangat tidak mungkin muncul secara terus-menerus di semua sampel stack). Memeriksa kecocokan untuk symbol+0x atau symbol.cfi+0x dalam perluasan stack. Hanya tersedia di build userdebug atau eng; masalah keamanan pada build pengguna menghasilkan hak istimewa terbatas yang mencegah pemeriksaan ini.

{i>ro.llk.blacklist.process<i}

llkd tidak mengamati proses yang ditentukan. Defaultnya adalah 0,1,2 (kernel, init, dan [kthreadd]) ditambah nama proses init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]. Proses dapat berupa referensi comm, cmdline, atau pid. Default otomatis bisa lebih besar dari ukuran properti maksimum saat ini, yaitu 92.

ro.llk.blacklist.parent

llkd tidak memantau proses yang memiliki induk yang ditentukan. Defaultnya adalah 0,2,adbd&[setsid] (kernel, [kthreadd], dan adbd hanya untuk zombie setsid). Pemisah ampersand (&) menentukan bahwa induk hanya diabaikan dalam kombinasi dengan proses turunan target. Ampersand dipilih karena tidak pernah menjadi bagian dari nama proses; namun, setprop dalam shell memerlukan ampersand untuk di-escape atau diapit tanda kutip, meskipun file init rc tempat hal ini biasanya ditentukan tidak memiliki masalah ini. Proses induk atau target dapat berupa referensi comm, cmdline, atau pid.

ro.llk.blacklist.uid

llkd tidak memantau proses yang cocok dengan UID yang ditentukan. Daftar nama atau nomor UIS yang dipisahkan koma. Default-nya kosong atau false.

ro.llk.blacklist.process.stack

llkd tidak memantau subset proses yang ditentukan untuk tanda tangan tumpukan kunci aktif. Defaultnya adalah nama proses init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd. Mencegah pelanggaran sepolicy yang terkait dengan proses yang memblokir ptrace (karena tidak dapat diperiksa). Hanya aktif di build userdebug dan eng. Untuk mengetahui detail tentang jenis build, lihat Building Android.

Masalah arsitektur

  • Properti dibatasi hingga 92 karakter (tetapi, ini diabaikan untuk default yang ditentukan dalam file include/llkd.h di sumber).
  • Daemon [khungtask] bawaan terlalu umum dan menempatkan kode driver yang terlalu lama berada dalam status D. Beralih ke S akan membuat tugas dapat dihentikan (dan dapat diaktifkan kembali oleh pengemudi jika perlu).

Antarmuka library (opsional)

Anda dapat menyertakan llkd ke dalam daemon dengan hak istimewa lain secara opsional menggunakan antarmuka C berikut dari komponen libllkd:

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

Jika nama thread diberikan, thread akan otomatis muncul. Jika tidak, pemanggil harus memanggil llkCheckMilliseconds dalam loop utamanya. Fungsi tersebut menampilkan jangka waktu sebelum panggilan yang diharapkan berikutnya ke pengendali ini.