
PoC for CVE-2026-53360: KVM SEV-SNP पेज स्टेट चेंज (PSC) हैंडलिंग में अतिथि-प्रेरित हीप आउट-ऑफ-बाउंड्स रीड/राइट।
KVM के SEV-SNP पेज स्टेट चेंज (PSC) हैंडलिंग में हीप आउट-ऑफ-बाउंड्स रीड और राइट का प्रूफ ऑफ कॉन्सेप्ट। एक दुर्भावनापूर्ण SEV-SNP अतिथि होस्ट कर्नेल को उसके स्लैब आवंटन के अंत से एक PSC एंट्री ऐरे को चलाने पर मजबूर करता है। इससे पड़ोसी kmalloc-cg-32 ऑब्जेक्ट्स का लेआउट लीक हो जाता है और उनमें एक नियंत्रित छोटा मान लिखा जाता है, और अतिथि जितनी बार चाहे इसे दोहरा सकता है।
पूर्ण विवरण: https://cyberstan.co.uk/sev-snp-oob/
| CVE | CVE-2026-53360 |
| घटक | KVM SNP होस्ट समर्थन, arch/x86/kvm/svm/sev.c |
| प्रस्तुत | 9b54e248d264 (पहला KVM SNP PSC हैंडलिंग, मई 2024, ~v6.10) |
| सुधारित | db3f219 (मेनलाइन, मई 2026, Cc: stable), टैग Fixes: 4af663c |
| रिपोर्ट की गई | [email protected], 8 अप्रैल 2026 |
| प्रभावित | केवल SEV-SNP होस्ट पथ। KVM सादे SEV-ES अतिथियों के लिए PSC सक्षम नहीं करता। |
कोई भी SEV-SNP अतिथि होस्ट कर्नेल के हीप को दूषित कर सकता है और एक गलत PSC अनुरोध भेजकर उसके लेआउट के बारे में जानकारी वापस पढ़ सकता है। यह अतिथि से होस्ट दिशा है: SEV-SNP अतिथि को एक अविश्वसनीय होस्ट से बचाने के लिए बनाया गया है, लेकिन होस्ट को अभी भी एक दुर्भावनापूर्ण अतिथि के खिलाफ अपनी रक्षा करनी होती है, और यह हैंडलर ऐसा नहीं करता।
इसके लिए वास्तविक SEV-SNP हार्डवेयर चाहिए। इसे Intel पर पुन: उत्पन्न नहीं किया जा सकता, और नेस्टेड वर्ट आपको SNP अतिथि नहीं देगा।
हार्डवेयर:
*.metal, Hetzner AX, और इसी प्रकार)।होस्ट कर्नेल:
CONFIG_KASAN=y
CONFIG_KASAN_GENERIC=y
CONFIG_KVM=y
CONFIG_KVM_AMD=y
CONFIG_KVM_AMD_SEV=y
CONFIG_CRYPTO_DEV_SP_PSP=y
kvm_amd.sev=1 kvm_amd.sev_es=1 kvm_amd.sev_snp=1 kasan_multi_shot
cat /sys/module/kvm_amd/parameters/sev_snp # Y
ls /dev/sev # /dev/sev
होस्ट यूज़रस्पेस:
git clone https://github.com/AMDESE/qemu.git
cd qemu && git checkout snp-latest
mkdir build && cd build
../configure --target-list=x86_64-softmmu && make -j$(nproc)
अतिथि:
build-essential और linux-headers-$(uname -r) स्थापित हों ताकि आप उसमें मॉड्यूल बना सकें।SEV-SNP अतिथि GHCB के माध्यम से होस्ट से बात करते हैं, जो एक 4 KB साझा पेज है। एक PSC अनुरोध SW_EXITCODE को SVM_VMGEXIT_PSC (0x80000010) पर सेट करता है, SW_SCRATCH को एक डिस्क्रिप्टर की ओर इंगित करता है, और डिस्क्रिप्टर की लंबाई SW_EXITINFO2 में डालता है।
डिस्क्रिप्टर एक struct psc_buffer है: एक 8-बाइट हेडर जिसके बाद 8-बाइट एंट्री का ऐरे है। कोई स्पष्ट गणना फ़ील्ड नहीं है। होस्ट hdr->cur_entry से hdr->end_entry तक एंट्री संसाधित करता है, दोनों अतिथि-नियंत्रित।
struct psc_hdr {
u16 cur_entry;
u16 end_entry;
u32 reserved;
} __packed; /* 8 bytes */
struct psc_entry {
u64 cur_page : 12;
u64 gfn : 40;
u64 operation : 4;
u64 pagesize : 1;
u64 reserved : 7;
} __packed; /* 8 bytes */
GHCB v2+ अतिथि को अपने स्क्रैच क्षेत्र को GHCB के 2032-बाइट साझा बफर के अंदर रखना चाहिए, ताकि होस्ट अपने मौजूदा मैपिंग का पुन: उपयोग कर सके। (2032 - 8) / 8 = 253 एंट्री वहाँ फिट होती है, जहाँ से प्रोटोकॉल अधिकतम VMGEXIT_PSC_MAX_COUNT (253) आता है। यह संख्या केवल तब समझ में आती है जब बफर वास्तव में साझा बफर हो।
यदि अतिथि स्क्रैच क्षेत्र को GHCB के बाहर इंगित करता है, तो होस्ट अपनी मैपिंग का उपयोग नहीं कर सकता, इसलिए setup_vmgexit_scratch() अतिथि द्वारा मांगे गए आकार का एक अलग बफर आवंटित करता है। SNP को कभी भी यह पथ नहीं लेना चाहिए, लेकिन इसे रोकने वाला कुछ नहीं है:
scratch_va = kvzalloc(len, GFP_KERNEL_ACCOUNT); /* len == exit_info_2, guest-controlled */
len सीधे अतिथि से आता है, और GFP_KERNEL_ACCOUNT आवंटन को cgroup-गणना वाले kmalloc-cg-N कैश में रखता है। exit_info_2 = 24 मांगें और आपको 32-बाइट kmalloc-cg-32 स्लॉट में 24-बाइट का आवंटन मिलता है: हेडर के लिए जगह और दो एंट्री। entries[1] से आगे सब कुछ किसी दूसरी वस्तु की मेमोरी है।
फिर snp_begin_psc() एंट्री गणना को प्रोटोकॉल स्थिरांक के विरुद्ध जांचता है, न कि उस बफर के विरुद्ध जो वास्तव में आवंटित किया गया था:
idx_end = hdr->end_entry;
if (idx_end >= VMGEXIT_PSC_MAX_COUNT) { /* checks 253, NOT the buffer size */
snp_complete_psc(svm, ...);
return 1;
}
for (idx = idx_start; idx <= idx_end; idx++) {
entry_start = entries[idx]; /* OOB once idx >= 2 */
...
}
24-बाइट बफर में केवल दो एंट्री मौजूद हैं, लेकिन जांच end_entry को 252 तक अनुमति देती है। इसे 252 पर सेट करें और लूप आवंटन से लगभग 2 KB आगे चलता है, पड़ोसी स्लैब ऑब्जेक्ट्स के पार।
प्रत्येक चरण अंत के बाद स्लैब मेमोरी के अगले 8 बाइट को psc_entry के रूप में पुनर्व्याख्या करता है और इसे PSC कोड के माध्यम से चलाता है। यह तीन चीजें देता है:
entry.gfn और entry.operation को मेमोरी से खींचता है जो बफर के पास कभी नहीं थी। यह वह स्लैब-आउट-ऑफ-बाउंड्स रीड है जिसे KASAN पकड़ता है।KVM_HC_MAP_GPA_RANGE के रूप में भेजी जाती है, तो पूर्णता कोड उसी OOB स्लॉट में वापस लिखता है: entries[idx].cur_page = entry.pagesize ? 512 : 1। एक शब्द के निचले 12 बिट्स में दो छोटे मानों में से एक, दोहराने योग्य।SW_EXITINFO2 में प्रतिक्रिया उस इंडेक्स को रिपोर्ट करती है जिस पर वह रुकी थी। end_entry को एक-एक करके बढ़ाने से लीक होता है, स्लॉट दर स्लॉट, क्या पड़ोसी मेमोरी नो-ऑप में डिकोड हुई या कुछ ऐसी जो असफल रही, जो ऑब्जेक्ट सीमाएँ खोजने और शून्य बनाम गैर-शून्य बताने के लिए पर्याप्त है।प्रत्येक VMGEXIT स्क्रैच बफर को पुनः आवंटित करता है, इसलिए बार-बार अनुरोध अलग-अलग फ्रीलिस्ट स्लॉट में उतरते हैं और अतिथि को एक के साथ अटके रहने के बजाय पड़ोसियों में स्वीप करने देते हैं। एक साथ रखने पर यह हीप लेआउट प्रकटीकरण, उपरोक्त संयमित राइट, और अनुरोधों के बीच उपयोग-पश्चात-मुक्त देता है।
trigger.c एक अतिथि कर्नेल मॉड्यूल है। इसे SEV-SNP अतिथि के अंदर लोड करें और यह एकल insmod से होस्ट के विरुद्ध चार चरण चलाता है:
cur_page लिखकर और पुष्टि करके कि बाद का अनुरोध इसे छोड़ देता है।end_entry=200 के साथ एक अनुरोध भेजता है और मापता है कि OOB रीड गैर-शून्य डेटा से टकराने से पहले कितनी दूर तक पहुँचता है।entries[3..10] आउट-ऑफ-बाउंड्स हैं, जिनमें से प्रत्येक होस्ट पर KASAN रिपोर्ट ट्रिगर करता है।मॉड्यूल एक पेज आवंटित करता है, इसे set_memory_decrypted() से डिक्रिप्टेड चिह्नित करता है, इसे स्क्रैच क्षेत्र के रूप में उपयोग करता है, और GHCB PSC अनुरोध हाथ से बनाता है। यह -EAGAIN के साथ बाहर निकलता है ताकि यह लोड न रहे।
अपने सेटअप के अनुसार OVMF और डिस्क पथ समायोजित करें:
qemu-system-x86_64 \
-enable-kvm -cpu EPYC-v4 \
-machine q35,confidential-guest-support=sev0,memory-backend=ram1 \
-object memory-backend-memfd,id=ram1,size=4G \
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,policy=0x30000 \
-smp 4 -m 4G \
-bios OVMF_SNP.fd \
-drive file=guest.qcow2,format=qcow2,if=virtio \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=net0 \
-nographic
trigger.c और Makefile को अतिथि में कॉपी करें, फिर:
make
insmod trigger.ko
मॉड्यूल पहले CPUID के माध्यम से SEV-SNP की जाँच करता है और कहीं और चलने से मना कर देता है। यह अपने चार चरण चलाता है और स्वयं को अनलोड करता है (init -EAGAIN लौटाता है, इसलिए यह कभी निवासी नहीं रहता)।
होस्ट पर:
dmesg | grep -E "KASAN|BUG|snp_begin_psc"
अपेक्षित आउटपुट:
BUG: KASAN: slab-out-of-bounds in snp_begin_psc+0x126/0x890
Read of size 8 at addr ffff888219ffb5e0 by task qemu-system-x86/2199
BUG: KASAN: slab-out-of-bounds in snp_begin_psc+0x468/0x890
Write of size 8 at addr ffff888351566648 by task qemu-system-x86/2199
The buggy address belongs to the object at ffff888XXXXXXXXX
which belongs to the cache kmalloc-cg-32 of size 32
एकल insmod ने परीक्षण होस्ट पर 73 KASAN रिपोर्ट उत्पन्न कीं (62 slab-out-of-bounds, 7 slab-use-after-free, 4 use-after-free), सभी kmalloc-cg-32 के विरुद्ध। परीक्षण होस्ट: AMD EPYC 7443P, Ubuntu 24.04.4, कर्नेल 6.11.11 with KASAN, अतिथि AMDESE QEMU (snp-latest) के अंतर्गत।
अपस्ट्रीम समाधान GHCB v2 और बाद के लिए setup_vmgexit_scratch() में किसी भी बाहरी-GHCB स्क्रैच क्षेत्र को अस्वीकार करता है, जो बफर को एक निश्चित, ज्ञात आकार से बांधता है ताकि लूप कभी भी अंत से बाहर न भागे:
} else {
+ /* GHCB v2 requires the scratch area to be within the GHCB. */
+ if (to_kvm_sev_info(svm->vcpu.kvm)->ghcb_version >= 2)
+ goto e_scratch;
+
/*
* The guest memory must be read into a kernel buffer, so
* limit the size
वे चार पंक्तियाँ db3f219 हैं। वे एक बड़ी श्रृंखला के भाग के रूप में आईं जो एंट्री गणना को वास्तविक बफर आकार के विरुद्ध भी सीमित करती है और डिस्क्रिप्टर को एक बार READ_ONCE() के माध्यम से पुनः पढ़ती है, जो उसी हैंडलर में बफर-में-ऑफसेट संस्करण और टाइम-ऑफ-चेक/टाइम-ऑफ-यूज़ रेस को बंद करता है।
| फ़ाइल | विवरण |
|---|---|
trigger.c | अतिथि कर्नेल मॉड्यूल जो चार PoC चरण चलाता है |
Makefile | चल रहे अतिथि कर्नेल के विरुद्ध trigger.ko बनाता है |
not an SEV-SNP guest: QEMU sev-snp-guest के साथ लॉन्च नहीं किया गया, या होस्ट SNP बंद है।SEV-SNP not supported: /sys/module/kvm_amd/parameters/sev_snp, BIOS सेटिंग्स और बूट पैरामीटर जाँचें।LAUNCH_START failed: PSP आरंभ नहीं हुआ है। dmesg | grep psp और CONFIG_CRYPTO_DEV_SP_PSP=y जाँचें।CONFIG_KASAN=y और kasan_multi_shot सत्यापित करें।यह होस्ट कर्नेल हीप मेमोरी को दूषित करता है, KASAN को ट्रिप करता है, और होस्ट को क्रैश कर सकता है। इसे केवल एक डिस्पोजेबल परीक्षण होस्ट पर चलाएँ जिसे आप नियंत्रित करते हैं, एक VM के अंदर जो आपका है। इसे साझा या उत्पादन बुनियादी ढाँचे के विरुद्ध न चलाएँ।
db3f219 को linux-cve-announce पर ट्रैक करें)db3f219, लेखक Mike Roth, समीक्षक Tom Lendacky, प्रतिबद्ध Paolo Bonzini9b54e248d264; समाधान टैग Fixes: 4af663ctrigger.c GPL-2.0 है, अपने MODULE_LICENSE से मेल खाता है। LICENSE देखें।
LICENSE | GPL-2.0, मॉड्यूल के MODULE_LICENSE से मेल खाता है |