Ce projet fournit un Makefile qui automatise :
- la récupération des sources du noyau Linux 4.19.322 ;
- l’ajout d’un appel système personnalisé
get_pid_info; - la compilation et l’installation du noyau ;
- la création d’une image rootfs minimale (BusyBox) pour les tests avec QEMU ;
- la création et l’utilisation d’une VM Debian ;
- la compilation et l’injection de binaires de test dans le rootfs.
Toutes les commandes suivantes se lancent depuis la racine du dépôt.
Attention : certaines cibles exécutent des commandes
sudo
(installation du noyau, mise à jour de GRUB, création de VM, etc.).
Avant d’utiliser le Makefile, assurez-vous d’avoir :
- Un environnement Linux x86_64 avec les outils de build classiques :
gcc,make,wget,tar…
qemu-system-x86_64pour exécuter le noyau/les VM.- Les droits
sudopour :- installer un noyau dans
/usr/srcet/boot; - mettre à jour
initramfsetgrub; - gérer les fichiers dans
/lib/modules.
- installer un noyau dans
make
# ou
make allCette cible construit et installe le noyau Linux 4.19.322 dans le système en y ajoutant le syscall get_pid_info.
De manière résumée, elle effectue :
- Téléchargement de l’archive du noyau si nécessaire (
linux-4.19.322.tar.xz). - Décompression et copie des sources vers
/usr/src/linux-4.19.322. - Ajout du fichier
get_pid_info.cdans l’arborescence du noyau et mise à jour de la table des syscalls. - Ajout de
get_pid_info.odanskernel/Makefilesi nécessaire. - Configuration du noyau via
defconfig+ activation deCONFIG_VIRTIO_PCI=y. - Compilation du noyau (
bzImage) en utilisant tous les cœurs (moins 1). - Installation du noyau et des modules dans le système (
/boot,/lib/modules…). - Mise à jour de
initramfset degrub.
Utilisez cette cible si vous souhaitez tester ce noyau personnalisé directement sur la machine (en le sélectionnant ensuite dans le menu de boot).
make devCette cible est prévue pour le développement et les tests rapides dans une VM QEMU avec un rootfs minimal BusyBox.
Elle réalise notamment :
- Téléchargement + unpack du noyau dans
/usr/src/linux-4.19.322si besoin. - Configuration du noyau (
defconfig+CONFIG_VIRTIO_PCI=y). - Compilation du noyau dans
/usr/src/linux-4.19.322. - Création (ou réinstallation) de l’image rootfs de développement
disks/rootfs.ext4. - Compilation du binaire de test
bin/get_pid_info/tests/test-1. - Injection du binaire de test dans le rootfs.
- Lancement de QEMU via
./scripts/kernel-exec.shavec :- le noyau compilé (
arch/x86/boot/bzImage) ; - l’image rootfs
disks/rootfs.ext4.
- le noyau compilé (
Utilisez make dev si vous voulez tester le syscall et les binaires associés sans installer le noyau dans le système host.
make busyboxCette cible utilise QEMU pour démarrer le noyau + rootfs BusyBox déjà construits.
- Elle suppose que le noyau et le rootfs (
disks/rootfs.ext4) existent déjà. - Elle appelle le script
./scripts/kernel-exec.shavec les bons paramètres.
Pratique pour relancer rapidement l’environnement minimal sans recompiler le noyau.
make test-1-in-busyboxCette cible :
- Compile le binaire de test
bin/get_pid_info/tests/test-1(statiquement, avec les bons-Iverssrc/get_pid_info/include/). - Ajoute ce binaire dans le rootfs BusyBox via
./scripts/busybox.sh --add.
Ensuite, en démarrant la VM BusyBox (make busybox ou make dev), vous pourrez exécuter ce test depuis l’intérieur de la VM.
make vm-installCette cible sert à créer une VM Debian (image disque) via le script :
./scripts/vm.sh --install ./disks/debian.imgElle prépare une image ./disks/debian.img qui pourra ensuite être lancée.
make vm-launchLance la VM Debian précédemment installée :
./scripts/vm.sh --launch ./disks/debian.imgUtilisez ce couple vm-install / vm-launch si vous avez besoin d’un environnement Debian plus complet que le rootfs BusyBox minimal.
make remove-linuxSupprime le noyau 4.19.322 installé dans le système (fichiers /boot/vmlinuz-4.19.322, /lib/modules/4.19.322, etc.) et met à jour initramfs et grub, à condition que ce noyau ne soit pas celui actuellement en cours d’utilisation.
Concrètement :
- Si
uname -rne contient pas4.19.322, les fichiers liés à ce noyau sont supprimés etupdate-initramfs/update-grubsont exécutés. - Sinon, l’opération est annulée avec un message indiquant que le noyau est en cours d’utilisation.
make cleanActuellement, cette cible se contente d’afficher [CLEAN].
Elle est prévue pour être étendue si besoin (par exemple, nettoyage des builds noyau locaux).
make fcleanFait un nettoyage plus agressif :
- Appelle
make clean. - Supprime l’image rootfs de développement :
disks/rootfs.ext4 - Supprime l’archive du noyau :
linux-4.19.322.tar.xz
Utilisez-la pour revenir à un état quasi vierge (mais sans effacer les sources déjà décompressées dans /usr/src).
-
Installer le noyau patché sur la machine host
make # ou make allPuis sélectionner le noyau
4.19.322au boot. -
Développer/tester le syscall dans une VM minimaliste
make dev # compile + lance QEMU sur rootfs BusyBox(recommandé pour éviter de rebooter la machine host).
-
Relancer la VM BusyBox déjà prête
make busybox
-
Injecter (ou réinjecter) le test
test-1dans le rootfsmake test-1-in-busybox
-
Créer puis lancer une VM Debian plus complète
make vm-install make vm-launch
-
Désinstaller le noyau 4.19.322 installé dans le système
make remove-linux
-
Nettoyer les fichiers générés
make clean # léger make fclean # plus agressif (supprime aussi l’archive + rootfs)
Ce document décrit les principales structures, fonctions et macros provenant du noyau Linux et utilisées dans l’implémentation du syscall get_pid_info.
- Définie dans
<linux/sched.h>. - Représente un processus ou un thread dans le noyau.
- Contient notamment :
- les identifiants de process (PID, TGID, etc.) accessibles via des helpers comme
task_pid_nr(), - l’ID du parent via
task_ppid_nr(), - l’état d’ordonnancement (running, sleeping, zombie, etc.),
- les informations de temps (
start_time, etc.), - un pointeur vers la structure
fs_struct(task->fs), - l’adresse de la pile noyau (
task->stack), - les structures de planification, cgroups, etc.
- les identifiants de process (PID, TGID, etc.) accessibles via des helpers comme
Dans le code, on manipule un struct task_struct *task obtenu par get_pid_task().
- Définie dans
<linux/pid.h>. - Représente un identifiant de processus dans le noyau, avec gestion de références.
- C’est un objet plus riche qu’un simple
int: il gère les différents espaces de PID, les types (PID de thread, de groupe, etc.). - On l’obtient par exemple avec
find_vpid(pid)puis on demande latask_structassociée avecget_pid_task().
- Définie dans
<linux/fs_struct.h>. - Représente l’état « filesystem » d’un processus :
- répertoire courant (
pwd), - répertoire racine (
root) (par ex. après unchroot), umask,- références sur les chemins correspondants.
- répertoire courant (
- Accessible via le champ
task->fs. - Certains threads noyau n’ont pas de
fs_struct(d’où le testif (!fs)dans le code).
- Définie dans
<linux/path.h>. - Représente un chemin dans le VFS (Virtual File System) :
- un
struct vfsmount *mnt(montage), - un
struct dentry *dentry(entrée dans la dcache).
- un
- Utilisée avec
get_fs_root()etget_fs_pwd()pour stocker respectivement la racine et le répertoire courant d’un processus.
- Renvoie le temps courant de l’horloge monotone (MONOTONIC) en nanosecondes (
u64). - L’horloge monotone ne recule pas et est relative au boot de la machine (avec ajustements monotoniques éventuels).
- Le code l’utilise pour calculer l’âge du processus.
- Champ de
struct task_struct. - Timestamp de création du processus (fork/exec), en nanosecondes.
- En le combinant avec
ktime_get_ns(), on obtient la durée de vie du process.
- Macro définissant le nombre de nanosecondes dans une seconde :
1000000000. - Permet de convertir un intervalle exprimé en nanosecondes en secondes entières.
- Helper défini dans
<linux/sched.h>. - Convertit l’état interne du processus (
task->state,task->exit_state, etc.) en un caractère :'R': running,'S': sleeping,'D': uninterruptible sleep,'T': stopped/traced,'Z': zombie,- etc.
- Le code mappe ce caractère vers l’
enum process_state_eutilisateur (PROC_STATE_RUNNING,PROC_STATE_SLEEPING,PROC_STATE_ZOMBIE).
- Helper pour obtenir le PID (int) d’un
task_structdans l’espace PID visible. - Évite de gérer directement les structures
struct pid. - Utilisé dans le code pour remplir
kpid_info.pid.
- Similaire à
task_pid_nr(), mais renvoie l’ID du parent (PPID). - Utilisé pour remplir
kpid_info.parent_pid.
- Définie dans
<linux/pid.h>. - Recherche un objet
struct pid *pour le numéronrdans l’espace global des PID. - Incrémente le refcount sur l’objet
pidtrouvé. - Retourne NULL si aucun processus n’a ce PID.
- Toujours dans
<linux/pid.h>. - À partir d’un
struct pid *et d’un type (PIDTYPE_PID,PIDTYPE_TGID, etc.), renvoie lestruct task_struct *associé. - Incrémente le refcount de la task (comme
get_task_struct()). - Retourne NULL si aucune task ne correspond.
- Type de PID utilisé pour désigner un processus ou thread individuel.
- Passé à
get_pid_task()pour obtenir la task associée à ce PID « classique ».
- Fonction de gestion de référence sur les
task_struct. - Décrémente le refcount sur la task obtenu précédemment (ici via
get_pid_task()). - Lorsque le refcount atteint zéro, la structure peut être libérée.
- Primitives RCU (Read-Copy-Update) pour protéger les lectures sur des données partagées.
task->fsest protégé par RCU ; il doit être accédé sousrcu_read_lock().- Garantit que la valeur lue ne sera pas libérée tant que la section critique RCU est ouverte.
- Définie dans
<linux/fs_struct.h>. - Récupère la racine (
root) du process (éventuellement différente de/à cause dechroot). - Incrémente les références sur le
struct pathrésultat (et sous-jacents).
- Également dans
<linux/fs_struct.h>. - Récupère le répertoire courant (
pwd) du process. - Prend une référence sur le
struct pathretourné.
- Fonction du VFS qui convertit un
struct pathen chemin absolu lisible. - Retourne un pointeur dans le buffer
bufou un pointeur d’erreur (encodé viaERR_PTR()). - Utilisée pour répercuter les chemins root et cwd dans la struct utilisateur.
- Libère une référence sur un
struct pathobtenue parget_fs_root()ouget_fs_pwd(). - Décrémente les refcounts sur la dentry et le vfsmount associés.
- Constante définissant la taille maximale d’un chemin dans le noyau.
- Utilisée pour allouer le buffer temporaire passé à
d_path().
- Fonction d’allocation mémoire en espace noyau.
GFP_KERNEL: allocation standard, autorisée à dormir (contexte process).- Retourne un pointeur valide ou
NULLen cas d’échec.
- Libération de mémoire précédemment allouée par
kmallocou équivalent.
- Flag passé à
kmallocpour indiquer un contexte d’allocation « normal » côté noyau (peut dormir / faire du reclaim).
- Définie dans
<linux/uaccess.h>. - Copie
noctets depuis l’espace noyau (from) vers l’espace utilisateur (to). - Vérifie la validité de l’adresse utilisateur et gère les fautes de page.
- Retourne le nombre d’octets non copiés (0 si succès).
- Utilisée dans
struct pid_info __user *upid_info. - Indique qu’il s’agit d’un pointeur vers l’espace utilisateur.
- Aide les outils d’analyse statique et de vérification de sécurité à détecter les erreurs d’usage.
- Macro définie dans
<linux/syscalls.h>pour déclarer un syscall avec 2 arguments. - Exemple :
SYSCALL_DEFINE2(get_pid_info,
struct pid_info __user *, upid_info,
int, pid)- Génère la fonction avec la bonne signature et l’intègre à la table des syscalls (selon l’architecture et la configuration).
- Macros de log noyau :
pr_info: messages d’information,pr_err: messages d’erreur.
- S’appuient sur
printken interne. - Les messages sont visibles via
dmesgou le journal du système.
- Valeurs standard d’erreurs dans le noyau (définies dans
<linux/errno.h>), renvoyées par les syscalls :-EINVAL: argument invalide (par ex.upid_info == NULL),-ESRCH: processus introuvable (find_vpidouget_pid_taska échoué),-EFAULT: adresse utilisateur invalide ou non accessible (échec decopy_to_user).
- Initialise une zone mémoire à la valeur
csurnoctets. - Utilisée pour zero-iser la structure
kpid_infoavant remplissage.
- Copie une chaîne de
srcversdstavec limite de taille, en garantissant la terminaison\00sisize > 0. - Plus sûre que
strcpy, évite les dépassements de buffer (troncation contrôlée).
- Macro pour tester si un pointeur encode une erreur.
- Certaines fonctions (comme
d_path) renvoient soit un pointeur valide, soit un pointeur d’erreur créé avecERR_PTR(-errno). IS_ERR(p)permet de distinguer les deux cas.
- Champ de
struct task_structqui pointe vers la pile noyau du thread/process. - Dans votre code, il est utilisé pour renseigner
kpid_info.stack_pointer. - C’est une adresse en espace noyau, uniquement utile pour le debug / diagnostic, pas exploitable directement en espace utilisateur.