
लिनक्स कर्नेल स्पेक्टर-बीटीआई user-space शमन (mitigations) को prctl और seccomp के माध्यम से बायपास करने का तकनीकी विश्लेषण और प्रमाण-अवधारणा, कोड-स्तर स्पष्टीकरण और परीक्षण परिणामों के साथ।
José Oliveira (esoj)
Rodrigo Branco (BSDaemon)
जब Spectre-BTI हमलों की सफलता दर का परीक्षण कर रहे थे, तो हमने कर्नेल API को मिटिगेशन के रूप में उपयोग करने पर एक अजीब पैटर्न का पता लगाया[^1]। हमारे परीक्षणों से पता चला कि Linux कर्नेल syscall के बाद थोड़े समय के लिए प्रक्रिया को उजागर छोड़ते हुए हमले को सही ढंग से मिटिगेट करने में विफल रहता है।
आगे की जांच से पता चला कि कर्नेल syscall के दौरान तुरंत IBPB जारी नहीं करता है। ib_prctl_set[^2] फ़ंक्शन कार्य के लिए Thread Information Flags (TIFs) को अपडेट करता है और __speculation_ctrl_update [^3] फ़ंक्शन पर SPEC_CTRL MSR को अपडेट करता है, लेकिन IBPB केवल अगले शेड्यूल पर जारी किया जाता है, जब TIF बिट्स की जाँच की जाती है। यह पीड़ित को prctl syscall से पहले BTB में पहले से इंजेक्ट किए गए मानों के प्रति संवेदनशील छोड़ता है। व्यवहार केवल कार्य के पुनर्निर्धारण के बाद ठीक किया जाता है। इसके अलावा, कर्नेल प्रवेश (syscall के कारण), डिफ़ॉल्ट परिदृश्यों में IBPB जारी नहीं करता है (यानी, जब कर्नेल retpoline या eIBRS के माध्यम से स्वयं की रक्षा करता है)।
निम्नलिखित का उपयोग करके spectre-BTI हमलों को मिटिगेट करने के लिए prctl निष्पादित करना:
prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0);
कर्नेल 5.15 पर ib_prctl_set[^2] फ़ंक्शन की ओर ले जाता है। जब विकल्प SPEC_DISABLE का उपयोग किया जाता है तो task_set_spec_ib_disable के लिए TIF बिट सेट किया जाता है और task_update_spec_tif को कॉल किया जाता है:
static int ib_prctl_set(struct task_struct *task, unsigned long ctrl)
[...]
case PR_SPEC_FORCE_DISABLE:
/*
* Indirect branch speculation is always allowed when
* mitigation is force disabled.
*/
if (spectre_v2_user_ibpb == SPECTRE_V2_USER_NONE &&
spectre_v2_user_stibp == SPECTRE_V2_USER_NONE)
return -EPERM;
if (!is_spec_ib_user_controlled())
return 0;
task_set_spec_ib_disable(task);
if (ctrl == PR_SPEC_FORCE_DISABLE)
task_set_spec_ib_force_disable(task);
task_update_spec_tif(task);
break;
task_set_spec_ib_disable set_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE); को कॉल करता है और यदि लक्ष्य कार्य वर्तमान है तो यह speculation_ctrl_update_current(); को कॉल करता है:
static void task_update_spec_tif(struct task_struct *tsk)
{
/* Force the update of the real TIF bits */
set_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE);
/*
* Immediately update the speculation control MSRs for the current
* task, but for a non-current task delay setting the CPU
* mitigation until it is scheduled next.
*
* This can only happen for SECCOMP mitigation. For PRCTL it's
* always the current task.
*/
if (tsk == current)
speculation_ctrl_update_current();
}
speculation_ctrl_update_current speculation_ctrl_update रैपर के बाद tifp = ~tifp के साथ __speculation_ctrl_update निष्पादित करता है, यहाँ STIBP सेट करने के लिए wrmsr का अद्यतन निष्पादित किया जाता है लेकिन कोई IBPB जारी नहीं किया जाता है:
static __always_inline void __speculation_ctrl_update(unsigned long tifp,
unsigned long tifn)
{
unsigned long tif_diff = tifp ^ tifn;
u64 msr = x86_spec_ctrl_base;
bool updmsr = false;
lockdep_assert_irqs_disabled();
/* Handle change of TIF_SSBD depending on the mitigation method. */
if (static_cpu_has(X86_FEATURE_VIRT_SSBD)) {
if (tif_diff & _TIF_SSBD)
amd_set_ssb_virt_state(tifn);
} else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD)) {
if (tif_diff & _TIF_SSBD)
amd_set_core_ssb_state(tifn);
} else if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) ||
static_cpu_has(X86_FEATURE_AMD_SSBD)) {
updmsr |= !!(tif_diff & _TIF_SSBD);
msr |= ssbd_tif_to_spec_ctrl(tifn);
}
/* Only evaluate TIF_SPEC_IB if conditional STIBP is enabled. */
if (IS_ENABLED(CONFIG_SMP) &&
static_branch_unlikely(&switch_to_cond_stibp)) {
updmsr |= !!(tif_diff & _TIF_SPEC_IB);
msr |= stibp_tif_to_spec_ctrl(tifn);
}
if (updmsr)
wrmsrl(MSR_IA32_SPEC_CTRL, msr);
}
seccomp syscall भी ib_prctl_set[^2] का उपयोग मिटिगेशन के रूप में करता है, arch_seccomp_spec_mitigate[^4] के अंदर, इसलिए seccomp के साथ भी वही परिणाम अपेक्षित है।
जबकि कोड विश्लेषण के माध्यम से हमें यकीन है कि शोषण के लिए विंडो मौजूद है, यह स्पष्ट नहीं था कि यह पीड़ित को रहस्य लोड करने और हमलावर को उन्हें लीक करने के लिए पर्याप्त बड़ी है या नहीं (क्योंकि रहस्य prctl कॉल जारी होने तक पीड़ित के पता स्थान में नहीं होने की उम्मीद है)। परीक्षण एक बेयर मेटल मशीन पर हार्डवेयर मिटिगेशन के समर्थन के साथ, ubuntu 22.04.1 LTS स्थापित के साथ निष्पादित किए गए थे:
Kernel is Linux 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64
CPU is Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
* Hardware support (CPU microcode) for mitigation techniques
* Indirect Branch Restricted Speculation (IBRS)
* SPEC_CTRL MSR is available: YES
* CPU indicates IBRS capability: YES (SPEC_CTRL feature bit)
* Indirect Branch Prediction Barrier (IBPB)
* CPU indicates IBPB capability: YES (SPEC_CTRL feature bit)
* Single Thread Indirect Branch Predictors (STIBP)
* SPEC_CTRL MSR is available: YES
* CPU indicates STIBP capability: YES (Intel STIBP feature bit)
* Speculative Store Bypass Disable (SSBD)
परीक्षण कोड में एक ही लॉजिक कोर पर निष्पादित होने वाली दो प्रक्रियाएँ होती हैं। हमलावर लगातार BTB को पीड़ित प्रक्रिया पर मौजूद एक spectre गैजेट के पते से जहर देता है। पीड़ित प्रक्रिया यह जाँच करके गलत अनुमान दर को मापती है कि क्या एक परीक्षण चर को spectre गैजेट फ़ंक्शन द्वारा एक्सेस किया गया था। यह आमतौर पर निम्नलिखित आउटपुट देता है:
esoj@oxigenio:~/CPU_exploits/prctlbleed$ ./attacker 0x55555554123 0x55555555345 0 &
esoj@oxigenio:~/CPU_exploits/prctlbleed$ ./victim-PRCTL 0x55555554123 0x55555555345 0
Rate: 941/1000
Rate: 1000/1000
Rate: 999/1000
Rate: 1000/1000
Rate: 1000/1000
Rate: 997/1000
Rate: 994/1000
Rate: 996/1000
Rate: 998/1000
Rate: 993/1000
Total misspredict rate: 9918/10000 (99.18 %)
फिर, हमले को मिटिगेट करने के लिए PRCTL का उपयोग किया जाता है। मिटिगेशन को प्रोग्राम की शुरुआत में prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0); जोड़कर सक्षम किया जा सकता है। इससे spectre-BTI हमले को मिटिगेट करने की उम्मीद है:
PRCTL GET value 0x9
Rate: 0/1000
Rate: 0/1000
Rate: 0/1000
Rate: 0/1000
Rate: 0/1000
Rate: 0/1000
Rate: 0/1000
Rate: 0/1000
Rate: 0/1000
Rate: 0/1000
Total misspredict rate: 0/10000 (0.00 %)
हालांकि, कुछ परीक्षणों ने एक अलग परिणाम दिखाया:
Rate: 50510/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Total misspredict rate: 50510/1000000 (5.05 %)
और 'nice' (प्राथमिकता) बदलने से गलत अनुमान दर प्रभावित होती प्रतीत होती है:
esoj@oxigenio:~/CPU_exploits/prctlbleed$ sudo nice -n -19 ./victim-PRCTL 0x55555554123 0x55555555345 0
Rate: 99994/100000
Rate: 7716/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Total misspredict rate: 107710/1000000 (10.77 %)
esoj@oxigenio:~/CPU_exploits/prctlbleed$ sudo nice -n 19 ./victim-PRCTL 0x55555554123 0x55555555345 0
Rate: 16715/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Total misspredict rate: 16715/1000000 (1.67 %)