Topik wawancara representatif

Wawancara Umum: Bagaimana Cara Unix Domain Socket Mengirimkan File Descriptor?

UmumSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Dua proses lokal berkoordinasi melalui Unix domain socket: proses berhak istimewa membuka file atau membuat listening socket dan menyerahkannya ke worker yang kurang berhak istimewa. Jelaskan cara meneruskan descriptor, apa yang diterima oleh penerima, dan bagaimana menangani risiko pemotongan (truncation) dan otorisasi.

Prompt dan konteks

Dua proses lokal berkoordinasi melalui Unix domain socket: proses berhak istimewa membuka file atau membuat listening socket dan menyerahkannya ke worker yang kurang berhak istimewa. Jelaskan cara meneruskan descriptor, apa yang diterima oleh penerima, dan bagaimana menangani risiko pemotongan (truncation) dan otorisasi.

Ini menguji IPC Linux/Unix, perbedaan antara tabel file-descriptor dan open file description, serta protokol ancillary-data dari sendmsg/recvmsg. Linux unix(7) mendefinisikan SCM_RIGHTS untuk mengirim atau menerima sekumpulan file descriptor yang terbuka antar proses.

Apa yang diuji oleh pewawancara

  • Membedakan bilangan bulat fd lokal proses dari open file description milik kernel.
  • Mengetahui bahwa SCM_RIGHTS menggunakan ancillary data daripada menempatkan bilangan bulat dalam payload biasa.
  • Mengukur cmsghdr, CMSG_SPACE, dan CMSG_LEN dengan benar, serta memeriksa MSG_CTRUNC.
  • Mencakup izin path socket, identitas pengirim, batas sumber daya, waktu penutupan (close timing), dan pembersihan kegagalan.

Pertanyaan untuk diklarifikasi terlebih dahulu

  • Apakah proses-proses tersebut berbagi pengguna, dan di mana letak penurunan hak istimewa atau batas kepercayaan (trust boundary)?
  • Apakah descriptor berupa file reguler, socket yang terhubung, listening socket, epoll fd, atau device fd?
  • Apakah salurannya adalah SOCK_STREAM atau SOCK_DGRAM, dan apakah protokol memerlukan batas pesan serta tanda terima (acknowledgements)?
  • Bisakah penerima memverifikasi kredensial pengirim, jenis sumber daya, properti hanya-baca (read-only), dan jumlah fd yang diharapkan?

Jawaban 30 detik

Saya akan membuat pasangan Unix domain socket, membawa descriptor dalam pesan kontrol SOL_SOCKET/SCM_RIGHTS dari sendmsg, dan menempatkan versi protokol serta ID permintaan dalam payload sebenarnya. Penerima akan memanggil recvmsg dengan buffer CMSG_SPACE yang cukup besar, memeriksa level, tipe, panjang, dan MSG_CTRUNC, lalu menggunakan fd yang diterima sebagai bilangan bulat baru dalam prosesnya sendiri. Yang melintasi batasan tersebut adalah referensi ke open file description, sehingga penerima biasanya mendapatkan nomor fd yang berbeda. Protokol akan memverifikasi kredensial peer, membatasi jumlah, menyetel close-on-exec, dan menutup descriptor yang tidak diterima atau tidak digunakan pada setiap jalur kesalahan.

Pembahasan mendalam langkah demi langkah

Membedakan nomor fd dari open file description

Sebuah fd adalah indeks bilangan bulat dalam tabel fd milik suatu proses. Open file description adalah objek kernel yang menyimpan status terbuka seperti offset file dan flag status. SCM_RIGHTS menyalin referensi ke objek kernel tersebut; penerima biasanya mendapatkan bilangan bulat fd yang berbeda, yang secara semantik mirip dengan menduplikasi fd ke dalam tabel fd proses lain.

Menggunakan ancillary data alih-alih byte biasa

sendmsg dan recvmsg membawa rangkaian rekaman cmsghdr melalui msghdr.msg_control. Atur cmsg_level ke SOL_SOCKET, cmsg_type ke SCM_RIGHTS, dan letakkan array integer fd di area data. Payload biasa dapat membawa versi, tujuan, dan ID acknowledgement, tetapi tidak dapat menggantikan pesan kontrol.

Mengukur buffer kontrol dengan benar

Untuk jumlah sebenarnya, pengirim menggunakan CMSG_LEN(n * sizeof(int)) untuk cmsg_len; penerima mengalokasikan ruang yang disejajarkan (aligned space) setidaknya sebesar CMSG_SPACE(n * sizeof(int)). Parse dengan CMSG_FIRSTHDR dan CMSG_NXTHDR, menolak panjang yang terlalu pendek dan tipe yang tidak diharapkan.

Menangani pemotongan (truncation) dan batas aliran (stream boundaries)

Jika buffer kontrol penerima terlalu kecil, ancillary data dapat terpotong atau dibuang dan MSG_CTRUNC akan disetel; penerima tidak boleh menggunakan daftar descriptor yang parsial. Linux memerlukan setidaknya satu byte nyata bersama ancillary data pada SOCK_STREAM, dan ancillary data membentuk receive barrier, jadi ikat pesan kontrol ke ID permintaan daripada mengandalkan posisi byte-stream.

Menetapkan batas identitas dan otorisasi

Izin direktori dan socket adalah batas pertama untuk filesystem socket. Server juga harus menggunakan SO_PEERCRED atau SCM_CREDENTIALS dan mengonfirmasi penyewa (tenant), tujuan, dan jenis sumber daya pada lapisan aplikasi. Menerima fd tidak memberikan otoritas ekstra dengan sendirinya; pengirim hanya boleh mentransfer referensi yang sah.

Mengelola masa pakai dan batas sumber daya

Pengirim dapat menutup fd-nya sendiri setelah mengirim, tetapi kernel tetap menyimpan referensi in-flight sampai penerima menerimanya. Linux membatasi operasi ini dengan RLIMIT_NOFILE dan SCM_MAX_FD; halaman manual saat ini mencatat SCM_MAX_FD biasanya bernilai 253, sementara versi yang lebih lama menggunakan 255. Batasi descriptor per pesan dan per worker, serta buat penolakan dapat dipantau (observable).

c
struct msghdr msg = {0};
struct iovec iov = {.iov_base = "F", .iov_len = 1};
union { char buf[CMSG_SPACE(sizeof(int))]; struct cmsghdr align; } control;
msg.msg_iov = &iov; msg.msg_iovlen = 1;
msg.msg_control = control.buf; msg.msg_controllen = sizeof(control.buf);
struct cmsghdr *c = CMSG_FIRSTHDR(&msg);
c->cmsg_level = SOL_SOCKET; c->cmsg_type = SCM_RIGHTS;
c->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(c), &fd, sizeof(fd));
sendmsg(sock, &msg, MSG_NOSIGNAL);

Contoh jawaban yang kuat

Saya akan membuat ini sebagai protokol IPC lokal dengan acknowledgements. Pengirim menggunakan sendmsg pada Unix domain socket dengan pesan kontrol SOL_SOCKET/SCM_RIGHTS; payload sebenarnya membawa versi protokol, tujuan, dan ID permintaan. Penerima mengalokasikan ruang kontrol dengan CMSG_SPACE, memeriksa tipe, panjang, dan MSG_CTRUNC, memverifikasi kredensial peer, jumlah descriptor, dan jenis sumber daya, baru kemudian menyerahkannya ke worker. Poin semantik yang penting adalah bahwa transfer tersebut mereferensikan open file description, sehingga bilangan bulat penerima biasanya berbeda dan offset file atau status terbuka dapat dibagikan. Saya akan menyetel close-on-exec, membatasi descriptor in-flight, menangani RLIMIT_NOFILE dan SCM_MAX_FD, serta menutup dan mengaudit setiap jalur kegagalan.

Kesalahan umum

  • Menulis bilangan bulat fd ke dalam JSON atau payload byte dan berasumsi proses lain dapat langsung menggunakannya.
  • Mengabaikan perataan (alignment) CMSG_SPACE dan hanya mengalokasikan sizeof(int) untuk data kontrol.
  • Melewatkan pengecekan MSG_CTRUNC dan menggunakan daftar descriptor yang telah terpotong.
  • Hanya memeriksa path Unix socket tanpa memverifikasi kredensial peer dan tujuan.
  • Lupa bahwa penerima mendapatkan nomor fd baru atau gagal menentukan semantik status dan offset bersama.
  • Mengabaikan close-on-exec, batas fd, kegagalan kirim, dan penutupan descriptor yang tidak digunakan.

Tindak lanjut dan tanggapan

Apakah penerima mendapatkan file descriptor yang sama?

Biasanya bukan bilangan bulat yang sama. Kernel menyalin referensi ke open file description yang sama ke dalam tabel fd milik penerima, sehingga offset file dan beberapa status terbuka dapat dibagi bersama. Jika offset independen diperlukan, buka kembali file atau salin datanya alih-alih berasumsi nomor fd-nya sama.

Mengapa harus mengirim satu byte nyata?

Linux Unix stream socket memerlukan setidaknya satu byte nyata dalam sendmsg yang sama saat ancillary data dikirim; hal ini juga memungkinkan protokol untuk mengaitkan pesan kontrol dengan sebuah permintaan. Linux datagram dapat mengabaikannya, tetapi kode yang portabel harus tetap menyertakan satu byte nyata.

Bagaimana jika buffer kontrol terlalu kecil?

Ancillary data dapat terpotong atau dibuang dan MSG_CTRUNC disetel. Tutup descriptor yang tidak valid atau berlebih, kembalikan kesalahan protokol, dan catat kejadian tersebut; jangan pernah memperlakukan daftar parsial sebagai otorisasi lengkap.

Bagaimana cara mencegah proses berhak istimewa menyerahkan sumber daya yang salah?

Gunakan izin socket dan kredensial peer untuk membatasi koneksi, lalu ikat ID permintaan, tenant, tujuan, dan jenis sumber daya pada lapisan aplikasi. Pengirim hanya memilih dari allowlist; penerima memeriksa properti hanya-baca, status path atau socket, dan mengaudit setiap otorisasi dan penutupan.

Sumber publik

Pertanyaan terkait