Karat di kernel Linux





Pada  postingan sebelumnya  , Google mengumumkan bahwa Android kini mendukung bahasa pemrograman Rust yang digunakan dalam pengembangan OS itu sendiri. Dalam hal ini, penulis publikasi ini juga memutuskan untuk menilai seberapa banyak bahasa Rust dibutuhkan dalam pengembangan kernel Linux. Posting ini mencakup aspek teknis dari pekerjaan ini dengan beberapa contoh sederhana.



Selama hampir setengah abad, C telah menjadi bahasa utama untuk pengembangan kernel, karena C menyediakan tingkat kontrol dan kinerja yang dapat diprediksi yang diperlukan untuk komponen kritis tersebut. Kepadatan bug keamanan memori di kernel Linux biasanya sangat rendah karena kualitas kode sangat tinggi, tinjauan kode sesuai standar yang ketat, dan pengamanan diterapkan dengan hati-hati. Namun,  bug yang terkait dengan keamanan memori masih muncul secara teratur . Di Android, kerentanan kernel umumnya dianggap sebagai kelemahan serius, karena terkadang memungkinkan model keamanan dilewati karena kernel berjalan dalam mode istimewa.



Rust seharusnya sudah cukup matang untuk bekerja sama dengan C sebagai bahasa untuk implementasi praktis dari kernel. Rust membantu mengurangi potensi bug dan kerentanan keamanan dalam kode istimewa, sambil berintegrasi dengan baik dengan inti inti dan mempertahankan kinerja yang baik.



Dukungan karat



Prototipe utama  driver Binder dikembangkan untuk membandingkan karakteristik keamanan dan kinerja versi C yang ada dan mitra Rustnya secara  memadai. Kernel Linux memiliki lebih dari 30 juta baris kode, jadi kami tidak menetapkan tujuan untuk menulis ulang seluruhnya di Rust, tetapi untuk menyediakan kemampuan untuk menambahkan kode yang diperlukan di Rust. Kami percaya pendekatan inkremental ini membantu meningkatkan implementasi kinerja tinggi di kernel sambil memberikan pengembang kernel alat baru untuk meningkatkan keamanan memori dan mempertahankan kinerja saat runtime.



Kami bergabung dengan organisasi  Rust for Linuxdimana komunitas telah dan terus melakukan banyak hal untuk menambahkan dukungan Rust ke sistem build kernel Linux. Kami juga perlu merancang sistem sehingga fragmen kode yang ditulis dalam dua bahasa dapat berinteraksi satu sama lain: kami terutama tertarik pada abstraksi yang aman tanpa overhead yang memungkinkan kode Rust menggunakan fungsionalitas inti yang ditulis dalam C, serta kemampuan untuk mengimplementasikannya. fungsionalitas dalam Rust idiomatik, yang dapat dipanggil dengan lancar dari bagian kernel yang ditulis dalam C. 



Karena Rust adalah bahasa inti baru, kami juga memiliki kemampuan untuk mewajibkan pengembang mematuhi praktik terbaik untuk dokumentasi dan konsistensi. Misalnya, kami memiliki persyaratan khusus yang dapat diverifikasi mesin terkait penggunaan kode tidak aman: untuk setiap fungsi tidak aman, pengembang harus mendokumentasikan persyaratan yang harus dipenuhi oleh penelepon - sehingga memastikan bahwa penggunaannya aman. Selain itu, setiap kali fungsi yang tidak aman dipanggil, merupakan tanggung jawab pengembang untuk mendokumentasikan persyaratan yang harus dipenuhi oleh pemanggil sebagai jaminan bahwa penggunaan akan aman. Selain itu, untuk setiap panggilan ke fungsi yang tidak aman (atau penggunaan konstruksi yang tidak aman, misalnya,ketika mendereferensi pointer mentah), pengembang harus mendokumentasikan alasan mengapa begitu aman untuk melakukannya.



Rust terkenal tidak hanya karena keamanannya, tetapi juga karena berguna dan ramah pengguna bagi pengembang. Selanjutnya, mari kita lihat beberapa contoh yang menunjukkan bagaimana Rust dapat berguna bagi pengembang kernel saat menulis driver yang aman dan benar.



Contoh pengemudi



Pertimbangkan implementasi perangkat simbolik semaphore. Setiap perangkat memiliki nilai aktual; saat menulis n  byte, nilai perangkat meningkat sebesar n ; dengan setiap pembacaan, nilai ini dikurangi 1 hingga nilainya mencapai 0, dalam hal ini perangkat ini diblokir hingga operasi penurunan seperti itu dapat dilakukan tanpa turun di bawah 0.



Katakanlah  semaphore



 ini adalah file yang mewakili perangkat kita. Kita dapat berinteraksi dengannya dari shell seperti ini: 



> cat semaphore

      
      





Kapan  semaphore



 perangkat yang baru saja diinisialisasi, perintah yang ditunjukkan di atas terkunci karena nilai perangkat saat ini adalah 0. Ini akan dibuka jika kita menjalankan perintah berikutnya dari shell lain, karena akan menambah nilai dengan 1, sehingga memungkinkan pembacaan operasi asli untuk diselesaikan:



> echo -n a > semaphore

      
      





Kita juga bisa menambah penghitung lebih dari 1 jika kita menulis lebih banyak data, misalnya:



> echo -n abc > semaphore

      
      





meningkatkan penghitung sebesar 3, sehingga 3 pembacaan berikutnya tidak akan menghalangi. 



Untuk mendemonstrasikan beberapa aspek lagi dari Rust, mari tambahkan fitur berikut ke driver kami: ingat nilai maksimum yang dicapai selama seluruh siklus hidup, dan juga ingat berapa banyak pembacaan setiap file yang dilakukan pada perangkat.



Sekarang mari kita tunjukkan bagaimana driver seperti itu akan  diimplementasikan di Rust , membandingkan opsi ini dengan implementasi di C.... Namun, kami mencatat bahwa pengembangan topik ini di Google baru saja dimulai, dan di masa mendatang semuanya dapat berubah. Kami ingin menyoroti bagaimana Rust dapat berguna bagi pengembang di setiap aspek. Misalnya, pada waktu kompilasi, ini memungkinkan kita untuk menghilangkan atau sangat mengurangi kemungkinan seluruh kelas bug merayap ke dalam kode, sekaligus menjaga agar kode tetap fleksibel dan berjalan dengan overhead yang minimal.



Perangkat karakter



Pengembang perlu melakukan hal berikut untuk mengimplementasikan driver untuk perangkat karakter baru di Rust:



  1. Menerapkan suatu sifat  FileOperations



    : Semua fungsi yang terkait dengannya bersifat opsional, jadi pengembang hanya perlu mengimplementasikan yang relevan untuk skenario yang diberikan. Mereka sesuai dengan bidang dalam struktur C  struct file_operations



    .
  2. Menerapkan suatu sifat  FileOpener



     adalah padanan tipe-aman dari bidang C  open



     dari struct  struct file_operations



    .
  3. Daftarkan tipe perangkat baru untuk kernel: ini akan memberi tahu kernel fungsi apa yang perlu dipanggil sebagai respons terhadap operasi pada file tipe baru.


Berikut ini adalah perbandingan dua langkah pertama dari contoh pertama kami di Rust dan C:







Perangkat karakter di Rust memiliki sejumlah manfaat keamanan:



  • Manajemen status siklus hidup file demi file:  FileOpener::open



     Mengembalikan objek yang masa pakainya sejak saat itu dimiliki oleh pemanggil. Objek apa pun yang mengimplementasikan sifat dapat dikembalikan  PointerWrapper



    , dan kami menyediakan implementasi untuk 
    Kotak <T>
     dan 
    Busur <T>
    , , Rust, , .



     FileOperations



      self



     ( ),  release



    , ( ).  release



      , - , . ยซยป ( , ).



    , Rust , C. C , Rust, , Rust, , , . , C, Rust , Rust. , open , , , , ioctl



    /read



    /write



      ( ) ,  filp->private_data



    , ..



  • : , open



      release



       self



    , , Rust , .



    ( ),   : Mutex



      
    SpinLock



     ( 
    atomics) .



    , ( ), ( ). 





    : , , Rust . , , ,  FileOperation::open



    .  Arc, .



    ,  FileOperation




      ( , ,  open



    ,  FileOperations



    ) โ€“ .



    , , . , C miscdevice



    ,  filp->private_data



    ;  cdev



    , inode->i_cdev



    . , ,  container_of



    , . Rust .









    : , Rust , . . C , , (void



     *) : , , . Rust .





    : ,  FileOperations



    , . , impl FileOperations for Device



    ,  Device



     โ€“ , ( FileState



    ). , , , . (  neovim



      LSP- rust-analyzer



     .)



    Rust, , C, struct file_operations



    . (  declare_file_operations



    ): , , ,  const



    , , .



    Ioctl 



     ioctl, ioctl



    , FileOperations



    , . 







    Ioctl , , , , , (, , , , ) . Rust  (  cmd.dispatch



    ), .



       . , , ioctl, Rust : cmd.raw



      ioctl ( , ).



    , , , - , :





    C, ( , ) ; Rust  unsafe



    , . Rust:





     



    . ; , C Rust , , , , : 







    , C, ,    ยซยป ,  unix  ,     ,   .



    Rust:



    •  Semaphore::inner



        , ,  lock



      . , . C, , count



        max_seen



        semaphore_state



        , . there is no enforcement that the lock is held while they're accessed.
    • (RAII): , (inner



        ) . , : , , , , ; : , , , drop



      .
    • ,  Lock



      , , , Mutex



        SpinLock



      , , C, . , , , .
    • Rust , . , , . C  semaphore_consume



        Linux: , ,  mutex_unlock



         prepare_to_wait



      , . 
    • : , , , , , . , ioctl , , . Rust ,   . , C, atomic64_t



      , , . 






    ,  open



    read



     write



    :















    Rust:



    •  ?



       operator: open



        read



        Rust ; , , , . C , - . 
    • : Rust , , - . C . open



      , , C kref_get



       ( ); Rust  clone



       ( ), .
    • RAII: Rust , , inner



        , , .
    • : Rust , . write



      , , . C , , . 







    .



    10% !






All Articles