File descriptor yang dikembalikan memfd_create() dapat bermula sebagai file anonim yang writable dan dapat diubah ukurannya, lalu beralih menjadi objek dengan operasi mutasi yang telah dikurangi secara permanen. Linux menerapkan transisi tersebut melalui file seal. Mekanisme ini melekat pada file yang mendasarinya, bukan pada satu descriptor, sehingga pengiriman descriptor duplikat melintasi batas proses tidak membentuk state sealing yang terpisah.
Properti ini membuat sealing lebih dari sekadar fasilitas untuk penyimpanan sementara. Sealing mengubah otoritas yang dibawa setiap descriptor yang merujuk ke objek memfd yang sama. Transisinya monoton: seal dapat ditambahkan, tetapi tidak dapat dihapus.
Sealing merupakan pembatasan pada level inode
memfd_create() membuat file anonim dan mengembalikan file descriptor terbuka. Objek tersebut berperilaku seperti file reguler untuk operasi seperti ftruncate(), write(), pread(), dan mmap(), sesuai flag dan permission yang relevan bagi tiap operasi.
Sealing harus diaktifkan saat objek dibuat:
int fd = memfd_create("payload", MFD_CLOEXEC | MFD_ALLOW_SEALING);Tanpa MFD_ALLOW_SEALING, Linux membuat file dengan F_SEAL_SEAL yang sudah terpasang sehingga penambahan seal berikutnya terhalang. Jika sealing diaktifkan, set seal awal kosong.
Proses menambahkan pembatasan melalui fcntl(fd, F_ADD_SEALS, mask) dan membaca set saat ini melalui fcntl(fd, F_GET_SEALS). Set seal merupakan milik objek file. dup(), pengiriman descriptor melalui Unix-domain socket, dan cara lain untuk memperoleh descriptor ke objek yang sama tidak menyalin file ke domain sealing baru.
Cakupan ini merupakan bagian utama dari batas capability. Jika satu pemegang berhasil menambahkan F_SEAL_GROW, pemegang lain tidak dapat melewati pembatasan tersebut hanya dengan memakai descriptor yang diperoleh sebelumnya.
Seal ukuran memisahkan pertumbuhan dari pemangkasan
F_SEAL_GROW mencegah ukuran file bertambah. F_SEAL_SHRINK mencegah ukurannya berkurang. Keduanya menjadi pembatasan terpisah karena perubahan ukuran membawa konsekuensi berbeda bagi konsumen.
Produsen dapat mengisi memfd, menetapkan panjang final, lalu menambahkan kedua seal:
if (ftruncate(fd, payload_size) == -1)
return -1;
if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) == -1)
return -1;Setelah seal tersebut terpasang, operasi yang melanggar batas ukuran tetap akan gagal. Byte yang sudah ada belum menjadi immutable hanya karena seal ukuran. Pemegang masih dapat mengubah data di dalam rentang yang telah ditetapkan kecuali seal lain menghapus otoritas tersebut.
Perbedaan ini penting bagi protokol yang mengomunikasikan offset ke objek bersama. Panjang tetap dapat menstabilkan rentang alamat yang valid tanpa menstabilkan nilai byte di dalamnya.
F_SEAL_WRITE memiliki prasyarat mapping
F_SEAL_WRITE menghapus otoritas write dengan cakupan lebih luas. Setelah terpasang, operasi berorientasi write terhadap file ditolak dan shared mapping writable baru tidak dapat dibuat.
Transisi ini tidak selalu dapat dilakukan seketika. Jika mapping MAP_SHARED yang writable terhadap file masih ada, penambahan F_SEAL_WRITE gagal dengan EBUSY. Linux menolak menyatakan properti immutability yang lebih kuat selama masih ada mapping yang dapat mengubah file.
Perilaku tersebut menampilkan batas lifecycle:
buat -> atur ukuran -> isi -> hapus shared mapping writable
-> tambah F_SEAL_WRITE -> distribusikanUrutan ini bukan sekadar pilihan gaya. Produsen yang mendistribusikan descriptor sebelum menyelesaikan transisi dapat memberi proses lain kesempatan membuat shared mapping writable. Mapping tersebut kemudian dapat menghalangi pemasangan F_SEAL_WRITE.
Protokol yang bergantung pada write sealing penuh karena itu memerlukan aturan kepemilikan untuk fase mutable. Transfer descriptor merupakan perubahan terhadap pihak yang dapat mengganggu transisi.
F_SEAL_FUTURE_WRITE memungkinkan handoff bertahap
Linux juga menyediakan F_SEAL_FUTURE_WRITE. Seal ini memblokir otoritas write baru sambil membiarkan shared mapping writable yang sudah ada tetap mengubah file melalui mapping tersebut.
Batas ini berbeda dari F_SEAL_WRITE. Produsen dapat mempertahankan mapping writable yang sudah ada untuk pembaruan terkendali sambil mencegah pembuatan mapping writable baru dan operasi write biasa memperluas otoritas mutasi ke jalur tambahan. Mapping lama tetap menjadi pengecualian yang disengaja.
Perbedaannya dapat diamati. Konsumen yang menerima descriptor setelah F_SEAL_FUTURE_WRITE tidak dapat menyimpulkan bahwa byte sudah immutable karena mapping writable yang dibuat sebelumnya mungkin masih mengubahnya. Seal tersebut membatasi perolehan akses write pada masa berikutnya; seal itu tidak mencabut akses write yang sudah direpresentasikan mapping tersebut.
Untuk protokol publikasi, F_SEAL_FUTURE_WRITE cocok hanya jika writer yang tersisa memang menjadi bagian dari kontrak. Menyamakan seal ini dengan F_SEAL_WRITE akan menghilangkan kanal mutasi aktif dari model.
F_SEAL_SEAL menutup transisi kebijakan
F_SEAL_SEAL mencegah penambahan seal apa pun setelahnya. Seal ini tidak memperkuat pembatasan data secara mandiri; fungsinya membekukan kebijakan seal saat ini.
Urutan karena itu penting. Menambahkan F_SEAL_SEAL sebelum seal data yang dibutuhkan akan mencegah pembatasan yang belum ada dipasang secara permanen. Transisi final dapat menggabungkan seal yang diinginkan dalam satu operasi F_ADD_SEALS:
int seals = F_SEAL_GROW |
F_SEAL_SHRINK |
F_SEAL_WRITE |
F_SEAL_SEAL;
if (fcntl(fd, F_ADD_SEALS, seals) == -1)
return -1;Pemanggilan ini tetap bergantung pada prasyarat setiap seal yang diminta. Secara khusus, shared mapping writable yang tidak kompatibel dapat membuat operasi gagal alih-alih meninggalkan caller dengan state final yang diminta. Kode harus memeriksa hasil sebelum memperlakukan objek sebagai sealed.
F_GET_SEALS kemudian dapat menampilkan state kebijakan yang dipelihara kernel kepada penerima. Pemeriksaan state tersebut lebih kuat daripada mempercayai metadata yang dikirim bersama descriptor karena pembatasan diterapkan pada operasi terhadap objek itu sendiri.
Seal membatasi mutasi, bukan seluruh bentuk identitas atau persistensi
memfd yang sudah sealed tetap merupakan file descriptor yang merujuk ke objek kernel dengan lifetime yang ditentukan oleh referensi ke objek tersebut. Sealing tidak memberikan pathname persisten, tidak mengubah penyimpanan volatile menjadi penyimpanan durable, dan tidak membuat nomor descriptor bermakna secara global.
Sealing juga tidak membuat interpretasi pada level aplikasi aman secara otomatis. Penerima tetap memerlukan pemeriksaan batas, validasi format, aturan versi, dan sinkronisasi yang sesuai dengan protokol data. Seal dapat mencegah mutasi tertentu yang terlihat kernel; seal tidak memvalidasi byte yang sudah ada sebelum pembatasan dipasang.
Properti arsitektural yang berguna lebih sempit sekaligus lebih kuat: produsen mutable dapat secara sengaja mengurangi kumpulan operasi kernel yang boleh dilakukan pemegang berikutnya terhadap objek file yang sama. Karena pengurangan tersebut monoton dan dibagi di antara descriptor, kernel dapat menerapkan transisi capability yang tidak dapat direplikasi oleh bookkeeping lokal proses setelah descriptor melintasi batas proses.
Publikasi bergantung pada titik transisi
Batas paling penting berada pada saat produsen berhenti menjadi pemilik mutable dan mulai mendistribusikan objek terbatas. Sebelum titik itu, mapping writable, truncation, dan write dapat menjadi bagian valid dari konstruksi. Setelah seal yang dibutuhkan dikonfirmasi, operasi tersebut dapat dikeluarkan sesuai mask yang terpasang.
Transisi ini memberi protokol berbasis descriptor perubahan state konkret, bukan konvensi seperti “jangan ubah setelah dikirim”. Transisi tersebut juga membuat race terlihat pada perilaku API: shared mapping writable yang tersisa dapat memblokir write sealing penuh, F_SEAL_SEAL yang terlalu awal dapat membekukan kebijakan yang belum lengkap, dan F_SEAL_FUTURE_WRITE dapat dengan sengaja mempertahankan writer yang sudah ada.
Kontrak akhirnya presisi ketika setiap seal diperlakukan sebagai otoritas tertentu yang dihapus. Ukuran tetap, write yang diblokir, mapping lama yang dipertahankan, dan set seal yang dibekukan merupakan properti terpisah. Kombinasi yang disengaja menghasilkan capability berbasis file dengan permukaan mutasi yang sesuai dengan protokol lintas proses yang membawanya.