Notifikasi Pengguna seccomp Memindahkan System Call Terpilih ke Supervisor

Filter seccomp tidak terbatas pada mengizinkan system call atau menolaknya di kernel. Saat filter mengembalikan SECCOMP_RET_USER_NOTIF, Linux dapat menahan thread pemanggil lalu mengirim deskripsi system call yang sedang dicoba ke supervisor di user space.

Mekanisme ini membentuk batas interposisi untuk call tertentu. Salah satu pemakaiannya adalah saat proses dengan privilege lebih rendah memerlukan operasi yang dimediasi proses lain, misalnya container manager yang menangani call yang tidak dapat dijalankan langsung oleh container. Batas ini sengaja lebih sempit daripada mesin kebijakan keamanan umum: state notifikasi dapat berpacu dengan memori target yang masih dapat berubah, dan dokumentasi kernel memperingatkan agar inspeksi supervisor tidak dijadikan primitive otorisasi.

Listener melekat pada filter

Proses memasang filter seccomp dengan SECCOMP_FILTER_FLAG_NEW_LISTENER. Jika berhasil, seccomp() mengembalikan listener file descriptor yang terkait dengan filter tersebut.

int listener = seccomp(SECCOMP_SET_MODE_FILTER,
                       SECCOMP_FILTER_FLAG_NEW_LISTENER,
                       &prog);

Program BPF menentukan system call yang menghasilkan notifikasi dengan mengembalikan SECCOMP_RET_USER_NOTIF. Call yang cocok dengan action lain tetap mengikuti semantik action tersebut.

Listener dapat dipindahkan ke supervisor melalui mekanisme pengiriman file descriptor biasa, termasuk SCM_RIGHTS melalui UNIX domain socket. Objek yang relevan adalah filternya: jika task yang memakai filter tersebut melakukan fork, notifikasi dari descendant yang mempertahankan filter dapat masuk melalui listener yang sama.

Sifat ini memisahkan routing notifikasi dari satu process identifier. Supervisor memantau event stream milik filter, bukan memasang kanal intersepsi terpisah pada setiap task.

Call yang menghasilkan notifikasi menunggu respons

Saat system call yang cocok mencapai filter, kernel tidak langsung mengeksekusinya. Thread pemanggil tertahan sementara notifikasi tersedia pada listener.

Supervisor mengambil event dengan SECCOMP_IOCTL_NOTIF_RECV:

struct seccomp_notif *req;

/* req dialokasikan memakai ukuran dari SECCOMP_GET_NOTIF_SIZES. */
if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, req) == -1) {
    /* tangani error */
}

Request memuat ID notifikasi, identifier task pemicu sebagaimana terlihat dari PID namespace milik listener, serta struct seccomp_data yang berisi nomor system call, nilai architecture, instruction pointer, dan nilai mentah argumen.

ID notifikasi mengikat respons berikutnya ke operasi pending tertentu. Supervisor dapat memeriksa ID melalui SECCOMP_IOCTL_NOTIF_ID_VALID sebelum bertindak atas state yang mungkin sudah kedaluwarsa.

Ukuran struktur diperoleh melalui SECCOMP_GET_NOTIF_SIZES, bukan diasumsikan dari header yang dipakai saat membangun supervisor. Interface kernel mengizinkan struktur notifikasi berkembang, sehingga alokasi berdasarkan ukuran yang dilaporkan menghindari asumsi bahwa layout saat build selalu sama dengan ABI saat runtime.

Argumen mentah bukan data pointer yang stabil

struct seccomp_data menangkap argumen system call pada tingkat register. Jika sebuah argumen berupa pointer, notifikasi membawa nilai pointer, bukan salinan immutable dari memori target yang ditunjuknya.

Ambil contoh argumen pathname. Supervisor dapat membaca memori target untuk memperoleh string, tetapi thread lain pada target dapat mengubah memori itu saat thread yang menghasilkan notifikasi masih tertahan. Keputusan berdasarkan satu pembacaan tidak membekukan byte yang nantinya dapat dikonsumsi kernel.

Perbedaan ini sangat penting pada SECCOMP_USER_NOTIF_FLAG_CONTINUE. Respons tersebut meminta kernel melanjutkan system call asli. Di antara inspeksi supervisor dan kelanjutan call, data yang ditunjuk pointer masih dapat berubah. Karena itu, dokumentasi seccomp user notification menyatakan jalur ini memiliki race time-of-check/time-of-use dan secara eksplisit menolaknya sebagai dasar keputusan kebijakan keamanan.

Interpretasi yang tepat lebih sempit: supervisor dapat memediasi operasi ketika batas keamanan lain sudah membatasi target dan ketika data target yang disalin diperlakukan sesuai sifat mutabilitasnya.

Respons dapat mengemulasi hasil atau melanjutkan eksekusi

Supervisor mengirim struct seccomp_notif_resp melalui SECCOMP_IOCTL_NOTIF_SEND. Respons membawa ID notifikasi yang sama dan dapat memberikan return value atau error tanpa menjalankan system call asli pada target.

struct seccomp_notif_resp resp = {
    .id = req->id,
    .val = -1,
    .error = EPERM,
    .flags = 0,
};

ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp);

Respons lain dapat mengaktifkan SECCOMP_USER_NOTIF_FLAG_CONTINUE, sehingga kernel menjalankan call asli milik target. Jalur tersebut tetap memakai penanganan kernel normal setelah batas notifikasi, tetapi juga membawa race argumen mutable yang sudah dijelaskan.

Emulasi memiliki batas lain. Jika operasi seharusnya menghasilkan file descriptor, mengembalikan integer saja tidak membuat open descriptor yang sesuai pada target. Linux menyediakan SECCOMP_IOCTL_NOTIF_ADDFD agar supervisor dapat memasang file descriptor ke descriptor table milik target. Dengan SECCOMP_ADDFD_FLAG_SEND, injeksi descriptor dan respons notifikasi dapat digabung secara atomik dari sudut pandang interface.

Karena itu, operasi yang menghasilkan descriptor berbeda secara material dari emulasi return value skalar. Hasilnya adalah state descriptor yang dikelola kernel, bukan sekadar angka yang ditempatkan pada return register.

Precedence filter tetap berlaku

User notification adalah salah satu action seccomp, bukan override untuk setiap filter yang terpasang pada task. Jika filter lain yang berlaku mengembalikan action dengan precedence lebih tinggi, listener tidak menerima call tersebut sebagai user notification.

Hal ini penting saat beberapa filter ditumpuk. Supervisor tidak dapat menganggap pemasangan listener menjamin visibilitas atas setiap call yang ditandai SECCOMP_RET_USER_NOTIF oleh program BPF miliknya. Kebijakan seccomp gabungan menentukan action yang menang.

Field architecture juga tetap menjadi bagian dari filtering syscall yang benar. Nomor system call saja tidak cukup pada arsitektur yang menyediakan beberapa calling convention. Filter yang mengambil keputusan berdasarkan nomor syscall perlu memperhitungkan nilai architecture sebelum menetapkan perilaku notifikasi.

Batasnya adalah mediasi, bukan otorisasi

Seccomp user notification memindahkan penanganan system call terpilih dari keputusan filter di kernel menjadi protokol request-response dengan proses di user space. Mekanisme ini memungkinkan emulasi, injeksi descriptor, dan continuation terkontrol tanpa menjadikan supervisor sebagai bagian dari kernel.

Batas yang sama juga menunjukkan keterbatasannya. Nilai register ditangkap, memori yang ditunjuk pointer dapat tetap berubah, filter dapat berinteraksi melalui precedence, dan target menunggu selama supervisor menangani setiap request pending. Karakteristik tersebut sesuai untuk mediasi system call ketika model keamanan di sekelilingnya sudah ditetapkan.

Menjadikan mekanisme ini sebagai lapisan otorisasi umum akan mengubah asumsi yang membuatnya berguna. Listener adalah kanal interposisi terkontrol; ia bukan transaksi yang membekukan seluruh state target sampai supervisor memberikan respons.