Sebuah broker dapat mengalokasikan objek berbasis memori, mengisi isinya, lalu mengirim file descriptor objek itu ke proses lain melalui UNIX domain socket. Penerima dapat memperlakukan byte tersebut sebagai konfigurasi immutable, kode terkompilasi, atau artefak terserialisasi. Asumsi itu tidak aman jika pengirim atau pemegang lain masih dapat mengubah inode yang sama setelah validasi. Linux memfd_create() dan file seal menyediakan cara yang dipaksakan kernel untuk mempersempit permukaan mutasi tersebut tanpa memberi objek sebuah pathname filesystem persisten.
Properti keamanan berasal dari seal, bukan dari sifat anonim. Memfd adalah file dengan semantik descriptor dan mapping normal; tanpa seal yang relevan, pemegang akses tulis dapat mengubah isinya. Sealing mengubah operasi mutasi tertentu menjadi pembatasan kernel permanen yang berlaku pada setiap descriptor yang merujuk ke inode tersebut.
Sealing adalah properti inode, bukan janji pada satu descriptor
memfd_create() mengembalikan descriptor yang terbuka untuk baca dan tulis. Penggunaan MFD_ALLOW_SEALING membuat file baru dimulai dengan set seal kosong. Tanpa flag itu, set awal berisi F_SEAL_SEAL, yang memblokir penambahan seal lain dan dengan demikian mencegah transisi berikutnya ke keadaan yang lebih ketat.
Seal ditambahkan dengan fcntl(fd, F_ADD_SEALS, mask) dan diperiksa dengan F_GET_SEALS. Seal melekat pada inode, bukan pada satu descriptor. Proses tidak dapat menghindari seal yang sudah diterapkan dengan menduplikasi descriptor, mewariskannya melalui fork(), atau menerima descriptor lain untuk objek yang sama. Seal dapat ditambahkan, tetapi tidak dapat dihapus.
Karakter ini cocok untuk serah terima produsen-konsumen. Produsen dapat membentuk objek saat masih mutable, menambahkan seal yang diperlukan, memeriksa mask seal hasilnya, lalu mengekspos descriptor kepada komponen dengan tingkat kepercayaan lebih rendah. Transfer descriptor sendiri tidak menghasilkan immutability; keadaan seal yang menghasilkan batas tersebut.
Seal penulisan dan seal ukuran mencakup operasi yang berbeda
F_SEAL_WRITE memblokir penulisan biasa dan pembuatan shared writable mapping baru. Seal ini juga memblokir hole punching yang akan mengubah isi file. Seal tidak dapat ditambahkan selama shared writable mapping masih ada; F_ADD_SEALS gagal dengan EBUSY pada kondisi tersebut. Produsen karena itu harus menghapus mapping semacam itu sebelum menyelesaikan write seal yang ketat.
Ukuran memiliki kontrol terpisah. F_SEAL_SHRINK mencegah pengecilan file, sedangkan F_SEAL_GROW mencegah pembesaran. Konsumen yang mengharapkan byte stabil sekaligus panjang stabil memerlukan seal ukuran yang sesuai bersama write seal. Menganggap F_SEAL_WRITE sebagai jaminan bentuk objek yang lengkap akan menyisakan operasi perubahan ukuran di luar batas yang dimaksud.
F_SEAL_SEAL memiliki peran lain: membekukan set seal itu sendiri. Seal ini lazim diterapkan setelah pembatasan yang diinginkan sudah ada. Penerapan terlalu awal mengunci objek pada keadaan seal saat itu dan mencegah penambahan berikutnya.
Serah terima ringkas dapat memakai urutan seperti berikut:
int fd = memfd_create("policy", MFD_CLOEXEC | MFD_ALLOW_SEALING);
/* populate fd, then remove shared writable mappings */
int seals = F_SEAL_WRITE |
F_SEAL_GROW |
F_SEAL_SHRINK |
F_SEAL_SEAL;
if (fcntl(fd, F_ADD_SEALS, seals) == -1)
/* abort handoff */;Hasil pemanggilan harus diperiksa. Desain yang terus berjalan setelah operasi sealing gagal hanya memiliki konvensi aplikasi, bukan pembatasan kernel yang dituju.
F_SEAL_FUTURE_WRITE sengaja mempertahankan mapping lama
F_SEAL_FUTURE_WRITE melayani model berbagi yang berbeda. Seal ini memblokir write() dan mencegah pembuatan shared writable mapping baru, tetapi shared writable mapping yang dibuat sebelum seal tetap dapat mengubah file. Dengan demikian, satu proses dapat mempertahankan mapping mutable sementara penerima berikutnya dibatasi pada jalur akses read-only.
Properti tersebut berguna untuk shared buffer aktif, tetapi lebih lemah daripada serah terima immutable. Penerima yang memvalidasi byte lalu mengasumsikan byte tidak dapat berubah tidak boleh menyamakan F_SEAL_FUTURE_WRITE dengan F_SEAL_WRITE selama writable mapping sebelumnya masih dapat bertahan.
Perbedaannya bersifat temporal. F_SEAL_WRITE mensyaratkan shared writable mapping yang berkonflik sudah tidak ada sebelum seal diterima. F_SEAL_FUTURE_WRITE dapat berdampingan dengan mapping terdahulu dan hanya membatasi jalur tulis berikutnya. Tinjauan keamanan karena itu perlu memperhitungkan mapping yang dibuat sebelum transisi seal, bukan hanya descriptor yang dipegang sesudahnya.
Masa hidup dan pewarisan descriptor tetap menjadi batas terpisah
Memfd yang sudah di-seal tetap mengikuti aturan masa hidup descriptor biasa. MFD_CLOEXEC menetapkan FD_CLOEXEC pada descriptor hasil sehingga descriptor ditutup saat execve() berhasil. Tanpa close-on-exec, descriptor dapat melintasi batas eksekusi dan tersedia bagi program yang semula tidak dimaksudkan untuk menerimanya.
File sealing tidak menggantikan disiplin kapabilitas descriptor. Konsumen read-only masih mungkin dapat menduplikasi atau meneruskan descriptornya. Akses terhadap byte tetap merupakan akses terhadap byte; seal membatasi mutasi, bukan pengungkapan atau delegasi lanjutan.
Nama memfd juga tidak menyediakan batas otorisasi. Nama itu muncul pada link descriptor di /proc untuk keperluan diagnostik, tetapi tidak berfungsi sebagai pathname filesystem yang kerahasiaannya mengendalikan akses. Otoritas yang relevan adalah kepemilikan atau perolehan descriptor, dengan tunduk pada kontrol proses dan /proc di sekitarnya.
Status executable memiliki semantik seal tersendiri
Linux modern juga mendefinisikan F_SEAL_EXEC. Seal ini mencegah perubahan berikutnya pada execute mode bits dan secara implisit menambahkan seal grow, shrink, write, serta future-write. Pembatasan tulis implisit tersebut menjaga memfd executable agar tidak tetap bebas dimutasi setelah transisi itu.
Linux juga menyediakan flag pembuatan dan kebijakan pid namespace untuk memfd non-executable. MFD_NOEXEC_SEAL membuat memfd non-executable dengan F_SEAL_EXEC terpasang, sedangkan MFD_EXEC meminta status executable. Kebijakan vm.memfd_noexec dapat mengubah atau membatasi perilaku untuk pemanggilan yang tidak menyertakan flag status executable secara eksplisit.
Kontrol ini bergantung pada versi kernel dan kebijakan deployment. Kode yang mengandalkannya memerlukan kontrak versi kernel minimum yang eksplisit dan perlu memperlakukan kebijakan namespace sebagai bagian dari lingkungan eksekusi, bukan sebagai properti yang disimpulkan dari nama memfd.
Seal membatasi mutasi, bukan mengautentikasi asal
Memfd dengan write seal penuh dapat menyediakan rangkaian byte stabil setelah transisi sealing, tetapi tidak mengidentifikasi pihak yang menghasilkan byte tersebut atau memastikan bahwa isinya telah disetujui. Jika penyerang mengendalikan produsen sebelum sealing, kernel dapat mempertahankan konten pilihan penyerang dengan tepat.
Hal ini memisahkan dua pertanyaan keamanan. File seal menjawab apakah operasi mutasi tertentu masih mungkin dilakukan setelah transisi. Asal dan persetujuan memerlukan mekanisme lain, seperti produsen tepercaya, digest terverifikasi, kebijakan signature, atau control channel terautentikasi yang mengikat descriptor ke konten yang diharapkan.
Pemisahan yang sama berlaku pada parsing. Byte stabil menghapus satu kelas race mutasi time-of-check/time-of-use antarkomponen, tetapi data immutable yang malformed tetap dapat memicu cacat parser. Sealing memperkuat batas stabilitas objek; sealing tidak memvalidasi format objek.
Serah terima terkuat adalah transisi keadaan yang terverifikasi
Serah terima memfd yang kuat ditentukan oleh keadaan kernel yang dapat diamati: produsen menyelesaikan mutasi, menghapus writable mapping yang tidak kompatibel, menambahkan seal yang diperlukan, memeriksa keberhasilan, lalu mentransfer descriptor hanya setelah transisi. Penerima dapat memakai F_GET_SEALS ketika kebijakannya bergantung pada set seal tertentu, alih-alih mempercayai metadata dari pengirim.
Model ini menjaga jaminan tetap sempit dan dapat diuji. F_SEAL_WRITE menutup jalur tulis berikutnya hanya setelah shared writable mapping terdahulu sudah tidak ada; seal ukuran menstabilkan panjang; F_SEAL_SEAL mencegah penambahan seal berikutnya; kontrol status executable menangani dimensi permission yang terpisah. Jika digabungkan secara sengaja, mekanisme ini mengubah file bersama yang mutable menjadi objek dengan permukaan mutasi tersisa yang ditentukan kernel, bukan konvensi proses.