From 53c9b9588a3a811bdf8d7ac2ff371bc2f95ed261 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Fri, 21 Aug 2020 11:49:56 -0400 Subject: [PATCH] runtime: check held locks with staticlockranking When lock ranking is enabled, we can now assert that lock preconditions are met by checking that the caller holds required locks on function entry. This change adds the infrastructure to add assertions. Actual assertions will be added for various locks in subsequent changes. Some functions are protected by locks that are not directly accessible in the function. In that case, we can use assertRankHeld to check that any lock with the rank is held. This is less precise, but it avoids requiring passing the lock into the functions. Updates #40677 Change-Id: I843c6874867f975e90a063f087b6e2ffc147877b Reviewed-on: https://go-review.googlesource.com/c/go/+/245484 Run-TryBot: Michael Pratt TryBot-Result: Go Bot Trust: Michael Pratt Reviewed-by: Michael Knyszek --- src/runtime/lockrank_off.go | 8 +++++ src/runtime/lockrank_on.go | 70 +++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/runtime/lockrank_off.go b/src/runtime/lockrank_off.go index 32378a9627..c04b61edc7 100644 --- a/src/runtime/lockrank_off.go +++ b/src/runtime/lockrank_off.go @@ -44,3 +44,11 @@ func releaseLockRank(rank lockRank) { //go:nosplit func lockWithRankMayAcquire(l *mutex, rank lockRank) { } + +//go:nosplit +func assertLockHeld(l *mutex) { +} + +//go:nosplit +func assertRankHeld(r lockRank) { +} diff --git a/src/runtime/lockrank_on.go b/src/runtime/lockrank_on.go index fbc5ff58b7..850f7cdd38 100644 --- a/src/runtime/lockrank_on.go +++ b/src/runtime/lockrank_on.go @@ -86,6 +86,18 @@ func lockWithRank(l *mutex, rank lockRank) { }) } +//go:systemstack +func printHeldLocks(gp *g) { + if gp.m.locksHeldLen == 0 { + println("") + return + } + + for j, held := range gp.m.locksHeld[:gp.m.locksHeldLen] { + println(j, ":", held.rank.String(), held.rank, unsafe.Pointer(gp.m.locksHeld[j].lockAddr)) + } +} + // acquireLockRank acquires a rank which is not associated with a mutex lock //go:nosplit func acquireLockRank(rank lockRank) { @@ -109,6 +121,8 @@ func acquireLockRank(rank lockRank) { // checkRanks checks if goroutine g, which has mostly recently acquired a lock // with rank 'prevRank', can now acquire a lock with rank 'rank'. +// +//go:systemstack func checkRanks(gp *g, prevRank, rank lockRank) { rankOK := false if rank < prevRank { @@ -135,9 +149,7 @@ func checkRanks(gp *g, prevRank, rank lockRank) { if !rankOK { printlock() println(gp.m.procid, " ======") - for j, held := range gp.m.locksHeld[:gp.m.locksHeldLen] { - println(j, ":", held.rank.String(), held.rank, unsafe.Pointer(gp.m.locksHeld[j].lockAddr)) - } + printHeldLocks(gp) throw("lock ordering problem") } } @@ -212,3 +224,55 @@ func lockWithRankMayAcquire(l *mutex, rank lockRank) { gp.m.locksHeldLen-- }) } + +//go:systemstack +func checkLockHeld(gp *g, l *mutex) bool { + for i := gp.m.locksHeldLen - 1; i >= 0; i-- { + if gp.m.locksHeld[i].lockAddr == uintptr(unsafe.Pointer(l)) { + return true + } + } + return false +} + +// assertLockHeld throws if l is not held by the caller. +// +// nosplit to ensure it can be called in as many contexts as possible. +//go:nosplit +func assertLockHeld(l *mutex) { + gp := getg() + + systemstack(func() { + held := checkLockHeld(gp, l) + if !held { + printlock() + print("caller requires lock ", l, " (rank ", l.rank.String(), "), holding:\n") + printHeldLocks(gp) + throw("not holding required lock!") + } + }) +} + +// assertRankHeld throws if a mutex with rank r is not held by the caller. +// +// This is less precise than assertLockHeld, but can be used in places where a +// pointer to the exact mutex is not available. +// +// nosplit to ensure it can be called in as many contexts as possible. +//go:nosplit +func assertRankHeld(r lockRank) { + gp := getg() + + systemstack(func() { + for i := gp.m.locksHeldLen - 1; i >= 0; i-- { + if gp.m.locksHeld[i].rank == r { + return + } + } + + printlock() + print("caller requires lock with rank ", r.String(), "), holding:\n") + printHeldLocks(gp) + throw("not holding required lock!") + }) +} -- 2.48.1