ProgrammationDéveloppeur système

Expliquez comment fonctionne l'FFI (Foreign Function Interface) dans Rust. Quelles exigences et pièges existent pour un appel sécurisé de fonctions C?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

L'FFI dans Rust permet d'appeler des fonctions déclarées dans des bibliothèques externes (par exemple, C/C++), et d'exporter des fonctions Rust à l'extérieur. Pour cela, le mot-clé extern est utilisé. Exigences :

  • Tous les appels FFI doivent être enveloppés dans une déclaration avec unsafe ;
  • L'ABI (Application Binary Interface) doit correspondre (généralement "C") ;
  • Les types de données doivent être compatibles — il est important d'utiliser des primitifs de taille fixe (i32, u64, etc.) ;
  • Gestion de la mémoire — attention particulière à la possession, aux fuites et à la double libération.

Exemple d'enveloppement :

extern "C" { fn abs(input: i32) -> i32; } fn main() { unsafe { println!("{}", abs(-5)); } }

Pour exporter une fonction depuis Rust pour une utilisation en C, cela peut se faire comme suit :

#[no_mangle] pub extern "C" fn sum(a: i32, b: i32) -> i32 { a + b }

Question piège

Question : Rust garantit-il que si l'on enveloppe un appel de fonction C dans unsafe, tout fonctionnera correctement en termes de sécurité des threads et de gestion d'UB ?

Réponse : Non ! unsafe est une promesse au compilateur que vous vous portez garant de la conformité à toutes les exigences de sécurité (aliasing, sécurité des threads, accès à la mémoire). Rust ne fait pas de vérifications de code à l'intérieur de C. Par exemple, une condition de course ou un UB dans le code de la bibliothèque peut "casser" le runtime du programme Rust. Même si le code Rust se compile, l'exécution réelle peut entraîner un crash.


Histoire

Dans un fournisseur de données financières, l'équipe a intégré une bibliothèque C via l'FFI, sans vérifier les tailles des types. Sur x86_64, il était attendu que long et i64 coïncident, mais il s'est avéré que les tailles ne correspondaient pas sur d'autres plateformes — une mauvaise lecture de la mémoire a entraîné des bugs dans les calculs.

Histoire

Un développeur a créé un enrobage pour l'API C, en oubliant les pointeurs passés aux buffers. Comme Rust libérait automatiquement la mémoire, la fonction C travaillait parfois avec de la mémoire déjà libérée, entraînant des pannes et des crashes.

Histoire

Dans un produit client-serveur, le module Rust faisait appel à une bibliothèque C, sans tenir compte de la sécurité multi-thread. La bibliothèque n'était pas thread-safe, et les programmes Rust y accédaient de différents threads simultanément, ce qui a entraîné des crashes et des corruptions de données.