seccomp User Notification Mendelegasikan Syscall Terpilih ke Supervisor

Filter seccomp dapat menghentikan system call terpilih sebelum kernel mengeksekusinya, lalu mengirim notifikasi ke supervisor di user space. Thread target tetap terblokir saat supervisor menerima event dan mengirim disposisi. Perilaku ini mengubah hasil filter menjadi handoff terkontrol pada batas kernel dan user space.

Mekanismenya adalah SECCOMP_RET_USER_NOTIF. Berbeda dari action seccomp biasa, filter BPF tidak menyelesaikan keputusan sendirian. Sebuah listener file descriptor menjadi titik koordinasi untuk menerima notifikasi, mengirim respons, dan secara opsional menginjeksi file descriptor.

Listener terikat pada filter

Proses memasang filter seccomp dengan SECCOMP_FILTER_FLAG_NEW_LISTENER. Jika berhasil, seccomp() mengembalikan listener file descriptor yang terkait dengan filter tersebut. Rule yang mengembalikan SECCOMP_RET_USER_NOTIF membuat call yang cocok menghasilkan event pada descriptor ini.

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

Descriptor terikat pada filter, bukan hanya pada satu task. Jika beberapa task yang berbagi filter menghasilkan call yang cocok, notifikasinya masuk melalui listener yang sama. Descriptor juga dapat dipindahkan ke proses lain, misalnya dengan SCM_RIGHTS, sehingga supervisor terpisah dapat memiliki handling loop.

Hanya satu listener yang dibuat dengan SECCOMP_FILTER_FLAG_NEW_LISTENER yang dapat dipasang untuk sebuah thread. Batas ini menjadikan kepemilikan listener bagian dari konstruksi filter, bukan side channel tanpa batas yang dapat ditambahkan berulang kali.

Syscall yang cocok tertahan sebelum eksekusi

Untuk call yang cocok, SECCOMP_RET_USER_NOTIF tidak berarti “eksekusi lalu laporkan.” Call tersebut belum dieksekusi. Target terblokir di kernel sementara notifikasi disediakan untuk supervisor.

Jalur penerimaan memakai SECCOMP_IOCTL_NOTIF_RECV:

struct seccomp_notif req = {0};

if (ioctl(notify_fd, SECCOMP_IOCTL_NOTIF_RECV, &req) == -1) {
    /* handle error */
}

req.data membawa nomor syscall, nilai arsitektur, instruction pointer, dan nilai argumen register yang direpresentasikan oleh struct seccomp_data. Notifikasi juga membawa identifier dan PID task pemicu sebagaimana terlihat dari PID namespace milik listener.

Supervisor kemudian mengirim struct seccomp_notif_resp dengan SECCOMP_IOCTL_NOTIF_SEND. Supervisor dapat menetapkan return value atau error sehingga target kembali berjalan seolah call tersebut menghasilkan nilai itu.

target                kernel                 supervisor
  | syscall             |                        |
  |-------------------->|                        |
  |                      | USER_NOTIF event       |
  |       terblokir      |----------------------->|
  |                      |                        | putuskan
  |                      |<-----------------------|
  |<---------------------| respons                |
  | berjalan lagi        |                        |

Pemisahan ini memungkinkan proses dengan privilege lebih tinggi menjalankan operasi atas nama target yang dibatasi tanpa memberikan ambient privilege yang sama kepada target.

Data notifikasi bukan snapshot memori yang ditunjuk pointer

Argumen system call di struct seccomp_data adalah nilai register. Karena itu, argumen pointer berisi alamat, bukan salinan immutable dari byte yang tersimpan pada alamat tersebut.

Perbedaan ini merupakan batas penting. Supervisor yang membaca pathname atau objek lain melalui pointer dari memori target sedang mengamati memori proses yang mutable di luar struktur notifikasi seccomp. Thread lain pada target dapat mengubah memori tersebut ketika thread pemicu masih terblokir.

Karena itu, user-notification interface tidak cocok sebagai mesin security policy mandiri untuk keputusan yang bergantung pada dereference memori target. Secara khusus, menyetujui call lalu memakai SECCOMP_USER_NOTIF_FLAG_CONTINUE membuka interval time-of-check/time-of-use: memori yang diperiksa supervisor dapat berbeda dari memori yang kemudian dikonsumsi kernel.

Filter seccomp di kernel menghindari kelas dereference ini karena classic seccomp BPF bekerja pada metadata syscall di struct seccomp_data; filter tersebut tidak dapat mengikuti user pointer secara arbitrer.

CONTINUE mengembalikan eksekusi ke jalur kernel asli

Respons dapat menetapkan SECCOMP_USER_NOTIF_FLAG_CONTINUE. Dalam kondisi ini, syscall asli dilanjutkan di kernel dan tidak menerima hasil sintetis dari supervisor.

Opsi ini berguna ketika supervisor memutuskan bahwa jalur kernel normal harus menangani operasi. Namun, opsi tersebut bukan otorisasi atomik atas isi pointer yang mutable. Target dapat mengubah memori terkait di antara pemeriksaan supervisor dan konsumsi oleh kernel.

Desain yang kuat karena itu memperlakukan CONTINUE sebagai delegasi kembali ke enforcement normal kernel, bukan pengganti access control yang ditegakkan kernel. User-notification mechanism ditujukan untuk skenario mediasi seperti layanan privileged atas nama proses dengan privilege lebih rendah.

Identifier notifikasi menutup race pada lifetime request

Setiap event membawa id. Supervisor menyalin identifier tersebut ke respons. Target dapat hilang saat notifikasi sedang diproses, sehingga interface juga menyediakan SECCOMP_IOCTL_NOTIF_ID_VALID untuk memeriksa apakah identifier masih mengacu pada request yang aktif.

__u64 id = req.id;

if (ioctl(notify_fd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) == 0) {
    /* request is still valid */
}

Validasi tidak membekukan address space target. Perannya lebih sempit: supervisor dapat mendeteksi bahwa notifikasi sudah stale sebelum melakukan pekerjaan yang mengasumsikan request masih ada.

Perbedaan ini penting untuk operasi yang memiliki efek eksternal. Supervisor yang menjalankan aksi mahal atau privileged setelah request lenyap dapat menghasilkan state yang tidak akan pernah diterima oleh call target.

ADDFD memasukkan open file description ke target

Sebagian operasi yang diintersep secara alami menghasilkan file descriptor. Mengembalikan integer saja tidak cukup karena target harus menerima descriptor nyata yang merujuk pada open file description.

SECCOMP_IOCTL_NOTIF_ADDFD memungkinkan supervisor memasang descriptor ke target yang terkait dengan notifikasi pending. SECCOMP_ADDFD_FLAG_SEND dapat menggabungkan pemasangan descriptor dengan respons notifikasi, sehingga nomor descriptor yang diinjeksi menjadi return value yang diterima target.

Mekanisme ini menghindari rekonstruksi state descriptor melalui angka semu. Kernel melakukan insertion ke descriptor table sehingga semantik file descriptor nyata tetap berlaku pada proses target.

Precedence filter tetap berlaku

User notification mengikuti aturan precedence action seccomp yang normal. Jika filter lain yang terpasang menghasilkan action dengan precedence lebih tinggi daripada SECCOMP_RET_USER_NOTIF, supervisor tidak menerima notifikasi untuk call tersebut.

Listener karena itu tidak berada di luar seccomp filter stack. Ia merupakan salah satu hasil yang mungkin di dalam stack tersebut. Perilaku kill, trap, errno, trace, log, dan allow tetap mengikuti pengurutan action seccomp.

Jika listener tidak tersedia ketika filter mengembalikan SECCOMP_RET_USER_NOTIF, call gagal dengan ENOSYS. Jalur notifikasi bergantung pada hasil filter sekaligus listener aktif yang terpasang.

Delegasi adalah abstraksi yang bertahan

SECCOMP_RET_USER_NOTIF paling tepat dimodelkan sebagai delegasi syscall, bukan emulasi seluruh syscall layer di user space. Target berhenti pada batas kernel, supervisor menerima metadata syscall yang terbatas, lalu respons menentukan apakah target memperoleh hasil sintetis, melanjutkan eksekusi melalui kernel, atau menerima descriptor yang dipasang kernel.

Batas kerasnya sama penting: memori proses yang ditunjuk pointer tetap mutable, lifetime notifikasi dapat berakhir secara asinkron, dan precedence filter tetap aktif. Batas tersebut menjaga mekanisme ini berguna untuk mediasi privileged tanpa menjadikannya pengganti implisit bagi security policy kernel.