Google के पॉल टर्नर द्वारा लिखित टिप्पणियों में sgbj द्वारा उल्लिखित लेख बहुत अधिक विस्तार से बताता है, लेकिन मैं इसे एक शॉट दूंगा:
जहां तक मैं इस समय सीमित जानकारी से एक साथ मिल सकता है, एक रेटपोलिन एक वापसी ट्रम्पोलिन है जो एक अनंत लूप का उपयोग करता है जिसे सीपीयू को अप्रत्यक्ष कूद के लक्ष्य पर सट्टा लगाने से रोकने के लिए कभी भी निष्पादित नहीं किया जाता है।
मूल दृष्टिकोण एंडी क्लेन की कर्नेल शाखा में इस मुद्दे को संबोधित करते हुए देखा जा सकता है :
यह __x86.indirect_thunk
कॉल लक्ष्य को लोड करने वाली नई कॉल का परिचय देता है जिसका मेमोरी एड्रेस (जिसे मैं कॉल करूंगा ADDR
) स्टैक के शीर्ष पर संग्रहीत होता है और एक RET
निर्देश का उपयोग करके जंप को निष्पादित करता है । इसके बाद खुद को ठग NOSPEC_JMP / CALL मैक्रो का उपयोग करने के लिए कहा जाता है , जिसका उपयोग कई (यदि सभी नहीं) अप्रत्यक्ष कॉल और जंपर्स को बदलने के लिए किया गया था। मैक्रो बस कॉल लक्ष्य को स्टैक पर रखता है और यदि आवश्यक हो तो रिटर्न पता सही तरीके से सेट करता है (गैर-रैखिक नियंत्रण प्रवाह पर ध्यान दें):
.macro NOSPEC_CALL target
jmp 1221f /* jumps to the end of the macro */
1222:
push \target /* pushes ADDR to the stack */
jmp __x86.indirect_thunk /* executes the indirect jump */
1221:
call 1222b /* pushes the return address to the stack */
.endm
call
अंत में प्लेसमेंट आवश्यक है ताकि जब अप्रत्यक्ष कॉल समाप्त हो NOSPEC_CALL
जाए, तो मैक्रो के उपयोग के पीछे नियंत्रण प्रवाह जारी रहे , इसलिए इसे नियमित रूप से इस्तेमाल किया जा सकता हैcall
ठग खुद को इस प्रकार देखता है:
call retpoline_call_target
2:
lfence /* stop speculation */
jmp 2b
retpoline_call_target:
lea 8(%rsp), %rsp
ret
नियंत्रण प्रवाह यहां थोड़ा भ्रमित कर सकता है, इसलिए मुझे स्पष्ट करें:
call
स्टैक के लिए वर्तमान निर्देश सूचक (लेबल 2) को धक्का देता है।
lea
स्टैक पॉइंटर में 8 जोड़ता है , प्रभावी रूप से सबसे हाल ही में धकेल दिया गया क्वाडवर्ड, जो कि अंतिम रिटर्न एड्रेस है (लेबल 2 में)। इसके बाद, वास्तविक रिटर्न एड्रेस ADDR पर स्टैक पॉइंट्स का शीर्ष फिर से।
ret
कूदता है *ADDR
और कॉल स्टैक की शुरुआत में स्टैक पॉइंटर को रीसेट करता है।
अंत में, यह पूरा व्यवहार व्यावहारिक रूप से सीधे कूदने के बराबर है *ADDR
। हमें जो लाभ मिलता है, वह यह है कि शाखा भविष्यवक्ता का उपयोग रिटर्न स्टेटमेंट्स (रिटर्न स्टैक बफर, आरएसबी) के लिए किया जाता है, जब call
निर्देश निष्पादित करते हैं , तो यह मान लेता है कि संबंधित ret
स्टेटमेंट लेबल 2 पर कूद जाएगा।
लेबल 2 के बाद का हिस्सा वास्तव में कभी निष्पादित नहीं होता है, यह केवल एक अनंत लूप है जो सिद्धांत में निर्देश पाइपलाइन को JMP
निर्देश के साथ भर देगा । का उपयोग करके LFENCE
, PAUSE
या आम तौर पर एक निर्देश के कारण अनुदेश पाइपलाइन स्टाल होने से सीपीयू को इस सट्टा निष्पादन पर किसी भी शक्ति और समय को बर्बाद करने से रोकता है। इसका कारण यह है कि यदि कॉल retpoline_call_target सामान्य रूप से वापस आ जाएगी, तो LFENCE
निष्पादित होने वाला अगला निर्देश होगा। यह भी वही है जो शाखा प्रीडेटर मूल रिटर्न एड्रेस (लेबल 2) के आधार पर भविष्यवाणी करेगा
इंटेल के आर्किटेक्चर मैनुअल से उद्धरण के लिए:
LFENCE से पहले निर्देश LFENCE से पहले मेमोरी से प्राप्त किए जा सकते हैं, लेकिन LFENCE पूरा होने तक वे निष्पादित नहीं करेंगे।
हालांकि ध्यान दें कि विनिर्देश का उल्लेख नहीं है कि LFENCE और PAUSE पाइपलाइन को स्टाल करने का कारण है, इसलिए मैं यहां लाइनों के बीच थोड़ा पढ़ रहा हूं।
अब आपके मूल प्रश्न पर वापस आते हैं: कर्नेल मेमोरी जानकारी प्रकटीकरण दो विचारों के संयोजन के कारण संभव है:
हालांकि सट्टा निष्पादन गलत होने पर साइड-इफेक्ट मुक्त होना चाहिए, सट्टा निष्पादन अभी भी कैश पदानुक्रम को प्रभावित करता है । इसका मतलब है कि जब मेमोरी लोड को सट्टा रूप से निष्पादित किया जाता है, तो यह अभी भी कैश लाइन को बेदखल करने का कारण हो सकता है। कैश पदानुक्रम में इस परिवर्तन की पहचान उसी कैश सेट पर मैप की गई मेमोरी तक पहुंच के समय को सावधानीपूर्वक मापकर की जा सकती है।
आप मनमाने ढंग से मेमोरी के कुछ बिट्स भी लीक कर सकते हैं जब मेमोरी रीड का स्रोत पता खुद कर्नेल मेमोरी से पढ़ा जाता है।
इंटेल सीपीयू के अप्रत्यक्ष शाखा भविष्यवक्ता केवल स्रोत निर्देश के सबसे निचले 12 बिट्स का उपयोग करते हैं, इस प्रकार उपयोगकर्ता नियंत्रित मेमोरी पते के साथ सभी 2 ^ 12 संभावित भविष्यवाणी इतिहास को जहर करना आसान है। ये तब हो सकते हैं, जब कर्नेल के भीतर अप्रत्यक्ष कूद की भविष्यवाणी की जाती है, विशेष रूप से कर्नेल विशेषाधिकारों के साथ निष्पादित किया जाता है। कैश-टाइमिंग साइड-चैनल का उपयोग करके, आप इस प्रकार मनमानी कर्नेल मेमोरी को लीक कर सकते हैं।
अद्यतन: कर्नेल मेलिंग सूची पर , एक चल रही चर्चा है जो मुझे विश्वास दिलाती है कि रेटपोलिन शाखा भविष्यवाणी के मुद्दों को पूरी तरह से कम नहीं करता है, क्योंकि जब रिटर्न स्टैक बफर (आरएसबी) खाली चलता है, तो हाल ही में इंटेल आर्किटेक्चर (स्काइलेक +) वापस गिर जाता है। निशक्त शाखा लक्ष्य बफर (BTB):
शमन रणनीति के रूप में रेटपोलिन रिटर्न के लिए अप्रत्यक्ष शाखाओं को स्वैप करता है, बीटीबी से आने वाली भविष्यवाणियों का उपयोग करने से बचने के लिए, क्योंकि उन्हें एक हमलावर द्वारा जहर दिया जा सकता है। स्काईलेक + के साथ समस्या यह है कि एक आरएसबी अंडरफ्लो एक बीटीबी भविष्यवाणी का उपयोग करने के लिए वापस आती है, जो हमलावर को अटकलों पर नियंत्रण रखने की अनुमति देता है।