mirror of
https://github.com/github/codeql.git
synced 2026-03-16 04:26:47 +01:00
86 lines
2.8 KiB
Plaintext
86 lines
2.8 KiB
Plaintext
/**
|
|
* @name Lock may not be released
|
|
* @description A lock that is acquired one or more times without a
|
|
* matching number of unlocks may cause a deadlock.
|
|
* @kind problem
|
|
* @id cpp/unreleased-lock
|
|
* @problem.severity error
|
|
* @security-severity 5.0
|
|
* @precision low
|
|
* @tags security
|
|
* external/cwe/cwe-764
|
|
* external/cwe/cwe-833
|
|
*/
|
|
|
|
import cpp
|
|
import semmle.code.cpp.commons.Synchronization
|
|
|
|
predicate lockBlock(MutexType t, BasicBlock b, int locks) {
|
|
locks = strictcount(int i | b.getNode(i) = t.getLockAccess())
|
|
}
|
|
|
|
predicate unlockBlock(MutexType t, BasicBlock b, int unlocks) {
|
|
unlocks = strictcount(int i | b.getNode(i) = t.getUnlockAccess())
|
|
}
|
|
|
|
/**
|
|
* Holds if there is a call to `lock` or `tryLock` on `t` in
|
|
* `lockblock`, and `failblock` is the successor if it fails.
|
|
*/
|
|
predicate failedLock(MutexType t, BasicBlock lockblock, BasicBlock failblock) {
|
|
exists(ControlFlowNode lock |
|
|
lock = lockblock.getEnd() and
|
|
lock = t.getLockAccess() and
|
|
lock.getAFalseSuccessor() = failblock
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `b` locks `t` a net `netlocks` times. For example, if `b`
|
|
* locks `t` twice and unlocks `t` four times, then `netlocks` will be
|
|
* `-2`.
|
|
*/
|
|
predicate lockUnlockBlock(MutexType t, BasicBlock b, int netlocks) {
|
|
lockBlock(t, b, netlocks) and not unlockBlock(t, b, _)
|
|
or
|
|
exists(int unlocks |
|
|
not lockBlock(t, b, _) and unlockBlock(t, b, unlocks) and netlocks = -unlocks
|
|
)
|
|
or
|
|
exists(int locks, int unlocks |
|
|
lockBlock(t, b, locks) and unlockBlock(t, b, unlocks) and netlocks = locks - unlocks
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if there is a control flow path from `src` to `b` such that
|
|
* on that path the net number of locks is `locks`, and `locks` is
|
|
* positive.
|
|
*/
|
|
predicate blockIsLocked(MutexType t, BasicBlock src, BasicBlock b, int locks) {
|
|
lockUnlockBlock(t, b, locks) and src = b and locks > 0
|
|
or
|
|
exists(BasicBlock pred, int predlocks, int curlocks, int failedlock | pred = b.getAPredecessor() |
|
|
blockIsLocked(t, src, pred, predlocks) and
|
|
(if failedLock(t, pred, b) then failedlock = 1 else failedlock = 0) and // count a failed lock as an unlock so the net is zero
|
|
(
|
|
not lockUnlockBlock(t, b, _) and curlocks = 0
|
|
or
|
|
lockUnlockBlock(t, b, curlocks)
|
|
) and
|
|
locks = predlocks + curlocks - failedlock and
|
|
locks > 0 and
|
|
locks < 10 // arbitrary bound to fail gracefully in case of locking in a loop
|
|
)
|
|
}
|
|
|
|
from Function c, MutexType t, BasicBlock src, BasicBlock exit, FunctionCall lock
|
|
where
|
|
// restrict results to those methods that actually attempt to unlock
|
|
t.getUnlockAccess().getEnclosingFunction() = c and
|
|
blockIsLocked(t, src, exit, _) and
|
|
exit.getEnd() = c and
|
|
lock = src.getANode() and
|
|
lock = t.getLockAccess()
|
|
select lock, "This lock might not be unlocked or might be locked more times than it is unlocked."
|