ADR-155 — Le kernel d’Air se bâtit avec Clang/LLVM, et avec une seule chaîne
Statut : Accepté (2026-08-13, BDFL). Sert ADR-025 — un même tag doit produire des artefacts bit-pour-bit identiques — et prolonge le seuil d’exécution d’ ADR-046 D6, en bornant ce qu’un flot de contrôle détourné peut accomplir dans le noyau.
Catégorie : Fondateur outillage + sécurité. Décide la chaîne de construction du kernel qu’Air livrera dans son image, et les pins qui l’accompagnent.
Promu depuis l’instruction
instruction-chaine-construction-kernel-fr.md,
qui reste la trace du raisonnement et des alternatives pesées.
Contexte
Air devra produire une image installable, donc un kernel. C’est le premier artefact du projet qui ne soit ni du Rust ni la libc d’Air : il est écrit en C, et il faut choisir avec quoi le compiler.
Une idée reçue devait ê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’était donc
pas « est-ce que ça marche » mais « qu’est-ce que chacune apporte que l’autre n’a pas ».
Quatre critères décident, et ils ne sont pas ceux d’une distribution généraliste :
- Périmètre matériel — Linux tier-1, x86_64 et ARM64, rien d’autre (ADR-004, ADR-014) ;
- Reproductibilité — chaque chaîne supplémentaire est une chaîne de plus à figer (ADR-025) ;
- Posture de durcissement — Air borne quel programme démarre ; ce qui borne ce qu’un flot de contrôle détourné peut faire s’y ajoute au lieu de s’y substituer ;
- Nombre de pièces mouvantes — le reste du stack est déjà entièrement LLVM.
Décision
D1 — Le kernel se bâtit avec Clang/LLVM (make LLVM=1)
Et GCC n’est pas ajouté par-dessus.
D2 — Il y a du Rust dans le kernel
Décidé, et c’est ce qui fait basculer le cadre : la question cesse d’être « quelle chaîne » pour devenir « une chaîne, ou deux ».
LLVM est dans le lot quel que soit le choix, pour deux raisons indépendantes dont une
seule suffirait : rustc est LLVM par construction, et bindgen — obligatoire pour dériver
les liaisons Rust des en-têtes C du kernel — utilise libclang. Choisir GCC ne retirerait
donc pas LLVM de l’ensemble à figer : cela y ajouterait GCC.
D3 — Air ne livre que son propre kernel
Aucun module kernel d’Air n’a vocation à se charger sur un noyau qu’Air n’a pas bâti.
Cela dissipe une confusion durable : le plancher 6.12 (LTS) de
macro-architecture-fr.md est un plancher
d’exécution — ce sur quoi le userland d’Air sait tourner — et n’a jamais rien dit du
kernel qu’Air bâtit. Rust-in-kernel est une propriété du second, pas du premier.
D4 — Une seule rustc pour le userland et le kernel
rust-toolchain.toml reste la source unique. Même arbitrage que D1, un étage plus bas.
D5 — Parité de version LLVM entre clang et rustc
C’est la condition pour que kCFI couvre le code Rust et pour que le LTO s’étende à tout le noyau. Sans elle, on obtient un kernel dont le C est protégé et le Rust ne l’est pas — soit l’inverse de ce qu’on cherche en écrivant du Rust.
Conséquences
Ce que Clang apporte et que GCC n’a pas : kCFI, ShadowCallStack (arm64), LTO/ThinLTO sur
le kernel, KMSAN, et une seule chaîne pour les deux architectures (LLVM=1 ARCH=…, sans
cross-chaîne par arche).
Ce que GCC aurait apporté, et pourquoi cela ne pèse pas. Deux plugins de durcissement —
latent_entropy, et stackleak si son support Clang manque toujours. Mais aucun des deux
n’est actif par défaut : ce sont des options opt-in, qui exigent CONFIG_GCC_PLUGINS, les
en-têtes gcc-plugin-dev, et se paient en performance pour stackleak. Bâtir avec GCC ne les
donne pas — cela donne le droit de les activer.
Leurs substituts existent des deux côtés : CONFIG_INIT_STACK_ALL_ZERO
(-ftrivial-auto-var-init=zero) couvre une large part de ce que vise stackleak, et
l’entropie de démarrage que cherchait latent_entropy est aujourd’hui fournie par le
matériel (RDRAND/RDSEED, RNDR) et le bootloader. Le kernel a d’ailleurs réduit sa
dépendance aux plugins GCC — randstruct a été réimplémenté nativement pour s’en affranchir.
Le prix de D4, et il se paiera. Le choix de version du noyau devient contraint par le pin
rustc, et réciproquement ; monter de version devient un geste couplé — userland et
kernel ensemble. Un besoin userland ne pourra plus se satisfaire seul : il attendra le noyau,
ou l’entraînera. D3 rend ce prix payable : sans contrat de compatibilité de modules, monter
les deux de concert est un geste coordonné, pas une rupture.
Le point de vigilance est contre-intuitif. Le risque n’est pas une 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. Air
épingle aujourd’hui rustc 1.96.0 ; l’appariement naturel est un noyau contemporain, pas
une LTS de deux ans plus tôt.
Un contrôle reste à outiller. Le jour où un kernel entre dans la construction, un gate
doit vérifier que la rustc de rust-toolchain.toml est celle que ce noyau accepte
(make LLVM=1 rustavailable). Sans lui, D4 et D5 n’existent que dans ce document — et un
document ne fait pas échouer une construction.
Alternatives rejetées
- GCC seul. Rejeté : avec du Rust dans le kernel, LLVM reste requis (
rustc,bindgen), donc GCC serait une chaîne en plus, pas à la place. Et l’on perdrait kCFI sur le Rust et le LTO. - Les deux chaînes (l’une en production, l’autre en vérification). Rejeté : double le coût de pinning et de reproductibilité pour un bénéfice — débusquer les bugs propres à une chaîne — qui relève de l’amont. 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 — le raisonnement qu’ADR-004 écarte.
- Reporter le choix jusqu’à l’image. Rejeté : kCFI et ShadowCallStack se décident à la
configuration du kernel ; 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. - Deux pins
rustc(un userland, un kernel). Rejeté par D4 : desserrerait la contrainte de version au prix d’une chaîne de plus à figer — le contraire de ce que D1 vient de décider.
Ce qui reste à vérifier à la construction
Ces points sont factuels et ne remettent pas la décision en cause ; ils conditionnent la configuration :
- Les versions minimales de
clang,rustcetbindgende la version ciblée, dansDocumentation/process/changes.rstetDocumentation/rust/quick-start.rst— la seule source qui fasse foi, et elle bouge ; - L’état du support
stackleakcôté Clang — s’il manque, acter qu’on s’en passe ; - Les architectures que Rust-for-Linux supporte sur cette version : x86_64 et arm64 doivent l’être, ce qui couvre le tier-1 d’Air.
Licence du document : MPL 2.0