Untuk memitigasi terhadap pengungkapan memori kernel atau lintas-proses ( serangan Specter ), kernel Linux 1 akan dikompilasi dengan opsi baru , -mindirect-branch=thunk-extern
diperkenalkan gcc
untuk melakukan panggilan tidak langsung melalui apa yang disebut retpoline .
Tampaknya ini adalah istilah yang baru ditemukan karena pencarian Google hanya muncul pada penggunaan yang sangat baru (umumnya semua pada tahun 2018).
Apa itu retpoline dan bagaimana mencegah serangan pengungkapan informasi kernel baru-baru ini?
1 Ini bukan Linux spesifik, namun - konstruksi yang mirip atau identik tampaknya digunakan sebagai bagian dari strategi mitigasi pada OS lain.
security
assembly
x86
cpu-architecture
BeeOnRope
sumber
sumber
gcc
menunjuk ke sana! Saya tidak mengenali lkml.org/lkml/2018/1/3/780 di situs Mailing List Kernel Linux, bahkan tidak pernah saya melihat di sana (dan disajikan snapshot karena sedang offline).Jawaban:
Artikel yang disebutkan oleh sgbj dalam komentar yang ditulis oleh Paul Turner dari Google menjelaskan hal-hal berikut ini dengan lebih detail, tetapi saya akan mencobanya:
Sejauh yang saya bisa menyatukan ini dari informasi terbatas saat ini, retpoline adalah trampolin balik yang menggunakan infinite loop yang tidak pernah dijalankan untuk mencegah CPU berspekulasi pada target lompatan tidak langsung.
Pendekatan dasar dapat dilihat pada cabang kernel Andi Kleen yang membahas masalah ini:
Ini memperkenalkan
__x86.indirect_thunk
panggilan baru yang memuat target panggilan yang alamat memorinya (yang akan saya panggilADDR
) disimpan di atas tumpukan dan menjalankan lompatan menggunakanRET
instruksi. Thunk itu sendiri kemudian dipanggil menggunakan NOSPEC_JMP / CALL makro, yang digunakan untuk mengganti banyak (dan tidak semua) panggilan tidak langsung dan lompatan. Makro hanya menempatkan target panggilan pada tumpukan dan menetapkan alamat kembali dengan benar, jika perlu (perhatikan aliran kontrol non-linear):Penempatan
call
pada akhirnya diperlukan sehingga ketika panggilan tidak langsung selesai, aliran kontrol berlanjut di belakang penggunaanNOSPEC_CALL
makro, sehingga dapat digunakan di tempat biasacall
Ketukan itu sendiri terlihat sebagai berikut:
Aliran kontrol bisa sedikit membingungkan di sini, jadi izinkan saya mengklarifikasi:
call
mendorong penunjuk instruksi saat ini (label 2) ke tumpukan.lea
menambahkan 8 ke penunjuk tumpukan , secara efektif membuang kata kunci yang paling baru didorong, yang merupakan alamat pengembalian terakhir (ke label 2). Setelah ini, bagian atas tumpukan menunjuk kembali ADDR alamat asli.ret
melompat ke*ADDR
dan mengatur ulang penunjuk tumpukan ke awal tumpukan panggilan.Pada akhirnya, seluruh perilaku ini praktis setara dengan melompat langsung ke
*ADDR
. Satu manfaat yang kita dapatkan adalah bahwa prediktor cabang yang digunakan untuk pernyataan pengembalian (Return Stack Buffer, RSB), ketika mengeksekusicall
instruksi, mengasumsikan bahwaret
pernyataan yang sesuai akan melompat ke label 2.Bagian setelah label 2 sebenarnya tidak pernah dieksekusi, itu hanyalah sebuah loop tak terbatas yang secara teori akan mengisi pipa
JMP
instruksi dengan instruksi. Dengan menggunakanLFENCE
,PAUSE
atau lebih umum suatu instruksi yang menyebabkan pipa instruksi menjadi macet menghentikan CPU dari membuang daya dan waktu pada eksekusi spekulatif ini. Ini karena jika panggilan ke retpoline_call_target akan kembali secara normal, ituLFENCE
akan menjadi instruksi berikutnya yang akan dieksekusi. Ini juga yang akan diprediksi oleh prediktor cabang berdasarkan alamat pengirim asli (label 2)Mengutip dari manual arsitektur Intel:
Namun perlu dicatat bahwa spesifikasi tidak pernah menyebutkan bahwa LFENCE dan PAUSE menyebabkan pipa macet, jadi saya membaca sedikit di antara kalimat di sini.
Sekarang kembali ke pertanyaan awal Anda: Pengungkapan informasi memori kernel dimungkinkan karena kombinasi dari dua ide:
Meskipun eksekusi spekulatif harus bebas efek samping ketika spekulasi salah, eksekusi spekulatif masih mempengaruhi hierarki cache . Ini berarti bahwa ketika beban memori dieksekusi secara spekulatif, mungkin masih menyebabkan garis cache digusur. Perubahan dalam hierarki cache ini dapat diidentifikasi dengan secara hati-hati mengukur waktu akses ke memori yang dipetakan ke dalam set cache yang sama.
Anda bahkan dapat membocorkan beberapa bit dari memori arbitrer ketika alamat sumber dari memory read itu sendiri dibaca dari memori kernel.
Prediktor cabang tidak langsung dari CPU Intel hanya menggunakan 12 bit yang paling rendah dari instruksi sumber, sehingga mudah meracuni ke-2 kemungkinan riwayat prediksi dengan alamat memori yang dikontrol pengguna. Ini kemudian, ketika lompatan tidak langsung diprediksi di dalam kernel, dieksekusi secara spekulatif dengan hak istimewa kernel. Dengan menggunakan saluran samping waktu-cache, Anda dapat membocorkan memori kernel sewenang-wenang.
UPDATE: Pada mailing list kernel , ada diskusi yang sedang berlangsung yang membuat saya percaya bahwa retpoline tidak sepenuhnya mengurangi masalah prediksi cabang, seperti ketika Return Stack Buffer (RSB) kosong, arsitektur Intel yang lebih baru (Skylake +) mundur untuk Branch Target Buffer (BTB) yang rentan:
sumber
push
/ret
bahwa itu tidak seimbang stack prediktor alamat-kembali. Ada satu mispredict (pergi kelfence
sebelum alamat pengirim yang sebenarnya digunakan), tetapi menggunakancall
+ memodifikasi yangrsp
seimbang ituret
.push
/ret
(dalam komentar terakhir saya). re: edit Anda: RSB underflow seharusnya tidak mungkin karena retpoline termasuk acall
. Jika pre-emption kernel melakukan perubahan konteks di sana, kami akan melanjutkan eksekusi dengan RSB yang disiapkan daricall
dalam scheduler. Tapi mungkin interrupt handler bisa diakhiri dengan cukupret
untuk mengosongkan RSB.Sebuah retpoline dirancang untuk melindungi terhadap injeksi sasaran cabang ( CVE-2017-5715 ) mengeksploitasi. Ini adalah serangan di mana instruksi cabang tidak langsung dalam kernel digunakan untuk memaksa eksekusi spekulatif dari sepotong kode yang sewenang-wenang. Kode yang dipilih adalah "gadget" yang entah bagaimana berguna bagi penyerang. Misalnya kode dapat dipilih sehingga akan membocorkan data kernel melalui bagaimana hal itu mempengaruhi cache. Retpoline mencegah eksploitasi ini hanya dengan mengganti semua instruksi cabang tidak langsung dengan instruksi pengembalian.
Saya pikir apa kunci dari retpoline hanyalah bagian "ret", yang menggantikan cabang tidak langsung dengan instruksi pengembalian sehingga CPU menggunakan predictor stack stack alih-alih prediksi branch yang dapat dieksploitasi. Jika dorongan sederhana dan instruksi pengembalian digunakan sebagai gantinya maka kode yang akan dieksekusi spekulatif akan menjadi kode fungsi akhirnya akan kembali ke anyways, bukan gadget yang berguna bagi penyerang. Manfaat utama dari bagian trampolin tampaknya adalah untuk mempertahankan tumpukan kembali sehingga ketika fungsi sebenarnya kembali ke pemanggilnya, ini diprediksi dengan benar.
Ide dasar di balik injeksi target cabang sederhana. Ini mengambil keuntungan dari kenyataan bahwa CPU tidak mencatat alamat lengkap dari sumber dan tujuan cabang di buffer target cabangnya. Jadi penyerang dapat mengisi buffer menggunakan lompatan di ruang alamatnya sendiri yang akan menghasilkan prediksi prediksi ketika lompatan tidak langsung tertentu dieksekusi di ruang alamat kernel.
Perhatikan bahwa retpoline tidak mencegah pengungkapan informasi kernel secara langsung, itu hanya mencegah instruksi cabang tidak langsung dari yang digunakan untuk secara spekulatif menjalankan gadget yang akan mengungkapkan informasi. Jika penyerang dapat menemukan cara lain untuk secara spekulatif mengeksekusi gadget maka retpoline tidak mencegah serangan.
Makalah Spectre Attacks: Eksploitasi Eksekusi Spekulatif oleh Paul Kocher, Daniel Genkin, Daniel Gruss, Werner Haas, Mike Hamburg, Moritz Lipp, Stefan Mangard, Thomas Prescher, Michael Schwarz, dan Yuval Yarom memberikan tinjauan berikut tentang bagaimana cabang tidak langsung dapat dieksploitasi :
Entri blog yang berjudul Membaca memori istimewa dengan saluran samping oleh tim Project Zero di Google memberikan contoh lain tentang bagaimana injeksi target cabang dapat digunakan untuk membuat exploit yang berfungsi.
sumber
Pertanyaan ini ditanyakan beberapa saat yang lalu, dan pantas mendapatkan jawaban yang lebih baru.
Ringkasan Eksekutif :
Urutan "Retpoline" adalah perangkat lunak yang memungkinkan cabang tidak langsung diisolasi dari eksekusi spekulatif. Ini dapat diterapkan untuk melindungi binari sensitif (seperti sistem operasi atau implementasi hypervisor) dari serangan injeksi target cabang terhadap cabang tidak langsung mereka.
Kata " ret poline " adalah portmanteau dari kata "return" dan "trampoline", seperti peningkatan " rel poline " yang diciptakan dari "panggilan relatif" dan "trampolin". Ini adalah konstruksi trampolin yang dibangun menggunakan operasi balik yang juga secara kiasan memastikan bahwa setiap eksekusi spekulatif terkait akan "terpental" tanpa akhir.
Penggunaan opsi kompiler ini hanya melindungi terhadap Specter V2 pada prosesor yang terpengaruh yang memerlukan pembaruan mikrokode untuk CVE-2017-5715. Ini akan ' bekerja ' pada kode apa pun (bukan hanya kernel), tetapi hanya kode yang berisi "rahasia" yang layak diserang.
The LLVM compiler telah memiliki
-mretpoline
saklar sejak sebelum 4 Jan 2018 . Tanggal itu adalah ketika kerentanan pertama kali dilaporkan kepada publik . GCC membuat tambalan mereka tersedia 7 Januari 2018.Tanggal CVE menunjukkan bahwa kerentanan itu ' ditemukan ' pada tahun 2017, tetapi memengaruhi beberapa prosesor yang diproduksi dalam dua dekade terakhir (sehingga kemungkinan ditemukan sejak lama).
Pertama, beberapa definisi:
Trampolin - Kadang-kadang disebut sebagai lompatan vektor tidak langsung, trampolin adalah lokasi memori yang memiliki alamat yang menunjuk pada interupsi rutinitas servis, rutinitas I / O, dll. Eksekusi melompat ke trampolin dan kemudian segera melompat keluar, atau memantul, oleh karena itu disebut trampolin. GCC secara tradisional mendukung fungsi bersarang dengan membuat trampolin yang dapat dieksekusi pada saat dijalankan ketika alamat fungsi bersarang diambil. Ini adalah sepotong kecil kode yang biasanya berada di tumpukan, dalam bingkai tumpukan fungsi yang berisi. Trampolin memuat register rantai statis dan kemudian melompat ke alamat sebenarnya dari fungsi bersarang.
Thunk - Thunk adalah subrutin yang digunakan untuk menyuntikkan perhitungan tambahan ke subrutin lainnya. Thunks terutama digunakan untuk menunda perhitungan sampai hasilnya diperlukan, atau untuk memasukkan operasi di awal atau akhir subrutin lainnya.
Memoisasi - Fungsi memoized "mengingat" hasil yang sesuai dengan beberapa set input tertentu. Panggilan berikutnya dengan input yang diingat mengembalikan hasil yang diingat daripada menghitung ulang, sehingga menghilangkan biaya utama panggilan dengan parameter yang diberikan dari semua kecuali panggilan pertama yang dibuat ke fungsi dengan parameter tersebut.
Secara sangat kasar, retpoline adalah trampolin dengan pengembalian sebagai pemukul , untuk ' merusak ' memoisasi dalam prediktor cabang tidak langsung.
Sumber : Retpoline termasuk instruksi PAUSE untuk Intel, tetapi instruksi LFENCE diperlukan untuk AMD karena pada prosesor itu instruksi PAUSE bukan instruksi serialisasi, sehingga loop pause / jmp akan menggunakan daya berlebih karena berspekulasi tentang menunggu pengembalian. salah menafsirkan target yang benar.
Arstechnica memiliki penjelasan sederhana tentang masalahnya:
Dari makalah Intel: " Retpoline: A Branch Target Injection Mitigation " ( .PDF ):
sumber