Mes contributions à LibVMI : cinq PR, une issue, une branche en attente
Catégorie : LibVMI
Résumé
Entre le 24 septembre et le 12 octobre 2026 : cinq PR, dont quatre fusionnées, une issue d'analyse, une branche validée mais pas proposée, et quatre branches de preuves sur mon fork. Tout tourne autour d'une seule question, comment LibVMI s'attache à un Windows qui n'est pas celui pour lequel il a été écrit, mais chaque pièce a sa propre histoire. Ce billet les liste avec leur état, leur raison d'être et ce que le mainteneur en a dit. Le détail technique est dans le billet sur le support de Windows 11, Server et 32 bits.
Le tableau
| Pièce | Sujet | Ouverte | État |
|---|---|---|---|
| #1123 | prendre le CR3 d'un vCPU qui exécute du code noyau à l'init | 27/09 | fusionnée le 27/09 |
| #1124 | repli sur un scan physique par pas de 2 Mo quand tous les contextes vCPU échouent | fin septembre | fusionnée le 30/09, sans effet sur Windows 11 |
| #1125 | issue : l'attache pendant le démarrage, trois cas, et Windows 10 sans profil | fin septembre | ouverte |
| #1126 | échec immédiat si la pagination du guest est désactivée | début octobre | fusionnée le 02/10 |
| #1127 | décoder le KdDebuggerDataBlock encodé, noyaux non contigus, CR3 d'init périmé |
08/10 | fusionnée le 10/10 |
| #1128 | invités 32 bits, base noyau depuis l'IDT sous KVA shadowing, échantillonnage des vCPU, version depuis l'en-tête PE | 11/10 | ouverte |
| branche locale | cas B et C de #1125 (noyau pas encore démarré, PsInitialSystemProcess NULL) |
validée, pas proposée |
Branches de preuves sur le fork : test-evidence/early-boot-2026-09-30, test-evidence/win10-kdbg-2026-10-06, test-evidence/kdbg-final-2026-10-08, test-evidence/32bit-win7-8-2026-10-11. Chacune contient les résumés, les extraits de logs, les captures d'écran et les sorties de CI cités dans les PR, avec un SHA256SUMS.
#1123 : le CR3 du vCPU 0
Le point de départ est drakvuf#1860, un « gel » de DRAKVUF au démarrage. Le backtrace montrait LibVMI en train de scanner toute la RAM à la recherche de System, parce que le vCPU 0 exécutait du code utilisateur à l'instant de l'attache et que son CR3 ne mappait pas le noyau. Le correctif prend le CR3 d'un autre vCPU dont le RIP est en espace noyau. 38 lignes, testées sur Win10 22H2 : 48 attaches après trois redémarrages de l'hôte, 0 gel, contre 2 démarrages sur 3 qui gelaient avant.
Réponse de Tamas : les utilisateurs de DRAKVUF passent normalement le kpgd en ligne de commande, et le test CI Debian bullseye peut être retiré. J'ai répondu que -k et ce correctif sont complémentaires, la valeur changeant à chaque démarrage, et j'ai retiré bullseye de container-compile.yml dans la même PR. Fusionnée le jour même.
#1124 : la PR qui ne faisait rien
Un scan de 2 Mo en 2 Mo pour retrouver le KDBG quand les contextes vCPU sont illisibles. Mes tests semblaient bons. En relisant les logs, le chemin passait par le JSON et mon code n'avait jamais tourné ; une autre série finissait en segfault avec 0 bloc trouvé. J'ai mis une bannière « do not merge » et corrigé mon commentaire. Elle a été fusionnée quand même. J'ai alors fait un A/B de non-régression (20 runs à froid, avec et sans ses commits) : pas de régression, pas de bénéfice. Elle est inoffensive, et elle m'a appris à mettre une ligne de log dans toute fonction dont je prétends qu'elle a tourné.
#1125 : l'issue qui explique le symptôme
Plutôt qu'une nouvelle PR, une issue avec l'analyse complète : le KDBG est encodé sur Windows 11 (6 builds sur 6), donc aucune recherche en clair ne peut réussir ; le vrai problème est l'instant de l'attache, avec trois cas selon l'état du guest, dont un où LibVMI annonce un succès avec un kpgd faux ; une carte du démarrage identique sur les 6 builds ; et un correctif minimal pour le premier cas, lié dans l'issue avant d'être proposé. Le 6 octobre, j'y ai ajouté les résultats de Windows 10 sans profil (LibVMI officiel échoue 14 fois sur 14, ma variante avec décodeur passe sur 8 builds et échoue sur 3 sans que je sache encore pourquoi). L'issue reste ouverte comme fil de discussion ; les cas B et C ne sont pas en amont.
#1126 : échouer vite
Le correctif du cas A, proposé après l'issue : si la pagination du guest est désactivée, init_core échoue tout de suite avec un message explicite. 327 à 418 ms au lieu de 200 s et plus, 6 builds sur 6, CI 10 sur 10, ASan 0, non-régression Linux. Fusionnée le 2 octobre.
#1127 : le décodeur
La plus grosse. Décodage du bloc sans symboles (la constante se déduit du KdVersionBlock), gestion des noyaux dont l'image n'est pas contiguë en mémoire physique (la raison des trois échecs de Windows 10), et rafraîchissement du CR3 d'init s'il est devenu périmé pendant la recherche. Windows 10 x64 sans profil : 14 sur 14 contre 0 sur 14 ; Windows 11 : 6 sur 6. Complexité du code 3389 contre 3440 sur master. Ouverte le 8 octobre, fusionnée le 10, sans demande de changement.
#1128 : 32 bits, Windows 7, 8 et Server
Deux bugs trouvés avec 14 builds Windows 10 x86 : le noyau 32 bits n'était jamais reconnu (LibVMI ne testait que des adresses 64 bits), et la base du noyau tirée de l'IDT était fausse sous KVA shadowing quand le vCPU 0 était en mode utilisateur. Puis, après un premier commentaire de review qui demandait Windows Server : deux commits de plus, l'échantillonnage des vCPU avant d'abandonner la recherche du KPCR (Server 2012 avait ses deux vCPU en mode utilisateur) et la version de Windows prise dans l'en-tête PE plutôt que devinée d'après la taille de l'en-tête du KDBG. Bilan dans la PR : 50 invités, 100 attaches, tout passe. Ouverte le 11 octobre, toujours ouverte.
La branche des cas B et C
Six changements pour que LibVMI échoue en 0,35 s au lieu de 692 s quand winload tourne, et refuse un PsInitialSystemProcess NULL au lieu d'annoncer un faux succès. Validée sur 9 fixtures et 30 attaches live, ASan 0, CI complète. Pas proposée : j'attends que #1128 soit traitée pour ne pas empiler trois PR sur les mêmes fichiers.
Ce qui reste à faire
- Suivre #1128 et la branche B/C.
vmi_init_complete()ne détruit pas l'instance à l'échec (fuite déjà présente avant moi), etpage_moden'est pas redétecté parvmi_init_os().- Le pointeur nul dans
find_kdbg_address_fast(kdbg.c:767) vu depuis DRAKVUF avec une attache trop tôt.
Sources
- Les PR et l'issue listées, et les quatre branches de preuves du fork.