[HN Gopher] Prusti: Static Analyzer for Rust ___________________________________________________________________ Prusti: Static Analyzer for Rust Author : aviramha Score : 226 points Date : 2022-10-13 13:55 UTC (9 hours ago) (HTM) web link (github.com) (TXT) w3m dump (github.com) | piterdevries wrote: | Why would you need a static analyzer for a language that promotes | itself as safe out of the box. | IshKebab wrote: | This is a fair question really. Calling it a static analyser is | misleading and seems to be editorialised. It's not like static | analysers in C++. | | It's actually a formal verification tool. They call it a | "static verifier" not a "static analyser". | | Most static analysis tools seek to find potential problems in | your code - generally common mistakes - but they aren't proving | anything usually. They have false positives and negatives. | Formal verification requires you to write properties about your | code and then it _proves_ it. | jerf wrote: | In addition to the many other fine points about how Rust | doesn't perfectly secure against everything, having a static | analyzer out of the compiler means that the static analyzer can | continue to develop on its own time frame without being tied to | the compiler releases. The importance of this is easy to | underestimate. It is really helpful to have external projects | able to iterate independently for this sort of thing. | a_humean wrote: | Rust promises that safe rust is memory/type safe. You can still | get interger over/under-flows, indexing out of bounds, and | allocation failures (oom), etc... all of which "panic" - which | means that rust will safely unwind the stack and exit in a way | that remains memory safe. | umanwizard wrote: | Rust claims to make a particular class of bugs more difficult | to write. It doesn't claim to magically eliminate all of them. | the-lazy-guy wrote: | It is written in the linked README, but I will state it here. | | Rust checks integer overflows at runtime (or not at all, if | building for maximum speed). It is safer than not checking at | all. But costs performance and can lead to (predictable) | crashes. | | This tool is a way to prove that overflows can not happen at | compile time. Which is extremely hard in the general case. | kibwen wrote: | Also note that the reason that Rust can get away with not | checking for integer overflow while still being memory-safe | is because indexing operations are bounds-checked, so an | overflowing index variable panics anyway. | cesarb wrote: | > so an overflowing index variable panics anyway. | | Unless it overflows all the way to a valid index. Which | might lead to unexpected results if the code does not | expect to be using a smaller index (for instance, a code | trying to access index i+2 might not be expecting it to | suddenly access indexes 0 or 1). | iudqnolq wrote: | But Rust is still memory safe because unsafe code | "morally" is unsound if it assumes something like that | can't happen. | naasking wrote: | > safe out of the box | | Safe Rust is memory safe and data race safe. There are other | forms of safety obviously, like overflow safety, numerous forms | of confidentiality and security properties, etc. | pjmlp wrote: | It is safe for the 70% of security flaws found out in languages | like C and C++. | | The remaining 30% still need to be tracked down. | Arch-TK wrote: | I wonder where you got those numbers from. | davidatbu wrote: | Almost every study of security vulnerabilities concludes | that roughly 70% of them are caused by memory unsafety. | dcsommer wrote: | https://alexgaynor.net/2019/aug/12/introduction-to-memory- | un... | Vecr wrote: | A report about Windows written by Microsoft, I think. | mynameisash wrote: | From Microsoft[0]: "As we've seen, roughly 70% of the | security issues that the MSRC assigns a CVE to are memory | safety issues." | | And from Google[1]: "memory safety bugs continue to be a | top contributor of stability issues, and consistently | represent ~70% of Android's high severity security | vulnerabilities." | | [0] https://msrc-blog.microsoft.com/2019/07/22/why-rust- | for-safe... | | [1] https://security.googleblog.com/2021/04/rust-in- | android-plat... | IshKebab wrote: | I would say at least half of the remaining 30% are eliminated | by Rust's stronger type system and borrow checker too. When | I'm writing Rust it feels like I write around 10x fewer bugs | than in C++. | pjmlp wrote: | Even formal methods and verification have bugs. | za3faran wrote: | What are people's experiences with static analyzers at companies? | Many people I have spoken with have either never heard of them, | or expressed no interest. Usually those same people use dynamic | languages like Ruby or Python. | heywire wrote: | Not sure how relevant my experience is, but I work on a 30+ | year old Windows application that started life as a DOS | application. It is written in a mix of C and C++. We still do | things pretty old school. As part of submitting your work for | code review (which is required for every change), you submit | the output of cppcheck. When we first introduced cppcheck, | there were thousands of warnings and errors that we slowly | worked through. Now it is expected that the cppcheck output is | empty. | johnny-lee wrote: | That's what scares a lot of people when they run a static | analyzer on code for the first time - all the possible | problems. | | Some of the possible problems are easy to confirm or deny | just by looking at one line of code, but others will require | much more analysis. | | You need a tool/viewer to show the possible problem and what | caused the possible problem. Such a tool improves the user | experience dealing with static analyzer results. | solomatov wrote: | Big tech uses static analyzers a lot. See for example, these | projects: | | - https://fbinfer.com/ (<- This one was a breakthrough in | static analysis in its time) | | - https://github.com/google/error-prone | | - https://github.com/facebook/SPARTA | | And many others | johnny-lee wrote: | Microsoft bought and adapted PREfix and created a simpler, | faster version called PREfast (and the Visual Studio | -analyzer switch for C/C++/C# code). | | see tools section in https://learn.microsoft.com/en- | us/previous-versions/tn-archi... | boulos wrote: | Many of Google's general checks in clang-tidy should be | directly available, too. For example, the absl checks seem to | be on the list of clang-tidy checks: | | https://clang.llvm.org/extra/clang-tidy/checks/list.html | Agingcoder wrote: | They work really well, and people, while initially skeptical, | eventually get the idea. The key is to run a first pass on your | codebase, find and fix real bugs, and share your findings. This | turns a very theoretical tool into 'see, it works, and that's | one nasty bug we won't have in production'. | ainar-g wrote: | I lead a Go team, and we pretty much never go (heh) anywhere | without a suite of static analysers. go vet, govulncheck, | errcheck, and staticcheck are the required minimum, but we do | use some others. | | Back when I used to write Ruby, lack of static analysis was a | serious problem. I've been able to add Rubocop later, but it's | not exactly on the same level as staticcheck, to say nothing | about Prusti from the OP. | smaddox wrote: | Could you comment on the others you use? I would like to | start using a lot more static analysis at $job. | ainar-g wrote: | Luckily for you, it's all open-source, heh. | | https://github.com/AdguardTeam/AdGuardDNS/blob/master/scrip | t... | | https://github.com/AdguardTeam/AdGuardDNS/blob/master/scrip | t... | | There's some mess in there, but if you only need a list of | analysers and examples of usage, these should be enough. | haskellandchill wrote: | We have some critical systems code in C that I'm going to try | to run static analysis on for our Hack Week. There are probably | 100 engineers at my company and not much interest in anything | beyond Google SRE and language specific release updates. We use | Bash, Php, Python, Ruby, Node, C, Go, and Rust. | nerdponx wrote: | A lot of Python teams use a linter that can catch errors like | "variable used before defined" as well as matters of code | style, and increasingly a static type checker in addition to | that (which also does its own set of such checks). | pm215 wrote: | I find it depends a lot on the static analyzer. (My experience | is largely with C code.) In particular, if the analyzer | produces a lot of false positives it is dumping a pile of work | onto the developers to work through each report and satisfy | themselves that it's invalid, and hiding the real bugs under a | pile of garbage. If it is more careful to avoid false positives | then it's more workable as a development tool (but of course it | then finds fewer interesting issues). | | The other key static analyzer question is "how easy is it to | run and does that happen early in the development cycle?". A | static analysis pass built right into the compiler and | generating warnings every time you compile has the most chance | of having its reports paid attention to. Something that runs at | CI time is more annoying. Something that runs only on trunk a | week or more behind the leading edge of development and which | just lists its reported issues on a webpage somewhere is in | grave danger of being outright ignored, or only read by one or | two enthusiasts who are forever fixing up other peoples' | code... | IshKebab wrote: | I can't even convince my co-workers to use Typescript. :-/ | | I think it varies by industry though. | pjmlp wrote: | In the typical enterprise projects I work on they are quite | typical, usually we tend to use stuff like SonarQube and it | breaks the build for specific errors. | | Usually it doesn't matter if devs themselves don't care, | because when devops have the management support, they will care | to fix that broken build. | aviramha wrote: | Static analyzers are free CR in most cases. I highly recommend | using it, and do know that in some companies people are unaware | of those unfortunately.. | drwoland wrote: | When I worked on a big c++ codebase I found them essential for | both ci/cd systems and actively debugging an issue. The | valgrind suite of tools like cachegrind are very useful for | both troubleshooting as well as classic static analysis and I | heartily recommend investing some time in learning valgrind if | you're writing c/c++ code for a platform valgrind runs on. | | On the other hand commercial tools have been more of a mixed | blessing but that is probably because every time ive seen them | deployed the budget hasnt included sufficient engineering time, | training or prof services to cut down huge numbers of false | positives. | wyldfire wrote: | > The valgrind suite of tools like cachegrind are very useful | for both troubleshooting as well as classic static analysis | and I heartily recommend investing some time in learning | valgrind | | valgrind is not a static analysis tool. But it is a great | tool, especially memcheck. | Ericson2314 wrote: | I'm a "hard core PLer" and I am skeptical. I don't like | automated solvers where there is no way to manually include a | proof (to be checked). | Klasiaster wrote: | It doesn't mention unsafe in the README but the website says: | The first versions of our tools are under development, and target | a small but interesting fragment of Rust without unsafe features; | in the future, we plan to extend our work to tackle a large | portion of the language, including certain patterns of unsafe | Rust code. | | I wonder if this can be used to prove that unsafe code is memory | safe. | Aurel300 wrote: | This is an active research topic in our group. Within unsafe | Rust code you lose the guarantees of the Rust ownership type | system, which are important for framing (figuring out which | parts of the memory _could_ be affected by the given | operations). As a result, for e.g. pointer-manipulating unsafe | code, the code will probably need to be annotated more heavily, | to track which values are "owned" by whom etc. | epilys wrote: | Nitpick, unsafe doesn't turn off the borrow checker. It just | allows you to dereference raw pointers which are the things | you must be careful about by reasoning about the actual | safety yourself as a programmer. Everything else that uses | safe pointers (references and mutable references) remain | safe. | nine_k wrote: | But how passing around a (constant) raw pointer is not | sidestepping borrow checker? Since the pointer (AFAICT) | does need to be borrowed, because it's manifestly | immutable, it could be passed into several functions that | alter the pointed-at memory in arbitrary order. | zozbot234 wrote: | It's worth noting that the GhostCell and similar patterns are | already powerful enough to safely express some code that | would normally require pointer manipulation or other unsafe | features. Of course GhostCell itself is quite unidiomatic and | unintuitive, but adding a more idiomatic annotation syntax | seems like it might be a sensible goal. | WalterBright wrote: | Looks like I need to fix my binary search code! | haskellandchill wrote: | This looks good, I'm going to have to try out their work for Go, | Gobra, since that's what I'm mostly writing these days. | nerdponx wrote: | According to the Readme, this is based on a more-general | verification framework called Viper, which apparently works for | several languages (including Rust): | https://www.pm.inf.ethz.ch/research/viper.html | | There also appear to be equivalents of this tool in Python | ("Nagini" https://www.pm.inf.ethz.ch/research/nagini.html) and Go | ("Gobra" https://www.pm.inf.ethz.ch/research/gobra.html). | | I'll definitely be checking out Nagini for my work! | hn92726819 wrote: | I don't understand how this could work from looking at the | readme. It says: | | > verifies absence of integer overflows and panics by proving | that statements such as unreachable!() and panic!() are | unreachable | | But integer overflows in release builds don't panic! and aren't | unreachable!. Additionally, clippy already checks this for you if | you enable an optional lint. | | So if it detects any panic! Then that's amazing. But if it only | detects panic for integer operations, we already have that | feature. Either way, the overflow/panic! wording is confusing | because it either only applies to debug builds or applies to more | than integer operations | Aurel300 wrote: | Yes, we could improve the wording -- suggestions and users are | welcome! The tool is indeed much more general purpose than | integer overflows: it is a based on a deductive verifier which | uses symbolic execution to figure out which nodes in the CFG | are reachable and under what conditions. panic!, unreachable!, | failed assert!s, etc are all checked. If one can be reached, | the error to show to the user is reconstructed from the | compiler's span information. | hn92726819 wrote: | If I'm reading your comment correctly, then this is so much | cooler than I thought :D. Closest thing to this would be | https://docs.rs/no-panic/latest/no_panic/ I believe, and the | error message leaves much to be desired. | | I will definitely be trying this out, but one last question: | std can panic when doing tons of things (slice indexing, | str.split_at, etc). Can this be used to make never-panicing | programs? | Aurel300 wrote: | The short answer is: it could be used for that. But there's | a couple of things to say: | | - Prusti is doing _modular_ verification: every method is | verified in isolation, and all calls in that method's code | only use the contracts declared on the call targets. This | is good for scalability and caching and it means that a | method's signature + contract is the entire API (you don't | depend on its internals). | | - Methods without a contract are assumed to have the | precondition `true` and postcondition `true` (in other | words, such a method can always be called and makes no | guarantees at all about what it did to its mutable | arguments or result). For methods declared within the | current project, this is fine: if they could panic, Prusti | would identify this when verifying them and the user would | have to declare a precondition. For external methods (whose | implementation is not verified), this is potentially | unsound. | | - However, we are in the process of creating a library of | specifications for stdlib methods. We use a large-scale | analysis framework (rust-corpus/qrates) to evaluate which | methods are used most often. We try to specify such methods | first to cover real-world Rust code usages. | | Making the default precondition for external functions | "false" (unless specified otherwise) would be sound but | would be quite restrictive. One goal of Prusti is also | incremental verification: you should be able to start using | Prusti for basic checks then gradually introduce more | specifications to get stronger and stronger guarantees | about the program's behaviour. | epilys wrote: | Thanks for your interesting work! I wanted to ask, how are | FFI boundaries handled? Are they ignored or is it an error to | call FFI functions? | Aurel300 wrote: | Currently they are not handled. But (you guessed it) we | also have a project working on this: attaching trusted | specifications to external methods. | | In the long term we might investigate a full integration | with external verifiers, e.g. to check that the | specifications declared on external methods in Rust is | justified by their actual implementation, say in C. This is | tricky because the specification language/level of | abstraction might differ. It might be necessary to prove | program refinement, for example. | wongarsu wrote: | I read that as "verifies (absence of integer overflows) and | (panics by proving that statements such as unreachable!() and | panic!() are unreachable)". | | I think the docs also support this reading, overflow detection | and panic detection are listed as separate features [1]. | | But it is poorly worded, and the readme could certainly be | improved. | | 1: https://viperproject.github.io/prusti-dev/user- | guide/verify/... | hn92726819 wrote: | Okay two separate checks, I don't know why my mind couldn't | come up with that wording. | kibwen wrote: | You can enable (or disable) panic-on-overflow in Rust via a | compiler flag or the corresponding value in Cargo.toml: | https://doc.rust-lang.org/cargo/reference/profiles.html#over... | hn92726819 wrote: | > If not specified, overflow checks are enabled if debug- | assertions are enabled, disabled otherwise | | Both of the values you can set don't give you a warning | though. The linked article is about producing a warning or | error when there can possibly be an overflow/panic, which | clippy already does. | | Edit: here's the lint that warns you that a panic or overflow | can be caused: https://rust-lang.github.io/rust- | clippy/master/#integer_arit... | Ericson2314 wrote: | You are overthinking things. Those are just usee to mark places | in the control flow it tries to proove are unreachable. The | meaning of those constructs is irrelevant. | hn92726819 wrote: | I think they're talking about literal panic!() calls though. | They used parenthesis in their examples too, which indicates | they're probably talking about the macro calls. | | This is a static analyzer so I would expect it can trace | calls to a panic handler (which both panic! and unreachable! | use). I might be overthinking things, but it looks to me like | this may be able to detect panic! calls even in, say, stdlib | wongarsu wrote: | That looks incredibly useful. Proving the absence of panics and | overflows is already great, and with the annotations you can | guarantee properties you'd normally write property tests for, | like in this example from the docs: impl List { | #[ensures(self.len() == old(self.len()) + 1)] pub fn | push(&mut self, elem: i32) { // TODO } | } | turboponyy wrote: | Can't wait for dependently typed type systems | Yoric wrote: | I've written code with dependently typed languages. It's very | powerful, but it really is time-consuming. That won't be | appropriate for all developments/developers. | dochtman wrote: | Was a bit disappointed to discover that the advertised Prusti | Assistant VS Code assistant sort of silently sits there waiting | ("Checking requirements...") if you don't have Java installed. | | I feel like assuming Java is installed doesn't really fit the | audience. | Aurel300 wrote: | We are working on improving the error reporting for the IDE | extension. Regardless of the audience, Prusti is based on the | Viper verification framework which is mainly implemented in | Scala. | insanitybit wrote: | This is great. I'm building something similar, an effects system | for rust, and it looks quite a bit like this. The difference is | that my effects system compiles to a seccomp + apparmor profile | so that your rust program is sandboxed at runtime based on info | at compile time. | | I have a notion of purity as well :P | | I think the applications of this sort of thing are pretty | limitless. Maybe rust has `unsafe` but with further verification | extensions to the language we can really push confidence in a | working, safe product. | littlestymaar wrote: | > The difference is that my effects system compiles to a | seccomp + apparmor profile so that your rust program is | sandboxed at runtime based on info at compile time. | | That's awesome! I started working to sandbox my Rust program | using seccomp-BPF, and I was quickly frustrated about having to | run my program with _strace_ to find out what syscalls I should | allow for my program, when it sounded like this information | should be available at compile time! | insanitybit wrote: | I've wanted to do this for years and I've tried doing it a | few times in some weird ways. I'm finally doing it in a hacky | but more straightforward way and it's going well. | jahewson wrote: | > compiles to a seccomp + apparmor profile | | That's a really nice demonstrable and practical impact of using | effects! | fire wrote: | > my effects system compiles to a seccomp + apparmor profile so | that your rust program is sandboxed at runtime based on info at | compile time. | | is this open or proprietary? I'd love a link to a repo | insanitybit wrote: | I'll see if I can open source it this weekend. I'm not trying | to be the "like and subscribe for updates" but if you want to | see it when it's open source I'd suggest following me on | Twitter (or Github? Does Github have a follow thing?) cause I | won't remember to reply on HN. | | Here's a little snippet: #[effect::declare( | args=(inner_tmp as I) returns=("/tmp/" + I) | )] fn tmp_dir(inner_tmp: Path) -> Path { | Path::from("tmp/").join(inner_tmp) } | | So it can reason about that Path's constraints. When that | Path gets used by, say, "File::create(path)", it gets turned | into a rule and added to an apparmor policy. | | Apparmor doesn't support a "hey I'm a process, please sandbox | me" so I have to write a privileged daemon that manages that | bit. | | I also have a way to apply effects to functions you don't | own, mutating functions, functions that branch, etc. None of | that is implemented yet, just designed. | fire wrote: | what's your twitter/gh? Could you add them to your hn | profile? | littlestymaar wrote: | Can't be certain, but given the content I'm pretty sure | that it's these ones: | | - Github : https://github.com/insanitybit | | - Twitter: https://twitter.com/InsanityBit | insanitybit wrote: | Thanks, yes. I'll add those to my profile. | fire wrote: | Nice, followed you on both | temp123789246 wrote: | Out of curiosity, does anyone know how this compares to something | like Liquid Haskell? Is one more or less powerful than the other? | Aurel300 wrote: | The two tools address similar problems. The kinds of properties | you can prove (automatically) should also be similar, because | both Prusti and Liquid Haskell ultimately use an SMT solver to | check if assertions hold. | | From what I have seen LH focuses on integrating into the type | system (it is Liquid as in the 2008 Liquid Types paper). | Generally it is possible to rewrite properties attached to a | type to contracts, e.g. a non-zero Int input becomes a | precondition that says that argument is non-zero. Checking | termination with Prusti is also something we are working on. ___________________________________________________________________ (page generated 2022-10-13 23:00 UTC)