Is Rust Secure? 10 Security Risks Everyone Using Rust Should Learn
Rust prevents most memory-corruption vulnerabilities, but it cannot secure your logic, your dependencies or your configuration. Learn the ten security risks that remain the developer's job, with real 2026 cases and practical fixes.
Rust is a programming language designed to prevent the memory errors behind most serious vulnerabilities in C and C++ software. Google uses it in Android, and the Linux kernel now accepts Rust code too. Rust prevents most memory-safety vulnerabilities because its compiler checks how a program uses memory before the program is allowed to run. It cannot, however, make an application secure on its own. This guide covers the ten security risks that remain the responsibility of everyone writing Rust, based on real Rust vulnerabilities from the last year and the controls that stop them. Rust security research is still new, so expect more findings to follow.
TL;DR: Rust stops most memory-corruption bugs. It does not stop logic flaws, broken authorization, crashes on bad input, malicious crates or mistakes made by AI assistants. Below are the ten risks and how to handle each.
What this guide covers
- Why Rust is growing: adoption numbers and the reasons behind them.
- The Rust world in five minutes: the compiler, Cargo, crates and the tools named in this guide.
- How Rust makes software safer, and what it cannot secure.
- Ten security risks, each with a real case, the security impact and the lessons learned.
- A review checklist, practice questions and references.
The ten risks:
- Logic vulnerabilities
- Authentication vs authorization
- Path traversal and other web attacks
- Input validation and panics
- Integer overflow
- Memory exhaustion
- Memory-safety bugs in
unsafecode - Supply chain attacks through malicious crates
- AI-generated vulnerabilities
- Insecure code that compiles
Why Rust is growing
A GitHub search shows 479,144 new Rust repositories in the past year, up from 228,632 the year before. Rust's share of all new GitHub repositories rose from 0.42% to 0.52% over the same period.
Teams choose Rust for three reasons:
- Safety. Google found about 1,000 times fewer memory-safety vulnerabilities per line in Android's Rust code than in its C and C++.
- Speed. Rust compiles to machine code and has no garbage collector, a background process that pauses programs to free memory. In the Computer Language Benchmarks Game, a public project that times the same tasks written in different languages, the fastest Rust program finished the spectral-norm task in 0.72 seconds, Java in 1.47 seconds and Python 3 in about 90 seconds.
- AI coding agents work well with it. Rust's compiler reports the exact line and cause of each error, and an agent can fix those errors in a loop. GitHub used agents to rewrite its Copilot runtime from TypeScript into 832,378 lines of Rust in 2026.
The Rust world in five minutes
If you are new to Rust, get familiar with the main parts of its ecosystem first.
- Compiler (
rustc). A compiler turns source code into a program the computer can run. Rust's compiler also checks the code for many mistakes and refuses to build it if it finds one. Languages such as Python skip this step and run the source code directly through an interpreter. - Borrow checker. The part of the compiler that tracks which piece of code owns each piece of memory and who may change it. It rejects code that could use memory after it is freed or change it from two places at once.
- Crate. A Rust library or program, the equivalent of a package in Python or JavaScript.
- crates.io. The official public registry where crates are published and downloaded. It lists 352,682 crates and has served more than 490 billion downloads.
- Cargo. Rust's build tool and package manager. It downloads crates, compiles the project and runs tests.
Cargo.lock. A file that records the exact version of every crate a project uses, so every build uses the same code.- rustup. The installer that manages Rust versions. A new stable version is released every six weeks; the current one is Rust 1.99, released on 1 October 2026. There are also beta and nightly channels for testing upcoming features.
- Clippy. Rust's official lint tool. A linter reads code and warns about risky patterns the compiler allows.
- rust-analyzer. The plugin that gives code editors such as VS Code their Rust support.
- RustSec. The public database of security advisories for crates.
cargo auditchecks a project against it. - Tokio and Hyper. Two of the most widely used crates: Tokio runs many tasks at once (asynchronous code) and Hyper handles HTTP. Many Rust web frameworks, such as axum, are built on them.
The commands you will use most often:
rustup updateinstalls the latest stable Rust version.cargo buildcompiles the project;cargo build --releasebuilds the optimised version used in production.cargo updatemoves dependencies to their newest compatible versions and updatesCargo.lock.cargo clippyruns the linter.cargo install cargo-auditonce, thencargo auditto check dependencies for known vulnerabilities.cargo treelists every dependency and shows which crate pulled it in.
How Rust makes software safer, and what it cannot secure
What Rust prevents
Rust blocks a group of memory-corruption vulnerabilities that cause most serious security flaws in C and C++ code. Attackers use them to crash programs, read secret data or run their own code.
The main memory-corruption vulnerabilities Rust prevents are:
- Use-after-free. The program keeps using memory after giving it back, and an attacker can place their own data there.
- Double free. The program gives the same memory back twice, which corrupts the memory manager.
- Buffer overflow. The program writes past the end of a block of memory and overwrites whatever is next.
- Null pointer dereference. The program follows a pointer that points to nothing and crashes.
- Data race. Two threads, parts of a program that run at the same time, change the same memory at once and leave it in a broken state.
The borrow checker and Rust's type system reject code that could cause these problems. Removing them is a large security gain.
What Rust cannot secure
Rust cannot secure what it does not understand. It does not know which users may read which invoices, which folders a file path may point to, or that an encryption key must never be reused. Those rules belong to your application, and you must write and test them.
There are two further limits. Code inside an unsafe block is code where the programmer tells the compiler "trust me" and skips some of its memory checks, usually for speed or to call C libraries. And when you build a project, Cargo runs code that comes with your dependencies. Rust's protection does not extend to either.
1. Logic vulnerabilities
In 2026, Canonical, the company behind Ubuntu, hired the security firm Zellic to audit uutils, the Rust rewrite of basic Linux commands such as ls, cp and chmod that Ubuntu now ships by default. The audit found 113 issues, and 44 of them received CVE IDs, the public identifiers for known vulnerabilities. An analysis of those 44 by Rust consultant Matthias Endler found no memory-corruption flaws, but found security flaws in how the tools handled files, input and permissions.
Examples of what the audit found:
- Race between check and use (TOCTOU).
installdeleted a file and then created a new one at the same path. In the gap between the two steps, an attacker could put a symbolic link (a shortcut to another file) at that path and makeinstallwrite somewhere else. - Permissions set too late. A new directory was created with default permissions and locked down a moment later. Another user could open it in that moment.
- Comparing text instead of files.
chmod --preserve-rootis meant to refuse to change the whole system. It compared the path as text, so/../, which also means the root folder, got past the check. - Steps in the wrong order.
chrootlooked up the user after switching to a new root folder, so it could load an attacker's library files and run them as root.
Security impact: these flaws let a local attacker overwrite files, read other users' files or gain root access.
Lessons learned: when you rewrite software in Rust, list the security rules the old version followed and test each one in the new version. Pay particular attention to file handling, permissions and the order of security checks, because the compiler checks none of them.
2. Authentication vs authorization
Authentication checks who a user is. Authorization checks whether that user may do a particular action, such as "can this user change this file?" RustFS, an object storage server written in Rust, mixed the two up. In April 2026, CVE-2026-40937 (CVSS 8.3) reported that four of its admin endpoints checked only that the caller was signed in. Every other admin endpoint also checked admin rights. Any signed-in user could change an admin setting and redirect other users' storage events to their own server.
Here is the same mistake in a typical Rust web handler, followed by the fix:
// Vulnerable: any signed-in user can change an admin setting.
async fn update_target(user: User, State(db): State<Db>, Json(target): Json<Target>) -> Result<(), ApiError> {
db.save_target(&target).await?;
Ok(())
}
// Fixed: check the permission before changing anything.
async fn update_target(user: User, State(db): State<Db>, Json(target): Json<Target>) -> Result<(), ApiError> {
if !user.is_admin() {
return Err(ApiError::Forbidden);
}
db.save_target(&target).await?;
Ok(())
}Both versions compile. The compiler checks that user exists, not what it is allowed to do.
Security impact: broken authorization lets ordinary users act as administrators or read other users' data. It is the top risk in the OWASP Top 10.
Lessons learned: keep all permission rules in one place instead of repeating them in each handler. Use your framework's middleware for this, such as a Tower layer in axum, or a policy library such as Casbin (casbin-rs). Then test every endpoint with a second account that has fewer rights, and confirm it is refused.
3. Path traversal and other web attacks
Rust web applications face the same attacks as web applications written in any other language.
- Path traversal. In Salvo, a Rust web framework, the proxy passed
../(go up one folder) to the backend server unchanged (CVE-2026-33242, CVSS 7.5). Attackers without an account could reach pages the routing rules were meant to block, such as admin dashboards. - Request smuggling. actix-http before version 3.12.1 accepted requests that gave their length in two different ways at once (CVE-2026-73051). When a proxy in front reads one length and the Rust server reads the other, an attacker can hide a second request inside the first.
- Archive extraction. In October 2026, RUSTSEC-2026-0328 reported that the
decompresscrate unpacked tar files without checking file names. A file named../../.ssh/authorized_keysinside an archive could overwrite the SSH keys of the user running the program. Cargo itself had a related bug in March 2026 (CVE-2026-33056).
Security impact: path traversal exposes or overwrites files, request smuggling bypasses access rules at the proxy, and archive bugs can give an attacker remote access through planted SSH keys.
Lessons learned: follow the OWASP Application Security Verification Standard (ASVS) and the OWASP Top 10 as you would in any language. Resolve every path to its full form and check that it is still inside the allowed folder before using it. Keep your web framework updated, and scan the running application with a web scanner such as OWASP ZAP or Burp Suite before each release.
4. Input validation and panics
A panic is Rust's way of stopping a program when something happens that the code did not handle, such as dividing by zero. Many Rust functions return either a value or an error. Calling .unwrap() or .expect() on that result means "give me the value, and panic if there is an error". AI-generated code uses both often because they are the shortest way to make code compile.
// Crashes if "page" is missing or is not a number.
let page: u32 = query.get("page").unwrap().parse().unwrap();
// Uses page 1 when "page" is missing, and rejects text that is not a number.
let page: u32 = match query.get("page") {
None => 1,
Some(text) => text.parse().map_err(|_| ApiError::BadRequest)?,
};In the uutils audit, sort panicked when it read a file name that was not valid UTF-8 text (CVE-2026-35348).
Security impact: denial of service. An attacker who finds an input that causes a panic can crash the service whenever they like. With Cargo's panic = "abort" setting, one bad request stops the whole server for every user.
Lessons learned: do not use unwrap or expect on data from users, files or the network, because you cannot control what arrives. Return an error with the ? operator instead. Turn on the Clippy lints unwrap_used and expect_used so the linter warns about every use. Test parsers with cargo-fuzz, a tool that feeds them millions of random inputs to find the ones that crash.
5. Integer overflow
Every integer type has a maximum value. A u8 can hold 0 to 255. When a calculation goes past the maximum, the result is called an overflow. Rust handles overflow differently in its two build modes:
- In a debug build, used during development and testing, an overflow panics.
- In a release build, used in production, the number silently wraps around to the start. 255 + 1 becomes 0. This is Cargo's default setting,
overflow-checks = false.
The result is that code can pass every test and then behave differently in production.
The bytes crate, used by Tokio and Hyper, had this bug in 2026 (CVE-2026-25541). A function added two numbers to check that there was enough room in a buffer, a block of memory that holds incoming data. In release builds, a very large value made the sum wrap around to a small number. The check passed when it should have failed, and the program could then read and write outside the buffer.
// In a release build, offset + len can wrap to a small number and pass the check.
if offset + len <= buf.len() { /* read the data */ }
// checked_add returns None on overflow, so the check fails safely.
match offset.checked_add(len) {
Some(end) if end <= buf.len() => { /* read the data */ }
_ => return Err(Error::OutOfBounds),
}Security impact: a wrapped number can let an attacker bypass size checks and read or overwrite memory, or change amounts in financial code. In the bytes case, safe Rust code ended up working on memory it should not have touched.
Lessons learned: use checked_add, checked_mul and the other checked_* functions for sizes, lengths, positions and money, so an overflow becomes an error you handle instead of a wrong number. For code that reads untrusted data, set overflow-checks = true in the [profile.release] section of Cargo.toml, so production builds also panic instead of wrapping.
6. Memory exhaustion
Rust releases memory correctly, but it will allocate as much memory as the code asks for. In October 2026, RUSTSEC-2026-0344 (CVSS 7.5) reported that the ruzstd decompression crate trusted a size written inside the file. A compressed file under 128 KB could make it request several gigabytes of memory at once.
Security impact: denial of service. The server runs out of memory and crashes for every user.
Lessons learned: treat everything that arrives from outside your program as attacker-controlled: HTTP requests, uploaded files, messages from other services and data read from disk. Rust cannot set limits for you because it does not know how large your inputs should be. Set them yourself:
- Limit request body size with
DefaultBodyLimitin axum orRequestBodyLimitLayerfromtower-http. - Limit request time with
TimeoutLayerfromtower-http. - Check sizes declared inside files against a maximum before allocating memory, and cap how far a decompressor may expand data.
7. Memory-safety bugs in unsafe code
Inside an unsafe block, Rust lets the programmer do five things the compiler cannot check, such as reading memory through a raw pointer, a plain memory address. The borrow checker still runs, but for those five operations the programmer, not the compiler, is responsible for memory safety. If the programmer gets it wrong, all the memory-corruption vulnerabilities listed earlier become possible again, and safe code that calls the unsafe block inherits the bug.
Recent cases:
- Data race in the Linux kernel. The first CVE in Rust code in the Linux kernel, CVE-2025-68260, was in Android's Binder driver. An
unsafeblock removed an item from a list, with a comment explaining why this was safe. The comment was wrong: another thread could change the same list item at the same moment, which corrupted the list. - Use-after-free in a popular cache. RUSTSEC-2026-0253 reported that the
lrucrate freed an item's memory but left it in its list if cleanup code panicked part-way through. Later code could then use the freed memory. Thesized-chunkscrate had a similar bug (RUSTSEC-2026-0255).
Security impact: the same as in C and C++: crashes, data leaks and, in the worst case, an attacker running their own code.
Lessons learned: avoid unsafe unless you need it, and keep each block a few lines long inside a function that is safe to call. Above each block, write a // SAFETY: comment that states exactly what must be true for the code to be correct, and ask two questions: what if another thread runs this at the same time, and what if this code panics half-way? Run your tests under Miri, a tool that detects undefined behaviour in unsafe code. Much of the unsafe code a project runs lives in its dependencies, so check those too, for example with cargo geiger.
8. Supply chain attacks through malicious crates
Attackers now publish malicious crates that steal passwords, keys and tokens from developers' computers as soon as a project is built. This works because of two Rust features:
- A build script (
build.rs) is a small Rust program included in a crate. Cargo runs it on your computer every time you build a project that uses the crate, before your own code is compiled. - A procedural macro is a crate that generates code, and it also runs on your computer while the project compiles.
Neither runs in a sandbox, so both can read your files and use the network. Opening a project in VS Code can be enough, because rust-analyzer runs build scripts and procedural macros by default to understand the code.
Two campaigns in 2026 used this:
- TrapDoor (May 2026). The security firm Socket found 34 malicious packages across npm, PyPI and crates.io. The six crates targeted developers working with Sui, a cryptocurrency blockchain, and Move, its programming language. Their build scripts searched the computer for cryptocurrency wallet keys, encrypted them and sent them to the attackers. The wider campaign also stole SSH keys and cloud credentials, and wrote instructions into
.cursorrulesandCLAUDE.mdfiles so that the developer's AI coding assistant would later help look for more credentials. - arrayref (August 2026). On 20 August, someone using a maintainer's account, which the Rust Security Response Team believes was compromised, published new versions of three popular crates:
arrayref,internmentandappend-only-vec. Each added a dependency calledproc-macro1, a name chosen to look like the widely usedproc-macro2. Its build script downloaded malware. The Rust Security Response Team removed the versions within 86 to 107 minutes.
To check whether a project used the arrayref versions, run cargo tree -i arrayref and look for version 0.3.10, or search Cargo.lock for arrayref 0.3.10, internment 0.8.7, append-only-vec 0.1.9 and proc-macro1. If you find them and the project was built, treat that computer as compromised and change every password, key and token it could access.
Rust's own tools have had bugs too. CVE-2026-5223 let crates from third-party registries overwrite other crates' cached code, and was fixed in Rust 1.96.0. In 2024, CVE-2024-24576 (CVSS 10.0) let attackers run commands on Windows through the standard library's Command when it started .bat or .cmd files.
Security impact: a stolen developer laptop or build server, with every secret on it: source code, cloud keys, signing keys and access to production.
Lessons learned:
- Commit
Cargo.lockand build withcargo build --locked, so a new crate version cannot slip in without review. - Run
cargo auditorcargo denyin your CI pipeline to block crates with known vulnerabilities, and usecargo vetto record which crates someone has reviewed. - Before adding a crate, check its downloads, its age, its maintainers and its repository. Be suspicious of new crates whose names are close to popular ones.
- Build unfamiliar projects in a container or virtual machine that holds no credentials.
- Do not give build jobs long-lived secrets. Deployment credentials belong in a later step that does not compile third-party code.
- Run
rustup updateregularly so you get fixes to Cargo and the standard library.
9. AI-generated vulnerabilities
A June 2026 study tested 14 AI models on 2,794 Rust coding tasks. 22.23% of the crates the models recommended did not exist. Rates ranged from 16.18% for Gemini 2.5 Pro to 26.90% for Claude 4 Opus. An attacker can register one of these invented names on crates.io and put a malicious build script in it, which runs on any computer whose developer accepts the suggestion. In tests on three open-source models, asking the model to review its own answer reduced the rate by only 2 to 3 percentage points.
A second study, from April 2026, asked three models to write encryption code in Rust using AES-256-GCM and ChaCha20-Poly1305, two widely used encryption methods. Of 240 programs, only 23.3% compiled. A crypto-specific checker found vulnerabilities in 57% of those that did, including reuse of the nonce, a value that must be unique for every encrypted message. Reusing a nonce lets an attacker recover information about the messages. CodeQL, a general code scanner, did not find these flaws.
Security impact: a hallucinated crate can become a supply chain attack, and weak encryption exposes the data it was meant to protect.
Lessons learned: check every crate an AI assistant adds before you build: does it exist on crates.io, who publishes it, how old is it and how many projects use it? Do not let AI write encryption code from scratch. Use a well-reviewed library through its high-level interface, and have a person review any code that handles keys, nonces or passwords.
10. Insecure code that compiles
AI coding agents usually work in a loop: write code, compile it, read the errors, fix them and compile again. The loop ends when the code compiles. When GitHub rewrote its Copilot runtime in Rust with AI agents, it classified the compiler errors the agents hit. About 84% were basic mistakes such as missing names, wrong types and missing methods. Errors about memory ownership were only 1.7%.
The compiler checks that code is valid Rust. It does not check permissions, limits, encryption or where a dependency came from. An AI agent that stops when the code compiles has not checked any of the risks in this guide, unless you ask it to.
Lessons learned: treat a successful build as the start of a security review, not the end. Ask an AI agent explicitly to check authorization, input limits, error handling and new dependencies, and review its changes with the checklist below. Never trust user input or a third-party library without checking it.
A checklist for Rust security reviews
- Search for
unsafein your code and dependencies, and read the safety comment on each block. - Search for
unwrap,expectand array indexing with[]on data from users, files or the network. - Use
checked_*functions for sizes, lengths and money, and decide whether release builds needoverflow-checks = true. - Test every endpoint with a lower-privileged account to confirm authorization works.
- Set size, time and depth limits on requests, uploads and decompression.
- Commit
Cargo.lock, build with--locked, runcargo auditorcargo denyin CI, and review every new crate. - Build untrusted code without secrets, and treat any build that pulled a malicious crate as a compromise.
- Fuzz parsers with
cargo-fuzzand testunsafecode with Miri.
Practise Rust security
The interview preparation platform cyberole.org now has a Rust security section. Its questions use real code snippets and the cases in this guide, from integer overflow and panics to malicious build scripts, authorization bugs and reviewing AI-generated Rust. Each answer explains the reasoning and links to the source. You can start practising for free today.
Conclusion
Rust is one of the best languages available for preventing memory-corruption vulnerabilities. Just remember that it does not guarantee the security of your code or applications. The 2026 record shows logic and authorization flaws, crashes, memory exhaustion, mistakes in unsafe code, malicious crates and AI-invented dependencies, all in code that compiled without errors. As more Rust is written by AI agents and reviewed quickly, these risks will become more common.
Every risk in this guide has a known control, and most can be enforced automatically in CI. Learn what the compiler checks, review what it does not, and follow the RustSec advisory database and the Rust blog for new advisories, because this field is young and new research will keep arriving.
References
Adoption and background
- Google Security: Rust in Android, move fast and fix things (November 2025)
- GitHub: Migrating the GitHub Copilot runtime to Rust, using Copilot (September 2026)
- The Computer Language Benchmarks Game: spectral-norm
- crates.io (crate and download totals from its public summary API, 15 October 2026)
- GitHub repository counts: GitHub search API,
language:Rustwithcreated:date ranges, queried 15 October 2026
The Rust world
- The Rust Programming Language (the Rust book), including ownership, panics, integer overflow, unsafe Rust and release channels
- The Cargo Book: profiles, build scripts and cargo tree
- rustup, Clippy and rust-analyzer configuration
Audits, advisories and CVEs
- Canonical: An update on rust-coreutils
- Matthias Endler: Bugs Rust won't catch
- CVE-2026-40937: RustFS admin endpoints without authorization
- CVE-2026-33242: Salvo proxy path traversal
- CVE-2026-73051: actix-http request smuggling
- RUSTSEC-2026-0328: tar-slip in decompress
- RUSTSEC-2026-0007: integer overflow in bytes
- RUSTSEC-2026-0344: decompression bomb in ruzstd
- CVE-2025-68260: race condition in Rust Binder
- RUSTSEC-2026-0253: use-after-free in lru and RUSTSEC-2026-0255: sized-chunks
- CVE-2024-24576: Command argument escaping on Windows
Supply chain and toolchain
- Rust blog: Supply chain attack on arrayref (August 2026)
- Corgea: TrapDoor across npm, PyPI and crates.io (May 2026)
- Rust blog: Security advisory for Cargo, CVE-2026-33056
- Rust blog: Security advisory for Cargo, CVE-2026-5223
- Sui
Research on AI-generated Rust
- Zheng, Guan and Liu: When LLMs Invent Rust Crates (arXiv 2606.08444)
- Elsayed, Fulton and Yang: An Empirical Security Evaluation of LLM-Generated Cryptographic Rust Code (arXiv 2604.27001)