Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Instruction — avec quelle chaîne bâtir le kernel d’Air ?

PROMUE EN ADR-155 le 2026-08-13. C’est l’ADR qui fait autorité. Ce document reste la trace de l’instruction — le raisonnement, les alternatives pesées, et l’ordre dans lequel les quatre décisions sont tombées. On ne le réécrit pas.

Date : 2026-08-13. Statut : instruction ouverte, décision de chaîne non prise — mais le Rust dans le kernel est tranché (BDFL, 2026-08-13 : oui), ce qui contraint fortement le reste. Deviendra un ADR dès que la décision sera arrêtée — elle est structurante (elle engage la reproductibilité, le durcissement, et le nombre de chaînes à figer).

Pourquoi la question se pose maintenant

Air devra produire une image installable, donc un kernel. Ce kernel est le premier artefact du projet qui ne soit ni du Rust ni de la libc d’Air : il est écrit en C, et il faut choisir avec quoi le compiler. Le choix n’est pas neutre — il décide de fonctions de sécurité disponibles ou non, et du nombre de chaînes que ADR-025 devra figer.

Ce qui n’est PAS en jeu

Une idée reçue mérite d’être écartée d’entrée : bâtir le kernel avec Clang n’a rien d’expérimental. Le support est en amont (make LLVM=1), x86_64 et arm64 sont des cibles de premier rang du projet ClangBuiltLinux, et c’est ce qui tourne sur la quasi-totalité des appareils Android ainsi que sur la flotte de production de Google.

La question n’est donc pas « est-ce que ça marche », mais « qu’est-ce que chacune apporte que l’autre n’a pas ».

Les critères qui décident, pour Air

Ils ne sont pas les mêmes que pour une distribution généraliste, et c’est ce qui rend la réponse différente de la sienne :

  1. Périmètre matériel — Linux tier-1, x86_64 et ARM64, rien d’autre (ADR-004, ADR-014).
  2. Reproductibilité — un même tag Git doit produire des artefacts bit-pour-bit identiques (ADR-025). Chaque chaîne supplémentaire est une chaîne de plus à pinner, et une source de non-déterminisme de plus à surveiller.
  3. Posture de durcissement — le projet borne quel programme démarre (ADR-046 D6, ADR-153). Ce qui borne ce qu’un flot de contrôle détourné peut faire s’y ajoute au lieu de s’y substituer.
  4. Nombre de pièces mouvantes — le reste du stack est déjà entièrement LLVM : rustc l’est par construction, les cibles *-unknown-linux-air aussi, et un audit de chaîne clang/LLVM existe déjà.

Ce que chaque chaîne apporte seule

Clang / LLVMGCC
kCFI (Control Flow Integrity kernel)❌ aucun équivalent
ShadowCallStack (arm64)
LTO / ThinLTO sur le kernel❌ non supporté en amont
KMSAN (mémoire non initialisée)
Une seule chaîne pour toutes les archesLLVM=1 ARCH=…❌ une cross-chaîne par arche
Architectures exotiques (m68k, alpha, parisc…)✅ — sans objet pour Air
Plugin latent_entropy
Plugin stackleak⚠️ à vérifier
randstruct✅ (natif, Clang 15+)✅ (plugin)
Exposition de terrain sur distributionsmoindre✅ majoritaire

Disponibles des deux côtés, donc hors débat : KASAN, KCSAN, -ftrivial-auto-var-init (INIT_STACK_ALL_ZERO), les protections de pile classiques.

Recommandation provisoire : Clang

Deux raisons, et la seconde pèse plus lourd que la première.

Les fonctions exclusives sont dans l’axe du projet. kCFI et ShadowCallStack bornent ce qu’un détournement de flot de contrôle peut accomplir — exactement la couche qui manque au-dessus du seuil d’exécution. GCC n’a rien à leur opposer.

Une chaîne de moins à figer. Le stack est déjà tout LLVM. Choisir GCC pour le seul kernel ajouterait une seconde chaîne à pinner, à reproduire et à faire vieillir, dans un projet dont ADR-025 exige le bit-pour-bit. Ce n’est pas un argument de confort : c’est une classe entière de non-déterminisme évitée.

Ce qu’il faut vérifier avant de trancher

Trois points, tous factuels, aucun tranché ici :

  1. Les versions minimales réelles, sur Documentation/process/changes.rst de la version ciblée — c’est la seule source qui fasse foi, et elle bouge. De mémoire, les 6.x récents demandent Clang/LLVM 13.0.1 et GCC autour de 5.1 (relevé à 8.1 sur les plus récents) ; certaines options exigent nettement plus haut (asm goto with outputs : Clang 15+). À relire, pas à recopier d’ici.
  2. L’état de stackleak côté Clang sur cette même version. C’est le seul durcissement où GCC pourrait manquer. S’il n’existe pas, il faut décider si on s’en passe, et le dire.
  3. Voulons-nous du Rust dans le kernel ? TRANCHÉ le 2026-08-13 (BDFL) : oui. Voir la section suivante, qui en tire les conséquences — elles sont plus lourdes que prévu.

Ce que le Rust dans le kernel change (décision BDFL du 2026-08-13)

Le cadre bascule. Tant qu’il ne s’agissait que de C, la question était « quelle chaîne choisir ». Avec du Rust dans le kernel, elle devient « une chaîne, ou deux » — et c’est une question différente.

LLVM est dans le lot, quel que soit le choix

Deux raisons indépendantes, et il suffit de l’une :

  • rustc est LLVM par construction. Il n’existe pas de version d’rustc qui ne le soit pas (gccrs et rustc_codegen_gcc existent, mais ne sont pas ce sur quoi Rust-for-Linux s’appuie) ;
  • bindgen utilise libclang pour lire les en-têtes C du kernel et en dériver les liaisons Rust. Il est obligatoire pour bâtir le Rust du kernel.

Conséquence : choisir GCC ne retire pas LLVM de l’ensemble à figer — cela y ajoute GCC. L’arbitrage n’est donc pas « LLVM contre GCC » mais « LLVM seul contre LLVM + GCC », dans un projet dont ADR-025 exige le bit-pour-bit sur chaque chaîne pinnée.

Ce qu’un kernel bâti avec GCC ferait perdre au Rust

Rust-for-Linux fonctionne sur un kernel bâti avec GCC : ce n’est pas un obstacle fonctionnel, et il faut le dire pour ne pas caricaturer. Ce qui se perd est ailleurs, et c’est précisément ce qui motivait le choix :

  • kCFI ne couvre pas le Rust sans parité LLVM entre clang et rustc. Un kernel où le C est protégé et le Rust ne l’est pas offre au premier venu la moitié non protégée — le contraire de ce qu’on cherche en écrivant du Rust ;
  • le LTO à l’échelle du kernel (C et Rust) exige la même LLVM des deux côtés.

Autrement dit : avec GCC, on entretient deux chaînes et on renonce aux deux fonctions qui justifiaient l’exercice.

Ce que cela ajoute à vérifier

  • La version d’rustc que la version ciblée du kernel accepte — Rust-for-Linux est exigeant et documente une version précise, pas seulement un plancher. make LLVM=1 rustavailable dit ce qui manque, et c’est l’outil à croire.
  • La version minimale de bindgen, documentée au même endroit.
  • La parité de version LLVM entre le clang retenu et la LLVM embarquée par rustc — la condition de kCFI et du LTO ci-dessus.
  • Les architectures que Rust-for-Linux supporte : x86_64 et arm64 en font partie, donc le périmètre tier-1 d’Air est couvert. À reconfirmer sur la version ciblée.

Effet sur la recommandation

Elle passe de raisonnable à quasi contrainte. Les trois décisions prises le 2026-08-13 — Rust dans le kernel, un seul kernel (le nôtre), une seule rustc — réduisent l’espace restant à presque rien : LLVM est obligatoire, la parité de version l’est aussi pour que kCFI couvre le Rust, et il ne reste à décider que si l’on ajoute GCC par-dessus pour deux plugins. Le seul motif restant de préférer GCC serait un durcissement qu’il détiendrait seul et qu’on jugerait indispensable — en pratique latent_entropy, et stackleak si son support Clang manque toujours. Il faudrait alors accepter, en échange : deux chaînes à figer, kCFI qui ne couvre pas le Rust, et pas de LTO. C’est un prix élevé pour deux plugins.

Deux planchers, et ils n’ont rien à voir (décision BDFL du 2026-08-13)

La question « Rust-in-kernel est-il possible sur 6.12 ? » naît d’une confusion qu’il faut dissiper une fois pour toutes, parce qu’elle reviendra.

Ce que c’estCe qu’il contraint
Plancher d’exécution — Linux 6.12 LTSLa version minimale sur laquelle le userland d’Air sait tourner (io_uring mature, Landlock, cgroups v2, eBPF complet — macro-architecture-fr.md)Les syscalls et ABI que la couche 0 peut supposer
Le kernel qu’Air produit pour son imageCe qu’on compile et livreRust-in-kernel, kCFI, LTO, le triplet de chaîne

Rust-in-kernel est une propriété du kernel qu’on BÂTIT, pas de celui sur lequel on s’exécute. Les deux planchers ne se rencontrent que dans un cas : vouloir qu’un module kernel Air écrit en Rust se charge sur un noyau qu’on n’a pas bâti.

Tranché : non — Air ne livre que son propre kernel. Trois conséquences, toutes libératrices :

  1. La maturité de Rust sur 6.12 est sans objet. Elle ne serait un sujet que pour du code destiné à un noyau étranger. Le plancher 6.12 reste ce qu’il est — un plancher d’exécution — et n’a jamais rien dit du kernel livré ;
  2. Aucun contrat de compatibilité de modules. Les abstractions Rust du kernel changent d’une version à l’autre ; cela ne devient un coût qu’au moment où nous décidons de monter de version, jamais imposé par un tiers. On peut donc utiliser les abstractions les plus récentes sans réserve ;
  3. On choisit et on fige le triplet kernel + rustc + bindgen, avec clang en parité LLVM — exactement ce qu’ADR-025 demande. La confiance ne vient pas d’un numéro de version du noyau, elle vient de ce qu’on le bâtit soi-même.

Le point de vigilance qui subsiste, et il est contre-intuitif

Ce n’est pas « rustc trop ancienne ». C’est l’inverse : le Rust du kernel s’appuie sur des fonctionnalités instables du langage, et une rustc nettement plus récente que le noyau casse régulièrement la construction. Le kernel accepte un minimum depuis qu’il a abandonné l’exigence d’une version exacte, mais « accepté » n’est pas « testé ».

Air épingle aujourd’hui rustc 1.96.0 (rust-toolchain.toml). L’appariement naturel est donc un noyau contemporain de cette version — pas une LTS de deux ans plus tôt.

Tranché le 2026-08-13 (BDFL) : une SEULE rustc pour le userland et le kernel. Même arbitrage qu’au-dessus — une chaîne, pas deux — appliqué un étage plus bas, et cohérent avec lui. rust-toolchain.toml reste la source unique.

Il faut en écrire le prix, parce qu’il est réel et qu’il se paiera :

  • Le choix de version du noyau devient contraint par le pin rustc, et réciproquement. On ne peut pas viser un noyau dont le Rust refuse la rustc épinglée, ni monter la rustc sans vérifier que le noyau suit ;
  • Monter de version devient un geste couplé : userland et kernel bougent ensemble. Un besoin userland — une fonctionnalité de langage, une correction — ne peut plus se satisfaire seul ; il attend le noyau, ou l’entraîne.

Ce que la décision précédente rend ce prix payable : Air ne livrant que son propre kernel et n’ayant aucun contrat de compatibilité de modules, faire monter les deux de concert est un geste coordonné, pas une rupture. C’est exactement pourquoi les deux décisions se tiennent : prise isolément, celle-ci serait pénible ; adossée à la précédente, elle ne coûte qu’un ordre de manœuvre.

Conséquence outillable : le jour où un kernel entre dans la construction, un contrôle doit vérifier que la rustc de rust-toolchain.toml est bien celle que ce noyau accepte (make LLVM=1 rustavailable). Sans ce contrôle, la contrainte n’existe que dans ce document — et un document ne fait pas échouer une construction.

Alternatives rejetées

  • Bâtir avec les deux (GCC en production, Clang en vérification, ou l’inverse). Rejeté : double le coût de pinning et de reproductibilité pour un bénéfice — la détection de bugs propres à une chaîne — qui relève de l’amont, pas de nous. Air n’a pas les moyens d’être le laboratoire de deux chaînes.
  • Suivre le choix de la distribution hôte. Rejeté : Air produit sa propre image ; se caler sur une distribution reviendrait à hériter d’un choix fait pour un périmètre matériel qui n’est pas le nôtre — c’est précisément le raisonnement qu’ADR-004 écarte.
  • Reporter le choix jusqu’à l’image. Rejeté : les fonctions exclusives (kCFI, SCS) se décident à la configuration du kernel, et une image bâtie sans elles ne les gagne pas après coup sans tout reconstruire. Autant le savoir avant d’écrire le premier defconfig.

Ce qui déclencherait un ADR

La décision elle-même. Elle engage la reproductibilité (ADR-025), la posture de durcissement, et le nombre de chaînes que le projet entretient — les trois critères qui font qu’une décision n’est pas un détail d’implémentation.