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
को कॉल करना होगा. यह फ़ंक्शन, इस हैंडलर को अगले कॉल मिलने से पहले का समय दिखाता है.