RE: Thread Safety Analysis and the Linux kernel

Puchert, Aaron
2025-03-06T23:24:20Z

> From: Paul E. McKenney <paulmck@kernel.org>
> On Wed, Mar 05, 2025 at 11:54:14PM +0000, Puchert, Aaron wrote:
> > Re-entrant locks are thus not necessarily an impediment: if one function
> acquires a lock and calls another function that also acquires it, the analysis won't
> complain. It analyzes functions in isolation. You'll only get a warning if you acquire
> a lock twice in one function without releasing it in between, or if the function is
> annotated as requiring the lock and acquires it again. However, in both cases the
> second acquisition can always be dropped. You can argue that this isn't a deadlock
> and hence not a bug, but the acquisition is unnecessary because the lock is
> already known to be held.
> >
> > In theory I could construct cases where there are still problems: maybe we're
> locking twice because we're calling another function that unlocks once, but we
> still want the lock to be held afterwards. However, I've never seen this. In our
> code, re-entrant locks are only used because the caller might already hold the
> lock for some reason. The sophisticated patterns that I could think of never
> appeared. If this is different in the kernel, I'd be curious how it looks like.
>
> We do have macros that acquire locks and enter RCU read-side critical
> sections. Also, depending on exactly where the analysis is done, static
> inline functions might cause this to happen.
>
> These can easily result in a function with nested calls to (for example)
> rcu_read_lock().

Macros would be a problem, functions not. As long as the function doesn't have __attribute__((locks_excluded(...))), which would be pointless with re-entrant mutexes, we don't know when checking the caller that the callee acquires the lock a second time. Similarly, in the function itself we don't know where we're called from, so we don't know that the caller already holds the mutex (in some cases). We only know when we have an __attribute__((requires_capability(...))), but then it would be pointless to acquire in the function.

Aaron