Android लाइव-लॉक डीमन (llkd)

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 जांच लगातार बनी रहती है. साथ ही, 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 में लिए जाते हैं). एबीए सुरक्षा न होने की वजह से, गलत ट्रिगर को रोकने का यही एक तरीका है. सिंबल फ़ंक्शन, उस लॉक को कॉल करने वाले फ़ंक्शन के नीचे दिखना चाहिए जो विरोध कर सकता है. अगर लॉक नीचे मौजूद है या सिंबल फ़ंक्शन में है, तो सिंबल उन सभी प्रोसेस में दिखता है जिन पर समस्या का असर हुआ है. यह सिर्फ़ वह प्रोसेस नहीं है जिसकी वजह से लॉकअप हुआ था.

कवरेज

llkd को डिफ़ॉल्ट रूप से लागू करने पर, init, [kthreadd] या [kthreadd] स्पॉन की निगरानी नहीं की जाती. [kthreadd]-स्पॉन्ड धागे को कवर करने के लिए, llkd के लिए:

  • ड्राइवर हमेशा D मोड में नहीं होने चाहिए,

या

  • अगर थ्रेड को बाहर से बंद किया जाता है, तो ड्राइवर के पास उसे वापस लाने के तरीके होने चाहिए. उदाहरण के लिए, wait_event() के बजाय wait_event_interruptible() का इस्तेमाल करें.

अगर ऊपर दी गई कोई भी शर्त पूरी होती है, तो llkd की पाबंदी वाली सूची में बदलाव करके, इसे कर्नेल कॉम्पोनेंट के लिए इस्तेमाल किया जा सकता है. स्टैक सिंबल की जांच करने के लिए, ptrace ऑपरेशन को ब्लॉक करने वाली सेवाओं पर सुरक्षा नीति के उल्लंघनों को रोकने के लिए, एक और प्रोसेस की ज़रूरत होती है.

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 के लिए थ्रेड के सैंपल. डिफ़ॉल्ट रूप से, यह समय दो मिनट पर सेट होता है.

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 को पोल करने के अलावा, एबीए को शेड्यूल नहीं करती. इसलिए, स्टैक सिंबल बहुत कम और थोड़े समय के लिए होने चाहिए. ऐसा बहुत कम होता है कि कोई सिंबल, स्टैक के सभी सैंपल में लगातार दिखे. यह जांच करता है कि स्टैक एक्सपैंशन में, 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, बताए गए यूआईडी से मैच होने वाली प्रोसेस को नहीं देखता. यूआईएस नंबर या नामों की कॉमा लगाकर अलग की गई सूची. डिफ़ॉल्ट रूप से, यह खाली या 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 पर स्विच करने से, टास्क खत्म हो जाएंगे (और अगर ज़रूरी हो, तो ड्राइवर उन्हें दोबारा इस्तेमाल कर सकते हैं).

लाइब्रेरी इंटरफ़ेस (ज़रूरी नहीं)

आपके पास llkd को किसी अन्य खास डेमन में शामिल करने का विकल्प है. इसके लिए, libllkd कॉम्पोनेंट के इस C इंटरफ़ेस का इस्तेमाल करें:

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

अगर थ्रेड का नाम दिया गया है, तो थ्रेड अपने-आप बन जाती है. अगर ऐसा नहीं है, तो कॉलर को अपने मुख्य लूप में llkCheckMilliseconds को कॉल करना होगा. यह फ़ंक्शन, इस हैंडलर को अगले कॉल मिलने से पहले का समय दिखाता है.