diff options
author | Bhasker Hariharan <bhaskerh@google.com> | 2021-03-03 12:18:04 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-03-03 12:24:21 -0800 |
commit | 3e69f5d088d121f1d3c4bf44ca637a48f13c4819 (patch) | |
tree | 153fa7d51f509c8a5cf066a7aea90fd334a08899 /tools/checklocks/README.md | |
parent | 80bc67c268dba0126cd258075c06d744399e0f02 (diff) |
Add checklocks analyzer.
This validates that struct fields if annotated with "// checklocks:mu" where
"mu" is a mutex field in the same struct then access to the field is only
done with "mu" locked.
All types that are guarded by a mutex must be annotated with
// +checklocks:<mutex field name>
For more details please refer to README.md.
PiperOrigin-RevId: 360729328
Diffstat (limited to 'tools/checklocks/README.md')
-rw-r--r-- | tools/checklocks/README.md | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/tools/checklocks/README.md b/tools/checklocks/README.md new file mode 100644 index 000000000..dfb0275ab --- /dev/null +++ b/tools/checklocks/README.md @@ -0,0 +1,142 @@ +# CheckLocks Analyzer + +<!--* freshness: { owner: 'gvisor-eng' reviewed: '2020-10-05' } *--> + +Checklocks is a nogo analyzer that at compile time uses Go's static analysis +tools to identify and flag cases where a field that is guarded by a mutex in the +same struct is accessed outside of a mutex lock. + +The analyzer relies on explicit '// +checklocks:<mutex-name>' kind of +annotations to identify fields that should be checked for access. + +Individual struct members may be protected by annotations that indicate how they +must be accessed. These annotations are of the form: + +```go +type foo struct { + mu sync.Mutex + // +checklocks:mu + bar int + + foo int // No annotation on foo means it's not guarded by mu. + + secondMu sync.Mutex + + // Multiple annotations indicate that both must be held but the + // checker does not assert any lock ordering. + // +checklocks:secondMu + // +checklocks:mu + foobar int +} +``` + +The checklocks annotation may also apply to functions. For example: + +```go +// +checklocks:f.mu +func (f *foo) doThingLocked() { } +``` + +This will check that the "f.mu" is locked for any calls, where possible. + +In case of functions which initialize structs that may have annotations one can +use the following annotation on the function to disable reporting by the lock +checker. The lock checker will still track any mutexes acquired or released but +won't report any failures for this function for unguarded field access. + +```go +// +checklocks:ignore +func newXXX() *X { +... +} +``` + +***The checker treats both 'sync.Mutex' and 'sync.RWMutex' identically, i.e, as +a sync.Mutex. The checker does not distinguish between read locks vs. exclusive +locks and treats all locks as exclusive locks***. + +For cases the checker is able to correctly handle today please see test/test.go. + +The checklocks check also flags any invalid annotations where the mutex +annotation refers either to something that is not a 'sync.Mutex' or +'sync.RWMutex' or where the field does not exist at all. This will prevent the +annotations from becoming stale over time as fields are renamed, etc. + +# Currently not supported + +1. The analyzer does not correctly handle deferred functions. e.g The following + code is not correctly checked by the analyzer. The defer call is never + evaluated. As a result if the lock was to be say unlocked twice via deferred + functions it would not be caught by the analyzer. + + Similarly deferred anonymous functions are not evaluated either. + +```go +type A struct { + mu sync.Mutex + + // +checklocks:mu + x int +} + +func abc() { + var a A + a.mu.Lock() + defer a.mu.Unlock() + defer a.mu.Unlock() + a.x = 1 +} +``` + +1. Anonymous functions are not correctly evaluated. The analyzer does not + currently support specifying annotations on anonymous functions as a result + evaluation of a function that accesses protected fields will fail. + +```go +type A struct { + mu sync.Mutex + + // +checklocks:mu + x int +} + +func abc() { + var a A + f := func() { a.x = 1 } <=== This line will be flagged by analyzer + a.mu.Lock() + f() + a.mu.Unlock() +} + +``` + +# Explicitly Not Supported + +1. Checking for embedded mutexes as sync.Locker rather than directly as + 'sync.Mutex'. In other words, the checker will not track mutex Lock and + Unlock() methods where the mutex is behind an interface dispatch. + +An example that we won't handle is shown below (this in fact will fail to +build): + +```go +type A struct { + mu sync.Locker + + // +checklocks:mu + x int +} + +func abc() { + mu sync.Mutex + a := A{mu: &mu} + a.x = 1 // This won't be flagged by copylocks checker. +} + +``` + +1. The checker will not support guards on anything other than the cases + described above. For example, global mutexes cannot be referred to by + checklocks. Only struct members can be used. + +2. The checker will not support checking for lock ordering violations. |