Anda memiliki codebase C yang terlalu besar untuk rewrite dalam Rust dan terlalu kritis untuk dibiarkan rentan terhadap buffer overflow. Perangkat keras capability CHERI menjanjikan untuk menangkap pelanggaran keamanan memori di tingkat CPU, tetapi internet terus memberitahu Anda bahwa pointer CHERI berukuran 128 bit dan kode Anda mengasumsikan 64.

Kabar baiknya adalah CHERI tidak memaksakan migrasi serba-atau-tidak-sama-sekali. ABI hibrida memungkinkan Anda mengompilasi kode C yang sudah ada dengan perubahan minimal, menjalankannya di perangkat keras CHERI nyata, dan secara bertahap memperketat keamanan di mana pun itu paling penting. Anda tidak perlu mem-porting sejuta baris kode sebelum mendapatkan pointer yang dilindungi pertama Anda.

Mengapa porting bertahap kini memungkinkan

Arsitektur capability awal tidak bisa ditawar-tawar. iAPX 432 mengharuskan setiap pointer menjadi capability, titik. Itu merusak setiap compiler dan sistem operasi yang sudah ada, dan proyek itu gagal.

CHERI belajar dari kegagalan itu. CHERI mendukung tiga mode kompilasi: baseline (hanya pointer integer warisan), hibrida (pointer integer dan capability berdampingan), dan purecap (setiap pointer adalah capability). ABI hibrida adalah jembatanannya. ABI ini memungkinkan Anda mempertahankan kode yang sudah ada sebagian besar tidak tersentuh sambil secara selektif memperkenalkan capability di mana mereka memberikan nilai terbesar.

Dalam mode hibrida, void* masih 64 bit. Pointer capability adalah tipe yang berbeda, void* __capability, yang Anda pilih secara eksplisit. Struct, linked list, dan hash table Anda tidak berubah ukuran kecuali Anda memintanya. Ini bukan lapisan kompatibilitas. Ini adalah ABI kelas satu yang didukung oleh perangkat keras dan compiler.

Apa yang sebenarnya rusak saat Anda mengompilasi untuk CHERI hibrida

Langkah pertama adalah mencoba mengompilasi kode Anda dengan toolchain CHERI dan melihat apa yang meledak. Sebagian besar kode C yang berperilaku baik dikompilasi tanpa perubahan. Masalah-masalah tersebut berkumpul di sekitar segelintir pola yang CHERI nyatakan ilegal, yang mana itu memang tujuannya.

Casting pointer ke integer. Kode yang meng-cast pointer ke uintptr_t, menyamarkan beberapa bit, dan meng-cast kembali akan gagal. Di CHERI, uintptr_t adalah tipe capability, bukan integer biasa. Operasi bitwise pada capability menghilangkan bit tag, dan nilai yang dihasilkan akan trap saat dereference.

Asumsi tentang ukuran pointer. Kode yang menuliskan secara kaku sizeof(void*) == 8 atau menserialisasi pointer sebagai nilai 8-byte akan rusak dalam mode purecap. Dalam mode hibrida ini sebagian besar berfungsi, tetapi menjadi masalah saat Anda mencampur pointer capability dan integer dalam struct yang sama.

Aritmatika pointer melintasi objek yang tidak terkait. Programmer C terkadang menghitung jarak antara dua pointer sembarang atau membandingkan pointer dari alokasi yang berbeda. Capability CHERI membawa metadata batas, dan mengurangi capability dari alokasi yang berbeda tidak terdefinisi. Compiler akan menolaknya atau perangkat keras akan trap.

Assembly inline. Setiap assembly yang ditulis tangan yang memindahkan pointer di register integer perlu diperbarui. CHERI memiliki register capability khusus, dan compiler perlu tahu register mana yang Anda sentuh.

Toolchain LLVM CHERI memberikan Anda diagnostik yang sangat baik. Anda tidak mendapatkan error linker yang misterius. Anda mendapatkan pesan yang jelas seperti “cast dari capability ke integer tidak diizinkan” atau “aritmatika pada capability dari alokasi yang berbeda.” Perbaiki error pertama, kompilasi ulang, dan kejar yang berikutnya.

Contoh konkret: membungkus parser dengan capability

Bayangkan Anda memiliki parser paket jaringan yang membaca input yang tidak tepercaya ke dalam buffer dan kemudian mengurai header. Ini persis jenis kode yang diuntungkan dari pengecekan batas perangkat keras. Berikut cara membungkusnya tanpa rewrite parser itu sendiri.

Pertama, kompilasi parser dalam mode hibrida. Toolchain CHERI adalah LLVM standar dengan target CHERI:

# Mengompilasi satu file dengan ABI hibrida
$ clang --target=riscv64-unknown-freebsd \
    -march=rv64imafdcxcheri \
    -mabi=lp64d \
    -mno-relax \
    -c parser.c -o parser.o

Flag -mabi=lp64d mempertahankan pointer integer 64-bit. Tipe void* dan char* yang sudah ada tetap tidak berubah. Parser dikompilasi apa adanya.

Sekarang tambahkan wrapper yang aware-capability dalam file terpisah:

#include <cheriintrin.h>
#include <stddef.h>
#include <stdint.h>

// Parser yang sudah ada dari parser.c
extern int parse_packet(const char *data, size_t len);

// Entry point yang aware-capability
int parse_packet_safe(const char * __capability data, size_t len) {
    // Mempersempit capability hingga tepat sepanjang buffer.
    // Bahkan jika pemanggil melewatkan alokasi yang lebih besar,
    // parser tidak bisa membaca melewati 'len' byte.
    const char * __capability narrowed =
        cheri_bounds_set(data, len);

    // Dalam mode hibrida, kita melewatkan pointer biasa ke parser warisan.
    // Compiler menyisipkan konversi capability-ke-integer di sini.
    // Jika capability tidak muat dalam 64 bit (tidak akan muat untuk
    // alamat large-capability), ini akan memunculkan peringatan atau error.
    //
    // Untuk migrasi purecap, Anda sebaiknya mengubah parse_packet
    // agar menerima pointer capability.
    return parse_packet((const char *)narrowed, len);
}

Dalam contoh ini, parse_packet masih menggunakan pointer integer warisan. Wrapper mempersempit capability pemanggil hingga tepat sepanjang input, lalu mengonversinya kembali. Langkah pempersempitan berarti bahkan jika pemanggil secara tidak sengaja melewatkan buffer 4KB padahal yang dimaksud adalah 64 byte, perangkat keras akan memberlakukan batas 64-byte pada capability yang dipersempit.

Ini bukan keadaan akhir. Ini adalah batu loncatan. Anda mendapatkan bounds enforcement di batas API sekarang, dan Anda dapat memigrasi parse_packet ke purecap dalam refactor nanti.

Jalur migrasi dari hibrida ke purecap

Mode hibrida adalah titik awal, bukan tujuan akhir. Mode ini memberikan Anda kompatibilitas tetapi tidak memberikan manfaat keamanan penuh dari capability. Tujuan jangka panjang adalah purecap, di mana setiap pointer membawa batas.

Migrasi praktis terlihat seperti ini:

  1. Kompilasi dalam mode hibrida dan perbaiki error build. Ini biasanya berarti memperbaiki casting pointer-ke-integer dan asumsi sizeof(void*). Jangan ubah struktur data Anda dulu. Dapatkan saja build yang bersih.

  2. Identifikasi target bernilai tinggi. Parser jaringan, decoder format file, dan rutin deserialisasi adalah kandidat terbaik untuk capability enforcement. Mereka memproses input yang tidak tepercaya dan di situlah sebagian besar bug keamanan memori berada.

  3. Bungkus target-target itu dengan capability yang dipersempit. Gunakan cheri_bounds_set untuk membuat capability yang dibatasi di batas-batas kepercayaan. Lewatkan mereka ke kode yang sudah ada Anda.

  4. Migrasikan fungsi leaf ke purecap. Mulailah dengan fungsi utilitas yang mengalokasikan dan mengembalikan pointer. Ubah tanda tangannya untuk menggunakan __capability dan kompilasi mereka dengan -mabi=purecap. Kerjakan ke atas di call stack.

  5. Akhirnya ubah seluruh modul ke purecap. Ketika unit kompilasi tidak memiliki pointer integer yang tersisa, kompilasi dengan -mabi=purecap dan link dengan sisa kode hibrida Anda. Toolchain CHERI mendukung linking ABI-campuran.

Ini bukan proyek akhir pekan untuk codebase yang besar. Tetapi ini juga bukan rewrite. Anda dapat melindungi jalur kode yang paling rentan dalam hitungan hari, dan memigrasi sisanya secara bertahap saat Anda menyentuhnya.

Seperti apa sebenarnya pengorbanannya

ABI hibrida memiliki biaya nyata, dan Anda harus mengetahuinya sebelum Anda berkomitmen.

Pertama, ABI campuran memperumit build Anda. Anda kini memiliki file objek yang dikompilasi dengan ukuran pointer yang berbeda dalam binary yang sama. Linker harus menangani relocasi capability dan integer. Toolchain CHERI mendukung ini, tetapi sistem build Anda mungkin belum mengetahuinya. Anda perlu mengajarkannya.

Kedua, konversi capability-ke-integer di batas-batas hibrida bersifat lossy. Jika Anda mempersempit capability lalu meng-castnya ke pointer biasa, Anda kehilangan batasnya. Memori yang mendasarinya masih dilindungi oleh capability yang Anda mulai dengan, tetapi fungsi warisan tidak menerima hardware enforcement. Inilah mengapa purecap adalah tujuannya: setiap pointer dalam rantai panggilan membawa batasnya sendiri.

Ketiga, debugging berubah. GDB di CHERI memahami register capability dan dapat mencetak metadata batas. Dukungan LLDB sedang meningkat. Jika alur kerja debugging Anda saat ini mengandalkan inspeksi nilai pointer mentah, Anda perlu mempelajari nama register CHERI.

Overhead performa mode hibrida biasanya dapat diabaikan untuk kode yang sebagian besar menggunakan pointer integer. Overhead purecap biasanya berupa persentase digit tunggal untuk sebagian besar beban kerja, meningkat hingga 10-15% untuk struktur data yang banyak menggunakan pointer. Itu kompetitif dengan mitigasi perangkat lunak seperti ASAN, dan tidak seperti ASAN, CHERI berjalan dengan kecepatan penuh di produksi.

Anda bisa mulai hari ini dengan QEMU

Anda tidak memerlukan board Morello untuk bereksperimen. Proyek CHERI memelihara dukungan QEMU dan image Docker dengan toolchain LLVM yang sudah diinstal sebelumnya.

# Pull image Docker toolchain CHERI
$ docker run --rm -it ctsrd/cheri-sdk:latest

# Di dalam kontainer, kompilasi kode Anda untuk RISC-V CHERI
$ clang --target=riscv64-unknown-freebsd \
    -march=rv64imafdcxcheri \
    -mabi=lp64d \
    -o myapp myapp.c

Mulailah dengan mengompilasi proyek Anda dalam mode hibrida dan menghitung errornya. Angka tersebut akan memberi tahu Anda seberapa banyak pekerjaan yang menanti. Build yang bersih pada percobaan pertama jarang terjadi tetapi tidak mustahil untuk C modern yang mematuhi standar. Beberapa ratus error adalah hal yang biasa untuk codebase lama dengan banyak casting pointer.

Perbaiki error dalam urutan ini: casting integer-ke-pointer terlebih dahulu, lalu aritmatika pointer melintasi alokasi, kemudian assembly inline. Setiap kategori memiliki perbaikan mekanis. Proyek CHERI menerbitkan panduan porting dengan contoh sebelum-dan-sesudah untuk pola-pola yang paling umum.

Perangkat keras capability bukan masa depan yang teoritis. Ini adalah toolchain yang berfungsi, ABI yang didukung, dan jalur migrasi yang tidak mengharuskan Anda membakar codebase Anda hingga rata dengan tanah. Mulailah dengan satu file, satu fungsi, satu capability yang dipersempit. Perangkat keras akan menyelesaikan sisanya.