Memory safety
"Written in Rust" is not a security claim on its own. This page is the specific version: which code is in the safe subset, which is not, and what that does and does not buy you.
Where unsafe code is, and is not
Five crates carry #![forbid(unsafe_code)], which makes an
unsafe block a compile error rather than a review
comment. Everything that touches a network byte is among them.
| Crate | What it does | unsafe |
|---|---|---|
xin-h1 | HTTP/1 parser — request line, headers, chunked encoding | forbidden at compile time |
xin-engine | Request phases, rewrites, ranges, gzip, variables | forbidden at compile time |
xin-ir | The lowered configuration representation | forbidden at compile time |
xin-facade-nginx | Configuration lexer, parser and lowering | forbidden at compile time |
xin-k8s | Kubernetes Gateway API controller | forbidden at compile time |
xin-resolver | DNS resolution | none present |
xin-core | Process model: privilege drop, core pinning, fd passing | syscall wrappers only |
The remaining unsafe lives in xin-core and is
libc plumbing: setrlimit, geteuid, CPU affinity,
and passing listening file descriptors across a fork for binary upgrade.
It is a small, fixed surface that does not grow with traffic and never
parses input.
What this does not claim. xin depends on third-party
crates that contain unsafe — the TLS stack and the async
runtime among them. "No unsafe in the parser" is a statement about xin's
own code, which is where a protocol parser's bugs live, not a claim that
no unsafe code runs in the process.
What the compiler rules out
In the safe subset these are not bugs you can write. Each has been a real nginx advisory:
- Buffer overflow / overread. Slice indexing is bounds-checked; an off-by-one panics instead of writing past an allocation.
- Use-after-free. Ownership and lifetimes make a freed value unnameable.
- Null pointer dereference. There is no null; absence is
Option, and ignoring it does not compile. - Memory disclosure. Reads cannot run past the end of a buffer into whatever was there before.
- Signed/unsigned confusion into a memcpy. The 2013 chunked-encoding remote root was exactly this shape.
What it does not rule out
Rust is not a correctness proof, and it would be dishonest to imply otherwise. Of the 62 advisories on nginx's published list, 22 are not memory-safety defects — request smuggling, TLS session reuse, OCSP bypass, resource-exhaustion denial of service. xin has no structural immunity to any of them. They are listed in the same table as everything else on the advisory page, unfiltered.
How the numbers on this site are counted
The advisory list is copied from nginx's own security advisories page, with the id, description, severity and affected range verbatim. Two fields are our judgement, and the rule for each is written into the data file:
- Memory-safety (40 of 62). The advisory's own description names a buffer overflow/overread/overwrite/underflow, use-after-free, memory corruption, memory disclosure, or a null/invalid pointer dereference. Resource-exhaustion DoS is not counted — Rust writes that bug just as happily.
- In a path xin implements (15). The defect is in work xin also does — request parsing, rewrite, byte ranges, upstream response handling, the resolver. The other 25 are in modules xin does not ship (mp4, dav, SSI, mail, SCGI, HTTP/3), which is a weaker claim and presented as one.
Where a classification is arguable it is marked as not shipped rather than the more flattering option.
Beyond the type system
- Fuzz targets run against the HTTP/1 head parser, chunked decoding,
server_namematching and cipher-string parsing. - A differential fuzzer compares behaviour against real nginx, so divergences are found rather than assumed absent.
- Dependency advisories are gated in CI via
cargo-deny.