Re: [PATCH 00/13] [RFC] Rust support
From: Peter Zijlstra
Date: Fri Apr 16 2021 - 10:19:18 EST
On Fri, Apr 16, 2021 at 02:07:49PM +0100, Wedson Almeida Filho wrote:
> On Fri, Apr 16, 2021 at 01:24:23PM +0200, Peter Zijlstra wrote:
> > int perf_event_task_enable(void)
> > {
> > + DEFINE_MUTEX_GUARD(event_mutex, ¤t->perf_event_mutex);
>
> There is nothing in C forcing developers to actually use DEFINE_MUTEX_GUARD. So
> someone may simply forget (or not know that they need) to lock
> current->perf_event_mutex and directly access some field protected by it. This
> is unlikely to happen when one first writes the code, but over time as different
> people modify the code and invariants change, it is possible for this to happen.
>
> In Rust, this isn't possible: the data protected by a lock is only accessible
> when the lock is locked. So developers cannot accidentally make mistakes of this
> kind. And since the enforcement happens at compile time, there is no runtime
> cost.
>
> This, we believe, is fundamental to the discussion: we agree that many of these
> idioms can be implemented in C (albeit in this case with a compiler extension),
> but their use is optional, people can (and do) still make mistakes that lead to
> vulnerabilities; Rust disallows classes of mistakes by construction.
Does this also not prohibit constructs where modification must be done
while holding two locks, but reading can be done while holding either
lock?
That's a semi common scheme in the kernel, but not something that's
expressible by, for example, the Java sync keyword.
It also very much doesn't work for RCU, where modification must be done
under a lock, but access is done essentially lockless.
I would much rather have a language extention where we can associate
custom assertions with variable access, sorta like a sanitizer:
static inline void assert_foo_bar(struct foo *f)
{
lockdep_assert_held(&f->lock);
}
struct foo {
spinlock_t lock;
int bar __assert__(assert_foo_bar);
};
Such things can be optional and only enabled for debug builds on new
compilers.
> Another scenario: suppose within perf_event_task_enable you need to call a
> function that requires the mutex to be locked and that will unlock it for you on
> error (or unconditionally, doesn't matter). How would you do that in C? In Rust,
> there is a clean idiomatic way of transferring ownership of a guard (or any
> other object) such that the previous owner cannot continue to use it after
> ownership is transferred. Again, this is enforced at compile time. I'm happy to
> provide a small example if that would help.
C does indeed not have the concept of ownership, unlike modern C++ I
think. But I would much rather see a C language extention for that than
go Rust.
This would mean a far more agressive push for newer C compilers than
we've ever done before, but at least it would all still be a single
language. Convertion to the new stuff can be done gradually and where
it makes sense and new extentions can be evaluated on performance impact
etc.