Publication de Rust 1.96. Évaluation de l'adéquation de Rust pour la création de firmware pour microcontrôleurs

La version 1.96 du langage de programmation Rust a été publiée, initialement développée par Mozilla, mais actuellement maintenue sous l'égide de l'organisation à but non lucratif Rust Foundation. Le langage se concentre sur la gestion sécurisée de la mémoire et fournit des moyens d'atteindre un haut niveau de parallélisme dans l'exécution des tâches, tout en se passant d'un ramasse-miettes et d'un runtime (le runtime se limite à une initialisation de base et à l'accompagnement de la bibliothèque standard).

Les méthodes de gestion de la mémoire dans Rust visent à éliminer les erreurs lors de la manipulation des pointeurs et à protéger contre les problèmes liés à une gestion de la mémoire à bas niveau, tels que l'accès à une zone mémoire après sa libération, la déréférence de pointeurs nuls, les débordements de tampon, etc. Pour la distribution de bibliothèques, la construction et la gestion des dépendances, un gestionnaire de paquets nommé Cargo est développé. Un dépôt crates.io est maintenu pour l'hébergement des bibliothèques.

La sécurité de la mémoire dans Rust est assurée au moment de la compilation grâce à la vérification des références, au suivi de la propriété des objets, à la prise en compte de la durée de vie des objets (portée) et à l'évaluation de l'accès mémoire pendant l'exécution du code. Rust offre également des outils pour se protéger contre les débordements d'entiers, exige l'initialisation obligatoire des valeurs des variables avant leur utilisation, gère mieux les erreurs dans la bibliothèque standard, applique le concept d'immuabilité (immutable) pour les références et les variables par défaut, et propose une forte typage statique pour minimiser les erreurs logiques.

Les principales nouveautés :

  • Un module range a été ajouté avec la mise en œuvre de nouveaux types, développés pour remplacer les types obsolètes Range, RangeInclusive, RangeToInclusive et RangeFrom, permettant de stocker des plages dans des structures Copy. Le type Range définit des plages, limitées par une valeur minimale et maximale (sans inclure la valeur maximale), le type RangeFrom définit des nombres à partir d'une valeur donnée, tandis que le type RangeInclusive inclut les valeurs des bornes limitantes. De nouveaux types RangeFull et RangeTo apparaîtront dans de futures versions, l'ancienne implémentation sera transférée dans core::range::legacy::*, et la syntaxe «N..M» sera mise à jour vers les nouveaux types.

    Les nouveaux types se distinguent par le fait qu'ils implémentent le trait IntoIterator au lieu du trait Iterator, c'est-à-dire qu'ils définissent comment convertir un type en itérateur au lieu d'utiliser un itérateur intégré. Cette approche permet d'utiliser l'opération de copie (le trait Copy, qui indique que les valeurs d'un type peuvent être dupliquées par simple copie), qui était précédemment indisponible en raison d'incompatibilités avec les types disposant d'itérateurs intégrés.
    Par exemple, les nouveaux types permettent de conserver les bornes d'une tranche dans une structure qui est entièrement copiée sans avoir à sauvegarder séparément les valeurs de début et de fin :

    use core::range::Range;

    #[derive(Clone, Copy)]
    pub struct Span(Range);

    impl Span {
    pub fn of(self, s: &str) -> &str {
    &s[self.0]
    }
    }

  • Les macros «assert_matches!» et «debug_assert_matches!» ont été ajoutées, vérifiant la conformité des valeurs avec le modèle spécifié et terminant l'exécution en cas de divergence. Par rapport aux expressions «assert!(matches!(..))» et «debug_assert!(matches!(..))», les nouvelles macros se distinguent par l'affichage d'informations de débogage sur les valeurs ayant causé l'échec. Pour éviter les interférences avec d'autres macros fournies avec des noms similaires, les nouvelles macros nécessitent une importation explicite de la bibliothèque «core::assert_matches».

    utiliser core::assert_matches;

    fn get_random_number() -> u32 {
    4
    }

    fn main() {
    assert_matches!(get_random_number(), 1..=6);
    }

  • Lors de la compilation pour la plateforme cible WebAssembly, l'option «—allow-undefined» a été supprimée du linkeur, autorisant la liaison en cas de symboles indéfinis, qui étaient transformés en importation depuis le module «env». Lors de la compilation pour WebAssembly, tous les symboles liés au linkage doivent désormais être définis par défaut. Pour retourner à l'ancien comportement, on peut utiliser la variable d'environnement «RUSTFLAGS=-Clink-arg=—allow-undefined» ou l'expression ‘#[link(wasm_import_module = «env»)]’ dans le code.
  • Une nouvelle série d'API a été stabilisée dans la catégorie des API stables, notamment des méthodes et des implémentations de traits stabilisées :
    • assert_matches!
    • debug_assert_matches!
    • From pour AssertUnwindSafe
    • From pour LazyCell
    • From pour LazyLock
    • core::range::RangeToInclusive
    • core::range::RangeToInclusiveIter
    • core::range::RangeFrom
    • core::range::RangeFromIter
    • core::range::Range
    • core::range::RangeIter
  • Une vulnérabilité dans le gestionnaire de paquets Cargo, CVE-2026-5223, a été corrigée, qui pouvait être exploitée pour écraser le code source d'un autre crate-packet dans le cache local de paquets provenant du même référentiel à travers des manipulations de liens symboliques à l'intérieur du crate des paquets. La vulnérabilité se manifeste uniquement lors de l'utilisation de référentiels de paquets tiers et n'affecte pas les utilisateurs du référentiel crates.io, car le téléchargement de paquets avec des liens symboliques est interdit sur crates.io.

On peut également noter la publication (PDF) des résultats d'une analyse de l'adéquation du langage Rust au développement de firmwares pour microcontrôleurs et systèmes embarqués à ressources limitées.
L'étude a été réalisée par STMicroelectronics avec la participation de plusieurs universités européennes. Deux équipes de développeurs isolées ont été chargées de réaliser le même firmware pour les microcontrôleurs STM32U585AI avec un cœur Arm Cortex-M33. La première équipe a développé le firmware en C, tandis que la seconde a utilisé Rust.

Les tests effectués n'ont pas révélé d'avantages significatifs à l'utilisation du langage C par rapport à Rust lors du développement de firmwares pour microcontrôleurs, en ce qui concerne la consommation de mémoire et la performance. De plus, l'utilisation du runtime système écrit en Rust provenant du projet ouvert Ariel OS a permis d'obtenir une consommation de mémoire dans le projet Rust inférieure à celle de l'implémentation en C, utilisant une pile traditionnelle pour le développement de firmwares basés sur la bibliothèque newlib.

La taille du firmware résultant était de 84100 octets dans le projet Rust et de 76744 octets dans le projet C (soit 10% de moins), mais la consommation de mémoire dans le firmware Rust s'est révélée significativement inférieure — 24640 octets contre 42608 octets. En ce qui concerne la performance, lors des tests des prototypes initiaux, développés en 6 semaines, l'implémentation en Rust a dépassé par deux fois celle en C, mais les deux implémentations ont significativement ralenti par rapport à la performance maximale théorique. Après 4 semaines consacrées à l'optimisation, les deux implémentations ont atteint un résultat à peu près identique, proche du maximum théorique.



Source : opennet.ru
Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster