Library / SDK
BurtonQin/lockbud avatar
BurtonQin/lockbud

lockbud: a Rust static analyser for deadlocks, atomicity violations and use after free

Detect concurrency and memory bugs and possible panic locations in Rust projects

590 stars39 forksRustBSD-3-Clause

At a glance

What is it?
lockbud is a static analyser for Rust that detects double-lock, conflicting lock order, condvar misuse, atomicity violations, use-after-free, invalid free and likely panic locations, built on rustc internals and pinned to a nightly toolchain. The author is candid that the memory detectors produce mostly false positives from dependencies and that the panic detector reports nearly every panic site.
Who is it for?
lockbud fits a Rust team doing concurrent work that wants a second opinion on lock ordering and atomicity, especially since the deadlock detectors are the ones the author rates as performing best, and the analysis runs on your own code rather than a whole dependency graph.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What lockbud detects and where it comes from

lockbud statically detects memory and concurrency bugs and possible panic locations in Rust. It is the artifact of a research paper titled Understanding and Detecting Real-World Safety Issues in Rust, published in TSE in 2024, and it builds on earlier work on memory and thread safety practices in real world Rust programs, published in PLDI in 2020.

The detector list is organised by bug class, and the flags select which class runs.

The concurrency bugs split into blocking and non-blocking. The blocking group covers double-lock, conflicting-lock-order and condvar misuse, and it runs with -k deadlock. The condvar misuse detector is noted as not having appeared in the paper. The non-blocking group is the atomicity violation detector, which runs with -k atomicity_violation.

The memory bugs are use-after-free and invalid-free, and they run with -k memory. Again, the invalid-free detector is noted as not having appeared in the paper, so it is newer work than the published study.

Panic locations are their own category with -k panic, and they are the weakest of the set, as the announcements explain.

The users are Rust developers working on concurrent code who want a static check on top of clippy and the borrow checker, and researchers who want to reproduce the study results. The Data/ directory in the repository holds the study artefacts, with BugStudy.xlsx recording the results of the research study and BugReport.xlsx recording the experimental results.

The nightly toolchain requirement and the install path

lockbud currently supports rustc nightly-2026-02-07, and the install is a source build:

bash
$ git clone https://github.com/BurtonQin/lockbud.git
$ cd lockbud
$ rustup +nightly-2026-02-07 component add rust-src rustc-dev llvm-tools-preview
$ cargo +nightly-2026-02-07 install --path .

The three components matter and are not optional. rust-src gives you the standard library source, rustc-dev gives you the compiler internals that a rustc_private tool links against, and llvm-tools-preview supplies the LLVM tooling the analysis needs. The Cargo.toml confirms the design, with a package.metadata.rust-analyzer section setting rustc_private=true, which is the declaration that the crate links against unstable compiler internals.

The consequence is stated plainly in the documentation: you must use the same rustc nightly version as lockbud to detect your project. There are two ways to arrange that, either override your rustc version for the run, or specify a rust-toolchain file in your project.

The repository ships a rust-toolchain.toml at the root for its own build, and the manifest declares two binaries, lockbud from src/main.rs and cargo-lockbud from src/bin/cargo-lockbud.rs, which is how the cargo subcommand form works. The version in the manifest is 0.1.0, and the edition is 2021.

The dependency list also tells you what kind of analysis this is. clap handles the command line, serde_json formats the output, petgraph provides the graph structures that lock ordering analysis needs, and regex and once_cell do the pattern matching. There is no SMT solver in the list, and the author explains why.

Why there is no SMT solver, and what that costs

The single most useful thing the documentation tells you about lockbud is what it deliberately does not do.

To reduce the detectors' overhead, the author did not introduce SMTs or other expensive analyses. The consequence is stated directly: as a result, the panic location detector may report nearly all the panic locations, making it less useful.

That is a real trade, and it is worth understanding rather than resenting. A panic location analysis that is sound would need to reason about which paths can actually reach a panic, which is where constraint solving earns its cost. Without it, the detector is a syntactic approximation that finds a superset of the real set, so you get every place that looks like it could panic. For a large codebase that is most of the codebase.

The same reasoning applies to the bug detectors, and the author says the bug patterns are based on observations from the research, and that he has tried to minimise the false positives and false negatives caused by the program analysis over-approximation. Over-approximation is the term for exactly this: the analysis reports a superset of the true bugs, so a human has to filter the output.

The deadlock detectors are the exception and the author says so: the double-lock and conflicting-lock-order detectors perform better than the other detectors, since the project was initially designed for deadlock detection. So the tool is strongest where it was designed to be strong, and weakest where it was bolted on, and the announcements ask the reader to hope a unified static analysis framework for Rust emerges to address these limitations.

The false positive problem and how to scope the run

The announcements section is blunt about the current state: the codebase was implemented quickly, the author plans to refactor it in the future, and the todo list is in issue 58.

Then comes the practical advice for anyone running it today. When checking your project, ignore the bug reports originating from the standard library and common dependencies, especially the memory detectors, as they are mostly false positives. To focus on your own project instead of dependencies, you can pass the -l your_project_name flag to lockbud.

That is the single most important operational detail in the documentation, and it is a hint about the tool's shape. A static analyser that loads a whole dependency graph will report on the dependencies, and the standard library is full of unsafe code and lock usage that the analyser's patterns will match. Scoping the run to your own crate names is how you get signal instead of noise.

The flag vocabulary is small. The -k flag selects the detector class, with values such as deadlock, atomicity_violation, memory and panic. The -l flag is followed by a list of crate names separated by commas, and the -b flag turns that list into a blacklist rather than a whitelist. A run that omits those dependencies looks like this:

bash
$ cd YourProject; cargo clean; cargo lockbud -k deadlock -b -l cc,tokio_util,indicatif

The documentation explains the effect: you have omitted the checking of dependencies cc, tokio_util and indicatif. So -l is the filter and -b chooses whether the list is what you want skipped or what you want checked.

Running it on a project and reading the JSON output

The project ships example projects in the toys/ directory and a detect.sh script. Running the detector on one of them is the quickest way to see the output shape:

bash
$ ./detect.sh toys/inter

The documentation says this prints 15 double-lock bugs in JSON format, and gives one of them in full. The structure is a bug_kind field naming the detector, a possibility field, a diagnosis object with the two lock types, the two source spans and the call chains that connect them, and an explanation.

Reading the example is instructive. The first lock is a parking_lot WriteLock acquired at src/main.rs:77, the second is a parking_lot ReadLock acquired at src/main.rs:84, and the first lock reaches the second through the call site at src/main.rs:79. The explanation states the reason: the first lock is not released when acquiring the second lock.

So the output is not a file and line number. It is a diagnosis of the lock relationship, with the call chain that connects the two acquisitions, which is the part that makes the finding actionable.

The conflicting lock order case is the same shape with two diagnosis records instead of one, and the records form a cycle. The documentation walks through the semantics: a list of records means it is possible that the first lock is acquired and waits for the second in one thread, while the second prime lock is acquired and waits for the first prime in another thread, which incurs a conflict lock bug.

Note the possibility field, which reads Possibly in both examples. The analyser is not claiming certainty, and the output is designed to be read as a hypothesis to check.

The cargo subcommand and the Docker image

detect.sh is described as mainly for development of the detector, because it brings more flexibility. You can modify it to use the release version of lockbud to detect large and complex projects, and you can use the LOCKBUD_FLAGS variable to select detectors and projects to be checked, with a commented export in the script as the starting point.

For day to day use there is a cargo subcommand form:

bash
$ cd toys/inter; cargo clean; cargo lockbud -k deadlock

The cargo clean step is not optional housekeeping. The documentation says you need to run it before re-running lockbud, and the reason is mechanical: the analyser works from compiled artefacts and a stale target directory will produce a stale result.

The compiler version can be pinned for a single run too:

bash
$ cargo +nightly-2024-12-01 lockbud

There is a Docker image, burtonqin/lockbud, and the usage is one command:

shell
docker run --rm -it -v ./toys/inter/:/volume burtonqin/lockbud -k deadlock

The image executes cargo clean and then cargo lockbud with the deadlock detector in the /volume directory, so your project is mounted and analysed in place. The caveat the documentation attaches is important for a real workflow: it compiles your project inside the container, so you need to manually remove the target directory before you are ready to work on it again, otherwise the container build leaves artefacts that the host build will reuse.

The other entry point in the tree is measure_time.sh, which fits the research framing, since measuring detector overhead is one of the things the paper needed.

Where lockbud is the wrong tool

The first limit is that this is a research artefact and says so. The version in the manifest is 0.1.0, the author says the codebase was implemented quickly and plans to refactor it, and the todo list lives in an issue rather than in a documented roadmap. The repository publishes no GitHub releases, so there is no tagged version to pin.

The second limit is the one the author states first, which is the false positive rate of the memory detectors, which are mostly false positives from the standard library and common dependencies. The tool's own documentation tells you to filter them out, which means a report from lockbud is not a pass or fail signal in the way clippy's is.

The third is the panic detector, which may report nearly all the panic locations. On a real codebase that is a wall of output, and the author concludes it is less useful as a result.

The fourth is the toolchain coupling. A rustc_private tool is pinned to one nightly, and you must use the same nightly as the analyser. When the next nightly lands, the pinned version is not the one your team is on, and the analysis is unavailable until either the tool is updated or your project pins an old compiler. That is an ongoing cost that a lint rule does not impose.

Compare that with the tools lockbud is layered on top of. Clippy and the borrow checker are part of the standard toolchain, run on every build, and are tuned to be quiet. A whole program analyser that reasons about lock ordering across functions cannot be, so it needs a separate binary, a separate toolchain, and a human to read the results. The difference in approach is that the standard linters are designed to be run on every commit, while lockbud is designed to be run when you want a deeper look at concurrency, and the second kind of tool is not a replacement for the first.

Licence, research context and what to verify first

lockbud is released under the BSD-3-Clause licence, with the text in the LICENSE file at the repository root. That is a permissive licence with a short attribution requirement, which is a notably lighter constraint than a copyleft licence, and it matters for a tool that a commercial team may want to run in its own build pipeline. Read the LICENSE file yourself for the exact terms.

The project sits alongside academic work rather than inside a company, and the tree reflects that. There is a CONTRIBUTING.md file, an .editorconfig, a .gitmessage file, the Data/ directory with the two Excel workbooks that record the study and the experimental results, the toys/ directory of test projects, and measure_time.sh next to detect.sh.

What to verify first is narrow. Run the analyser on your own crate names only, using the -l filter, and start with the deadlock detector since that is the one the author rates as performing best. Read the output as a hypothesis list rather than a verdict, and treat the possibility field as part of the finding rather than as a detail.

Then check the compiler coupling. The tool supports rustc nightly-2026-02-07, and your project must use the same nightly, either by overriding it or by specifying a rust-toolchain file. If your team is on stable, this is a separate toolchain to install and keep, and that ongoing maintenance is the real cost of adopting the analyser rather than the analysis itself.

Editorial conclusion

lockbud fits a Rust team doing concurrent work that wants a second opinion on lock ordering and atomicity, especially since the deadlock detectors are the ones the author rates as performing best, and the analysis runs on your own code rather than a whole dependency graph. It does not fit a project that expects a clean bill of health from a single command, because the memory detectors are dominated by false positives from the standard library and common dependencies, and the panic detector reports nearly every panic location. Before adopting it, run it with the -l flag restricted to your own crate names, read the panic output critically, and pin the same nightly the analyser was built with, since a mismatched rustc version will not produce a usable result.

Frequently asked questions

What kinds of bugs does lockbud detect in Rust?

It detects concurrency bugs including double-lock, conflicting-lock-order and condvar misuse under -k deadlock, atomicity violations under -k atomicity_violation, memory bugs including use-after-free and invalid-free under -k memory, and possible panic locations under -k panic.

How do I install lockbud?

Clone the repository, add the rust-src, rustc-dev and llvm-tools-preview components for the supported nightly with rustup, then build with cargo install --path . using that same nightly. The supported compiler is rustc nightly-2026-02-07, and you must use the same nightly to detect your own project.

How do I stop lockbud from reporting bugs in my dependencies?

Pass the -l flag with a comma separated list of crate names, and add -b to treat the list as a blacklist, for example cargo lockbud -k deadlock -b -l cc,tokio_util,indicatif. The documentation advises ignoring reports from the standard library and common dependencies, especially from the memory detectors, as they are mostly false positives.

What does the lockbud output look like?

It prints JSON entries naming the bug kind, a possibility field, a diagnosis with the two lock types, their source spans and the call chains connecting them, and an explanation. A double-lock report has one diagnosis record, while a conflicting-lock-order report has two or more that form a cycle.

Can I run lockbud in Docker?

Yes. The image burtonqin/lockbud runs cargo clean followed by cargo lockbud in the mounted volume, for example docker run --rm -it -v ./toys/inter/:/volume burtonqin/lockbud -k deadlock. It compiles your project inside the container, so you have to remove the target directory manually before working on the project again.

What licence does lockbud use?

lockbud is released under the BSD-3-Clause licence, with the terms in the LICENSE file at the repository root. The project is the artifact of a research paper published in TSE in 2024 and builds on earlier work published in PLDI in 2020.

Official sources

  1. BurtonQin/lockbud on GitHub
  2. Issues
  3. License: BSD-3-Clause
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/burtonqin-lockbud.svg)](https://hysenlabs.com/projects/burtonqin-lockbud)

Was this useful?