Apa itu retpoline dan bagaimana cara kerjanya?


244

Untuk memitigasi terhadap pengungkapan memori kernel atau lintas-proses ( serangan Specter ), kernel Linux 1 akan dikompilasi dengan opsi baru , -mindirect-branch=thunk-externdiperkenalkan gccuntuk 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.


6
Artikel dukungan yang menarik dari Google.
sgbj

2
oh, jadi diucapkan / ætræmpəˈlin / (Amerika) atau / ætræmpəˌliːn / (Inggris)
Walter Tross

2
Anda mungkin menyebutkan bahwa ini adalah kernel Linux , meskipun gccmenunjuk 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).
PJTraill

@PJTraill - menambahkan tag kernel Linux
RichVel

@ PJTraill - bagus, saya memperbarui teks pertanyaan. Perhatikan bahwa saya melihatnya pertama kali di kernel Linux karena proses pengembangannya yang relatif terbuka, tetapi tidak diragukan lagi teknik yang sama atau serupa digunakan sebagai mitigasi di seluruh spektrum OS open source dan closed. Jadi saya tidak melihat ini sebagai Linux-spesifik, tetapi tautannya tentu saja.
BeeOnRope

Jawaban:


158

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_thunkpanggilan baru yang memuat target panggilan yang alamat memorinya (yang akan saya panggil ADDR) disimpan di atas tumpukan dan menjalankan lompatan menggunakan RETinstruksi. 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):

.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

Penempatan callpada akhirnya diperlukan sehingga ketika panggilan tidak langsung selesai, aliran kontrol berlanjut di belakang penggunaan NOSPEC_CALLmakro, sehingga dapat digunakan di tempat biasacall

Ketukan itu sendiri terlihat sebagai berikut:

    call retpoline_call_target
2:
    lfence /* stop speculation */
    jmp 2b
retpoline_call_target:
    lea 8(%rsp), %rsp 
    ret

Aliran kontrol bisa sedikit membingungkan di sini, jadi izinkan saya mengklarifikasi:

  • call mendorong penunjuk instruksi saat ini (label 2) ke tumpukan.
  • leamenambahkan 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.
  • retmelompat ke *ADDRdan 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 mengeksekusi callinstruksi, mengasumsikan bahwa retpernyataan 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 JMPinstruksi dengan instruksi. Dengan menggunakan LFENCE, PAUSEatau 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, itu LFENCEakan 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:

Instruksi mengikuti LFENCE dapat diambil dari memori sebelum LFENCE, tetapi mereka tidak akan dijalankan sampai LFENCE selesai.

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:

Retpoline sebagai strategi mitigasi menukar cabang tidak langsung untuk pengembalian, untuk menghindari menggunakan prediksi yang berasal dari BTB, karena mereka dapat diracuni oleh penyerang. Masalah dengan Skylake + adalah bahwa RSB underflow kembali menggunakan prediksi BTB, yang memungkinkan penyerang mengendalikan spekulasi.


Saya rasa instruksi LFENCE tidak penting, implementasi Google menggunakan instruksi PAUSE. support.google.com/faqs/answer/7625886 Perhatikan bahwa dokumentasi yang Anda kutip mengatakan "tidak akan mengeksekusi" tidak akan "tidak akan dieksekusi secara spekulatif".
Ross Ridge

1
Dari halaman Google FAQ itu: "Instruksi jeda dalam loop spekulatif kami di atas tidak diperlukan untuk kebenaran. Tetapi itu berarti bahwa eksekusi spekulatif non-produktif menempati unit yang kurang fungsional pada prosesor." Jadi tidak mendukung kesimpulan Anda bahwa LFENCE adalah kunci di sini.
Ross Ridge

@RossRidge Saya setuju sebagian, bagi saya ini terlihat seperti dua kemungkinan implementasi dari infinite loop yang mengisyaratkan CPU untuk tidak secara spekulatif mengeksekusi kode mengikuti PAUSE / LFENCE. Namun jika LFENCE yang sedang dieksekusi kembali spekulatif dan tidak digulung karena spekulasi itu benar, ini akan bertentangan dengan klaim bahwa hal itu hanya akan dieksekusi sekali beban memori telah selesai. (Jika tidak, seluruh rangkaian instruksi yang telah dieksekusi secara spekulatif harus dibatalkan dan dieksekusi lagi untuk memenuhi spesifikasi)
Tobias Ribizel

1
Ini memiliki keuntungan dari push/ retbahwa itu tidak seimbang stack prediktor alamat-kembali. Ada satu mispredict (pergi ke lfencesebelum alamat pengirim yang sebenarnya digunakan), tetapi menggunakan call+ memodifikasi yang rspseimbang itu ret.
Peter Cordes

1
oops, keunggulan lebih dari push / ret(dalam komentar terakhir saya). re: edit Anda: RSB underflow seharusnya tidak mungkin karena retpoline termasuk a call. Jika pre-emption kernel melakukan perubahan konteks di sana, kami akan melanjutkan eksekusi dengan RSB yang disiapkan dari calldalam scheduler. Tapi mungkin interrupt handler bisa diakhiri dengan cukup retuntuk mengosongkan RSB.
Peter Cordes

46

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 :

Memanfaatkan Cabang Tidak Langsung. Menggambar dari pemrograman berorientasi kembali (ROP), dalam metode ini penyerang memilih gadgetdari ruang alamat korban dan memengaruhi korban untuk menjalankan gadget secara spekulatif. Tidak seperti ROP, penyerang tidak bergantung pada kerentanan dalam kode korban. Sebagai gantinya, penyerang melatih Branch Target Buffer (BTB) untuk salah memperkirakan cabang dari instruksi cabang tidak langsung ke alamat gadget, yang mengakibatkan eksekusi spekulatif gadget. Sementara instruksi yang dieksekusi secara spekulatif ditinggalkan, efeknya pada cache tidak dikembalikan. Efek ini dapat digunakan oleh gadget untuk membocorkan informasi sensitif. Kami menunjukkan bagaimana, dengan pemilihan gadget yang cermat, metode ini dapat digunakan untuk membaca memori arbitrer dari korban.

Untuk mengacaukan BTB, penyerang menemukan alamat virtual gadget di ruang alamat korban, lalu melakukan cabang tidak langsung ke alamat ini. Pelatihan ini dilakukan dari ruang alamat penyerang, dan tidak masalah apa yang berada di alamat gadget di ruang alamat penyerang; semua yang diperlukan adalah cabang yang digunakan untuk cabang pelatihan untuk menggunakan alamat virtual tujuan yang sama. (Faktanya, selama penyerang menangani pengecualian, serangan dapat bekerja bahkan jika tidak ada kode yang dipetakan pada alamat virtual gadget di ruang alamat penyerang.) cabang yang digunakan untuk pelatihan dan alamat cabang yang ditargetkan. Dengan demikian, penyerang memiliki fleksibilitas yang signifikan dalam menyiapkan pelatihan.

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.


9

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.

Untuk memitigasi pengungkapan memori kernel atau lintas-proses (serangan Specter), kernel Linux [1] akan dikompilasi dengan opsi baru, -mindirect-branch=thunk-externdiperkenalkan ke gcc untuk melakukan panggilan tidak langsung melalui apa yang disebut retpoline.

[1] Ini bukan spesifik Linux, namun - konstruksi yang mirip atau identik tampaknya digunakan sebagai bagian dari strategi mitigasi pada OS lain.

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.

Tampaknya ini adalah istilah yang baru ditemukan karena pencarian Google hanya muncul pada penggunaan yang sangat baru (umumnya semua pada tahun 2018).

The LLVM compiler telah memiliki -mretpolinesaklar 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).

Apa itu retpoline dan bagaimana mencegah serangan pengungkapan informasi kernel baru-baru ini?

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:

"Setiap prosesor memiliki perilaku arsitektur (perilaku terdokumentasi yang menggambarkan bagaimana instruksi bekerja dan bahwa programmer bergantung pada untuk menulis program mereka) dan perilaku arsitektur mikro (cara implementasi aktual dari arsitektur berperilaku). Ini dapat menyimpang dengan cara yang halus. Sebagai contoh, secara arsitektur, sebuah program yang memuat nilai dari alamat tertentu dalam memori akan menunggu sampai alamat diketahui sebelum mencoba melakukan pemuatan. Namun secara mikroarsitektur, prosesor mungkin mencoba menebak secara spekulatif pada alamat sehingga dapat memulai memuat nilai dari memori (yang lambat) bahkan sebelum benar-benar yakin alamat mana yang harus digunakan.

Jika prosesor salah menebak, prosesor akan mengabaikan nilai duga-at dan melakukan pemuatan lagi, kali ini dengan alamat yang benar. Perilaku yang didefinisikan secara arsitektur dipertahankan. Tapi tebakan yang salah itu akan mengganggu bagian prosesor lainnya — khususnya isi cache. Gangguan arsitektur mikro ini dapat dideteksi dan diukur dengan menentukan waktu yang diperlukan untuk mengakses data yang seharusnya (atau tidak seharusnya) ada di cache, memungkinkan program jahat untuk membuat kesimpulan tentang nilai yang disimpan dalam memori. "

Dari makalah Intel: " Retpoline: A Branch Target Injection Mitigation " ( .PDF ):

"Urutan retpoline mencegah eksekusi spekulatif prosesor dari menggunakan" predictor cabang tidak langsung "(salah satu cara memprediksi aliran program) untuk berspekulasi ke alamat yang dikendalikan oleh exploit (elemen yang memuaskan 4 dari lima elemen injeksi target cabang (Spectre varian 2) ) mengeksploitasi komposisi yang tercantum di atas).

Catatan, elemen 4 adalah: "Eksploitasi harus berhasil mempengaruhi cabang tidak langsung ini untuk salah memperkirakan dan mengeksekusi gadget. Gadget ini, yang dipilih oleh eksploit, bocor data rahasia melalui saluran samping, biasanya oleh waktu-cache.".

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.