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

Audit FACE-LIBC — completeness critic pré-sceau couche-1-v1.0 (roadmap §5.1)

Rôle du document. Livrable de l’étape §5.1 de la roadmap couche 1 (docs/roadmap-couche1-libc-pal-fr.md). Il rejoue le périmètre libc v1 (ADR-046) contre l’inventaire réel des crates couche 1 et statue, famille par famille, si la brique couche 1 (API Rust) existe pour porter la face C correspondante. Ce n’est pas l’implémentation de la libc (qui vit au-dessus du sceau) : c’est le critère de complétude qui autorise le sceau couche-1-v1.0 (§5.4).

Méthode. S’appuie sur les 41 audits docs/notes/audits/audit-musl-*.md (périmètre de fonctions attendu par famille) et le RAPPORT-CONSOLIDE-libc-fr.md. L’inventaire couche 1 a été établi par lecture directe des pub fn/struct/enum des crates crates/. Tous les symboles cités sont réels (vérifiés par grep, pas devinés).

0. Rappel du cadre décisionnel (ce qui compte pour le verdict)

Trois clauses d’ADR-046 gouvernent l’analyse :

  • D2 — layering strict. La libc ne dépend QUE de la couche 1, jamais de la couche 0 (air-sys-*). Conséquence contraignante : « c’est à la couche 1 d’exposer tout ce dont la libc a besoin, y compris des passthroughs fidèles au kernel ». La couche 1 offre deux tiers : un tier kernel-faithful (fidèle, EINTR remonté, sans politique) et des helpers ergonomiques. La libc se lie au tier fidèle.
  • D4 — errno thread-local. Fourni en bordure (shim POSIX), jamais globale interne.
  • D7 — périmètre. ~25-30 familles ; cible pilote OpenSSH ~15 familles / 250-350 fn ; différés v1+ : aio, SysV IPC, mq, ldso, wordexp/glob, regex.

Grille de lecture pour « bloquant vs additif ». Le modèle de re-sceau de la couche 1 (roadmap §5.3/§5.4, précédent couche 0 ADR-051) est le descellement additif v1.x : les manques futurs s’ajoutent sans refonte des jalons scellés. Donc :

  • BLOQUANT pour le sceau = une brique fondatrice absente dont l’ajout obligerait à refondre une API déjà scellée (rupture de signature / de contrat figé).
  • ADDITIF = le manque se comble par un nouveau pub fn/pub use dans une crate existante (ou une crate neuve), sans toucher aux signatures scellées. La face C se construit au-dessus.

⚠️ Constat structurant, répété tout au long de cet audit. La couche 0 (air-sys-syscall) possède déjà la surface kernel complète dont la libc a besoin (openat2/read/write/lseek/dup_fd, signalfd_create/block_signals/kill/tgkill, famille terminal complète, ppoll, mmap/mprotect/munmap…). Mais la couche 1 n’en re-expose aujourd’hui qu’un sous-ensemble ergonomique curé. Le tier kernel-faithful que D2 impose à la couche 1 est donc, pour plusieurs familles fondatrices, pas encore construit. C’est exactement le travail annoncé par ADR-046 D2 (« la couche 1 doit grandir ») et séquencé par la roadmap en §5.3 (combler les manques additifs) avant §5.4 (sceller). Aucun de ces manques n’est bloquant au sens refonte — mais leur volume est substantiel et fondateur pour la cible OpenSSH.

1. Inventaire couche 1 (rappel synthétique des briques réelles)

CrateRôle libc-faceSymboles-clés réels
air-base-coreerrno, chemins octets, temps, encodages, log, idAirError/AirErrorKind/AirResult, AirPath, AirInstant/AirSystemTime/AirDuration, sleep/sleep_until, encoding::{base64,hex}, AirLog, AirUuid/AirId128
air-base-libi18n Unicode (icu4x), calendaireAirString (norm./casing-locale/segmentation/collation), AirLocale, AirDateTime/AirCalendar
air-libmmath no_stdpub use libm::* (surface fdlibm complète)
air-envenviron octets (lecture)get, vars/Vars, set_environ (pas de set/unset/secure_get)
air-stdiohandles bruts 0/1/2stdin/stdout/stderr, StandardStream::{read,write,is_terminal,lock}, reset_after_fork
air-terminaltty + modèle/codectty::{isatty,ttyname}, AirTerminal/AnsiCodec/TerminalCodec, modèle TermIn/TermOut, Winsize (re-export). Pas de tcgetattr/tcsetattr/PTY publics
air-filesystemfs haut-niveau + watchcanonicalize/resolve_within/exists, write_atomic/read_to_bytes, read_dir/glob, copy/copy_tree, AirTempDir, AirFileSystemWatcher (inotify). Pas de handle open/read/write/lseek générique
air-socketTCP/UDP/Unix + DNSAirTcpSocket/AirTcpListener/AirUdpSocket/AirUnix*, AirNameResolver/AirDnsSource (RFC1035), AirIpAddress::parse, send_fd/recv_fd
air-processclone3/exec/wait/pidfdAirCommand/AirProcess, wait_timeout/wait_until/try_wait, signal/kill (enfant), air_pipe, drop_privileges, exit_process. Re-export PollFd/PollEvents
air-threadmutex/rwlock/sem/chan + spawnAirMutex/AirRwLock/AirSemaphore, air_channel, atomic::*, AirThreadBuilder/join, runtime_primitives::{current_tid,create_thread,futex_wait_word}
air-runtimeTCB/TLS/errno/fork natifAirRuntime::{errno,set_errno,current_tid}, thread::spawn/JoinHandle, ThreadControlBlock, ThreadLocalStorage, fork::AtForkHandlers, start::bootstrap
air-memoryarènes/pool/slab + mappingAirArena/AirObjectPool/AirSlab, AirMemoryTracker, raw_mapping::{map_anonymous,unmap,RawMapping}
air-allocallocateur globalGlobalAllocator (impl GlobalAlloc), Arena::{allocate,deallocate,reallocate,allocate_zeroed}, reset_after_fork
air-cryptoCSPRNG + hash/AEAD/KDF/sigAirRandom::{fill,u64,generate_key}, AirHash/AirHmac/AirAead/AirHkdf, AirArgon2::{hash_password,verify_password}, AirSigningKey/AirEcdh*
air-configbackends /etc (pwd/grp/shadow…)PasswdBackend (emit/import passwd/group/shadow), AccountsDomain, accounts_config::Reader, BackendRegistry

2. Tableau famille → brique(s) couche 1 → statut → bloquant/additif

Légende : brique présente · partielle (manque précisé) · manquante · différée v1+ (ADR-046 D7). Colonne « B/A » : B = bloquant sceau · A = additif · = n/a.

2.1 Cœur userland + cible OpenSSH (~15 familles)

#Famille (audit musl)Brique(s) couche 1 réelle(s)StatutB/A
1errno (errno)air-base-core::AirError/AirErrorKind (round-trip errno()), shim thread-local air-runtime::AirRuntime::{errno,set_errno} + ThreadControlBlock::errno (D4)
2string / mem (string)air-base-core::AirPath (octets), encoding; air-base-lib::AirString (UTF-8). memcpy/strlen/strcmp/explicit_bzero = calcul pur sur core/alloc (aucune brique syscall requise)
3ctype (ctype)air-base-lib::AirString::to_*case_in_locale + propriétés icu4x ; ASCII isalpha/isdigit = pur sur core
4multibyte / wchar (multibyte)air-base-lib::AirString (UTF-8 garanti), segmentation grapheme/word/sentence (icu4x), NormalizationForm. La machine d’état C mbstate_t/mbrtowc se construit au-dessus (ADR-053)◐ (state-machine C = face-C)A
5locale / i18n (locale)air-base-lib::AirLocale (BCP 47), collation compare_in_locale, casing locale, normalisation — socle icu4x présent (ADR-053). setlocale/localeconv POSIX = face-C
6time (time)air-base-core::{AirInstant,AirSystemTime,AirDuration}, sleep/sleep_until (nanosleep, EINTR retry couche 1) ; air-base-lib::AirDateTime/AirCalendar (calcul calendaire icu4x). strftime/mktime/gmtime = formatage face-C au-dessus du calcul présent✅ (horloge/sleep/calcul) ; ◐ formatageA
7math (libm) (math/complex/fenv)air-libm = pub use libm::* (surface fdlibm complète, no_std). Prérequis ADR-053 satisfait✅ (base réelle) ; ⏭ complex/fenv avancésA
8malloc / stdlib (malloc,stdlib)air-alloc::GlobalAllocator (impl GlobalAlloc) + Arena::{allocate,deallocate,reallocate,allocate_zeroed} (malloc/free/calloc/realloc). atoi/strtol/qsort/bsearch/abs = pur sur core
9stdio (brut) (stdio)air-stdio : handles fd 0/1/2, read/write (1 syscall, EINTR remonté), lock/StreamGuard, is_terminal, reset_after_fork. FILE*/vfprintf/scanf/bufferisation = face-C au-dessus (normal, D7)✅ (brut requis)
10env (env)air-env::{get,vars,set_environ} (octets). Manque : setenv/putenv/unsetenv/clearenv (mutation différée, ADR-047) et secure_getenv (OpenSSH utilise setenv)A
11I/O fichier — unistd cœur (unistd,fcntl)Couche 0 complète (openat2/openat/read/write/pread/pwrite/lseek/close/dup_fd). Couche 1 : air-filesystem n’expose que des ops ergonomiques fichier-entier (write_atomic/read_to_bytes/copy) ; aucun handle open/read/write/lseek/fstat/dup/fcntl générique. air-process::air_pipe/AirPipeReader/Writer + air-socket couvrent read/write sur pipes/sockets uniquement◐ (tier passthrough fichier absent en couche 1)A (fondateur, à combler §5.3)
12stat (stat)Couche 0 statx (utilisé en interne par copy). Couche 1 : pas de stat/fstat/lstat public retournant les métadonnées ; seuls exists/AirFileType (via read_dir) affleurentA
13dirent (dirent)air-filesystem::read_dir (getdents64 → Vec<AirDirEntry> bornes ADR-032). DIR* streaming + seekdir/telldir/rewinddir = face-C au-dessus✅ (getdents64 couvert)
14poll / select (select)Couche 0 ppoll. Couche 1 : seulement air-process::AirProcess::wait_until(&mut [PollFd]) (couplé au pidfd de l’enfant), + re-export des types PollFd/PollEvents. Aucun multiplexeur poll()/select() générique sur un ensemble de fd arbitraire (boucle principale OpenSSH)◐ (types oui, multiplexeur générique absent)A (fondateur, à combler §5.3)
15termios / PTY (termios)Couche 0 air-sys-syscall::terminal complète (tcgetattr/tcsetattr/tcdrain/tcflush/tcflow/tcsendbreak/get_winsize/set_winsize/open_pty_master/pty_number/pty_unlock/pty_peer/tcgetpgrp/tcsetpgrp/session_id/set_ctty/clear_ctty). Couche 1 air-terminal : seulement isatty/ttyname + modèle/codec ; le tier termios/PTY/job-control n’est PAS re-exposé (usage interne pour isatty, et sous #[cfg(test)])◐ (passthrough termios/PTY absent en couche 1)A (fondateur OpenSSH, à combler §5.3)
16mman (mman)Couche 0 mmap/mprotect/munmap. Couche 1 : air-memory::{map_anonymous,unmap,RawMapping} (anonyme seul) ; air-alloc::MmapBacking (interne). Manque : mmap fichier-adossé, mprotect, madvise, shm_open, mlockA
17réseau (network)air-socket : AirTcpSocket/AirTcpListener/AirUdpSocket/AirUnix* (cycle complet socket/bind/connect/listen/accept/send/recv), résolveur AirNameResolver/AirDnsSource (RFC1035, /etc/hosts, resolv.conf) = équivalent getaddrinfo, AirIpAddress::parse (inet_pton), send_fd/recv_fd (SCM_RIGHTS). Manque additif : getsockopt/setsockopt génériques (seuls set_nodelay/set_keepalive exposés)A (options socket)
18process / exec (process,exit)air-process : AirCommand::spawn (clone3+execve, enfant AS-safe), AirProcess::{wait_timeout,wait_until,try_wait,wait_forever} (waitid), pidfd, drop_privileges (setresuid/gid/setgroups/capset/seccomp/Landlock), air_pipe, exit_process (exit_group) ; fork natif via air-runtime::fork::AtForkHandlers+thread::spawn. posix_spawn-classe couvert
19signal (signal)Couche 0 complète (signalfd_create/block_signals/unblock_signals/set_signal_mask/current_signal_mask/wait_for_signal/kill/tgkill/rt_sigqueueinfo). Couche 1 : seulement air-process::AirProcess::{signal,kill} (→ pidfd_send_signal sur l’enfant possédé). Manque en couche 1 : création signalfd, masquage (sigprocmask/pthread_sigmask), kill(pid) arbitraire, raise/sigsuspend/sigpending. ADR-020 pose la primauté signalfd, mais la brique n’est pas exposée en couche 1 (l’appelant « passe un signalfd pré-construit » — construit en couche 0)◐ (envoi-enfant seul ; face signal générale absente en couche 1)A (fondateur OpenSSH, à combler §5.3)
20thread / pthread (thread,sched)air-thread : AirMutex/AirRwLock/AirSemaphore (futex, sans poison), air_channel, atomic::*, AirThreadBuilder/join, runtime_primitives::{current_tid,create_thread,futex_wait_word,set_thread_pointer} ; air-runtime : thread::spawn/JoinHandle (clone3+CLONE_SETTLS), ThreadControlBlock/ThreadLocalStorage (TLS/TCB), errno TLS. Manque additif : condvar publique (futex interne), pthread_key/TSD ergonomique, sched_yield/sched_setaffinity public (affinité en set via AirThreadBuilder::cpu_affinity)✅ (primitives + spawn/join + TLS)A (condvar/TSD/sched)

2.2 Familles « longue traîne » (dans/adjacentes au périmètre v1)

Famille (audit musl)Brique(s) couche 1StatutB/A
crypt (crypt)air-crypto::AirArgon2 (Argon2id, salt CSPRNG interne, vérif constant-time) = réponse Air au hachage mot de passe. crypt() DES/MD5 legacy = compat face-C optionnelle✅ (hachage moderne)A (compat legacy)
prng (prng)air-crypto::AirRandom::{fill,u64} (getrandom kernel, fail-closed) = getentropy/arc4random. rand/random/rand48 déterministes = pur sur core
temp (temp)air-filesystem::AirTempDir (mkdtemp, O_EXCL 0700). mkstemp/mkostemp → **nécessite le handle fichier générique O_EXCL0600** (cf. famille 11)
misc (misc)realpathair-filesystem::canonicalize ✅ ; syslogair-base-core::AirLog (journald, transport différent — assumé) ✅ ; getopt/basename/dirname = pur ; openpty/getpass → dépendent du tier termios/PTY (cf. #15) ; wordexp NON porté (RCE, RAPPORT-CONSOLIDE §H)A
search (search)qsort/bsearch/lsearch/tsearch/hsearch = pur sur core/alloc (état non-global chez Air)✅ (aucune brique)
conf (conf)sysconf/pathconf/confstr non exposés ; constantes + rares syscalls (nb CPU via sched_getaffinity). Non-critique❌ (mineur)A
stdlib divers (stdlib)parsing/tri/abs/div = pur ; getenv/setenv cf. #10 ; exit/abort via air-process::exit_process + panic/abort RustA (setenv)
legacy (legacy)getpass/utmp/ftw… — majoritairement évités/différés (RAPPORT-CONSOLIDE §G6). N/A pour le sceau
linux (linux)syscalls Linux divers (prctl, getrandom, sysinfo, epoll, sendfile…) — couche 0 les a ; couche 1 sélective (getrandom via air-crypto ✅). Reste additifA
setjmp / longjmp (setjmp)ABSENT de tout le code (crates/ : zéro setjmp/longjmp/jmp_buf). Îlot asm incompressible identifié par ADR-047 P6 / RAPPORT-CONSOLIDE §D. À confiner en surface FFI unsafe le moment venu (OpenSSH n’en a guère besoin)A (îlot asm, FFI-only)

2.3 Familles différées v1+ (ADR-046 D7 — hors périmètre du sceau libc-v1)

Famille (audit musl)Justification du reportStatut
aio (aio)Réimplémentée sur io_uring (roadmap §4-K air-runtime async) ; consommateur = couche 2+, incluse dans la surface scellée mais pas d’audit face-toit ici
SysV IPC (ipc)Explicitement hors v1 (ADR-046 D7). Couche 0 a ipc.rs, mais la famille libc shmget/semop/msgget est différée
mq (mq)POSIX message queues — différé D7
ldso (ldso)Éditeur de liens dynamique — hors libc statique initiale ; air-runtime::reloc ne couvre que le static-PIE (R_*_RELATIVE). ld.so complet = post-v1
wordexp / globwordexp jamais porté (fork+exec /bin/sh = RCE). glob() libc différé (mais air-filesystem::glob existe pour l’usage interne Air)
regex (regex)Différé post-sceau (roadmap §4-I) : crate additive neuve, son ajout ne descelle rien. Ne bloque que regcomp/find_regex (non-critiques)
complex / fenv (complex,fenv)Axe FP avancé, adjacent à libm ; base air-libm suffit à icu4x. Sentinelle FE_DFL_ENV=(-1) bannie ADR-021

3. Points d’attention spécifiques (tranchés)

  • stdio. air-stdio couvre bien le brut requis (fd 0/1/2, read/write 1-syscall EINTR-fidèle, verrou par flux, is_terminal, reset_after_fork). Le FILE* bufferisé, vfprintf (moteur de format, sans %n par défaut — RAPPORT-CONSOLIDE §B6) et scanf restent légitimement face-C au-dessus (D7). Verdict : brique suffisante.
  • pwd / grp / shadow. air-config::PasswdBackend est la brique : il émet et importe passwd/group/shadow (hash en shadow seul, mode 0600 dès création via openat2, buffers Zeroizing, élision du secret dans les erreurs) et expose accounts_config::Reader (name/uid/gid/ gecos/home/shell/password_hash). C’est la source de vérité contre laquelle getpwnam/getpwuid/ getgrnam/getgrgid/getspnam se câblent en face-C. Nuance : l’API est orientée projection d’artefact ↔ /etc, pas lookup ponctuel indexé ; les itérateurs/lookups getpw*_r owned se construisent au-dessus (additif). Verdict : brique présente (◐ sur l’ergonomie lookup, additif).
  • string / ctype / multibyte. air-base-core (octets/encodages) + air-base-lib (icu4x : casing-locale, normalisation, collation, segmentation) fournissent le socle ADR-053. Le ctype ASCII et la machine mbstate_t sont du calcul pur/face-C au-dessus du socle. Verdict : brique présente.
  • setjmp / longjmp. Absent (grep crates/ = 0). C’est l’unique résidu asm réellement incompressible (ADR-047 P6, RAPPORT-CONSOLIDE §D) : inexprimable en Rust safe (saute par-dessus les Drop). À confiner en surface FFI C-ABI unsafe le moment venu. Additif, non-bloquant (nouvelle unité asm/FFI, aucune API scellée touchée ; OpenSSH n’en dépend guère).
  • regex. Différé post-sceau (ADR-046 / roadmap §4-I). Crate additive : ne descelle rien.

4. Gaps BLOQUANTS pour le sceau

Aucun gap bloquant au sens refonte. Justification (à charge, un completeness critic doit prouver l’absence de bloquant, pas la postuler) :

  1. Toutes les briques kernel nécessaires existent déjà en couche 0 (air-sys-syscall) — fichier, signal, termios/PTY, poll, mman — et tous les types sont en air-sys-types. Aucune brique fondatrice n’est à inventer ; il s’agit de re-exposer un tier couche-1 fidèle.
  2. Chaque manque identifié en §2 se comble par ajout (pub fn/pub use/module neuf, voire crate neuve) sans modifier une signature couche-1 déjà figée → descellement additif v1.x (modèle roadmap §5.3/§5.4, précédent ADR-051). Aucun ne force une rupture de contrat scellé.
  3. Le seul « trou » structurellement irréductible (setjmp/longjmp) est un îlot asm FFI-only, par nature hors de l’API Rust scellée.

Réserve honnête (ne pas la lire comme un bloquant, mais ne pas la sous-estimer). Le fait que rien ne soit bloquant-refonte ne signifie pas que la face libc v1 est portable en l’état. Un tier passthrough substantiel et fondateur pour OpenSSH reste à construire en couche 1 (§5.3) avant que la libc puisse être écrite : I/O fichier générique (#11), poll/select générique (#14), termios/PTY (#15), signal générique (#19), plus mman (#16) et setenv (#10). Ce sont les manques listés ci-dessous — additifs, mais prioritaires.

5. Gaps ADDITIFS post-sceau (attendus : la face C n’est pas encore écrite)

Classés par priorité pour la cible OpenSSH. Tous relèvent du §5.3 « combler les manques additifs » et aucun ne descelle l’API figée.

P0 — fondateurs pour OpenSSH, à exposer en couche 1 avant d’écrire la libc :

  1. I/O fichier générique (#11). Exposer en couche 1 un tier fidèle open/openat/read/write/ pread/pwrite/lseek/close/dup/fcntl(F_GETFL/F_SETFL/F_SETFD)/ftruncate (la couche 0 a tout : openat2/read/write/lseek/dup_fd). Sans lui, open(2)/read(2)/write(2) de la libc sont inexprimables (D2 interdit le saut vers couche 0).
  2. poll/select générique (#14). Exposer un multiplexeur ppoll sur fd arbitraires (la couche 0 a ppoll ; seul air-process::wait_until l’affleure, couplé au pidfd). Boucle principale OpenSSH.
  3. termios / PTY / job-control (#15). Re-exposer en air-terminal le tier tcgetattr/tcsetattr/ tc*/get_winsize/set_winsize/open_pty_master/pty_*/tcgetpgrp/tcsetpgrp/session_id/ set_ctty (tout en couche 0). cfmakeraw (Result, pas void — RAPPORT-CONSOLIDE §B2), openpty, getpass en dépendent.
  4. Signal générique (#19). Exposer en couche 1 la création signalfd (primauté ADR-020), le masquage (sigprocmask/pthread_sigmask), kill(pid) arbitraire, raise/sigsuspend. Ne PAS forcer SA_RESTART (EINTR remonté, RAPPORT-CONSOLIDE §H).

P1 — fondateurs mais moins critiques au démarrage :

  1. mman (#16). mmap fichier-adossé, mprotect, madvise, shm_open (..+O_NOFOLLOW, RAPPORT-CONSOLIDE §F4), mlock. Couche 0 a mmap/mprotect/munmap ; couche 1 n’a que l’anonyme.
  2. env mutation (#10). setenv/putenv/unsetenv/clearenv (+ gouvernance ADR-047) et secure_getenv (gating AT_SECURE, RAPPORT-CONSOLIDE §F9).
  3. stat/fstat/lstat public (#12) ; mkstemp/mkostemp (dépend de #11, O_EXCL|0600).

P2 — ergonomie / complétude :

  1. thread (#20) : condvar publique, pthread_key/TSD, sched_yield/sched_setaffinity public.
  2. réseau (#17) : getsockopt/setsockopt génériques.
  3. stdlib/time : moteur vfprintf/strftime (face-C au-dessus des briques présentes).
  4. setjmp/longjmp : îlot asm FFI-only (ADR-047 P6) — à créer si un userland C l’exige.
  5. conf/linux divers : sysconf/pathconf réels (pas de faux succès, RAPPORT-CONSOLIDE §H11).

Rappel de durcissement (RAPPORT-CONSOLIDE, à porter dans la face C, pas des gaps de brique) : malloc zero-on-free absent d’air-alloc (choix de durcissement, additif) ; neutraliser %n par défaut ; entropie unique CSPRNG (déjà air-crypto::AirRandom, fail-closed) ; zeroize secrets (déjà Zeroizing/ZeroizeOnDrop en air-config/air-crypto).

6. VERDICT de sceau-readiness FACE-LIBC

Le verdict se lit à deux niveaux, tous deux nécessaires à une réponse honnête :

(a) Au sens de la question posée — « rien de FONDATEUR ne manque, les manques sont additifs » : OUI. Les fondations de la couche 1 sont présentes et saines : errno + shim thread-local (D4), allocateur global, primitives thread futex + TLS/TCB, temps + sleep, environ (lecture), clone3/exec/ wait/pidfd/drop_privileges, réseau complet + résolveur RFC1035, socle i18n icu4x + libm, crypto CSPRNG/hachage, backends passwd/group/shadow. Aucun manque n’exige de refondre une API scellée : tous sont additifs (re-exposition d’un tier couche-1 fidèle par-dessus une couche 0 déjà complète, ou crate neuve). Le modèle de descellement additif v1.x (ADR-051, roadmap §5.3/§5.4) absorbe tout manque résiduel découvert après le sceau. Le sceau couche-1-v1.0 est donc SÛR : le sceller ne fige aucune erreur de conception irréversible.

(b) Au sens fonctionnel — « la face libc v1 (OpenSSH) est-elle portable sur la couche 1 en l’état » : NON, pas encore. Un tier passthrough kernel-faithful fondateur reste à exposer en couche 1 (travail §5.3 explicitement prévu avant §5.4) : I/O fichier générique, poll/select générique, termios/PTY, face signal générique (P0), puis mman et setenv (P1). Ce sont exactement les « manques additifs remontés par 5.1 » que la roadmap ordonne de combler avant de sceller.

Conclusion opérationnelle. OUI au sceau sous le régime additif — à la condition explicite d’exécuter d’abord §5.3 sur les 6 chantiers P0/P1 ci-dessus (aucun n’est un bloquant-refonte ; tous sont des ajouts de pub/crates au-dessus d’une couche 0 déjà dotée). Sceller sans §5.3 resterait techniquement sûr (rien d’irréversible) mais produirait une couche 1 incapable de porter la face libc v1 sans un descellement additif immédiat — ce que la séquence §5.3→§5.4 vise précisément à éviter. Recommandation : §5.3 (P0+P1) puis §5.4.


Complétude critique — checklist du critique. Périmètre v1 rejoué famille par famille (✅/◐/❌/⏭) ; symboles réels vérifiés par grep ; couche 0 confirmée comme socle des passthroughs manquants ; setjmp confirmé absent ; wordexp confirmé exclu ; distinction bloquant-refonte / additif tranchée et justifiée (pas postulée). Points d’attention du prompt (stdio, pwd/grp, string/ctype, setjmp, regex) tous tranchés en §3.