cargo / async-trait / audit
cargo : async-trait @ 0.1.89
PE Patrick Elsen signed 2026-05-27 published 2026-05-27

Claims

build-exec-deterministicbuild-exec-minimalbuild-exec-no-networkbuild-exec-no-write-outbuild-exec-safehas-binarieshas-build-exechas-fuzz-testshas-install-exechas-integration-testshas-property-testshas-unit-testsimpl-algorithmimpl-concurrencyimpl-cryptoimpl-datastructureimpl-interpreterimpl-jitimpl-parserimpl-protocolis-benignuses-concurrencyuses-cryptouses-environmentuses-execuses-filesystemuses-interpreteruses-jituses-networkuses-unsafe

Summary

async-trait 0.1.89 is a proc-macro that rewrites async fn in traits/impls into Pin<Box<dyn Future + Send>>-returning methods, restoring dyn Trait object-safety. Pure token rewriting over syn/quote; no unsafe, no I/O, no env or network access; upstream build.rs is correctly excluded from the published artefact. No findings; safe to deploy.

Report

Subject

async-trait is dtolnay's compiler-plugin-flavoured proc-macro attribute that rewrites async fn items inside Rust trait definitions and impl blocks into fns returning Pin<Box<dyn Future + Send + 'async_trait>>. This restores object safety (dyn Trait) for traits that contain async methods — a niche that the language's stabilised async fn in trait (Rust 1.75) deliberately does not cover. The crate is used pervasively across the async Rust ecosystem.

Methodology

The published crate was diffed against the upstream Git checkout at the commit recorded in .cargo_vcs_info.json. All ten source files in src/ (~1400 lines) were read or surveyed; the proc-macro entry point in src/lib.rs was read in full and the rewriting core in src/expand.rs was inspected. The integration-test crate (tests/test.rs, 1728 lines) and the trybuild UI suite (tests/compiletest.rs plus 16 cases under tests/ui/) were noted. The crate was scanned for unsafe, extern, build scripts, filesystem/env/network/process usage, and binary artefacts.

Results

All source and test files are byte-identical between the published crate and upstream. Cargo.toml differences are limited to cargo's standard normalisation; the upstream build.rs is correctly omitted from the published crate by an explicit exclude = ["build.rs"] (the published build = false confirms cargo did not interpret it as a build script). The upstream build.rs itself only emits two cargo: directives (rerun-if-changed and rustc-check-cfg) and is not reachable from a consumer's compile, so its content doesn't matter for the audit.

[lib] proc-macro = true makes the crate a proc-macro, which runs at compile time on every downstream consumer's machine — this is the primary reason has-build-exec is true. No build.rs runs (it is excluded from the published artefact), and the crate ships no binary artefacts or post-install scripts, justifying has-binaries and has-install-exec. The build-exec attack surface is the proc-macro itself.

The proc-macro exports one attribute, #[async_trait] (with an optional (?Send) argument). Its implementation is pure token rewriting over syn/quote/proc-macro2 ASTs: parsing a Trait or Impl item, collecting elided/explicit lifetimes, rewriting async fn signatures to the boxed-future form, replacing self references inside method bodies as appropriate, and re-emitting tokens. No unsafe blocks appear anywhere in src/; the only token-level mention of unsafe is parsing for the keyword in trait-item signatures (src/parse.rs). The crate's own documentation guarantees that the expanded code is safe Rust as well, justifying uses-unsafe.

No filesystem reads, no environment-variable access, no std::process::Command, and no networking primitives appear in the codebase — the only std imports are std::collections::BTreeSet and std::mem. The proc-macro is therefore deterministic and side-effect-free, justifying build-exec-safe, build-exec-deterministic, build-exec-no-network, build-exec-no-write-out, build-exec-minimal, and is-benign. The codebase was reviewed for cryptographic, network, filesystem, environment, exec, JIT, interpreter, concurrency, and unsafe usage, and none was found, justifying uses-network, uses-filesystem, uses-environment, uses-exec, uses-crypto, uses-jit, uses-interpreter, uses-concurrency, impl-crypto, impl-parser, impl-interpreter, impl-jit, impl-protocol, impl-datastructure, impl-algorithm, impl-concurrency.

Testing is supplied by tests/test.rs (1728 lines), a comprehensive integration suite covering every combination the macro is meant to handle: by-value/by-ref/by-mut-ref/no self; generics; explicit and elided lifetimes; associated types; default-body methods; non-Send mode (?Send); supertraits; mixing async and sync methods; and so on. A compiletest.rs harness drives trybuild over 16 expected-failure cases under tests/ui/, which capture the error spans the macro emits for misuse. There are no in-source #[test] blocks (has-unit-tests), no fuzz, and no property tests (has-fuzz-tests, has-property-tests).

No findings were recorded.

Conclusion

async-trait is a mature, well-tested proc-macro whose entire surface is mechanical token rewriting with no I/O, no network, no environment reads, no unsafe, and no binary artefacts. The published crate correctly excludes its upstream build.rs. No security, safety, correctness, or quality concerns were identified.

Findings

No findings.

Annotations(4)

src/lib.rs

Single #[proc_macro_attribute] entry point async_trait (line 252) that parses the input via syn, dispatches to expand::expand, and re-emits the rewritten trait/impl as tokens. The doc comment (line 121) explicitly states that the expanded code uses no unsafe. Justifies the proc-macro side of has-build-exec, and uses-unsafe.

tests/compiletest.rs

trybuild-driven UI test runner over tests/ui/*.rs (16 expected-failure cases covering missing async, lifetime spans, must-use, send bounds, type mismatches, etc.). Supports has-integration-tests.

tests/test.rs

1728-line integration-test crate exercising trait/impl rewrites across self-by-value/ref/mut-ref, generics, lifetimes, associated types, default-body fns, and ?Send mode. Compiled and run via the executor module (tests/executor/mod.rs). Backs has-integration-tests.