Dans le cadre des efforts visant à renforcer la sécurité des composants logiciels critiques de la plateforme Android, la société Google a réécrit en langage Rust le firmware pvmfm, utilisé pour gérer le fonctionnement des machines virtuelles lancées par l'hyperviseur pVM dans le cadre de l'Android Virtualization Framework. Auparavant, le firmware était écrit en langage C et implémenté au-dessus du chargeur d'amorçage U-Boot, dont le code avait déjà révélé des vulnérabilités dues à des problèmes de gestion de la mémoire.
Le firmware réécrit en Rust est inclus dans Android 14, et les bibliothèques polyvalentes créées au cours du développement du firmware sont structuré sous forme de paquets crate et ont été transmises à la communauté Rust. Par exemple, le paquet smccc a été publié pour le support des interfaces ARM PSCI (Power State Coordination Interface) et des appels SMCCC (SMC Calling Convention), ainsi que le paquet aarch64-paging pour manipuler les tables de pages mémoire. Des efforts ont également été déployés pour corriger les erreurs et étendre les fonctionnalités du paquet existant virtio-drivers avec la mise en œuvre de pilotes VirtIO. En plus de la plateforme Android, ces paquets sont utilisés dans le projet Oak, qui développe des composants pour la transmission, le stockage et le traitement de données dans des environnements sécurisés (TEE, Trusted Execution Environment).
L'hyperviseur pVM prend le contrôle à un stade précoce du démarrage et assure une isolation complète de la mémoire machines virtuelles et de l'environnement hôte, empêchant l'accès aux machines virtuelles protégées où des données sensibles sont traitées à partir du système hôte. Le firmware pvmfm (Protected Virtual Machine Firmware) prend le contrôle immédiatement après le démarrage de la machine virtuelle, exécute une vérification de l'environnement formé et décide d'interrompre le démarrage en cas de détection de problèmes d'intégrité ou génère un certificat de démarrage pour le système invité en cas de confirmation de la chaîne de confiance.
La réécriture en Rust permet de respecter plus simplement et plus sûrement la "règle des deux" appliquée par Google pour maintenir la sécurité des composants système d'Android. Selon cette règle, tout code ajouté ne doit répondre qu'à deux des trois conditions suivantes : travailler avec des données non vérifiées, utiliser un langage de programmation non sécurisé (C/C++) et être exécuté avec des privilèges accrus. De cette règle découle que le code destiné à traiter des données externes doit soit être restreint à des privilèges minimaux (isolé), soit être écrit dans un langage de programmation sécurisé. Selon Google, environ 70 % des vulnérabilités dangereuses identifiées dans Android sont causées par des erreurs de gestion de la mémoire.
Rust est axé sur la sécurité de la gestion de la mémoire et permet de réduire le risque d'apparition de vulnérabilités causées par des problèmes tels que l'accès à la mémoire après sa libération et le dépassement de tampon. La gestion sécurisée de la mémoire est assurée en Rust lors de la compilation par la vérification des références, le suivi de la propriété des objets et la prise en compte du temps de vie des objets (portée), ainsi que par l'évaluation de la validité de l'accès à la mémoire lors de l'exécution du code. Rust fournit également des moyens de protection contre les débordements d'entiers, exige une initialisation obligatoire des valeurs des variables avant 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 offre une forte typage statique pour minimiser les erreurs logiques.
Parmi les difficultés rencontrées lors du développement de composants bas niveau en Rust, tels que les pilotes, figure la nécessité de travailler avec des pointeurs bruts en mode unsafe, car Rust a été conçu en tenant compte de l'utilisation de la mémoire allouée dans le programme, et dans le code fonctionnant sans couches au-dessus du matériel, il faut accéder à la mémoire partagée et au MMIO. Actuellement, les capacités de Rust en matière de pointeurs bruts laissent à désirer, mais la situation devrait s'améliorer après la stabilisation du support des macros offset_of, slice_ptr_get et slice_ptr_len.
Parmi les inconvénients, on note également la nécessité d'une syntaxe améliorée pour accéder aux champs de structures et aux indices de tableaux via des pointeurs nus sans créer de références, ainsi que des limitations dans la création d'encapsulations sécurisées pour les opérations unsafe, qui peuvent entraîner un comportement indéterminé et ne peuvent pas être vérifiées par le compilateur. Par exemple, de telles encapsulations ne peuvent être créées pour les opérations sur les tables de pages mémoire, car le mappage des pages dans une partie du programme peut affecter d'autres parties.
Concernant la taille du code résultant, l'ancienne version du firmware pVM occupait 220 kB, tandis que la nouvelle version en occupe 460 kB, mais des fonctionnalités ont été ajoutées dans la version réécrite, permettant d'éliminer certains autres composants utilisés lors du démarrage. Au final, la taille totale de tous les composants de démarrage anciens et nouveaux s'est avérée comparable. Il est noté que lorsque la taille est plus importante que la performance, des résultats comparables à ceux du langage C peuvent être atteints en incluant des modes d'optimisation supplémentaires dans le compilateur pour réduire la taille, éliminer les dépendances inutiles et ne pas utiliser de moyens de formatage de chaînes.
Il est également mentionné que le travail se poursuit sur la mise en œuvre de la possibilité de lancer des applications dignes de confiance écrites en Rust (Trusted Application) dans le système d'exploitation Trusty, qui fournit un environnement d'exécution de confiance (Trusted Execution Environment) pour Android, s'exécutant parallèlement à Android sur le même processeur dans un environnement isolé distinct. Trusty est utilisé dans les appareils Pixel et utilise déjà Rust dans les bibliothèques et les composants système (le noyau reste en C).
Source : opennet.ru
