V8: satu tahun dengan Spectre

Pada 3 Januari 2018, Google Project Zero dan lainnya mengungkapkan tiga kerentanan pertama dari kelas baru yang memengaruhi prosesor eksekusi spekulatif. Mereka disebut Specter (1 dan 2) dan Meltdown . Dengan menggunakan mekanisme eksekusi CPU spekulatif , penyerang dapat melewati sementara pemeriksaan keamanan perangkat lunak eksplisit dan implisit yang mencegah program membaca data yang tidak dapat diakses dalam memori. Sementara eksekusi spekulatif dirancang sebagai bagian dari arsitektur mikro, tidak terlihat di tingkat arsitektur, program yang dirancang dengan cermat dapat membaca informasi yang tidak dapat diakses dalam blok spekulatif dan mengungkapkannya melalui saluran samping, seperti waktu pelaksanaan fragmen program.

Ketika ditunjukkan bahwa serangan Specter dimungkinkan menggunakan JavaScript, tim V8 ikut serta dalam menyelesaikan masalah. Kami membentuk tim tanggap darurat dan bekerja sama dengan tim Google lainnya, mitra browser kami yang lain, dan mitra perangkat keras. Bersama mereka, kami secara proaktif melakukan penelitian ofensif (membangun modul penyerang untuk membuktikan konsep) dan defensif (memitigasi potensi serangan).

Serangan Spectre terdiri dari dua bagian:

  • Kebocoran data yang tidak dapat diakses ke kondisi laten CPU . Semua serangan Spectre yang dikenal menggunakan spekulasi untuk mentransfer bit data yang tidak dapat diakses ke cache CPU.
  • Mengambil status tersembunyi untuk memulihkan data yang tidak dapat diakses. Untuk ini, seorang penyerang membutuhkan jam tangan yang cukup akurat. (Akurasi mengejutkan rendah, terutama dengan metode seperti edge thresholding - membandingkan dengan ambang batas sepanjang garis yang dipilih).

Secara teoritis, itu akan cukup untuk memblokir salah satu dari dua komponen serangan. Karena kami tidak tahu cara memblokirnya, kami mengembangkan dan menyebarkan mitigasi yang secara signifikan mengurangi jumlah informasi yang bocor ke dalam cache CPU dan mitigasi yang membuatnya sulit untuk memulihkan keadaan tersembunyi.

Timer presisi tinggi


Perubahan negara kecil yang tersisa setelah eksekusi spekulatif menghasilkan perbedaan temporal yang kecil, hampir tidak mungkin kecil, dari urutan sepersejuta detik. Untuk secara langsung mendeteksi perbedaan seperti itu, penyerang membutuhkan pengatur waktu presisi tinggi. Prosesor menawarkan penghitung waktu seperti itu, tetapi platform web tidak mengaturnya. Pengatur waktu paling akurat pada platform web performance.now() memiliki resolusi beberapa mikrodetik, yang awalnya dianggap tidak cocok untuk tujuan ini. Namun, dua tahun lalu, sebuah kelompok penelitian yang mengkhususkan diri dalam serangan mikroarsitektur menerbitkan sebuah artikel tentang penghitung waktu pada platform web. Mereka menyimpulkan bahwa memori bersama yang dapat berubah secara simultan dan berbagai metode pemulihan resolusi memungkinkan pembuatan timer dengan resolusi lebih tinggi, hingga nanosecond. Penghitung waktu semacam itu cukup akurat untuk mendeteksi setiap klik dan kesalahan cache L1. Dialah yang biasanya digunakan untuk menangkap informasi dalam serangan Specter.

Perlindungan timer


Untuk mengganggu kemampuan mendeteksi perbedaan kecil dalam waktu, pengembang browser telah memilih pendekatan multilateral. Di semua browser, resolusi performance.now() berkurang (di Chrome dari 5 mikrodetik menjadi 100) dan jitter acak diperkenalkan untuk mencegah pemulihan resolusi. Setelah berkonsultasi antara pengembang semua browser, kami memutuskan bersama untuk mengambil langkah yang belum pernah terjadi sebelumnya: segera dan secara surut menonaktifkan SharedArrayBuffer API di semua browser untuk mencegah pembuatan timer nanosecond.

Keuntungan


Pada awal penelitian ofensif kami, menjadi jelas bahwa pengurangan waktu saja tidak cukup. Salah satu alasannya adalah penyerang dapat menjalankan kodenya berulang kali sehingga perbedaan waktu kumulatif lebih dari satu hit atau cache miss. Kami dapat membuat "gadget" andal yang menggunakan banyak baris cache sekaligus, hingga seluruh kapasitas cache, yang memberikan perbedaan waktu hingga 600 mikrodetik. Kemudian, kami menemukan metode amplifikasi acak yang tidak dibatasi oleh kapasitas cache. Metode amplifikasi semacam itu didasarkan pada upaya berulang-ulang untuk membaca data rahasia.

Perlindungan JIT


Untuk membaca data yang tidak dapat diakses menggunakan Specter, seorang penyerang memaksa CPU untuk secara spekulatif mengeksekusi kode yang membaca data yang biasanya tidak dapat diakses dan menempatkannya dalam cache. Perlindungan dapat dipertimbangkan dari dua sisi:

  1. Cegah eksekusi kode spekulatif.
  2. Pencegahan membaca data yang tidak dapat diakses dari pipa spekulatif.

Kami bereksperimen dengan opsi pertama dengan memasukkan instruksi yang direkomendasikan untuk mencegah spekulasi, seperti Intel LFENCE dari setiap cabang kondisional kritis dan menggunakan retpolin untuk cabang tidak langsung. Sayangnya, mitigasi berat seperti itu secara signifikan mengurangi produktivitas (pelambatan 2-3x pada benchmark Octane). Sebaliknya, kami mengambil pendekatan kedua dengan memasukkan urutan mitigasi yang mencegah data sensitif dibaca karena spekulasi yang tidak tepat. Biarkan saya menggambarkan teknik dengan potongan kode berikut:

 if (condition) { return a[i]; } 

Untuk kesederhanaan, kita asumsikan bahwa kondisi 0 atau 1 . Kode di atas rentan jika CPU secara spekulatif membaca dari a[i] ketika i luar jangkauan, mendapatkan akses ke data yang biasanya tidak dapat diakses. Pengamatan penting adalah bahwa dalam kasus ini, spekulasi mencoba membaca a[i] ketika kondisinya 0 . Mitigasi kami menulis ulang program ini sehingga berperilaku persis sama dengan program asli, tetapi tidak membiarkan data yang dimuat secara spekulatif bocor.

Kami memesan satu register CPU, yang kami sebut "racun", untuk melacak apakah kode dieksekusi di cabang yang disalahtafsirkan. Register racun didukung di semua cabang dan panggilan kode yang dihasilkan, sehingga cabang yang diartikan salah menyebabkan register racun menjadi 0 . Lalu kami mengukur semua akses memori sehingga mereka tanpa syarat menutupi semua hasil unduhan dengan nilai register racun saat ini. Ini tidak mencegah prosesor memprediksi (atau salah menafsirkan) cabang, tetapi merusak informasi (berpotensi di luar batas) dari nilai yang dimuat karena cabang ditafsirkan salah. Kode alat ditunjukkan di bawah ini ( a adalah array angka).

 let poison = 1; // … if (condition) { poison *= condition; return a[i] * poison; } 

Kode tambahan tidak mempengaruhi perilaku normal (ditentukan oleh arsitektur) program. Ini hanya mempengaruhi keadaan mikro-arsitektur ketika bekerja pada CPU dengan eksekusi spekulatif. Jika Anda memasukkan program pada level kode sumber, optimisasi lanjutan dalam kompiler modern dapat menghapus instrumentasi tersebut. Di V8, kami mencegah kompiler menghapus mitigasi dengan memasukkannya pada tahap kompilasi yang sangat terlambat.

Kami juga menggunakan teknik keracunan ini untuk mencegah kebocoran dari cabang tidak langsung dalam loop bytecode interpreter dan dalam urutan panggilan fungsi JavaScript. Dalam penerjemah, kami menetapkan racun ke 0 jika bytecode handler (mis., Urutan kode mesin yang mengartikan satu bytecode) tidak cocok dengan bytecode saat ini. Untuk panggilan JavaScript, kami meneruskan fungsi target sebagai parameter (dalam register) dan mengatur racun ke 0 di awal setiap fungsi jika fungsi target yang masuk tidak cocok dengan fungsi saat ini. Dengan pelunakan ini, kita melihat perlambatan kurang dari 20% di benchmark Octane.

Mitigasi untuk WebAssembly lebih sederhana, karena pemeriksaan keamanan utama adalah untuk memastikan bahwa akses memori berada dalam batas-batas. Untuk platform 32-bit, sebagai tambahan pada pemeriksaan batas yang biasa, kami mengisi semua memori ke kekuatan dua berikutnya dan tanpa syarat menutupi bit atas dari indeks memori pengguna. Platform 64-bit tidak memerlukan mitigasi seperti itu, karena implementasinya menggunakan perlindungan memori virtual untuk pemeriksaan perbatasan. Kami bereksperimen dengan mengkompilasi pernyataan switch / case ke dalam kode pencarian biner alih-alih menggunakan cabang tidak langsung yang rentan, tetapi terlalu mahal untuk beberapa beban kerja. Panggilan tidak langsung dilindungi oleh retpolin.

Perlindungan Perangkat Lunak - Tidak Dapat Diandalkan


Untungnya atau sayangnya, penelitian ofensif kami berkembang jauh lebih cepat daripada defensif, dan kami dengan cepat menemukan tidak mungkin untuk secara terprogram mengurangi semua kemungkinan kebocoran selama serangan Specter. Ada beberapa alasan untuk ini. Pertama, upaya rekayasa untuk memerangi Specter tidak proporsional dengan tingkat ancaman. Di V8, kami menghadapi banyak risiko keamanan lain yang jauh lebih buruk, dari membaca langsung di luar perbatasan karena bug umum (yang lebih cepat dan lebih mudah daripada Spectre), menulis di luar perbatasan (ini tidak mungkin dengan Specter dan lebih buruk) dan potensi jarak jauh eksekusi kode (mustahil dengan Spectre dan jauh, jauh lebih buruk). Kedua, langkah-langkah mitigasi yang semakin canggih yang kami kembangkan dan implementasikan membawa kompleksitas yang signifikan, yang merupakan kewajiban teknis dan benar-benar dapat meningkatkan permukaan serangan dan overhead kinerja. Ketiga, menguji dan mempertahankan mitigasi kebocoran mikroarsitektur bahkan lebih sulit daripada merancang sendiri gadget untuk serangan, karena sulit untuk memastikan bahwa mitigasi terus bekerja dengan cara yang mereka rancang. Setidaknya sekali, mitigasi penting secara efektif dibatalkan oleh optimisasi kompiler kemudian. Keempat, kami menemukan bahwa secara efektif mengurangi beberapa opsi Specter, terutama opsi 4, tidak mungkin dilakukan dalam perangkat lunak, bahkan setelah upaya heroik dari mitra Apple kami untuk menangani masalah dalam kompiler JIT mereka.

Isolasi Situs


Penelitian kami mengarah pada kesimpulan: pada prinsipnya, kode yang tidak dipercaya dapat membaca seluruh ruang alamat dari suatu proses menggunakan Spectre dan saluran samping. Mitigasi perangkat lunak mengurangi efektivitas banyak gadget potensial, tetapi tidak efektif atau komprehensif. Satu-satunya ukuran yang efektif adalah memindahkan data sensitif di luar ruang alamat proses. Untungnya, Chrome telah mencoba selama bertahun-tahun untuk memisahkan situs ke dalam proses yang berbeda untuk mengurangi permukaan serangan karena kerentanan umum. Investasi ini terbayar, dan pada Mei 2018 kami membawa ke tahap kesiapan dan memperluas isolasi situs pada jumlah platform maksimum. Dengan demikian, model keamanan Chrome tidak lagi menganggap privasi bahasa selama proses render.

Spectre telah datang jauh dan menekankan manfaat kolaborasi pengembang dalam industri dan akademisi. Sejauh ini, topi putih lebih unggul dari yang hitam. Kami masih belum tahu tentang satu serangan nyata, dengan pengecualian para peneliti yang ingin tahu dan peneliti profesional mengembangkan gadget untuk membuktikan konsep tersebut. Varian baru dari kerentanan ini terus muncul dan ini akan berlanjut untuk beberapa waktu. Kami terus memantau ancaman ini dan menanggapinya dengan serius.

Seperti banyak programmer, kami juga berpikir bahwa bahasa yang aman memberikan batas yang tepat untuk abstraksi, mencegah program yang diketik dengan baik dari membaca memori sewenang-wenang. Sangat menyedihkan bahwa ini ternyata menjadi kesalahan - jaminan ini tidak sesuai dengan peralatan saat ini. Tentu saja, kami masih percaya bahwa bahasa yang aman memiliki lebih banyak keunggulan teknik, dan masa depan ada pada mereka, tapi ... pada peralatan saat ini mereka sedikit bocor.

Pembaca yang tertarik dapat mempelajari topik ini lebih dalam dan mendapatkan informasi lebih rinci dalam artikel ilmiah kami.

Source: https://habr.com/ru/post/id449546/


All Articles