Récemment j’ai expliqué comment j’en étais arrivé à développer Sanca , un scanner de vulnérabilités écrit en Rust. Après une mise à jour des dépendances, Sanca s’est mis à planter. Pas juste un crash, un SIGSEGV (segmentation violation, aussi appelé segmentation fault), une erreur mémoire qui fait qu’il a été tué par le système d’exploitation. Rust est censé empêcher ce genre d’erreurs.

Le problème ne survenait que sur OpenBSD, pas sur Linux. OpenBSD a des règles de sécurité plus strictes en ce qui concerne la mémoire (par exemple W^X), donc c’est cohérent.
J’utilise Sanca quotidiennement sur OpenBSD, il fallait donc que je corrige ça.

À la recherche du responsable

Sanca plante avec une erreur SIGSEGV

L’image ci-dessus montre l’erreur qui se produisait lorsque j’exécutais Sanca, on ne peut pas dire que ce soit détaillé.
Heureusement, OpenBSD sauvegarde l’état de la mémoire au moment du crash dans un fichier .core, permettant d’analyser la mémoire, les registres, tout ce qu’il faut pour identifier quelle partie du code fait planter le programme.

J’ai ouvert sanca_software.core dans gdb (GNU Debugger), qui m’a rapidement mis sur une piste.

Le fichier .core de Sanca ouvert dans GDB

À peine ouvert, GDB m’a indiqué qu’il y avait un problème avec aws-lc-sys, le wrapper Rust autour de la librairie aws-lc, écrite en C. En faisant quelques recherches j’ai découvert que aws-lc était une librairie cryptographique basée sur BoringSSL, un fork de OpenSSL. Le problème semblait donc lié à SSL / TLS, mais je voulais être sûr de l’origine du problème.

Sanca ne dépend pas directement de cette librairie, alors j’ai utilisé la commande cargo tree -i aws-lc-sys pour découvrir quelle dépendance de Sanca utilisait aws-lc-sys. Verdict, il s’agissait de reqwest.

Sanca utilise une librairie appelée “reqwest” pour envoyer des requêtes HTTP aux serveurs Web, reqwest utilise “rustls” pour gérer le protocole TLS, et rustls utilise “aws-lc-rs”, qui est un wrapper autour de “aws-lc-sys”, pour gérer la partie cryptographie.

Recherche et mise en place de la solution

Après avoir trouvé la source du problème, il me fallait une solution.
En faisant des recherches sur rustls, j’ai remarqué que la librairie proposait plusieurs “providers” de cryptographie, aws-lc était le nouveau provider par défaut, l’ancien était ring. Mais il était toujours possible d’installer rustls avec ring au lieu d’aws-lc, grâce à une fonctionnalité de Rust / Cargo appelée feature flags. Il restait à découvrir si reqwest était suffisamment flexible pour me laisser choisir le provider de rustls.

Heureusement, oui. Encore grâce aux feature flags, j’ai pu installer reqwest en lui précisant de n’installer aucun provider pour rustls, et en parallèle j’ai installé et initialisé moi-même rustls avec ring comme provider. Cette fois ça fonctionnait, je pouvais compiler et utiliser Sanca sans problème.
J’aurais pu m’en contenter, mais ça m’embêtait de juste retirer aws-lc de Sanca alors que c’était le choix par défaut de rustls, il devait bien y avoir une raison.

J’ai décidé d’utiliser moi aussi les feature flags dans Sanca, pour permettre à l’utilisateur d’utiliser soit aws-lc soit ring, au choix. Aujourd’hui il y a deux façons de compiler Sanca :

cargo build --release

pour compiler avec aws-lc, ce qui convient probablement à la plupart des utilisateurs FreeBSD, Linux, macOS et Windows. Ou

cargo build --release --no-default-features --features ring-provider

pour compiler Sanca avec ring, ce qui convient mieux aux utilisateurs OpenBSD pour le moment.
J’ai commenté la différence dans le fichier README, pour que chaque utilisateur puisse choisir la méthode qui lui convient.

Ce que je retiens

Les développements modernes dépendent beaucoup de librairies externes, et ce quel que soit le langage. Si JavaScript avec npm est le champion des supply chain attacks, ces compromissions de librairies externes qui impactent les projets qui s’en servent, Rust avec Cargo n’est pas à l’abri de problèmes non plus.

Ici ce n’est pas une attaque, c’est un simple bug. Mais un bug dans une dépendance affecte les projets qui s’en servent. Il faut savoir déboguer et trouver des solutions adaptées, qui fonctionnent aujourd’hui sans handicaper le projet demain. La librairie aws-lc supporte des algorithmes “post-quantiques”, ce qui n’est pas encore le cas de ring, donc ne pas faire de choix bloquant était important.

En Rust on retrouve souvent des dépendances à des librairies écrites en C. Ça s’applique également à d’autres langages comme Python ou PHP, mais Rust fait une promesse forte, celle que les accès mémoires sont sûrs. Ce n’est pas son seul avantage, mais c’est un point différenciant très important.
Le problème c’est qu’aujourd’hui beaucoup de programmes importants sont écrits en C ou en C++, que ce soient des librairies cryptographiques comme LibreSSL, OpenSSL ou BoringSSL, ou des systèmes d’exploitation comme OpenBSD, Linux, macOS ou Windows. C est un super langage, il tourne sur toutes les architectures et compile très rapidement, mais ses accès mémoires ne sont pas sûrs. Encore aujourd’hui, énormément de vulnérabilités sont liées à la gestion de la mémoire.

Aussi longtemps que Rust dépendra de librairies écrites en C, ce qui n’est pas prêt de changer puisque C est universel, on sera confrontés à des problèmes de mémoire. Une librairie cryptographique 100% écrite Rust est en cours de développement, mais il se passera plusieurs années avant qu’elle soit aussi complète et auditée que peuvent l’être OpenSSL ou aws-lc.


Une dépendance qui plante, c’est une vulnérabilité de plus dans votre chaîne d’approvisionnement.
Si vous voulez auditer vos propres outils ou migrer vers une stack plus maîtrisée, parlons-en .