{"id":15629580,"url":"https://github.com/pyaillet/rust-lkm","last_synced_at":"2026-05-17T00:04:05.382Z","repository":{"id":46088767,"uuid":"427951289","full_name":"pyaillet/rust-lkm","owner":"pyaillet","description":"Exemple de driver Linux écrit en 🦀 Rust","archived":false,"fork":false,"pushed_at":"2021-11-15T12:46:28.000Z","size":26,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-31T11:47:55.271Z","etag":null,"topics":["linux-kernel","rust","rust-lang"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pyaillet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-11-14T14:29:59.000Z","updated_at":"2025-02-07T13:25:48.000Z","dependencies_parsed_at":"2022-09-03T06:31:06.073Z","dependency_job_id":null,"html_url":"https://github.com/pyaillet/rust-lkm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pyaillet/rust-lkm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyaillet%2Frust-lkm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyaillet%2Frust-lkm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyaillet%2Frust-lkm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyaillet%2Frust-lkm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyaillet","download_url":"https://codeload.github.com/pyaillet/rust-lkm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyaillet%2Frust-lkm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279013970,"owners_count":26085429,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["linux-kernel","rust","rust-lang"],"created_at":"2024-10-03T10:27:40.509Z","updated_at":"2025-10-13T06:35:21.710Z","avatar_url":"https://github.com/pyaillet.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Écrire un module pour Linux en Rust\n\nLe 14 avril 2021 [une série de patch](https://lkml.org/lkml/2021/4/14/1023)\na été soumise sur la mailing list du\nkernel Linux pour initier la discussion et proposer une première RFC en vue\nd'ajouter le langage Rust comme second langage intégré au projet.\n\nLe 4 juillet, [une nouvelle série de patch](https://lkml.org/lkml/2021/7/4/171)\na été envoyée, cette fois-ci pour activer le support et le rendre accessible\naux développeurs du kernel.\n\nDans cet article, je vais vous présenter l'intérêt de cette démarche et un\nexemple \"simple\" de module Rust pour le noyau Linux.\n\n## L'intérêt de Rust\n\nL'objectif du langage Rust est de fournir un langage bas niveau, mais\nfournissant davantages de garanties qu'un langage comme le C.\n\nDes invariants du langage offrent des garanties sur la manipulation de la mémoire.\nExemples :\n- Une référence vers une valeur ne peut pas exister au delà de l'existence de la valeur\n- Une seule référence mutable vers une valeur peut exister à un instant donné\n\nCes invariants permettent d'éviter des comportements indéfinis liés à\nl'utilisation de pointeurs non-initialisés notamment le déréférencement de null\npointers ou les Use after-free.\n\n\nLe système de type de Rust nous permet également d'obtenir des garanties sur\nles données manipulées vérifiées par le compilateur alors que dans le cadre d'une\nimplémentation en C, c'est au développeur de prendre les précautions nécessaires.\n\nSi vous voulez en savoir plus à ce sujet, je vous invite à regarder\n[cette présentation](https://www.youtube.com/watch?v=46Ky__Gid7M) qui détaille\nce qu'on entend par comportement indéfini et comment Rust permet de les limiter.\n\n## Compilation du kernel avec support de Rust  \n\nPour commencer, nous allons devoir compiler notre propre kernel Linux avec le\nsupport de Rust activé.\nDe mon côté, j'ai décidé de faire ça sur une machine virtuelle Ubuntu 21.04.\n\nLa plupart des étapes de ce tutoriel sont basés sur la documentation officiel\ndu projet [Rust for Linux](https://github.com/Rust-for-Linux/linux/blob/rust/Documentation/rust/quick-start.rst).\n\nCommencer par l'installation des outils nécessaires à la compilation :\n\n```sh\n# Distro packages\nsudo apt update\nsudo apt install -y flex bison clang lld build-essential llvm git libelf-dev libclang-11-dev libssl-dev tmux\n```\n\nEnsuite installer la toolchain Rust qui sera utilisée pour la compilation du\nkernel et les dépendances nécessaires.\n\n`rust-src` : le code source de la standard librairie Rust est nécessaire car on\nva recompiler `core` et `alloc`\n`bindgen` : sera utilisé pour générer les bindings avec le C du kernel lors du build.\n\n```sh\n# Rust dependencies\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\nsource $HOME/.cargo/env\nrustup component add rust-src\ncargo install --locked --version 0.56.0 bindgen\n```\n\nCloner les sources du kernel intégrant les patchs nécessaires pour Rust :\n\n```sh\n# Clone kernel src\ngit clone --depth=1 https://github.com/Rust-for-Linux/linux.git\n```\n\nIl faut ensuite configurer le kernel pour activer le support de Rust et intégrer\nnos exemples.\n\nRecopier la configuration actuelle du kernel afin de minimiser\nles changements à effectuer.\n\n```sh\ncp /boot/config-$(uname -r) linux/.config\ncd linux\nmake oldconfig\n```\n\nConfigurer les options spécifiques :\n```sh\nmake menuconfig\n```\n\nVous pouvez vous baser sur cette liste pour savoir quoi activer/désactiver:\n\nIl est nécessaire de désactiver le versioning des modules :\n```\nEnable loadable module support =\u003e [ ] Module versioning support\n```\n\nActivaction du support de Rust :\n```\nGeneral Setup =\u003e [*] Rust support\n```\n\nEt activer la compilation d'un exemple de driver :\n```\nKernel Hacking =\u003e Sample kernel code =\u003e [*] Rust samples =\u003e \u003cM\u003e Character device\n```\n\nDe mon côté, j'ai également dû désactiver certaines options pour faire passer\nla compilation :\n```\nKernel Hacking =\u003e Compile-time checks and compiler options =\u003e [ ] Compile the kernel with debug info\nCryptographic API =\u003e Certificates for signature checking =\u003e () Additional X.509 keys for default system keyring \nCryptographic API =\u003e Certificates for signature checking =\u003e () X.509 certificates to be preloaded into the system blacklist keyring\n```\n\nLancer la compilation et aller se chercher 2~3 cafés...\nIl faudra adapter le `-j5` en fonction du nombre de core disponibles sur la\nmachine utilisée pour la compilation (en général, on choisit `nombre de core + 1`,\nce qui permet de lancer 5 tâches de compilation en parallèle, et d'occuper tous\nles core, même en prenant en compte le fait que certaines tâches attendant sur\ndes IO).\n```sh\nmake LLVM=1 -j5\n```\n\nUne fois la compilation terminée, installer les modules dans l'arborescence\ndu système et installer le kernel.\n\n```sh\nsudo make modules_install\nsudo make install\n```\n\n## Test \n\nPour tester vous pouvez alors redémarrer la machine et vérifier la version du\nkernel utilisé suite à ce redémarrage :\n\n```shell-session\n$ sudo reboot\n\n# Puis\n\n$ uname -a\nLinux lima-default 5.15.0+ #4 SMP PREEMPT Sun Nov 14 13:41:03 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux\n```\n\nIl est alors possible de charger le module écrit en Rust.\n```shell-session\n$ sudo insmod /lib/modules/$(uname -r)/kernel/samples/rust/rust_chrdev.ko\n$ lsmod | grep rust\nrust_chrdev            16384  0\n$ sudo rmmod rust_chrdev\n$ sudo dmesg | grep rust_chrdev\n[27357.104859] rust_chrdev: Rust character device sample (init)\n[27425.248428] rust_chrdev: Rust character device sample (exit)\n```\n\nNous avons donc pu charger notre module exemple écrit en Rust !\n\n## Exemple de module écrit en Rust avec explications\n\nJe vous propose maintenant de passer en revue les étapes nécessaires à\nl'écriture d'un module en Rust. Petite mise en garde néanmoins, ce n'est pas ma\nspécialité et il est possible que ma compréhension de certains aspects ne soit\nque partielle.\n\nLe premier module que nous avions chargé était directement intégré dans\nl'arborescence du noyau, mais ce n'est pas indispensable.\nVous pouvez retrouver cet exemple sur [ce repo](https://github.com/Rust-for-Linux/linux/tree/rust/samples/rust).\n\nNous allons commencer par le `Makefile` qui sera utilisé pour compiler notre\nmodule. \n```Makefile\n# Déclarer le module à compiler en indiquant le fichier objet résultant\nobj-m += rust_chrdev.o\n\n# Déclarer notre cible par défaut en précisant :\n# - LLVM=1 : Utilisation de LLVM\n# - -C /lib/modules/$(shell uname -r)/build : Utilisation du système de build du kernel\n# - M=$(PWD) : Le chemin du module\n# - modules : Compilation notre module\nall:\n\tmake LLVM=1 -j5 -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules\n\n# Ajout une cible pour indiquer comment faire le ménage\nclean:\n\tmake -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean\n```\n\nPassons maintenant au code de notre module, vous pouvez retrouver l'exemple\ncomplet [sur github](https://github.com/pyaillet/rust-lkm).\nNous allons commencer par un exemple simple, notre module créera un\n`character device` qui transmettra la chaîne `🦀 Hello from rust\\n` lorsqu'on\nlira dedans, une fois le fichier ouvert.\nNous stockerons également un état partagé qui nous permettra de comptabiliser\ncombien de fois le fichier a été ouvert.\n\nCommençons par déclarer les structures qui stockeront l'état de lecture du\nfichier device et l'état partagé de notre module :\n\n```rs\n/// État partagé dans notre module\nstruct Shared {\n    open_count: AtomicU64,\n}\n\n/// Compteur d'octets lus sur notre fichier\nstruct RustFile {\n    read_count: AtomicUsize,\n}\n```\n\nEnsuite, nous déclarons la structure qui contient l'enregistrement de notre\nmodule :\n\n```rs\n/// Structure correspondant à notre module, qui porte l'état partagé de\n/// l'enregistrement du device\nstruct Rustdev {\n    _dev: Pin\u003cBox\u003cRegistration\u003cRef\u003cShared\u003e\u003e\u003e\u003e,\n}\n```\n\nCette structure ne possède qu'un seul membre : l'enregistrement `Registration`\ndu device et qui porte l'état partagé.\n\nPour initialiser, le module et enregistrer notre device, il faut implémenter\nle trait `KernelModule` pour notre device :\n\n```rs\n/// \nimpl KernelModule for Rustdev {\n    /// Cette méthode est appelé au chargement de notre module et permet\n    /// d'effectuer les étapes d'initialisation\n    fn init(name: \u0026'static CStr, _module: \u0026'static ThisModule) -\u003e Result\u003cSelf\u003e {\n        // Cette macro permet d'afficher un message d'information dans `dmesg`\n        pr_info!(\"Rust device sample (init)\\n\");\n\n        // Initialisation de l'état partagé qui comptera le nombre d'accès à\n        // notre device\n        let shared = Ref::try_new(Shared {\n            open_count: AtomicU64::new(0),\n        })?;\n\n        // Création de la structure correspondant à notre module, et création de\n        // l'enregistrement qui portera notre état partagé.\n        Ok(Rustdev {\n            _dev: Registration::new_pinned::\u003cRustFile\u003e(name, None, shared)?,\n        })\n    }\n}\n```\n\nNous implémentons également le trait `Drop` qui sera utilisé lors de la suppression\ndu module.\n```rs\nimpl Drop for Rustdev {\n    fn drop(\u0026mut self) {\n        pr_info!(\"Rust device sample (exit)\\n\");\n    }\n}\n```\n\nIl faut également utiliser une macro pour finaliser les déclarations\nnécessaires à la prise en compte de notre module :\n```rs\nmodule! {\n    type: Rustdev,\n    name: b\"rust_mydev\",\n    author: b\"Pierre-Yves Aillet\",\n    description: b\"Rust character device sample\",\n    license: b\"GPL v2\",\n}\n```\n\nIl nous manque encore 2 traits à implémenter :\n- `FileOpener` pour traiter l'ouverture du fichier de notre `character device`\n- `FileOperations` pour implémenter le comportement lors de la lecture de ce fichier\n\n```rs\n/// Ce trait permet d'indiquer ce qui est réalisé lors de l'ouverture du\n/// device.\n/// Il est utilisé pour initialiser la structure qui correspond à l'état du\n/// fichier ouvert, et peut également être utilisé pour y associer l'état\n/// partagé (ce qui n'est pas fait dans cet exemple).\nimpl FileOpener\u003cRef\u003cShared\u003e\u003e for RustFile {\n    fn open(shared: \u0026Ref\u003cShared\u003e) -\u003e Result\u003cBox\u003cSelf\u003e\u003e {\n        // Mise à jour le compteur d'ouverture du fichier\n        shared.open_count.fetch_add(1, Ordering::SeqCst);\n\n        // Affichage dans le `dmesg` le nombre de fois que le device a été\n        // ouvert\n        pr_info!(\n            \"Opened the file {} times\\n\",\n            shared.open_count.load(Ordering::SeqCst)\n        );\n        // Initialisation et transfert de la structure correspondant à l'ouverture\n        // courante de notre fichier.\n        Ok(Box::try_new(Self {\n            read_count: AtomicUsize::new(0),\n        })?)\n    }\n}\n```\n\n```rs\n/// Constante correspondant à la chaîne que nous souhaitons renvoyer\nconst HELLO: \u0026'static str = \"🦀 Hello from rust\\n\\0\";\n\n/// Ce trait comporte l'ensemble des opérations possibles pour un fichier.\n/// Voir la documentation [ici](https://rust-for-linux.github.io/docs/kernel/file_operations/trait.FileOperations.html)\nimpl FileOperations for RustFile {\n    /// L'utilisation de cette macro permet de spécifier les opérations réellement\n    /// implémentée pour notre device\n    kernel::declare_file_operations!(read);\n\n    /// Cette méthode est appelé lorsqu'une opération de lecture est réalisée\n    /// sur le fichier device\n    fn read(\n        this: \u0026Self,\n        _file: \u0026File,\n        data: \u0026mut impl IoBufferWriter,\n        _offset: u64,\n    ) -\u003e Result\u003cusize\u003e {\n        let hello_bytes = HELLO.as_bytes();\n        // Si le fichier n'a pas déjà été lu\n        if hello_bytes.len() \u003e this.read_count.load(Ordering::SeqCst) {\n            // Et si le buffer fournit est assez grand pour y écrire le message\n            if data.len() \u003e= hello_bytes.len() {\n                // Écriture notre message dans ce buffer\n                data.write_slice(\u0026hello_bytes)?;\n                // Mise à jour le compteur d'octets lu pour cette ouverture\n                // de fichier\n                this.read_count.store(hello_bytes.len(), Ordering::SeqCst);\n                // Renvoie du nombre d'octets lus et réellement écrits\n                // dans le buffer\n                return Ok(hello_bytes.len());\n            }\n        }\n        // Dans les autres cas, aucun octet n'a été lu\n        Ok(0)\n    }\n}\n```\n\nVous pouvez retrouver l'exemple complet [ici](https://github.com/pyaillet/rust-lkm).\n\nVoici un exemple de session avec utilisation de ce module :\n\n```shell-session\n$ make\nmake LLVM=1 -j5 -C /lib/modules/5.15.0+/build M=/home/pyaillet.linux/rust-lkm modules\nmake[1]: Entering directory '/home/pyaillet.linux/linux'\n  RUSTC [M] /home/pyaillet.linux/rust-lkm/rust_chrdev.o\n  MODPOST /home/pyaillet.linux/rust-lkm/Module.symvers\n  CC [M]  /home/pyaillet.linux/rust-lkm/rust_chrdev.mod.o\n  LD [M]  /home/pyaillet.linux/rust-lkm/rust_chrdev.ko\nmake[1]: Leaving directory '/home/pyaillet.linux/linux'\n$ sudo insmod rust_chrdev.ko\n$ sudo dmesg | grep rust_mydev\n[   55.920542] rust_mydev: Rust device sample (init)\n$ sudo cat /dev/rust_mydev\n🦀 Hello from rust\n$ sudo cat /dev/rust_mydev\n🦀 Hello from rust\n$ sudo dmesg | grep rust_mydev\n[   55.920542] rust_mydev: Rust device sample (init)\n[   75.415790] rust_mydev: Opened the file 1 times\n[   76.808057] rust_mydev: Opened the file 2 times\n$ sudo cat /dev/rust_mydev\n🦀 Hello from rust\n$ sudo dmesg | grep rust_mydev\n[   55.920542] rust_mydev: Rust device sample (init)\n[   75.415790] rust_mydev: Opened the file 1 times\n[   76.808057] rust_mydev: Opened the file 2 times\n[   82.857408] rust_mydev: Opened the file 3 times\n$ sudo rmmod rust_chrdev\n$ sudo dmesg | grep rust_mydev\n[   55.920542] rust_mydev: Rust device sample (init)\n[   75.415790] rust_mydev: Opened the file 1 times\n[   76.808057] rust_mydev: Opened the file 2 times\n[   82.857408] rust_mydev: Opened the file 3 times\n[   95.155032] rust_mydev: Rust device sample (exit)\n$\n```\n\n## Conclusion\n\nIl reste encore du chemin à parcourir pour voir de nombreux drivers Linux\nimplémenter en Rust.\nComme décrit [ici](https://github.com/Rust-for-Linux/linux/blob/rust/Documentation/rust/coding.rst#abstractions-vs-bindings),\nune grosse partie du travail restant consiste à disposer des abstractions\npermettant d'interagir avec les APIs internes du kernel tout en conservant les\ngaranties fournies par Rust.\nSi le sujet vous intéresse je vous invite à regarder les présentations données\nen référence : \"[Rust for Linux](https://www.youtube.com/watch?v=46Ky__Gid7M)\" et \"[Rust in the Linux ecosystem](https://www.youtube.com/watch?v=jTWdk0jYy54)\"\n\n## Références\n\n- [Rust in the Linux kernel](https://security.googleblog.com/2021/04/rust-in-linux-kernel.html)\n- [Rust modules samples](https://github.com/Rust-for-Linux/linux/tree/rust/samples/rust)\n- [Rust for Linux](https://www.youtube.com/watch?v=46Ky__Gid7M)\n- [Rust in the Linux ecosystem](https://www.youtube.com/watch?v=jTWdk0jYy54)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyaillet%2Frust-lkm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyaillet%2Frust-lkm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyaillet%2Frust-lkm/lists"}