Akar Masalah: VT Switching, Delayed Release, dan Spoofing

Terminal emulator berbasis kernel mode-setting (KMS) seperti kmscon beroperasi langsung di atas subsistem DRM/KMS tanpa X server atau Wayland compositor. Ketika berganti virtual terminal (VT switching), hak akses ke perangkat grafis (DRM-Master) harus dialihkan antar-sesi secara instan.

Masalah utama integrasi kmscon dengan session manager (seperti systemd-logind) berkisar pada tiga titik kritis:

  • DRM Master Race Condition: Kernel Linux hanya mengizinkan satu DRM-Master aktif per primary node (/dev/dri/cardX). Saat VT beralih dari sesi A ke sesi B, jika sesi A terlambat melepas master via drmDropMaster() sebelum sesi B memanggil drmSetMaster(), sesi B gagal dengan galat -EACCES atau -EBUSY.
  • Sinyal Terlambat (Delayed Release): Kontrak D-Bus asinkron mengirimkan sinyal PauseDevice. Jika kmscon memblokir loop pemrosesan D-Bus (misalnya terhambat I/O lambat atau alokasi buffer), logind mengasumsikan klien tidak responsif, memutus akses kontrol secara paksa, dan menyebabkan konsol hang.
  • Socket Credential Spoofing: Pada komunikasi socket lokal internal antara kontroler konsol dan worker process, kegagalan memverifikasi identitas proses lawan membuka celah eskalasi hak akses jika socket IPC dapat diakses oleh UID non-root.

Autentikasi Peer IPC via SO_PEERCRED

Komunikasi IPC melalui UNIX domain socket harus memvalidasi kredensial peer pada saat koneksi terjalin untuk mencegah spoofing. Linux menyediakan opsi socket SO_PEERCRED yang mengembalikan PID, UID, dan GID proses lawan secara aman dari konteks kernel.

#define _GNU_SOURCE
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>

int validate_ipc_peer(int sock_fd, uid_t required_uid) {
    struct ucred cred;
    socklen_t len = sizeof(struct ucred);

    if (getsockopt(sock_fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0) {
        return -errno;
    }

    if (cred.uid != required_uid) {
        return -EACCES;
    }

    return 0;
}

Fungsi tersebut memastikan hanya pesan dari UID tepercaya (misalnya 0 untuk root/daemon) yang diproses. Jika transfer file descriptor DRM dilakukan melalui socket, gunakan kontrol pesan SCM_RIGHTS yang dipadukan dengan SCM_CREDENTIALS untuk validasi per-pesan.

Desain Kontrak Pesan Idempoten untuk DRM Control

Interaksi antara kmscon dan API org.freedesktop.login1.Session harus dirancang secara idempoten. Panggilan ganda atau sinyal yang datang di luar urutan (out-of-order) tidak boleh merusak state aplikasi.

1. Claim Device (TakeDevice)

Saat sesi aktif, kmscon memanggil method TakeDevice(major, minor). Logind mengembalikan file descriptor perangkat DRM beserta flag paused. Operasi ini harus idempoten: jika FD sudah terbuka, kmscon memakai kembali resource yang ada tanpa melakukan re-inisialisasi pipeline rendering.

2. Pause Device (PauseDeviceComplete)

Logind memancarkan sinyal PauseDevice(major, minor, type) sebelum sesi beralih. Tipe penanganan terbagi dua:

  • force: Sesi telah ditutup paksa oleh kernel/logind. Segera nonaktifkan rendering loop; jangan panggil drmDropMaster() karena fd sudah tidak valid.
  • normal: Lepas kontrol DRM sesegera mungkin, lalu kirim kembali konfirmasi PauseDeviceComplete(major, minor).

Implementasi State Machine Non-Blocking

Untuk menghindari deadlock saat VT switching, transisi state DRM harus dieksekusi tanpa pemanggilan fungsi blocking I/O di dalam event loop (misalnya epoll).

#include <xf86drm.h>
#include <errno.h>

typedef enum {
    STATE_INACTIVE,
    STATE_ACTIVE,
    STATE_PAUSING
} SessionState;

int handle_session_pause(int drm_fd, SessionState *state) {
    if (*state == STATE_INACTIVE) {
        return 0; /* Operasi idempoten */
    }

    *state = STATE_PAUSING;

    /* Drop master tanpa menunggu thread lain */
    if (drmDropMaster(drm_fd) < 0) {
        if (errno != EACCES && errno != EINVAL) {
            return -errno;
        }
    }

    *state = STATE_INACTIVE;
    return 0;
}

Kode di atas menangani EACCES dan EINVAL sebagai respons valid. Jika kernel telah mencabut hak DRM-Master sebelum aplikasi mengeksekusi drmDropMaster, sistem tetap menganggap status perangkat telah nonaktif tanpa menghentikan loop aplikasi.

Trade-off dan Debugging

Pendekatan asinkron berbasis event-driven menuntut sinkronisasi buffer render yang ketat. Jika thread rendering tetap menulis ke dumb buffer setelah state berubah menjadi STATE_INACTIVE, frame tearing atau crash subsistem grafis dapat terjadi.

Metode Debugging IPC dan DRM

  • Periksa traffic D-Bus logind secara real-time:
    busctl monitor org.freedesktop.login1
  • Aktifkan tracing DRM kernel untuk memverifikasi peralihan master:
    echo 0x1e > /sys/module/drm/parameters/debug
  • Pantau status seat perangkat:
    loginctl show-session $XDG_SESSION_ID