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

Registre de conformité — libc C-ABI d’Air

Registre central de la doctrine de conformité (roadmap roadmap-libc-openssh-fr.md §3). Chaque symbole exporté par la libc C-ABI d’Air y figure avec son étiquette de conformité, sa déviation éventuelle (listée + justifiée) et l’ADR de référence.

Source de vérité = le /// sur chaque extern "C" (roadmap §4). Ce fichier agrège ces étiquettes. Cohérence outillée : le test air-libc-capi/tests/conformance_labels.rs échoue si un symbole extern "C" défini dans air-libc-capi/src/ (errno, crt, shims malloc) n’a pas d’étiquette dans son /// ou n’apparaît pas ici. Les mem*/str* sont définis dans air-libc-strmem (ré-exportés par air_libc_capi::string) et y portent leurs étiquettes ; ils figurent au registre ci-dessous.

Mise à jour M2 (2026-07-04) : familles I/O fichier fd brut (fcntl.h/ unistd.h), métadonnées (sys/stat.h, struct stat ABI Air) et répertoires (dirent.h, DIR opaque) — logique mesurée air-libc-fileio, shims air-libc-capi::fileio, frontière variadique open/openat/fcntl via fileio_shim.c (option A, comme printf). Cf. section « M2 » et déviations D-M2.1..D-M2.5. Précédent :

Dernière mise à jour : 2026-07-04 (jalon M1-d — frontière variadique printf (option A) : famille printf/fprintf/sprintf/snprintf + v* via un mince shim C va_arg et la glue Rust mesurée air-libc-printf, au-dessus du moteur fuzzé air-libc-fmt ; sur M1-a/M1-b/M1-c). Architecture « logique / shims » étendue : logique stdio dans air-libc-stdio, exit/atexit/getenv dans air-libc-stdlib, glue printf dans air-libc-printf (crates mesurées) ; shims extern "C" minces dans air-libc-capi::{stdio,stdlib} (exclus du gate) ; 1ʳᵉ exception « zéro C » = le shim C va_arg (air-libc-c/csrc/printf_shim.c, cf. EXCEPTIONS.md). Six crates de logique libc mesurées : air-libc-fmt, air-libc-alloc, air-libc-stdio, air-libc-stdlib, air-libc-printf.

Taxonomie (roadmap §3)

ÉtiquetteSens
POSIXPile conforme C/POSIX : comportement, valeurs de retour, errno identiques à la norme.
POSIX-PARTIELFonctionnellement POSIX, avec déviations documentées (erreur plus stricte, zéro troncature muette, borne explicite). Chaque déviation listée + justifiée.
NON-POSIX-DÉLIBÉRÉOn choisit de dévier / de fournir un symbole hors POSIX — raison obligatoire + renvoi ADR.
AIR-EXTExtension Air (air_*) — hors POSIX, valeur ajoutée.

M0 — Fondations (air-libc-capi) : errno + crt

Fonctions

SymboleÉtiquetteDéviation / justificationADR
__errno_locationPOSIXSémantique conforme : rend l’lvalue errno par-thread, pointeur stable pour la vie du thread, jamais NULL. Nuance de nom : __errno_location n’est pas dans le texte POSIX (qui spécifie la macro errno) — c’est la convention ABI Linux/glibc, fournie à l’identique pour que le code C et les <errno.h> du système marchent sans modification. Socle : air_runtime::AirRuntime::errno_location() (canal unique, zéro globale, partagé libc ⇄ PAL).ADR-046 (D4), ADR-049 (D3), ADR-052
__libc_start_mainNON-POSIX-DÉLIBÉRÉNon-POSIX assumé : ABI de démarrage Linux/glibc (pas un symbole POSIX). Air en fournit une variante minimale à 4 arguments (main, argc, argv, envp) là où la glibc en prend 7 (init/fini/rtld_fini/stack_end) — le crt appelant est celui d’Air (bootstrap, couche 1), pas celui de gcc ; les crochets .init_array/atexit/ld.so sont hors M0 (M1 pour atexit/flush stdio). Élargissement à la signature 7-args = additif d’un jalon ultérieur si besoin réel.ADR-046, ADR-050
air_mainAIR-EXTBridge air_* interne à la chaîne de démarrage d’Air (_startbootstrapair_main__libc_start_main) — TARGET-ONLY (cfg(target_env = "air")), non destiné à l’appel par du code C applicatif. Convertit les types Air (usize/*const *const u8) vers la signature C-runtime.ADR-052, ADR-049 (D4)

Types

SymboleÉtiquetteNoteADR
AirMainFnPOSIX (support)typedef int (*AirMainFn)(int, char **, char **) — type du main C (ABI System V). Support de type pour __libc_start_main.ADR-046

Constantes E* (numéros errno Linux)

Étiquette : POSIX (toutes). Valeurs conformes à asm-generic/errno{,-base}.h (identiques x86_64 / aarch64). Source de vérité unique = couche 0 (air_sys_types::Errno) : les #define du header sont des littéraux croisés-vérifiés contre la couche 0 (test errno_constants_match_layer0), jamais inventés. Périmètre M0 = les errno déjà ancrés en couche 0 ; d’autres seront ajoutés par famille (jalons M1+), au fil des besoins réels d’OpenSSH.

EPERM(1) · ENOENT(2) · ESRCH(3) · EINTR(4) · E2BIG(7) · ENOEXEC(8) · EBADF(9) · ECHILD(10) · EAGAIN(11) · EWOULDBLOCK(11, alias EAGAIN) · ENOMEM(12) · EACCES(13) · EFAULT(14) · EBUSY(16) · EEXIST(17) · EXDEV(18) · ENODEV(19) · ENOTDIR(20) · EINVAL(22) · ENFILE(23) · EMFILE(24) · ENOTTY(25) · ENOSPC(28) · ESPIPE(29) · EPIPE(32) · ERANGE(34) · ENOSYS(38) · ELOOP(40) · ETIME(62) · EPROTO(71) · EBADMSG(74) · EMSGSIZE(90) · EOPNOTSUPP(95) · ECONNRESET(104) · ENOBUFS(105) · ETIMEDOUT(110) · EALREADY(114) · ECANCELED(125).

Code vs artefact (air-libc-capi / air-libc-c, ADR-029)

Les symboles ci-dessus sont définis dans la crate de code air-libc-capi (rlib pur — leur source de vérité /// + header committé y vivent). L’artefact partagé libair_c.so est produit par la crate sœur air-libc-c (cdylib, host-only), qui ne définit aucun symbole propre : elle se contente de ré-exporter (pub use) et d’ancrer (#[used]) ceux d’air-libc-capi pour forcer leur export dans la .so. Aucune entrée de registre supplémentaire n’est donc requise pour air-libc-c (surface ABI identique, zéro symbole ajouté).

M1 — tranche string/mem (air-libc-strmem ; ré-export air-libc-capi::string)

Primitives mem*/str* sans allocation : #[no_mangle] extern "C" définis dans la crate air-libc-strmem (isolée pour porter #![no_builtins], cf. le split) et ré-exportés par le module air_libc_capi::string (chemin stable + visibilité cbindgen via parse.include/extra_bindings). Shims minces sur une logique sûre (tranches bornées / marches de pointeurs bornées par n, zéro indexation paniquante, zéro UB — roadmap §5.①). Toutes POSIX : mêmes valeurs de retour, mêmes pointeurs, comparaisons en unsigned char conformes à la norme. Aucune déviation de comportement ; la seule garantie plus forte (sûreté, non observable côté C) est le court-circuit n == 0 (pointeurs nuls tolérés là où from_raw_parts/copy de Rust exigent du non-nul) — pas une déviation POSIX. strdup (allocation) est différé à la tranche stdlib.

SymboleÉtiquetteNoteADR
memcpyPOSIXRégions disjointes exigées (norme) ; rend dst.ADR-046
memmovePOSIXChevauchement géré (core::ptr::copy) ; rend dst.ADR-046
memsetPOSIXc réduit en unsigned char (sans as lossy) ; rend s.ADR-046
memcmpPOSIXComparaison en unsigned char ; signe conforme.ADR-046
memchrPOSIXRend void * (non const, convention norme) ou NULL.ADR-046
strlenPOSIXLongueur non bornée (marche de pointeur).ADR-046
strnlenPOSIXPOSIX.1-2008 ; ne lit jamais au-delà de maxlen.ADR-046
strcpyPOSIXNon borné (contrat C) ; rend dst.ADR-046
strncpyPOSIXPiège normé documenté : pas de NUL si strlen(src) ≥ n ; padding NUL sinon. Bornage strict ≤ n octets.ADR-046
strcatPOSIXNon borné (contrat C) ; rend dst.ADR-046
strncatPOSIXToujours NUL-terminé (≤ n+1 octets ajoutés).ADR-046
strcmpPOSIXComparaison en unsigned char.ADR-046
strncmpPOSIXBornée par n, comparaison unsigned char.ADR-046
strchrPOSIXPremière occurrence, NUL terminal compris ; rend char *.ADR-046
strrchrPOSIXDernière occurrence, NUL terminal compris.ADR-046
strstrPOSIXNaïf O(n·m), sans allocation ; aiguille vide ⇒ haystack.ADR-046
strtok_rPOSIXRéentrant (état dans *saveptr, zéro global) ; modifie s in place.ADR-046
strerrorPOSIXTable statique errno → message (standard Linux). Chaîne immuable statique (jamais invalidée, contrairement au buffer partagé glibc). Logique mesurée air-libc-strerror.ADR-076
strerror_rPOSIXVariante POSIX/musl (int, Air ABI-musl) : copie bornée dans le buffer, ERANGE si trop petit (tronqué + NUL).ADR-076
__xpg_strerror_rPOSIXAlias XSI de strerror_r (nom émis par std/glibc/musl pour la variante int) : même logique mesurée, copie bornée, ERANGE si trop petit (tronqué + NUL).ADR-076

M1-b — famille malloc (logique air-libc-alloc ; shims air-libc-capi::malloc)

Famille allocation de stdlib.h. Split « logique / shims C-ABI » : la LOGIQUE (malloc_impl/free_impl/…) vit dans la crate air-libc-alloc (Rust pur, mesurée à 100 %, aucun symbole #[no_mangle]), au-dessus de l’arène globale scellée d’air-alloc (verrou futex, fusion O(1), échec ⇒ null déterministe, ADR-056) ; les shims extern "C" minces qui délèguent vivent dans air_libc_capi::malloc (exclus du gate de couverture, car leurs #[no_mangle] surchargent le runtime de couverture LLVM). La logique porte le modèle d’en-tête borné (placé devant chaque bloc) qui rend free/realloc possibles sans que le C repasse taille/alignement. Sécurité #1 (roadmap §5.①) : overflow calloc checked (checked_mul), zéro as lossy (try_from / checked_* partout), zéro double-free/UAF introduit (chaque bloc libéré une fois via son en-tête ; realloc recopie puis libère l’ancien après succès du neuf). strdup/strndup allouent ⇒ ici, pas dans string.

SymboleÉtiquetteNote / déviationADR
mallocPOSIXmalloc(0) rend un pointeur unique, non-NULL, libérable (la norme autorise NULL ou un pointeur unique — on choisit le plus sûr). Échec ⇒ NULL + errno = ENOMEM.ADR-056
freePOSIXfree(NULL) = no-op. Double-free/UAF = UB (contrat C, comme toute libc).ADR-056
callocPOSIXProduit nmemb * size checked (débordement ⇒ NULL + errno = ENOMEM — anti heap-overflow). Mémoire entièrement à zéro. calloc(0, x)/calloc(x, 0) ⇒ pointeur unique libérable.ADR-056
reallocPOSIX-PARTIELrealloc(NULL, n) == malloc(n) ; realloc(p, 0) == free(p) puis rend NULL (comportement glibc historique ; C17 implementation-defined — déviation gravée). Échec (size ≠ 0) ⇒ NULL + errno = ENOMEM, ancien bloc conservé.ADR-056
aligned_allocPOSIX-PARTIELalignment puissance de deux non nulle sinon NULL + errno = EINVAL. Contrainte C11 « size multiple de alignment » relâchée (comme glibc) — déviation documentée (plus permissif, jamais moins sûr).ADR-056
posix_memalignPOSIXalignment puissance de deux et multiple de sizeof(void *) sinon EINVAL (sans toucher errno ni *memptr). Succès ⇒ 0 + *memptr. Échec ⇒ ENOMEM (ne pose pas errno, contrat POSIX).ADR-056
strdupPOSIX-PARTIELPOSIX.1-2008. Durcissement : strdup(NULL) rend NULL (au lieu du déréférencement de NULL indéfini — sécurité #1). Échec d’allocation ⇒ NULL + errno = ENOMEM.ADR-056
strndupPOSIX-PARTIELPOSIX.1-2008. Résultat toujours NUL-terminé (≤ n+1 octets). Durcissement : strndup(NULL, …) rend NULL (sécurité #1). Échec ⇒ NULL + errno = ENOMEM.ADR-056

Moteur de format (crate air-libc-fmtaucun symbole extern "C")

Le cœur du rendu printf ([air_libc_fmt], extrait dans sa propre crate pour être mesuré à 100 % par llvm-cov) est du Rust pur, sans unsafe, sans allocation, agnostique de la source des arguments (FormatArgs). Il n’exporte aucun symbole C (donc aucune entrée de registre requise, et rien à parser pour cbindgen) : le câblage variadique printf/snprintf/vfprintf (option A, extern "C", dans air-libc-capi) est le jalon M1-d. C’est LA surface d’attaque « format-string », isolée et fuzzée (cible fuzz_libc_format, cf. fuzz/). Invariants garantis par construction + prouvés au fuzz : zéro panic, zéro débordement du puits borné (snprintf-exact : écrit min(longueur théorique, capacité)), zéro boucle infinie/DoS (remplissages bornés par la capacité, compteur saturating_add), et %n REJETÉ — rendu littéral, aucun argument consommé, jamais une écriture mémoire (vecteur d’exploitation format-string classique neutralisé). Cf. déviation D-M1b.5 ci-dessous.

M1-c — stdio.h (FILE* bufferisé) : air-libc-stdio (logique) ; shims air-libc-capi::stdio

Famille stdio.h. Split « logique / shims C-ABI » : la LOGIQUE (FILE bufferisé, registre des flux, flush-at-exit) vit dans air-libc-stdio (Rust pur, mesurée, aucun symbole #[no_mangle]), au-dessus d’AirFile/air-stdio (couche 1) ; les shims extern "C" minces vivent dans air_libc_capi::stdio (exclus du gate). Bufferisation bloc/ligne/sans (_IOFBF/_IOLBF/_IONBF), verrou par-FILE (thread-safety POSIX, façon flockfile), boucle sur écriture courte + retry EINTR dans le toit (ADR-021 conv.2). Sécurité #1 : bornage strict des tampons (get/copy_from_slice, jamais d’indexation), zéro as lossy, overflow size*nmemb checked (fread/fwrite). Parseur de mode fopen fuzzé (fuzz_libc_stdio_mode) ; bufferisation fuzzée (fuzz_libc_stdio_buffer, backend mémoire).

SymboleÉtiquetteNote / déviationADR
fopenPOSIXPerm. de création 0666 masquées par l’umask ; modes tolérants façon glibc (b no-op Linux, x=O_EXCL avec w, octets inconnus ignorés) ; mode invalide ⇒ NULL+EINVAL.ADR-046
fclosePOSIXFlush + fermeture + désenregistrement + destruction du FILE.ADR-046
freadPOSIXProduit size*nmemb checked (débordement ⇒ 0). Rend le nb d’éléments complets.ADR-046
fwritePOSIXProduit size*nmemb checked. Rend le nb d’éléments complets.ADR-046
fflushPOSIXfflush(NULL) vide tous les flux ouverts (registre process-global).ADR-046
fseekPOSIXPosition logique corrigée de la bufferisation (pré-lecture / octets non flushés).ADR-046
ftellPOSIXPosition logique (offset descripteur ± tampon).ADR-046
rewindPOSIXfseek(f,0,SEEK_SET) + clearerr.ADR-046
fgetc / getcPOSIXLit un octet (0..=255) ou EOF. getc = fonction (pas seulement macro).ADR-046
getcharPOSIXgetc(stdin).ADR-046
fgetsPOSIXAu plus size-1 octets, s’arrête au \n (inclus)/EOF, NUL-termine. Jamais d’écriture au-delà de size (bornage strict). Verrou tenu sur toute la ligne (atomicité).ADR-046
fputc / putcPOSIXÉcrit un octet (réduit en unsigned char).ADR-046
putcharPOSIXputc(c, stdout).ADR-046
fputsPOSIXÉcrit la C-string (sans le NUL).ADR-046
putsPOSIXC-string + \n sur stdout (contrat C).ADR-046
feof / ferror / clearerrPOSIXIndicateurs EOF/erreur par-FILE.ADR-046
setvbufPOSIX-PARTIELDéviation de sécurité (D-M1c.1) : le tampon fourni par l’appelant est ignoré (jamais d’aliasing d’une mémoire C au cycle de vie incontrôlé) ; seuls mode/size honorés. Mode invalide ⇒ -1+EINVAL.ADR-046/032
setbufPOSIX-PARTIELsetvbuf (mêmes déviations ; buf nul ⇒ _IONBF, sinon _IOFBF).ADR-046/032
filenoPOSIXDescripteur brut sous-jacent (POSIX.1, hors C standard).ADR-046

Données (FILE *)

SymboleÉtiquetteNoteADR
stdin / stdout / stderrPOSIXTrois FILE pré-ouverts (static, sémantique OS). stdout = ligne sur terminal sinon bloc ; stderr = sans tampon (_IONBF). Symboles de données (FILE *), hors périmètre du scanner conformance_labels (qui ne lit que les extern "C" fn).ADR-046

Constantes <stdio.h>

Étiquette POSIX (toutes, littéraux ABI standard) : EOF(-1) · SEEK_SET(0) · SEEK_CUR(1) · SEEK_END(2) · _IOFBF(0) · _IOLBF(1) · _IONBF(2) · BUFSIZ(8192).

M1-c — stdlib.h : exit/atexit/abort + getenv (air-libc-stdlib ; shims air-libc-capi::stdlib)

Split « logique / shims » : le registre atexit (borné), la séquence de nettoyage (perform_exit_cleanup) et le cache getenv vivent dans air-libc-stdlib (mesurée) ; les shims — dont exit/_Exit/_exit/abort qui divergent (-> !, insondables par le gate, comme air-process/src/exit.rs) — vivent dans air_libc_capi::stdlib. Séquence exit : handlers atexit (ordre inverse) → flush tous les FILEair_process::exit_process.

SymboleÉtiquetteNote / déviationADR
exitPOSIXHandlers atexit + flush stdio + exit_group. Ne revient jamais.ADR-046
_Exit / _exitPOSIXSortie immédiate (ni handlers, ni flush).ADR-046
abortPOSIXSIGABRT (air-signal, coredump par défaut), aucun flush (contrat). Durcissement : sortie forcée 128+SIGABRT si le signal ne termine pas.ADR-046/064
atexitPOSIXRegistre borné (≥ 32, ATEXIT_MAX), ordre inverse. Durcissement (D-M1c.3) : atexit(NULL) rend une valeur non nulle.ADR-046
getenvPOSIX-PARTIELNuance de durée de vie gravée (D-M1c.2) : pointeur stable pour la vie du processus via un cache process-global ; un getenv répété rend le même pointeur. setenv/unsetenv invalident l’entrée de cache (lecture fraîche ensuite) en fuyant l’ancienne valeur (les pointeurs déjà rendus restent valides).ADR-046
setenvPOSIXoverwrite==0 + variable existante ⇒ no-op. Nom invalide ⇒ EINVAL, pointeur nul ⇒ EFAULT. Sur AirEnvironmentManager (couche 1, ADR-077).ADR-046/077
unsetenvPOSIXNom vide / contenant =EINVAL, pointeur nul ⇒ EFAULT. Sur AirEnvironmentManager.ADR-046/077

M1-d — frontière variadique printf (shim C va_arg + glue air-libc-printf)

Famille printf (stdio.h). Décision BDFL — option (A) (roadmap §6) : Rust-stable ne peut pas définir de fonction C variadique (c_variadic = nightly). Retenu : parseur + moteur de rendu 100 % Rust fuzzé (air-libc-fmt, la surface d’attaque « format-string » N°1) + un mince shim C (air-libc-c/csrc/ printf_shim.c) faisant le seul va_arg mécanique. C’est la 1ʳᵉ exception nommée à « zéro C » (cf. docs/EXCEPTIONS.md).

Architecture. Le shim ne parse rien (zéro logique de format en C) : il (a) demande le plan des types d’args à la glue Rust mesurée air-libc-printf (air_fmt_arg_kinds), (b) fait le va_arg(ap, <type promu>) dicté par le plan, (c) re-délègue au moteur Rust (air_fmt_render_buf/_stream). Le plan et le rendu passent par le même parseur (air_libc_fmt::format) ⇒ zéro divergence. Sécurité #1 : %n rejeté (rendu littéral, aucun argument tiré, jamais d’écriture-arrière — cf. déviation D-M1b.6) ; puits snprintf borné (jamais de débordement) ; promotions C par défaut respectées. Fuzzé : moteur (fuzz_libc_format) et glue (fuzz_libc_printf). Preuve d’exécution : harnais hello-world C (air-libc-c/tests/hello_world.rs, « Hello, Air 42 »).

Symboles fournis par le shim C (déclarés dans air_c.h, exportés dans libair_c.sonm -D) :

SymboleÉtiquetteNote / déviationADR
printfPOSIXstdout, bufferisé (flush-at-exit). %n rejeté (D-M1b.6). Rend le nb d’octets écrits (négatif sur erreur d’écriture).ADR-046
fprintfPOSIXVers un FILE *.ADR-046
sprintfPOSIXBuffer non borné (contrat C ; préférer snprintf). NUL-terminé.ADR-046
snprintfPOSIXÉcrit au plus size octets (NUL inclus — jamais de débordement) ; rend la longueur théorique (C99).ADR-046
vprintf / vfprintf / vsprintf / vsnprintfPOSIXVariantes va_list (mêmes sémantiques).ADR-046

Conversions supportées (moteur M1-b) : d i u x X o c s p % ; drapeaux - + espace 0 # ; largeur/précision (dont */.*) ; longueurs hh h l ll z t j. Différés (hors scope) : flottants e f g a (rendus littéralement tant qu’air-libm n’est pas câblé — aucun argument tiré) ; positions %n$ ; locale numérique. %n : rejeté (NON-POSIX-DÉLIBÉRÉ, sécurité — cf. D-M1b.6).

Points d’entrée de support de la glue Rust (air-libc-printf, #[no_mangle], exportés dans libair_c.so mais hors ABI libc standard — consommés uniquement par le shim ; non scannés par conformance_labels, hors air-libc-capi/src) :

SymboleÉtiquetteNote
air_fmt_arg_kindsAIR-EXT (support interne)Parse fmt, écrit le plan des kinds d’args (borné), rend le compte ou -1. Ne tire aucun va_arg.
air_fmt_render_bufAIR-EXT (support interne)Rendu borné (snprintf) : ≤ cap octets, rend la longueur théorique.
air_fmt_render_streamAIR-EXT (support interne)Rendu stream : pousse la sortie via un callback (fwrite/copie), rend la longueur théorique ou -1.

M2 — I/O fichier fd brut + métadonnées + répertoires (air-libc-fileio ; shims air-libc-capi::fileio)

Familles fcntl.h/unistd.h (fd brut), sys/stat.h (métadonnées) et dirent.h (répertoires). Split « logique / shims C-ABI » : la LOGIQUE (open_impl/read_impl/stat_impl/opendir_impl/readdir_impl/mkstemp_impl/…, le remplissage de la struct stat ABI Air, la bufferisation getdents64 derrière DIR) vit dans air-libc-fileio (Rust pur, mesurée, aucun symbole libc #[no_mangle] standard), au-dessus de la couche 0 (openat2/read/write/ pread/pwrite/close/lseek/statx/faccessat/getdents64/dup_fd/dup3/ pipe2/getrandom/getcwd/chdir) et de la couche 1 (canonicalizerealpath) ; les shims extern "C" minces vivent dans air_libc_capi::fileio (exclus du gate). Sécurité #1 : chemins via C-strings bornées (octets, zéro présomption UTF-8) ; buffers getdents64/readdir bornés (get, jamais d’indexation) ; zéro as lossy sur les champs stat (try_from/wrapping_shl) ; fd empruntés sans double-close.

Sémantique fd brut : le C gère la vie du fd (open rend un int désolidarisé, close reconstruit l’OwnedFd et le ferme une fois ; les autres empruntent). EINTR remonté (jamais de retry auto — ADR-021 conv.2). O_CLOEXEC : couche 0 le force toujours ; open/dup/dup2 effacent FD_CLOEXEC si l’appelant ne l’a pas demandé, rétablissant la sémantique POSIX (fd héritable par exec) — D-M2.1.

struct stat/dirent/DIR = ABI Air (largeur fixe, arch-indépendant, distinct de la glibc — comme FILE) : les programmes compilés contre air_c.h voient notre layout (D-M2.2). Le parseur getdents64dirent (données kernel externes) est fuzzé (fuzz_libc_dirent).

Fonctions non variadiques (shims air-libc-capi::fileio)

SymboleÉtiquetteNote / déviationADR
creatPOSIXopen(path, O_CREAT|O_WRONLY|O_TRUNC, mode) (non variadique).ADR-046
readPOSIXLecture partielle possible ; EINTR remonté.ADR-046
writePOSIXÉcriture partielle possible ; EINTR remonté.ADR-046
preadPOSIXN’altère pas l’offset ; offset < 0EINVAL.ADR-046
pwritePOSIXN’altère pas l’offset ; offset < 0EINVAL.ADR-046
readvPOSIXLecture vectorisée (iovcnt buffers). Sur AirFileManager::read_vectored (couche 0 readv, sans additif). fd<0EBADF, iovcnt<0EINVAL, iov nul ⇒ EFAULT.ADR-077
writevPOSIXÉcriture vectorisée. Sur AirFileManager::write_vectored.ADR-077
preadvPOSIXreadv à l’offset absolu (n’altère pas la position) ; offset<0EINVAL. Sur AirFileManager::read_vectored_at.ADR-077
pwritevPOSIXwritev à l’offset absolu ; offset<0EINVAL. Sur AirFileManager::write_vectored_at.ADR-077
spliceLinuxTransfert kernel fd_infd_out (au moins un pipe) ; off_in/off_out nuls ⇒ position courante, sinon départ explicite mis à jour. fd<0EBADF, *off<0EINVAL. Sur AirFileManager::splice (couche 0 splice, sans additif).ADR-077
closePOSIXFerme une fois (reconstruit l’OwnedFd).ADR-046
lseekPOSIXSEEK_SET/CUR/END + extensions Linux SEEK_DATA/SEEK_HOLE.ADR-046
dupPOSIXNon close-on-exec (efface FD_CLOEXEC, D-M2.1).ADR-046
dup2POSIXoldfd == newfd valide ⇒ no-op ; non close-on-exec (D-M2.1).ADR-046
dup3POSIXSeul O_CLOEXEC valide ; oldfd == newfdEINVAL.ADR-046
pipePOSIXNon close-on-exec (D-M2.1).ADR-046
pipe2POSIXO_CLOEXEC/O_NONBLOCK/O_DIRECT ; bit inconnu ⇒ EINVAL.ADR-046
statPOSIX-PARTIELstruct stat layout ABI Air (D-M2.2). Suit les liens.ADR-046
fstatPOSIX-PARTIELIdem stat sur un fd (AT_EMPTY_PATH). Layout ABI Air (D-M2.2).ADR-046
lstatPOSIX-PARTIELIdem, sans suivre le lien final. Layout ABI Air (D-M2.2).ADR-046
fstatatPOSIX-PARTIELflags AT_* ; layout ABI Air (D-M2.2).ADR-046
accessPOSIXR_OK/W_OK/X_OK/F_OK, suit les liens.ADR-046
faccessatPOSIXHonore AT_SYMLINK_NOFOLLOW/AT_EACCESS (bascule faccessat2).ADR-046
opendirPOSIXO_RDONLY|O_DIRECTORY ; DIR opaque.ADR-046
fdopendirPOSIXPrend possession du fd (fermé par closedir).ADR-046
readdirPOSIXdirent * interne réutilisé (écrasé au prochain appel) ; fin ⇒ NULL sans errno. Nom > NAME_MAX sauté (D-M2.5).ADR-046/032
closedirPOSIXFerme le fd + libère le DIR.ADR-046
rewinddirPOSIXlseek(0) + vidage du tampon.ADR-046
mkstempPOSIXO_CREAT|O_EXCL, 0600, entropie kernel (getrandom). Patron invalide ⇒ EINVAL ; espace épuisé ⇒ EEXIST.ADR-046
mkdtempPOSIXmkdir 0700, entropie kernel ; rend template.ADR-046
realpathPOSIX-PARTIELSur canonicalize (couche 1). realpath(path, NULL) (forme GNU malloc) différée ⇒ EINVAL (D-M2.4). Canonique ≥ PATH_MAXENAMETOOLONG.ADR-046
getcwdPOSIX-PARTIELgetcwd(NULL, 0) (forme GNU) différée ⇒ EINVAL (D-M2.4) ; buffer trop petit ⇒ ERANGE.ADR-046
chdirPOSIXChange le répertoire de travail.ADR-046
fchdirPOSIXIdem par descripteur de répertoire.ADR-046
mkdirPOSIXCrée un répertoire (mode filtré par umask). Sur AirFileManager (couche 1, ADR-077).ADR-046/077
rmdirPOSIXSupprime un répertoire vide (unlinkat+AT_REMOVEDIR). Sur AirFileManager.ADR-046/077
unlinkPOSIXSupprime fichier/lien (no-follow). Sur AirFileManager.ADR-046/077
ftruncatePOSIXFixe la taille d’un fd (tronque/étend). length < 0EINVAL. Sur AirFileManager.ADR-046/077
fsyncPOSIXFlush données et métadonnées d’un fd. Sur AirFileManager.ADR-046/077
fdatasyncPOSIXFlush données d’un fd. Sur AirFileManager.ADR-046/077
renamePOSIXRenomme/déplace (renameat2 sans drapeau). Sur AirFileManager.ADR-046/077
linkPOSIXLien physique (linkat, lien final non suivi). Sur AirFileManager.ADR-046/077
symlinkPOSIXLien symbolique (symlinkat). Sur AirFileManager.ADR-046/077
readlinkPOSIXCible d’un lien (readlinkat), sans NUL ; tronqué à bufsiz ; bufsiz==0EINVAL. Sur AirFileManager.ADR-046/077
chmodPOSIXPermissions (fchmodat, suit les liens). Sur AirFileManager.ADR-046/077
chownPOSIXPropriétaire/groupe (fchownat, suit les liens). Sur AirFileManager.ADR-046/077
lchownPOSIXComme chown mais no-follow (AT_SYMLINK_NOFOLLOW). Sur AirFileManager.ADR-046/077
dirfdPOSIXfd sous-jacent d’un DIR (possédé, ne pas fermer). Accès direct au flux.ADR-046
flockPOSIXVerrou consultatif (LOCK_SH/EX/UN ± NB). Sur AirFileManager.ADR-046/077
mkfifoPOSIXTube nommé (mknodat+S_IFIFO). Sur AirFileManager.ADR-046/077
utimensatPOSIXHorodatages atime/mtime (UTIME_NOW/UTIME_OMIT ; times nul ⇒ now). tv_nsec hors plage ⇒ EINVAL. Sur AirFileManager.ADR-046/077
fchmodPOSIXPermissions par fd (additif couche 0 fchmod, ADR-085). fd < 0EBADF. Sur AirFileManager::change_mode_by_fd.ADR-085
fchownPOSIXPropriétaire/groupe par fd (additif couche 0 fchown, ADR-085). (uid,gid)=(−1,−1) ⇒ inchangé ; fd < 0EBADF. Sur AirFileManager::change_owner_by_fd.ADR-085
futimensPOSIXHorodatages par fd (additif couche 0 futimens = utimensat(fd, NULL, …), ADR-085). times nul ⇒ now ; tv_nsec hors plage ⇒ EINVAL ; fd < 0EBADF. Sur AirFileManager::set_times_by_fd.ADR-085
sendfilePOSIXTransfert zéro-copie kernel in_fdout_fd (additif couche 0 sendfile, ADR-085). offset nul ⇒ position courante de in_fd ; sinon départ explicite mis à jour. Rend les octets transférés ; fd < 0EBADF, *offset < 0EINVAL. Sur AirFileManager::send_file.ADR-085

Fonctions variadiques (shim C fileio_shim.c, déclarées air_c.h — trailer)

SymboleÉtiquetteNote / déviationADR
openPOSIX3e arg mode lu si O_CREAT. Non close-on-exec par défaut (D-M2.1).ADR-046
openatPOSIXComme open, relatif à dirfd.ADR-046
fcntlPOSIX-PARTIELCommandes à argument int (F_DUPFD/F_DUPFD_CLOEXEC/F_GETFD/F_SETFD/F_GETFL/F_SETFL). F_GETFL sans mode d’accès (D-M2.3) ; verrous F_*LK différés ⇒ EINVAL.ADR-046

Points d’entrée de support (glue variadique, air-libc-fileio, #[no_mangle])

Exportés dans libair_c.so (consommés uniquement par le shim C fileio_shim.c ; hors ABI libc standard, non scannés par conformance_labels — hors air-libc-capi/src).

SymboleÉtiquetteNote
air_libc_openAIR-EXT (support interne)Point d’entrée non-variadique d’open (le shim a fait le va_arg du mode).
air_libc_openatAIR-EXT (support interne)Idem openat.
air_libc_fcntlAIR-EXT (support interne)Point d’entrée non-variadique de fcntl (arg entier déjà tiré).

Constantes <fcntl.h>/<unistd.h>/<sys/stat.h>/<dirent.h>

Étiquette POSIX (toutes, littéraux ABI Linux). O_* : O_RDONLY(0)…O_CLOEXEC (0o2000000) ; O_DIRECTORY/O_NOFOLLOW arch-dépendants (trailer #ifdef). F_* : F_DUPFD(0)…F_DUPFD_CLOEXEC(1030), FD_CLOEXEC(1). AT_* : AT_FDCWD (-100), AT_SYMLINK_NOFOLLOW(0x100), AT_REMOVEDIR/AT_EACCESS(0x200), AT_NO_AUTOMOUNT(0x800), AT_EMPTY_PATH(0x1000). Accès : F_OK(0)/X_OK(1)/ W_OK(2)/R_OK(4). S_* : S_IFMTS_IXOTH (+ macros S_IS* au trailer). DT_* : DT_UNKNOWN(0)…DT_SOCK(12).

Types (struct stat/timespec/struct dirent/DIR)

SymboleÉtiquetteNoteADR
struct statPOSIX-PARTIELLayout ABI Air (largeur fixe, arch-indépendant ; D-M2.2). Champs st_dev/st_ino/st_nlink/st_mode/st_uid/st_gid/st_rdev/st_size/st_blksize/st_blocks/st_atim/st_mtim/st_ctim.ADR-046
struct timespecPOSIXtv_sec/tv_nsec (i64).ADR-046
struct direntPOSIX-PARTIELLayout ABI Air ; d_name[256] borné (NAME_MAX+1).ADR-046
DIRPOSIXType opaque (typedef struct DIR DIR;), comme la norme.ADR-046

M3 — signaux (signal.h) : air-libc-signal ; shims air-libc-capi::signal

Famille signaux. Split « logique / shims C-ABI » : la LOGIQUE (sigaction_impl/kill_impl/sigprocmask_impl/…, la table process-globale de dispositions, la traduction struct sigactionDisposition) vit dans air-libc-signal (Rust pur, mesurée, aucun #[no_mangle]), au-dessus de la couche 1 (air-signal : install_handler async non-faute, masque, envoi, pending ; air-thread::raw_futex pour le sigsuspend interruptible) — jamais air-sys-syscall (empilement strict ADR-066). Les shims extern "C" minces vivent dans air_libc_capi::signal (exclus du gate).

LE POINT DE SÉCURITÉ (ADR-064). sigaction/signal sur les 4 fautes (SIGSEGV/SIGBUS/SIGFPE/SIGILL) ACCEPTENT (retour 0) et mémorisent la disposition (illusion d’API : les getters rendent la valeur posée) mais N’INSTALLENT RIEN côté kernel — une faute prend toujours l’action par défaut (coredump). Déviation NON-POSIX-DÉLIBÉRÉE majeure (D-M3.1). Les signaux gérables (non-faute) sont, eux, installés en async réel (le kernel appelle le handler C, ADR-066).

SymboleÉtiquetteNote / déviationADR
sigemptysetPOSIXVide sigset_t.ADR-046
sigfillsetPOSIXPeuple sigset_t (__bits[0] = tous).ADR-046
sigaddsetPOSIXsigno hors [1, 64]EINVAL.ADR-046
sigdelsetPOSIXIdem sigaddset.ADR-046
sigismemberPOSIX1/0 ; signo invalide ⇒ EINVAL.ADR-046
sigactionPOSIX (gérables) / NON-POSIX-DÉLIBÉRÉ (fautes)Non-fautes : install async réel (air-signal). Fautes : mémorisé mais jamais installé → coredump (D-M3.1). SIGKILL/SIGSTOPEINVAL.ADR-064/066
signalPOSIX (gérables) / NON-POSIX-DÉLIBÉRÉ (fautes)Sémantique glibc (SA_RESTART, masque vide). Rend le handler précédent ou SIG_ERR. Fautes idem sigaction (D-M3.1).ADR-064/066
sigprocmaskPOSIXMasque per-thread (air-signal). how invalide ⇒ EINVAL.ADR-046
pthread_sigmaskPOSIXIdem, convention pthread_* (rend le code errno).ADR-046
sigpendingPOSIXair-signal::pending. set nul ⇒ EFAULT.ADR-046
sigsuspendPOSIX-PARTIELComposé replace_mask + futex_wait interruptible ; petite fenêtre de course vs rt_sigsuspend atomique (D-M3.3). Rend toujours -1/EINTR.ADR-066
killPOSIX-PARTIELpid > 0 seulement ; pid <= 0 (groupe/diffusion) ⇒ EINVAL (D-M3.2). signum == 0 = test d’existence.ADR-046
killpgPOSIXSignal à tout un groupe de processus (pgrp == 0 = groupe de l’appelant ; pgrp < 0EINVAL). Sur l’additif couche 0 kill_process_group (ADR-085) via air-signal::send_signal_to_process_group. Lève la restriction « groupe différé » de D-M3.2.ADR-085
raisePOSIXEnvoie au thread appelant.ADR-046
sigaltstackPOSIXInstalle/interroge la pile de signal alternative du thread (pour un SIGSEGV de débordement de pile, usage std). Additif couche 0 sigaltstack (ADR-085) + type AltStack (stack_t) → AirSignalManager::set_alt_stack. Pile trop petite ⇒ ENOMEM/EINVAL.ADR-085
pausePOSIXSuspend jusqu’à un signal capté ; rend toujours -1/EINTR. Composé sur AirSignalManager::pause = ppoll(0 fd, ∞, masque courant) (sans additif couche 0). Chemin bloquant = exception de couverture (non injectable, comme le EINTR d’air-poll).ADR-085

Types & constantes <signal.h> (ABI Air, trailer hand-written)

sigset_t (128 octets, parité glibc ; seul __bits[0] porté), struct sigaction (union sa_handler/sa_sigaction, sa_mask, sa_flags, sa_restorerlayout ABI Air, D-M3.7), siginfo_t (128 octets, champs si_signo/si_code/si_addr/ si_pid/si_uid/si_status), sighandler_t (void (*)(int)). Constantes (POSIX) : SIG_DFL(0)/SIG_IGN(1)/SIG_ERR(-1) ; SIG_BLOCK(0)/SIG_UNBLOCK(1)/ SIG_SETMASK(2) ; SIGHUP(1)…SIGSYS(31), SIGRTMIN/SIGRTMAX ; SA_* (SA_NOCLDSTOP=1…SA_RESTART=0x10000000…SA_RESETHAND=0x80000000, identiques x86_64/aarch64). Écrits à la main dans le trailer cbindgen.toml (union + arch-vérifiés), exclus de la génération cbindgen.

M3 — threads (pthread.h) : air-libc-thread ; shims air-libc-capi::thread

Famille pthread. Split « logique / shims » : la LOGIQUE (pthread_mutex_lock_impl/pthread_cond_wait_impl/…) vit dans air-libc-thread (Rust pur, mesurée), bâtie sur air-thread::raw_futex opérant sur le mot futex dans la mémoire opaque du pthread_*_t fourni par l’appelant (jamais un AirMutex qui posséderait son stockage — ADR-066). Types opaques ABI Air à largeur fixe (parité taille glibc), zéro-initialisables (PTHREAD_MUTEX_INITIALIZER = {{0}}). Empilement strict : consomme uniquement air-thread (jamais air-sys-syscall). Le cycle de vie (pthread_create/join/ …) et le TLS reposent sur le spawn/std::thread d’air-threadhôte seulement (cfg(not(target_env = "air"))), comme air_thread::thread.

Synchronisation (bâtie sur futex, exportée partout)

SymboleÉtiquetteNoteADR
pthread_mutex_init / pthread_mutex_destroyPOSIXType via attribut ; destroy d’un mutex verrouillé ⇒ EBUSY.ADR-066
pthread_mutex_lock / pthread_mutex_trylock / pthread_mutex_unlockPOSIXMutex futex 3 états ; NORMAL/RECURSIVE/ERRORCHECK (owner = Tid) ; EDEADLK/EBUSY/EAGAIN/EPERM.ADR-066
pthread_mutex_timedlockPOSIXÉchéance absolue CLOCK_REALTIME ; ETIMEDOUT.ADR-066
pthread_mutexattr_init / destroy / settype / gettypePOSIXtype NORMAL/RECURSIVE/ERRORCHECK.ADR-066
pthread_cond_init / destroy / signal / broadcastPOSIXCondition futex par compteur de séquence ; broadcast = FUTEX_WAKE ALL.ADR-066
pthread_cond_wait / pthread_cond_timedwaitPOSIXDéverrouille/attend/ré-acquiert ; échéance absolue (REALTIME/MONOTONIC selon attribut) ; ETIMEDOUT.ADR-066
pthread_condattr_init / destroy / setclock / getclockPOSIXHorloge REALTIME (défaut) / MONOTONIC.ADR-066
pthread_rwlock_init / destroyPOSIXdestroy d’un verrou détenu ⇒ EBUSY.ADR-066
pthread_rwlock_rdlock / tryrdlock / wrlock / trywrlock / unlockPOSIX-PARTIELMot d’état lecteurs/écrivain sur futex ; lecteur-préférentiel (famine d’écrivain possible sous charge continue — D-M3.9) ; EBUSY/EPERM. Variantes timed* différées.ADR-066
pthread_rwlockattr_init / destroyPOSIXNo-op (aucun attribut honoré en M3).ADR-066
pthread_oncePOSIXExécute init_routine exactement une fois (état futex 3 valeurs).ADR-066
pthread_attr_init / destroy / setdetachstate / getdetachstate / setstacksize / getstacksizePOSIXAttributs honorés : état detach + taille de pile (autres différés).ADR-066

Cycle de vie + TLS (hôte uniquement, cfg(not(target_env = "air")))

SymboleÉtiquetteNote / déviationADR
pthread_createPOSIXSur le spawn d’air-thread. Honore detach/pile ; ENOMEM à l’échec. pthread_t = Tid noyau (D-M3.4).ADR-066
pthread_joinPOSIXRécupère la valeur de retour ; ESRCH/EINVAL. Thread paniqué ⇒ retour NULL.ADR-066
pthread_detachPOSIXRend non joignable ; ESRCH/EINVAL.ADR-066
pthread_selfPOSIXTid noyau du thread courant (air_thread::runtime_primitives::current_tid, D-M3.4). Export cible aussi (target_vendor="air", ADR-086).ADR-066
pthread_getattr_npPOSIX-PARTIELInitialise l’attribut avec les bornes de pile du thread principal (air_runtime::main_thread_stack, ADR-086). Threads spawnés différés. Export cible-only.ADR-086
pthread_attr_getstackPOSIXLit (stackaddr, stacksize) d’un attribut renseigné par pthread_getattr_np.ADR-086
pthread_attr_getguardsizePOSIXLit la taille de garde de pile d’un attribut renseigné par pthread_getattr_np.ADR-086
pthread_equalPOSIXÉgalité de pthread_t.ADR-066
pthread_exitNON-POSIX-DÉLIBÉRÉAdditif REMONTÉ (D-M3.5) : pas de sortie de thread propre en couche 1 + panic = "abort". Rejoue les destructeurs TLS puis sommeil indéfini du thread appelant (ne revient jamais, sans abort du processus).ADR-064/066
pthread_key_create / pthread_key_deletePOSIXRegistre de clés borné (PTHREAD_KEYS_MAX) ; EAGAIN si épuisé.ADR-066
pthread_setspecific / pthread_getspecificPOSIXValeurs par-thread. Hôte : std::thread_local! (destructeurs via Drop). Cible : #[thread_local] natif (target_tls, ADR-086) ; rejeu des destructeurs à la sortie d’un thread spawné = crochet couche 1 différé (thread principal réclamé à la sortie du processus).ADR-066

Types & constantes <pthread.h> (ABI Air, trailer hand-written)

Types opaques largeur/alignement FIXES (D-M3.7) : pthread_mutex_t (40), pthread_cond_t (48), pthread_rwlock_t (56), pthread_once_t (4), pthread_attr_t (56), pthread_mutexattr_t/pthread_condattr_t (4), pthread_rwlockattr_t (8) ; pthread_t = unsigned long (parité glibc, encode le Tid), pthread_key_t = unsigned int. Initialiseurs (POSIX) : PTHREAD_MUTEX_INITIALIZER/PTHREAD_COND_INITIALIZER/PTHREAD_RWLOCK_INITIALIZER (= {{0}}), PTHREAD_ONCE_INIT (= 0). Constantes : PTHREAD_MUTEX_NORMAL(0)/ RECURSIVE(1)/ERRORCHECK(2)/DEFAULT(0) ; PTHREAD_CREATE_JOINABLE(0)/ DETACHED(1) ; CLOCK_REALTIME(0)/CLOCK_MONOTONIC(1) ; struct timespec (partagée avec M2). Écrits à la main dans le trailer cbindgen.toml, exclus de la génération cbindgen.

Liste exhaustive des symboles M3 (référence du gate conformance_labels)

Chaque extern "C" ci-dessous porte son étiquette dans le /// de son shim (air-libc-capi::{signal,thread}) et figure verbatim ici (le test vérifie la présence de chaque nom) :

sigemptyset, sigfillset, sigaddset, sigdelset, sigismember, sigaction, signal, sigprocmask, pthread_sigmask, sigpending, sigsuspend, kill, raise, pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_timedlock, pthread_mutex_unlock, pthread_mutexattr_init, pthread_mutexattr_destroy, pthread_mutexattr_settype, pthread_mutexattr_gettype, pthread_cond_init, pthread_cond_destroy, pthread_cond_signal, pthread_cond_broadcast, pthread_cond_wait, pthread_cond_timedwait, pthread_condattr_init, pthread_condattr_destroy, pthread_condattr_setclock, pthread_condattr_getclock, pthread_rwlock_init, pthread_rwlock_destroy, pthread_rwlock_rdlock, pthread_rwlock_tryrdlock, pthread_rwlock_wrlock, pthread_rwlock_trywrlock, pthread_rwlock_unlock, pthread_rwlockattr_init, pthread_rwlockattr_destroy, pthread_once, pthread_attr_init, pthread_attr_destroy, pthread_attr_setdetachstate, pthread_attr_getdetachstate, pthread_attr_setstacksize, pthread_attr_getstacksize, pthread_create, pthread_join, pthread_detach, pthread_self, pthread_equal, pthread_exit, pthread_key_create, pthread_key_delete, pthread_setspecific, pthread_getspecific.

Journal des déviations (roadmap §8)

#SymboleType de déviationJustificationADR
D-M0.1__libc_start_mainSignature 4-args (vs 7-args glibc)crt d’Air (pas gcc) ; crochets init/fini hors M0ADR-046/050
D-M0.2__errno_locationNom = convention ABI Linux (hors texte POSIX)Compat ABI C indispensable pour OpenSSH ; sémantique POSIX conformeADR-046
D-M1b.1malloc / callocmalloc(0)/calloc(0,x) rendent un pointeur unique non-NULL libérableChoix POSIX-autorisé, plus sûr : free() du retour toujours valideADR-056
D-M1b.2reallocrealloc(p, 0) == free(p) puis rend NULLComportement glibc historique (C17 implementation-defined) — gravé et documentéADR-056
D-M1b.3callocProduit nmemb * size checked ⇒ débordement = NULL + ENOMEMDéfense anti heap-overflow (sécurité #1) — un calloc naïf débordant ouvrirait un overflowADR-056
D-M1b.4aligned_allocContrainte C11 « size multiple de alignment » relâchéeAligné sur glibc — plus permissif, jamais moins sûrADR-056
D-M1b.5strdup / strndupstrdup(NULL)/strndup(NULL, …) rendent NULL (durci)Évite le déréférencement d’un NULL indéfini (sécurité #1)ADR-056
D-M1b.6moteur de format (interne)%n rejeté — rendu littéral, aucune écriture mémoireVecteur d’exploitation format-string classique (écriture-arrière) neutralisé par construction (sécurité #1)ADR-046/047
D-M1c.1setvbuf / setbufTampon fourni par l’appelant IGNORÉ (seuls mode/size honorés)Sécurité #1 : ne jamais aliaser une mémoire C au cycle de vie incontrôlé (classe de bugs stdio) ; Air gère toujours son tampon interneADR-032/046
D-M1c.2getenvPointeur rendu stable pour la vie du processus (au lieu de « jusqu’au prochain setenv »)Environ scellé lecture seule en M1-c (pas de setenv) ⇒ aucune invalidation possible ; cache à pointeur figé, race-safe vis-à-vis du futur setenvADR-046
D-M1c.3atexitatexit(NULL) rend une valeur non nulle (durci)Évite l’appel indéfini d’un handler nul (sécurité #1)ADR-046
D-M2.1open/openat/creat/dup/dup2/pipeLe fd rendu n’est pas close-on-exec par défaut (POSIX) — on efface FD_CLOEXEC que la couche 0 forceLa couche 0 force O_CLOEXEC (posture Air) ; POSIX/OpenSSH exigent des fd héritables par exec. La fenêtre cloexec transitoire est un durcissementADR-021/046
D-M2.2struct stat/dirent/DIRLayout ABI Air (largeur fixe, arch-indépendant), distinct de la glibcC’est notre ABI (comme FILE) : les programmes compilent contre air_c.h. Évite le layout glibc arch-dépendant/paddingADR-046
D-M2.3fcntl (F_GETFL)Ne restitue pas le mode d’accès (O_RDONLY/WRONLY/RDWR) ; verrous F_*LK différés ⇒ EINVALLa couche 0 get_status_flags ne modélise que les drapeaux de statut ; le round-trip F_GETFL|O_NONBLOCKF_SETFL (cas OpenSSH) reste correct. Verrous = additif si besoin réelADR-046
D-M2.4realpath/getcwdForme GNU allouante (realpath(_,NULL)/getcwd(NULL,0)) différéeEINVALPas d’allocation malloc dans la crate mesurée (le buffer appelant suffit au cas courant OpenSSH). Additif possible via le shimADR-046
D-M2.5readdirUn nom d’entrée > NAME_MAX (255) est sauté (jamais tronqué en silence)ADR-032 (zéro troncature muette) : un nom débordant d_name[256] ne peut être rendu intègre ⇒ omis plutôt que tronquéADR-046/032
D-M3.1sigaction/signal (fautes)Les 4 fautes (SIGSEGV/SIGBUS/SIGFPE/SIGILL) acceptent (retour 0) et mémorisent la disposition, mais aucun handler n’est installé côté kernel — une faute prend toujours l’action par défaut (coredump)Doctrine « fail-fast » d’Air : aucun code utilisateur dans un contexte corrompu (un handler de crash est une surface d’exploitation). Illusion d’API maintenue pour la compat sourceADR-064
D-M3.2killpid <= 0 (groupe de processus / diffusion) non supporté ⇒ EINVALLa couche 1 send_signal_to_process modélise un Pid positif ; groupe/diffusion = additif si besoin réel OpenSSHADR-046
D-M3.3sigsuspendPetite fenêtre de course (masque posé puis attente, non atomiques) vs rt_sigsuspend kernelPas d’rt_sigsuspend atomique en couche 1 (escalade additif) ; composition replace_mask + futex_wait interruptible, correcte hors fenêtre minusculeADR-066
D-M3.4pthread_tpthread_t = Tid noyau (élargi en unsigned long), non un pointeur opaque de TCB glibcIdentité de thread stable, obtenue via air_thread::runtime_primitives::current_tid (couche 1) ; pthread_self/equal/join cohérents. Réutilisation de Tid après mort de thread = caveat pthread standardADR-066
D-M3.5pthread_exitNe réalise pas une sortie de thread propre : rejoue les destructeurs TLS puis endort indéfiniment le thread appelant (valeur non récupérable par join)air-thread n’expose aucune sortie de thread sans unwind, et l’artefact est panic = "abort" (pas d’unwind) → additif couche 1 REMONTÉ, sans invention de mécanisme ; comportement confiné au thread appelant (ni abort du processus, ni corruption)ADR-064/066
D-M3.6pthread_setspecific/getspecific (destructeurs)Destructeurs TLS rejoués à la sortie de thread via std (hôte) ; absents sur la cible *-linux-airair-thread n’expose pas de crochet de destruction TLS à la sortie de thread → additif couche 1 REMONTÉ ; sur l’hôte mesuré, la voie std (thread_local! Drop) les rejoue réellementADR-066
D-M3.7sigset_t/struct sigaction/pthread_*_tLayout ABI Air (largeur fixe, arch-indépendant, disposition interne propre), distinct de la glibc bit-à-bit (parité taille seulement)C’est notre ABI (comme FILE/struct stat) : les programmes compilent contre air_c.h. Évite le layout glibc arch-dépendantADR-046/066
D-M3.8pthread_cond_timedwait (broadcast)broadcast réveille tous les waiters (FUTEX_WAKE ALL, troupeau) plutôt que FUTEX_REQUEUECorrection avant performance (Principe 5) ; zéro réveil perdu, zéro deadlock. requeue = optimisation additive si mesure le justifieADR-066
D-M3.9pthread_rwlock_*Verrou lecteur-préférentiel (famine d’écrivain possible sous charge de lecture continue) ; variantes timed* différéesImplémentation correcte et simple sur futex (Principe 5) ; verrou d’équité = additif si un besoin réel émergeADR-066

Additifs couche 1 v1.x déclenchés par la libc (roadmap §8)

AdditifCrate couche 1Raison (libc)Statut
AirRuntime::errno_location() -> *mut i32air-runtimeSocle de __errno_location() : l’errno scellé n’exposait que errno()/set_errno() (par valeur) ; C exige l’adresse (lvalue).Additif v1.x — RFC de descellement additif requis (ADR-062). Premier additif révélé par la libc (roadmap §6).
air_env::set_var/unset_var (mutation d’environ)air-envsetenv/putenv/unsetenv de la libc — M1-c ne fournit que getenv (environ lecture seule). Le design du cache getenv (pointeur figé) est déjà compatible avec un setenv futur.Additif v1.x à poser (délégation B) si un besoin OpenSSH réel émerge — getenv seul suffit à la lecture d’$HOME/$PATH/… (le cas courant). Signalé par M1-c.
Sortie de thread propre (air-thread)air-threadpthread_exit(retval) doit terminer le thread appelant sans unwind (artefact panic = "abort") en rendant sa valeur récupérable par join. La couche 1 n’expose aucune telle primitive → M3 endort le thread (D-M3.5).Additif à poser — révélé par M3 (roadmap §8). Le spawn Air-natif (air-runtime, cible) devra exposer une sortie de thread propre.
Crochet de destruction TLS à la sortie de thread (air-thread)air-threadDestructeurs pthread_key_create à rejouer à la fin de chaque thread, y compris sur cible *-linux-air (M3 ne les rejoue que via std, hôte — D-M3.6).Additif à poser — révélé par M3. Lié au spawn Air-natif.
rt_sigsuspend atomique (couche 0/1)air-signalsigsuspend POSIX exige masque + attente atomiques ; M3 compose replace_mask + futex_wait (petite fenêtre de course, D-M3.3).Additif à poser si la course s’avère problématique — révélé par M3.
kill groupe de processus / diffusion (air-signal)air-signalsend_signal_to_process prend un Pid positif ; kill(pid<=0, …) (groupe/diffusion, killpg) non modélisé (D-M3.2).Additif à poser si un besoin OpenSSH réel émerge — révélé par M3.
Sommeil non-retry (air-base-core)air-base-corenanosleep POSIX doit rendre EINTR + le temps restant à l’appelant, sans retry auto ; or air-base-core::sleep réessaie en interne sur EINTR.Additif à poser — révélé par M4. Exige un sommeil couche 1 qui remonte EINTR+reste (base de nanosleep/clock_nanosleep/select/poll timeouts).
clock_getres (couche 1)air-base-coreclock_getres (résolution d’horloge) — non exposé par la couche 1.Additif à poser si un besoin réel émerge — révélé par M4.
Base tz + calendrier (strftime/localtime_r)air-base-lib (icu4x)strftime/localtime_r/gmtime_r exigent la base tz + le calendaire (roadmap i18n, icu4x).Différé — dépend du pipeline données i18n (ADR-059). Révélé par M4.

M4 — temps (time.h/sys/time.h) : air-libc-time ; shims air-libc-capi::time

Lectures d’horloge de la libc, bâties exclusivement sur la couche 1 (air-base-core : AirSystemTime = CLOCK_REALTIME, AirInstant = CLOCK_MONOTONIC) — jamais air-sys-syscall (empilement strict). La valeur monotone brute est reconstruite via l’API publique (AirInstant::now().duration_since(AirInstant::from_monotonic_micros(0, 0))), sans additif couche 1. Le sommeil (nanosleep), la résolution (clock_getres) et le formatage tz (strftime/localtime_r) sont remontés comme additifs (section suivante).

Fonctions

SymboleÉtiquetteNotesADR
clock_gettimePOSIXCLOCK_REALTIME/CLOCK_MONOTONIC ; horloge inconnue ⇒ EINVAL ; tp nul ⇒ EFAULT. Validation en amont (horloge avant pointeur).ADR-046
gettimeofdayPOSIXObsolescent. tv murale (s + µs, troncature ns→µs conforme) ; tz ignoré (obsolète, jamais déréférencé) ; tv nul ⇒ EFAULT.ADR-046
timePOSIXSecondes Unix murales ; time(NULL) valide (rend sans écrire) ; (time_t)-1 + errno sur échec (inatteignable).ADR-046

Types

timespec (POSIX, tag dédupliqué avec air-libc-fileio par cbindgen), timeval (POSIX), time_t/suseconds_t/clockid_t (POSIX) : largeur fixe arch-indépendantetv_sec/tv_nsec/tv_usec = int64_t (64 bits sur x86_64 ET aarch64), clockid_t = int. Constantes CLOCK_REALTIME/ CLOCK_MONOTONIC déjà émises par M3 (pthread_cond).

M4 — comptes (pwd.h) : air-libc-pwd ; shims air-libc-capi::pwd

Variantes réentrantes (_r) interrogeant /etc/passwd, lu via la couche 1 (AirFileSystem::read_to_bytes, bornée 1 MiB) — jamais air-sys-syscall. Parseur pur mesuré + empaquetage borné dans le buffer appelant (sécurité : getpwnam_r = vecteur d’overflow ; buffer trop petit ⇒ ERANGE, jamais de dépassement). Convention *_r : rendent l’errno directement (0 = trouvé/absent ; >0 = erreur), sans toucher l’errno global. Les formes non-réentrantes getpwnam/getpwuid (stockage statique, shims air-libc-capi::pwd) enveloppent les _r : elles rendent un struct passwd * (écrasé au prochain appel), NULL si absent, NULL + errno global sur erreur — non thread-safe par contrat POSIX.

Fonctions

SymboleÉtiquetteNotesADR
getpwnam_rPOSIXEntrée par nom ; *result=&pwd (trouvée) / NULL (absente), retour 0 ; ERANGE (buffer trop petit) ; EINVAL (pointeur nul) ; errno de lecture propagé. Empaquetage borné.ADR-046
getpwuid_rPOSIXComme getpwnam_r, recherche par UID.ADR-046
getpwnam/getpwuidPOSIXFormes non-réentrantes (requises par OpenSSH) : struct passwd * vers un stockage statique (NSS_BUFLEN_PASSWD = 1024), NULL si absent, NULL+errno global (ERANGE) sur erreur. Non thread-safe — variantes _r en concurrence.ADR-046

Types

struct passwd (POSIX : pw_name/pw_passwd/pw_uid/pw_gid/pw_gecos/pw_dir/pw_shell, char * dans le buffer appelant), uid_t/gid_t (POSIX, unsigned int 32 bits x86_64 ET aarch64).

M4 — groupes (grp.h) : air-libc-grp (binder) sur air-account

Variantes réentrantes (_r) interrogeant /etc/group via la brique couche 1 air-account (ADR-067) — la libc ne réimplémente rien, elle bind + empaquette l’ABI C. Empaquetage borné du buffer appelant, y compris le tableau gr_mem (char **, NULL-terminé, aligné pointeur — sécurité). Convention *_r : errno rendu directement (0 = trouvé/absent ; ERANGE/EINVAL).

Fonctions

SymboleÉtiquetteNotesADR
getgrnam_rPOSIXEntrée name de /etc/group empaquetée dans buf (dont gr_mem aligné/NULL-terminé) ; *result=&grp/NULL (retour 0) ; ERANGE ; EINVAL (pointeur nul).ADR-046, ADR-067
getgrgid_rPOSIXComme getgrnam_r, recherche par GID.ADR-046, ADR-067
getgrnam/getgrgidPOSIXFormes non-réentrantes (requises par OpenSSH) : struct group * vers un stockage statique (4096 octets, gr_mem inclus), NULL si absent, NULL+errno global (ERANGE) sur erreur. Non thread-safe — variantes _r en concurrence.ADR-046, ADR-067

Types

struct group (POSIX : gr_name/gr_passwd/gr_gid/gr_mem) — char * et char **gr_mem pointent dans le buffer appelant. gid_t (POSIX, unsigned int 32 bits, déjà émis par M4 comptes).

M4 — shadow (shadow.h) : air-libc-shadow (binder) sur air-account

Variante réentrante getspnam_r interrogeant /etc/shadow (root-only) via la brique couche 1 air-account (ADR-067). SENSIBLE : le hachage transite par air-account où il est zeroïsé (ShadowHash, écriture volatile) et jamais journalisé (Debug redacté) ; la comparaison temps-constant relève d’air-crypto. Le codec decode seul (l’écriture de /etc/shadow est différée, Principe 5).

Fonctions

SymboleÉtiquetteNotesADR
getspnam_rNON-POSIX-DÉLIBÉRÉshadow.h = extension Linux/glibc (hors POSIX), fournie car requise par OpenSSH (auth mot de passe). *result=&sp/NULL (retour 0) ; ERANGE ; EINVAL ; EACCES en non-privilégié (root-only).ADR-046, ADR-067

Types

struct spwd (NON-POSIX-DÉLIBÉRÉ, extension shadow : sp_namp/sp_pwdp/sp_lstchg/sp_min/sp_max/sp_warn/sp_inact/sp_expire/sp_flag) — champs de vieillissement long (-1 = non défini) ; sp_namp/sp_pwdp pointent dans le buffer appelant.

M4 — identifiants (unistd.h/grp.h) : air-libc-id sur air-process::credentials (ADR-068)

Getters/setters d’identifiants du processus courant, bindant la surface fine air-process::credentials (getters + set_resuid/set_resgid + groupes) sur la couche 0 existanteaucun additif couche 0. drop_privileges reste la voie recommandée du largage réel ; ces primitives sont les 1:1 POSIX. initgroups combine air_account::groups_of_user + setgroups.

Fonctions

SymboleÉtiquetteNotesADR
getuid/geteuid/getgid/getegidPOSIXInfaillibles ; réel/effectif via getresuid/getresgid.ADR-068
setuid/setgidPOSIXSémantique conditionnelle au privilège (privilégié ⇒ r/e/s ; sinon effectif seul si uid ∈ {real,saved}, sinon EPERM). 0/−1+errno.ADR-068
setresuid/setresgidNON-POSIX-DÉLIBÉRÉExtension Linux/BSD requise par OpenSSH ; -1 = ne pas changer.ADR-068
setgroupsNON-POSIX-DÉLIBÉRÉExtension ; EINVAL si size > NGROUPS_MAX/list nul ; EPERM non privilégié.ADR-068
initgroupsNON-POSIX-DÉLIBÉRÉExtension BSD/glibc ; groupes de /etc/group (+ group) → setgroups.ADR-068
getgroupsPOSIXGroupes supplémentaires courants (requise par OpenSSH uidswap.c : sauvegarde/restauration autour d’un changement d’UID). size == 0 ⇒ rend le nombre sans écrire ; sinon écrit jusqu’à size entrées et rend le nombre, ou −1+EINVAL si size insuffisant. Logique mesurée (air-libc-id).ADR-068

Chantier B — famille processus (unistd.h/sched.h) : air-libc-process sur air-process::AirTaskManager (ADR-077)

Identité et ordonnancement du processus courant, bindant l’objet médiateur AirTaskManager (couche 1) sur la couche 0 existanteaucun additif couche 0. Lot process-1 : symboles infaillibles (aucun errno). Contrôle (setpgid/setsid), attente (waitpid/waitid) et exec/spawn élargiront la famille (lots suivants).

SymboleÉtiquetteNote / déviationADR
getpidPOSIXPID courant. Infaillible. Sur AirTaskManager::current_pid.ADR-077
getppidPOSIXPID parent (0 si réattribué à l’init). Infaillible. Sur AirTaskManager::parent_pid.ADR-077
sched_yieldPOSIXCède le CPU ; rend 0 (ne peut échouer sous Linux). Sur AirTaskManager::yield_now.ADR-077
getpgidPOSIXGroupe de processus de pid (0 = courant). −1+errno (ESRCH…). Sur AirTaskManager::process_group.ADR-077
getsidPOSIXSession de pid (0 = courant). −1+errno. Sur AirTaskManager::session_id.ADR-077
setpgidPOSIXPlace pid (0 = courant) dans le groupe pgid (0 = son PID). 0/−1+errno. Sur AirTaskManager::set_process_group.ADR-077
waitpidPOSIXAttente d’un enfant (pid >0/-1/0/<-1) ; encode le wstatus (macros W*) via waitid couche 0. WNOHANG/WUNTRACED/WCONTINUED. 0 si WNOHANG sans événement ; −1+errno. Sur AirTaskManager::wait.ADR-077
sched_getaffinityPOSIX/LinuxAffinité CPU de pid (0 = tâche courante) écrite dans mask (au plus cpusetsize octets, reste à zéro). mask nul ⇒ EFAULT ; cpusetsize nul ⇒ EINVAL. Sur AirTaskManager::cpu_affinity (couche 0 get_cpu_affinity, sans additif syscall ; type CpuSet gagne as_mut_bytes).ADR-077
sched_setaffinityPOSIX/LinuxFixe l’affinité CPU de pid depuis mask. Masque vide ⇒ EINVAL ; mask nul ⇒ EFAULT. Sur AirTaskManager::set_cpu_affinity.ADR-077
execvePOSIXRemplace l’image du processus par path (argv/envp = tableaux char* NULL-terminés). Ne retourne pas en succès ; échec ⇒ -1+errno (path nul ⇒ EFAULT, ENOENT/EACCES/ENOEXEC…). Sur AirTaskManager::exec (couche 0 execve, sans additif).ADR-077
execveatPOSIXComme execve mais path relatif à dirfd (AT_FDCWD = cwd ; AT_EMPTY_PATH = exécuter dirfd, socle fexecve) + flags. dirfd<0 hors AT_FDCWDEBADF. Sur AirTaskManager::exec_at.ADR-077
execvPOSIXComme execve mais avec l’environnement courant implicite (bloc environ reflétant setenv, via AirEnvironmentManager::environ_block). path nul ⇒ EFAULT. Sur AirTaskManager::exec.ADR-077
execvpPOSIXComme execv mais recherche file dans PATH (sauf s’il contient / ; dir vide = cwd ; défaut confstr si PATH absent). file nul ⇒ EFAULT. Cœur exec_resolving sur AirTaskManager::exec.ADR-077
execvpeNON-POSIX (GNU)Comme execvp mais envp explicite. Cœur exec_resolving.ADR-077
chrootNON-POSIX (Unix/SVr4)Change le répertoire racine du processus (confinement, CAP_SYS_CHROOT ; EPERM sinon = exception PRIVILEGE). path nul ⇒ EFAULT. Sur additif couche 0 chroot (ADR-085) → AirTaskManager::change_root.ADR-085
posix_spawnPOSIXLance path (direct). Traduit les types opaques (posix_spawn_file_actions_t/posix_spawnattr_t, disposition ABI musl) → air_process::spawn_process (couche 1, fork+file-actions+exec async-signal-safe). Rend le numéro d’erreur (0 = succès), *pid reçoit le PID.ADR-077
posix_spawnpPOSIXComme posix_spawn mais recherche PATH si file sans / (candidats pré-résolus, l’enfant essaie en ordre).ADR-077
posix_spawn_file_actions_initPOSIXInitialise une liste d’actions vide.ADR-077
posix_spawn_file_actions_destroyPOSIXLibère la liste (chemins possédés inclus) ; idempotent.ADR-077
posix_spawn_file_actions_addopenPOSIXAjoute open(path, oflag, mode) → installé sur fildes. Chemin copié (POSIX).ADR-077
posix_spawn_file_actions_addclosePOSIXAjoute close(fildes).ADR-077
posix_spawn_file_actions_adddup2POSIXAjoute dup2(fildes, newfildes).ADR-077
posix_spawn_file_actions_addchdir_npNON-POSIX (GNU)Ajoute chdir(path) dans l’enfant.ADR-077
posix_spawnattr_initPOSIXAttributs à zéro (rien d’appliqué).ADR-077
posix_spawnattr_destroyPOSIXNo-op (sans état alloué).ADR-077
posix_spawnattr_setflagsPOSIXFixe POSIX_SPAWN_{SETPGROUP,SETSIGDEF,SETSIGMASK} (autres non supportés).ADR-077
posix_spawnattr_getflagsPOSIXLit les drapeaux dans *flags.ADR-077
posix_spawnattr_setpgroupPOSIXGroupe de processus de l’enfant (0 = le sien).ADR-077
posix_spawnattr_setsigmaskPOSIXMasque de signaux de l’enfant (1er mot du sigset_t = signaux 1..=64).ADR-077
posix_spawnattr_setsigdefaultPOSIXSignaux remis à l’action par défaut dans l’enfant.ADR-077

Chantier B — famille système (unistd.h) : air-libc-system sur air-system::AirSystemManager (ADR-077)

Fonctions

SymboleConformitéSémantiqueRéf
gethostnamePOSIXÉcrit le nom d’hôte (terminé NUL) dans name (≤ len octets). name nul ⇒ EFAULT ; buffer trop court ⇒ ENAMETOOLONG. Sur AirSystemManager::hostname (couche 0 gethostname via uname, sans additif).ADR-077
sethostnamePOSIX/LinuxFixe le nom d’hôte aux len octets de name. EPERM sans CAP_SYS_ADMIN (chemin succès = exception PRIVILEGE, runner CI root) ; NUL interne ⇒ EINVAL ; name nul ⇒ EFAULT. Sur AirSystemManager::set_hostname.ADR-077
sysconfPOSIXConfig système : _SC_PAGESIZE=4096 (x86_64/Pi4 4 Kio, à dériver d’AT_PAGESZ), _SC_CLK_TCK=100, _SC_OPEN_MAX=1024, _SC_ARG_MAX/_SC_NGROUPS_MAX/_SC_GET{PW,GR}_R_SIZE_MAX statiques ; _SC_NPROCESSORS_{ONLN,CONF} réels via AirSystemManager::online_processor_count (sched_getaffinity). name inconnu ⇒ -1.ADR-077
getauxvalPOSIX-PARTIELEntrée du vecteur auxiliaire (AT_PAGESZ/AT_HWCAP/AT_RANDOM…) via air_runtime::process_context::auxval (capturé au bootstrap, ADR-086). Entrée absente ⇒ 0. Export cible-only (target_vendor="air" ; l’auxv n’existe qu’on-target).ADR-086
syscallPOSIX-PARTIELTrappe générique long syscall(long, ...) que le pal std exige (futex/gettid en direct). EXCEPTION assumée temporaire (docs/EXCEPTIONS.md, ADR-087) : seul shim tapant la couche 0 (raw_syscall). Convention __syscall_ret (-errno-1+errno). Export cible-only. À retirer avec l’équipe Rust.ADR-087

Chantier B — famille mémoire (sys/mman.h) : air-libc-mem sur air-memory::AirMemoryManager (ADR-077 / ADR-085)

Fonctions

SymboleConformitéSémantiqueRéf
mmapPOSIXMapping anonyme (MAP_ANONYMOUS, fd < 0) ou fichier-adossé de length octets. Rend l’adresse, ou MAP_FAILED ((void *)-1) + errno. MAP_FIXED non supportéEINVAL (jamais un placement silencieux, ADR-032) ; offset négatif ⇒ EINVAL. Sur AirMemoryManager::map_anonymous/map_file.ADR-085
mprotectPOSIXModifie les protections de [addr, addr+len). 0/−1+errno (addr nul/non aligné ⇒ EINVAL). Sur AirMemoryManager::protect.ADR-085
munmapPOSIXLibère [addr, addr+len) via l’additif couche 0 munmap_raw (ADR-085). 0/−1+errno (addr nul/non aligné ⇒ EINVAL). Sur AirMemoryManager::unmap.ADR-085

M4 — famille réseau (arpa/inet.h, ADR-069 Lot 3)

Codec d’adresses et byte-order. Logique mesurée dans air-libc-socketio (errno-aware) et codec pur/fuzzé air-libc-socket ; shims extern "C" minces dans air-libc-capi::socket (exclus du gate de couverture).

SymboleÉtiquetteNotes
inet_ptonPOSIXGrammaire stricte (pas le legacy inet_aton) ; EAFNOSUPPORT sur famille inconnue.
inet_ntopPOSIXSortie canonique RFC 5952 (IPv6) ; ENOSPC si size insuffisant.
htonsPOSIXHôte → réseau (16 bits), fonction pure.
ntohsPOSIXRéseau → hôte (16 bits), fonction pure.
htonlPOSIXHôte → réseau (32 bits), fonction pure.
ntohlPOSIXRéseau → hôte (32 bits), fonction pure.

Aucune déviation. Familles fd-level (socket/bind/connect/…) : lots 3.b–3.d.

M4 — cycle de vie des sockets (sys/socket.h, ADR-069 Lot 3.b)

Logique mesurée air-libc-socketio (errno-aware) sur le moteur air-socket ; shims air-libc-capi::socket. Types : struct sockaddr, socklen_t.

SymboleÉtiquetteNotes
socketPOSIXSOCK_CLOEXEC forcé ; SOCK_NONBLOCK honoré ; domaine inconnu EAFNOSUPPORT.
bindPOSIXsockaddr décodé via le codec couche 0 (ADR-070).
connectPOSIXEINPROGRESS remonté (non bloquant).
listenPOSIXbacklog négatif traité comme 0.
acceptPOSIXaccept4(flags=0) ; adresse du pair tronquée à *addrlen.
accept4POSIXextension Linux (POSIX de fait) ; SOCK_CLOEXEC/SOCK_NONBLOCK.
getsocknamePOSIXlongueur réelle dans *addrlen (signale la troncature).
getpeernamePOSIXidem getsockname.
shutdownPOSIXSHUT_RD/SHUT_WR/SHUT_RDWR ; how inconnu EINVAL.
socketpairPOSIXpaire connectée écrite dans sv[2].

Aucune déviation. send/recv/sendmsg : lot 3.c ; setsockopt/getsockopt : lot 3.d.

M4 — transfert de données socket (ADR-069 Lot 3.c)

Logique mesurée air-libc-socketio::transfer sur le moteur air-socket ; shims air-libc-capi::socket.

SymboleÉtiquetteNotes
sendPOSIXsocket connecté ; MSG_NOSIGNAL ajouté par la couche 0.
recvPOSIX0 = pair fermé.
sendtoPOSIXdatagramme adressé (ou pair connecté si dest nul).
recvfromPOSIXécrit l adresse source dans src (si non nul).

Aucune déviation. sendmsg/recvmsg (msghdr + SCM_RIGHTS) et setsockopt/getsockopt : lots suivants.

M4 — résolution inverse numérique (netdb.h, ADR-069 Lot 4.a)

Logique mesurée air-libc-socketio::nameinfo. Numérique uniquement au jalon M4.

SymboleÉtiquetteNotes
getnameinfoPOSIX-PARTIELnumérique ; adresse + port en clair ; EAI_OVERFLOW si buffer court.
gai_strerrorPOSIXmessage statique pour un code EAI_*.

Déviation D-M4.2 : DNS inverse et résolution de service par nom non supportés. getaddrinfo/freeaddrinfo : lot 4.b.

M4 — résolution nom → adresses (netdb.h, ADR-069 Lot 4.b)

Logique mesurée air-libc-socketio::addrinfo. Liste possédée (une alloc Box par nœud).

SymboleÉtiquetteNotes
getaddrinfoPOSIX-PARTIELservice numérique ; nom numérique ou résolu ; filtre ai_family ; AI_PASSIVE.
freeaddrinfoPOSIXlibère la liste.

Déviation D-M4.3 : ai_canonname non rempli.

M4 — multiplexage evenements (poll.h, ADR-069 Lot 5.a)

Logique mesuree air-libc-socketio::poll sur air-poll (ppoll). Zero copie (struct pollfd reinterprete en PollFd couche 0, revents ecrits en place).

SymboleEtiquetteNotes
pollPOSIXtimeout < 0 = infini ; EINTR remonte ; poll(NULL,0,t) = veille bornee.

Aucune deviation. select/ppoll : lots suivants.

M4 — multiplexage historique (sys/select.h, ADR-069 Lot 5.b)

Logique mesuree air-libc-socketio::select, AU-DESSUS de poll (fd_set projetes en PollFd puis reconstruits depuis revents).

SymboleEtiquetteNotes
selectPOSIXvia poll ; nfds hors 0..=FD_SETSIZE => EINVAL ; timeout NULL = infini.

Macros FD_ZERO/FD_SET/FD_CLR/FD_ISSET fournies au header (trailer).

M4 — messages vectorises + passage de fd (sys/socket.h, ADR-069)

Logique mesuree air-libc-socketio::message sur le moteur air-socket. Parse/build des cmsg SCM_RIGHTS (buffer de controle C), sans fuite de fd (les non-livres sont fermes).

SymboleEtiquetteNotes
sendmsgPOSIXiovec + adresse + fds SCM_RIGHTS extraits du msg_control C.
recvmsgPOSIXecrit adresse source + fds recus (cmsg SCM_RIGHTS) ; CTRUNC si troncature.

Macros CMSG_* + FD_* fournies au header. Cle de la privsep OpenSSH.

Chantier B (std) — routines Unwind* (stubs)

Sonde build-std=std : std réfère 82 symboles libc absents de libair (surface pour lier std). Lot 0 = les 11 _Unwind_*, fournis en stubs (cible panic = abort ⇒ jamais invoqués ; présents pour le lien). Crate air-libc-capi::unwind (gaté not(test)).

SymboleÉtiquetteNotes
_Unwind_Backtrace .. _Unwind_SetIP (11)STUBpanic=abort ; avorte si atteint (bug). Pour le lien de std uniquement.