Re: [PATCH v9 12/27] rust: add `kernel` crate

comex
2022-10-03T02:04:48Z

>> On the other hand, it ought to be feasible to implement that kind of
>> ’negative reasoning' as a custom lint. It might not work as well as
>> something built into the language, but it should work decently well,
>> and could serve as a prototype for a future built-in feature.
>
> Interesting, do you have an example somewhere?
>
> Regards,
> Boqun

After some searching, I found this, which someone wrote several years ago for a
very similar purpose:

https://github.com/thepowersgang/tag_safe/

> This is a linter designed originally for use with a kernel, where functions
> need to be marked as "IRQ safe" (meaning they are safe to call within an IRQ
> handler, and handle the case where they may interrupt themselves).

> If a function is annotated with #[req_safe(ident)] (where ident can be
> anything, and defines the type of safety) this linter will check that all
> functions called by that function are either annotated with the same
> annotation or #[is_safe(ident)], OR they do not call functions with the
> reverse #[is_unsafe(ident)] annotation.

Note that the code won't work as-is with recent rustc. rustc's API for custom
lints is not stable, and in fact rustc has deprecated linter plugins entirely
[1], though there are alternative approaches to using custom lints [2]. Still,
it's a good example of the approach.

One fundamental caveat is that it doesn't seem to have the sophistication
needed to be sound with respect to indirect calls.

For example, suppose you have a function that fetches a callback from some
structure and calls it. Whether this function is IRQ-safe depends on whether
the callback is expected to be IRQ-safe, so in order to safety-check this, you
would need an annotation on either the callback field or the function pointer
type. This is more complex than just putting annotations on function
definitions.

Or suppose you have the following code:

fn foo() {
bar(|| do_something_not_irq_safe());
}

If `foo` is expected to be IRQ-safe, this may or may not be sound, depending on
whether `bar` calls the callback immediately or saves it for later. If `bar`
saves it for later, then it could be marked unconditionally IRQ-safe. But if
`bar` calls it immediately, then it's neither IRQ-safe nor IRQ-unsafe, but
effectively generic over IRQ safety. You could pessimistically mark it
IRQ-unsafe, but Rust has tons of basic helper methods that accept callbacks and
call them immediately; not being able to use any of them in an IRQ-safe context
would be quite limiting.

In short, a fully sound approach requires not just checking which functions
call which, but having some kind of integration with the type system. This is
the kind of issue that I was thinking of when I said a custom lint may not work
as well as something built into the language.

However, I do think it's *possible* to handle it soundly from a lint,
especially if it focuses on typical use cases and relies on manual annotations
for the rest. Alternately, even an unsound lint would be a good first step.
It wouldn't really comport with Rust's ethos of making safety guarantees
ironclad rather than heuristic, but it would serve as a good proof of concept
for a future language feature, while likely being helpful in practice in the
short term.

[1] https://github.com/rust-lang/rust/pull/64675/files
[2] https://www.trailofbits.com/post/write-rust-lints-without-forking-clippy