- Saya rasa orang-orang belum cukup menyadari betapa tidak lengkapnya dokumentasi API kernel Linux, dan bagaimana Rust menyelesaikan sebagian dari masalah itu
- Saya telah menulis abstraksi Rust untuk beberapa subsistem, dan di hampir semua kasus saya harus membaca kode sumber C untuk benar-benar memahami cara menggunakan API dengan aman
- Hanya dari signature fungsi serta komentar dokumentasi terkait atau dokumentasi eksplisit saja, sulit untuk memahami sepenuhnya cara penggunaan API yang aman
- apakah lock harus tetap dipegang
- apakah argumen reference count menyerahkan referensi atau justru mengambil referensinya sendiri
- apakah lock tetap dipegang saat callback dipanggil, atau harus diperoleh sendiri
- apakah ada hal khusus pada callback
free - urutan lock yang dimaksud itu seperti apa
- apakah ada situasi khusus di mana beberapa operasi dalam kondisi tertentu boleh menggunakan lock
- apakah argumen NULL diperbolehkan dan bagaimana penggunaan yang valid
- apa yang terjadi pada reference count saat terjadi error
- apakah pointer reference count yang dikembalikan sudah dinaikkan, atau hanya pinjaman implisit dari referensi argumen yang diberikan
- apakah nilai balik selalu pointer yang valid, bisa NULL, atau bahkan bisa berupa
ERR_PTR - apakah pointer yang dikembalikan melalui argumen tidak langsung dibersihkan menjadi NULL saat error, atau dibiarkan apa adanya
- apakah valid mengirimkan
NULL **ketika pointer hasil tidak diperlukan
- Terkadang kebutuhannya masuk akal, tetapi tidak didokumentasikan
- kadang kebutuhannya terlalu fleksibel atau kompleks, sehingga saat menulis abstraksi Rust saya harus membuat keputusan subjektif untuk mempersempitnya ke cara penggunaan yang aman
- kadang saya harus menambahkan lock tambahan di dalam abstraksi untuk menjamin keamanan
- kadang saya harus membuat sedikit perubahan agar kode C menjadi lebih ortogonal, lebih logis, dan lebih mudah digunakan (misalnya mengekspos fungsi unlock untuk dipakai ketika lock sedang dipegang)
- kadang locking-nya begitu halus sehingga abstraksi Rust yang aman tetap bisa ditulis, tetapi memerlukan komentar dokumentasi besar yang memperingatkan agar berhati-hati pada cara penggunaan dan urutan pelepasan demi mencegah deadlock (Rust pada dasarnya tidak mencegah deadlock)
- kadang masalahnya tidak bisa diselesaikan kecuali kode C dibuat lebih masuk akal (seperti pada
drm_sched)
- Namun dalam kebanyakan kasus, kompromi saat menulis abstraksi Rust justru menunjukkan masalah desain pada kode C dan arah perbaikannya
- pendekatan umumnya adalah "pertama tulis kode Rust dengan perubahan sesedikit mungkin pada kode C agar tidak memicu konflik, lalu usulkan perubahan pada kode C berdasarkan pelajaran yang didapat" (bagian kedua ini belum sempat saya kerjakan)
- Hasilnya, dalam kebanyakan kasus, cara penggunaan yang benar bisa diketahui hanya dengan melihat API Rust
- tidak perlu khawatir soal reference count, pointer NULL, lupa memeriksa hasil, atau melepas referensi saat error
- tidak perlu khawatir soal penggunaan lock yang benar, lupa mengambil referensi, atau double free
- tidak perlu bertanya-tanya tentang bagaimana nilai error return dienkodekan
- karena jika hal-hal itu salah, kodenya tidak akan bisa dikompilasi
- tentu API masih bisa disalahgunakan, tetapi dalam kasus terburuk hanya akan menghasilkan error return atau deadlock (deadlock mudah di-debug dengan
lockdep, dan integrasiArc<>dapat menangkap kesalahan locking yang terkait dengan free/unref)
- Bahkan API OpenFirmware/DeviceTree yang relatif terdokumentasi dengan ketat pun, di C tetap membosankan dan mudah salah untuk mengikuti semua aturannya
- jika melihat kode OF pada driver, kemungkinan besar ada kebocoran referensi
- kebanyakan sistem tidak mengompilasi kernel dengan
OF_DYNAMIC, sehingga reference count diabaikan dan masalah ini tidak ditemukan atau diperbaiki - tetapi abstraksi Rust untuk OF yang saya tulis menangani reference counting secara otomatis, jadi tidak perlu dipikirkan
- Keuntungan menulis kode kernel dengan Rust dibanding C
- saat menulis kode kernel dalam C, hanya ada dua pilihan
- coba saja dan berharap reviewer menemukannya, atau menderita saat debugging
- habiskan berjam-jam untuk memahaminya sebelum berani memakai kode tersebut, sambil berharap semua hal sudah tertangkap
- ini juga menambah beban kerja reviewer dan maintainer
- mereka harus meninjau submission untuk memastikan semua aturan tersembunyi yang tidak terdokumentasi dipatuhi
- kadang masalah terlewat, dan kadang masalahnya begitu besar sampai kode harus direfaktor besar-besaran
- Di Rust, semua ini hilang. Jika bisa dikompilasi, maka aman dan tidak akan ada malfungsi atau kebocoran referensi (kode
unsafeadalah pengecualian, tetapi hanya itu yang perlu ditinjau, dan ada aturan bahwa bagian itu harus terdokumentasi dengan baik)- tentu tetap dibutuhkan code review dan bantuan dari pakar subsistem tertentu. Rust tidak secara ajaib membuat kode menjadi sempurna
- tetapi Rust menghilangkan semua masalah dan kesalahan low-level yang konyol, sehingga kita bisa fokus pada masalah high-level
- Pandangan terhadap para developer Linux
- saya tidak menyalahkan para developer Linux atas dokumentasi yang tidak sempurna
- kernel Linux sangat kompleks dan harus menangani banyak persoalan halus
- sebagian besar API user space punya aturan yang jauh lebih sederhana untuk digunakan dengan aman
- kernel itu sulit
- bahkan developer kernel yang berpengalaman pun tetap sering salah dalam hal-hal seperti ini
- ini bukan masalah keterampilan teknis, tetapi karena mustahil bagi manusia untuk menyimpan semua aturan kompleks ini di kepala dan mengeksekusinya dengan tepat setiap saat
- Solusinya
- kita butuh alat bantu
- solusinya adalah Rust. Setelah semua aturan dienkodekan sekali ke dalam kode dan type system, kita tidak perlu mengkhawatirkannya lagi
- sama seperti solusi untuk perdebatan gaya coding adalah mengenkodekan semua aturan ke dalam formatter otomatis
- setelah itu kita bisa berhenti mencemaskan semua masalah low-level soal keamanan, ownership, dan sinkronisasi, lalu fokus pada hal yang lebih penting seperti desain driver dan subsistem tingkat tinggi
- Pemformatan kode di proyek Rust for Linux
- proyek Rust for Linux memang menerapkan
rustfmtpada submission - saat menulis Rust untuk kernel, tidak perlu khawatir soal pemformatan kode atau keluhan saat code review
- cukup jalankan
make rustfmt
- proyek Rust for Linux memang menerapkan
Opini GN⁺
- Tulisan ini dengan baik menunjukkan masalah dokumentasi API dan keamanan dalam pengembangan kernel Linux. Ini juga memperlihatkan dengan jelas keterbatasan bahasa C dan kelebihan Rust
- Namun, pernyataan bahwa "Rust adalah satu-satunya solusi" tampak agak berlebihan. Sebagian perbaikan juga mungkin dilakukan lewat cara lain seperti alat analisis statis
- Rust memang menyelesaikan banyak masalah seperti keamanan memori, tetapi code review dan pengujian yang teliti tetap diperlukan. Rust bukan peluru perak
- Beralih ke Rust juga dapat menghadirkan berbagai kesulitan, seperti kompatibilitas dengan kode C yang sudah ada dan kurva belajar developer. Adopsi bertahap tampaknya lebih diinginkan
- Untuk memperbaiki praktik dan budaya lama di kernel Linux, selain Rust kemungkinan juga dibutuhkan upaya dari banyak sisi seperti dokumentasi, mentoring, dan komunikasi
- Secara keseluruhan, tulisan ini menunjukkan potensi dan kelebihan Rust dalam pengembangan kernel Linux dengan baik, sambil tetap mewaspadai ekspektasi berlebihan atau pemujaan buta, sehingga menghadirkan sudut pandang yang seimbang. Meski adopsi Rust akan sulit secara teknis maupun kultural, dalam jangka panjang hal itu diharapkan dapat membantu meningkatkan keamanan dan kemudahan pemeliharaan kode kernel.
2 komentar
Rust... saya pribadi pernah mencoba mempelajarinya, tetapi di perusahaan kami masih belum mengadopsinya. Sudah ada segunung kode yang ditulis dengan C++, dan ada juga masalah bahwa SDM yang ada harus belajar Rust lagi... Saya dengar sudah ada perusahaan di Korea yang menerapkan Rust di production, jadi sepertinya akan bagus kalau ada yang membagikan pengalaman terkait atau semacamnya.
Komentar Hacker News
Bahasa seperti Rust dan Swift memiliki daya ekspresif tinggi sehingga compiler dapat memberi tahu keamanan thread dari tipe data atau metode
Banyak library Rust yang dokumentasinya kurang memadai
Mencoba menggunakan Rust seperti C lalu kesulitan karena borrow checker
&selfatau&mut self&mut self, maka untuk berbagi instance antar-thread perlu menggunakan mutexDengan melihat API Rust, dalam banyak kasus kita bisa tahu cara menggunakannya dengan benar
Sebagai contoh konkret di Rust, ada cara menggunakan lock untuk melindungi data
Di bahasa lain juga, jika API diimplementasikan secara redundan, kejelasan kode dan dokumentasi bisa meningkat
Saat memakai C dalam extension Python, ada masalah karena harus mengetahui calling convention
Orang-orang seperti ini adalah pahlawan dan melakukan pekerjaan yang luar biasa
Kita selangkah lebih dekat untuk mewujudkan kode yang sepenuhnya mendokumentasikan dirinya sendiri