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

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 :

  1. Périmètre matériel — Linux tier-1, x86_64 et ARM64, rien d’autre (ADR-004, ADR-014) ;
  2. Reproductibilité — chaque chaîne supplémentaire est une chaîne de plus à figer (ADR-025) ;
  3. 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 ;
  4. 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 :

  1. Les versions minimales de clang, rustc et bindgen de la version ciblée, dans Documentation/process/changes.rst et Documentation/rust/quick-start.rst — la seule source qui fasse foi, et elle bouge ;
  2. L’état du support stackleak côté Clang — s’il manque, acter qu’on s’en passe ;
  3. 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