-
Notifications
You must be signed in to change notification settings - Fork 66
Implement Concurrency9 package #819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
nicolaswill
merged 8 commits into
main
from
michaelrfairhurst/implement-concurrency9-package
Mar 27, 2025
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
713c675
Implement Concurrency9 package on top of Concurrency8 work
MichaelRFairhurst 42adff5
Formatting & json validation
MichaelRFairhurst 96171ee
cpp format
MichaelRFairhurst 8553103
regenerate query metadata
MichaelRFairhurst e1a0d6d
Merge branch 'main' into michaelrfairhurst/implement-concurrency9-pac…
nicolaswill dae8162
Address feedback
MichaelRFairhurst 00defdd
Fix typo
MichaelRFairhurst 37caf0e
Merge remote-tracking branch 'origin/main' into michaelrfairhurst/imp…
MichaelRFairhurst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import codingstandards.c.Objects | ||
|
||
newtype TSubObject = | ||
TObjectRoot(ObjectIdentity i) or | ||
TObjectMember(SubObject struct, MemberVariable m) { | ||
m = struct.getType().(Struct).getAMemberVariable() | ||
} or | ||
TObjectIndex(SubObject array) { array.getType() instanceof ArrayType } | ||
nicolaswill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
class SubObject extends TSubObject { | ||
string toString() { | ||
exists(ObjectIdentity i | | ||
this = TObjectRoot(i) and | ||
result = i.toString() | ||
) | ||
or | ||
exists(SubObject struct, Variable m | | ||
this = TObjectMember(struct, m) and | ||
result = struct.toString() + "." + m.getName() | ||
) | ||
or | ||
exists(SubObject array | | ||
this = TObjectIndex(array) and | ||
result = array.toString() | ||
) | ||
} | ||
|
||
Type getType() { | ||
exists(ObjectIdentity i | | ||
this = TObjectRoot(i) and | ||
result = i.getType() | ||
) | ||
or | ||
exists(Variable m | | ||
this = TObjectMember(_, m) and | ||
result = m.getType() | ||
) | ||
or | ||
exists(SubObject array | | ||
this = TObjectIndex(array) and | ||
result = array.getType().(ArrayType).getBaseType() | ||
) | ||
} | ||
|
||
/** | ||
* Holds for object roots and for member accesses on that root, not for array accesses. | ||
* | ||
* This is useful for cases where we do not wish to treat `x[y]` and `x[z]` as the same object. | ||
*/ | ||
predicate isPrecise() { not getParent*() = TObjectIndex(_) } | ||
|
||
SubObject getParent() { | ||
exists(SubObject struct, MemberVariable m | | ||
this = TObjectMember(struct, m) and | ||
result = struct | ||
) | ||
or | ||
exists(SubObject array | | ||
this = TObjectIndex(array) and | ||
result = array | ||
) | ||
} | ||
|
||
Expr getAnAccess() { | ||
exists(ObjectIdentity i | | ||
this = TObjectRoot(i) and | ||
result = i.getAnAccess() | ||
) | ||
or | ||
exists(MemberVariable m | | ||
this = TObjectMember(_, m) and | ||
result = m.getAnAccess() and | ||
result.(DotFieldAccess).getQualifier() = getParent().getAnAccess() | ||
nicolaswill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
or | ||
this = TObjectIndex(_) and | ||
result.(ArrayExpr).getArrayBase() = getParent().getAnAccess() | ||
} | ||
|
||
AddressOfExpr getAnAddressOfExpr() { result.getOperand() = this.getAnAccess() } | ||
|
||
ObjectIdentity getRootIdentity() { | ||
exists(ObjectIdentity i | | ||
this = TObjectRoot(i) and | ||
result = i | ||
) | ||
or | ||
result = getParent().getRootIdentity() | ||
nicolaswill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
93 changes: 93 additions & 0 deletions
93
c/common/src/codingstandards/c/initialization/GlobalInitializationAnalysis.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import cpp | ||
import codingstandards.c.Objects | ||
import codingstandards.cpp.Concurrency | ||
import codingstandards.cpp.Type | ||
|
||
signature module GlobalInitializationAnalysisConfigSig { | ||
/** A function which is not called or started as a thread */ | ||
default predicate isRootFunction(Function f) { | ||
not exists(Function f2 | f2.calls(f)) and | ||
nicolaswill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
not f instanceof ThreadedFunction | ||
} | ||
|
||
ObjectIdentity getAnInitializedObject(Expr e); | ||
|
||
ObjectIdentity getAUsedObject(Expr e); | ||
} | ||
|
||
module GlobalInitalizationAnalysis<GlobalInitializationAnalysisConfigSig Config> { | ||
final class FinalFunction = Function; | ||
|
||
final class FinalExpr = Expr; | ||
|
||
class RootFunction extends FinalFunction { | ||
RootFunction() { Config::isRootFunction(this) } | ||
} | ||
|
||
/** A function call which initializes a mutex or a condition */ | ||
class ObjectInit extends FinalExpr { | ||
ObjectIdentity owningObject; | ||
|
||
ObjectInit() { owningObject = Config::getAnInitializedObject(this) } | ||
|
||
ObjectIdentity getOwningObject() { result = owningObject } | ||
} | ||
|
||
/** | ||
* A function argument where that argument is used as a mutex or condition object. | ||
*/ | ||
class ObjectUse extends FinalExpr { | ||
ObjectIdentity owningObject; | ||
|
||
ObjectUse() { owningObject = Config::getAUsedObject(this) } | ||
|
||
ObjectIdentity getOwningObject() { result = owningObject } | ||
} | ||
|
||
predicate requiresInitializedMutexObject( | ||
Function func, ObjectUse mutexUse, ObjectIdentity owningObject | ||
) { | ||
mutexUse.getEnclosingFunction() = func and | ||
owningObject = mutexUse.getOwningObject() and | ||
not exists(ObjectInit init | | ||
init.getEnclosingFunction() = func and | ||
init.getOwningObject() = owningObject and | ||
mutexUse.getAPredecessor+() = init | ||
) | ||
or | ||
exists(FunctionCall call | | ||
func = call.getEnclosingFunction() and | ||
requiresInitializedMutexObject(call.getTarget(), mutexUse, owningObject) and | ||
not exists(ObjectInit init | | ||
call.getAPredecessor*() = init and | ||
init.getOwningObject() = owningObject | ||
) | ||
) | ||
or | ||
exists(C11ThreadCreateCall call | | ||
func = call.getEnclosingFunction() and | ||
not owningObject.getStorageDuration().isThread() and | ||
requiresInitializedMutexObject(call.getFunction(), mutexUse, owningObject) and | ||
not exists(ObjectInit init | | ||
call.getAPredecessor*() = init and | ||
init.getOwningObject() = owningObject | ||
) | ||
) | ||
} | ||
|
||
predicate uninitializedFrom(Expr e, ObjectIdentity obj, Function callRoot) { | ||
exists(ObjectUse use | use = e | | ||
obj = use.getOwningObject() and | ||
requiresInitializedMutexObject(callRoot, use, obj) and | ||
( | ||
if obj.getStorageDuration().isAutomatic() | ||
then obj.getEnclosingElement+() = callRoot | ||
else ( | ||
obj.getStorageDuration().isThread() and callRoot instanceof ThreadedFunction | ||
or | ||
callRoot instanceof RootFunction | ||
) | ||
) | ||
) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
158 changes: 158 additions & 0 deletions
158
c/misra/src/rules/DIR-5-1/PossibleDataRaceBetweenThreads.ql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/** | ||
* @id c/misra/possible-data-race-between-threads | ||
* @name DIR-5-1: There shall be no data races between threads | ||
* @description Threads shall not access the same memory location concurrently without utilization | ||
* of thread synchronization objects. | ||
* @kind problem | ||
* @precision medium | ||
* @problem.severity error | ||
* @tags external/misra/id/dir-5-1 | ||
* correctness | ||
* concurrency | ||
* external/misra/c/2012/amendment4 | ||
* external/misra/obligation/required | ||
*/ | ||
|
||
import cpp | ||
import codingstandards.c.misra | ||
import codingstandards.c.Objects | ||
import codingstandards.c.SubObjects | ||
import codingstandards.cpp.Concurrency | ||
|
||
newtype TNonReentrantOperation = | ||
TReadWrite(SubObject object) { | ||
object.getRootIdentity().getStorageDuration().isStatic() | ||
or | ||
object.getRootIdentity().getStorageDuration().isAllocated() | ||
} or | ||
TStdFunctionCall(FunctionCall call) { | ||
call.getTarget() | ||
.hasName([ | ||
"setlocale", "tmpnam", "rand", "srand", "getenv", "getenv_s", "strok", "strerror", | ||
"asctime", "ctime", "gmtime", "localtime", "mbrtoc16", "c16rtomb", "mbrtoc32", | ||
"c32rtomb", "mbrlen", "mbrtowc", "wcrtomb", "mbsrtowcs", "wcsrtombs" | ||
]) | ||
} | ||
|
||
class NonReentrantOperation extends TNonReentrantOperation { | ||
string toString() { | ||
exists(SubObject object | | ||
this = TReadWrite(object) and | ||
result = object.toString() | ||
) | ||
or | ||
exists(FunctionCall call | | ||
this = TStdFunctionCall(call) and | ||
result = call.getTarget().getName() | ||
) | ||
} | ||
|
||
Expr getARead() { | ||
exists(SubObject object | | ||
this = TReadWrite(object) and | ||
result = object.getAnAccess() | ||
) | ||
or | ||
this = TStdFunctionCall(result) | ||
} | ||
|
||
Expr getAWrite() { | ||
exists(SubObject object, Assignment assignment | | ||
this = TReadWrite(object) and | ||
result = assignment and | ||
assignment.getLValue() = object.getAnAccess() | ||
) | ||
or | ||
this = TStdFunctionCall(result) | ||
} | ||
|
||
string getReadString() { | ||
this = TReadWrite(_) and | ||
result = "read operation" | ||
or | ||
this = TStdFunctionCall(_) and | ||
result = "call to non-reentrant function" | ||
} | ||
|
||
string getWriteString() { | ||
this = TReadWrite(_) and | ||
result = "write to object" | ||
or | ||
this = TStdFunctionCall(_) and | ||
result = "call to non-reentrant function" | ||
} | ||
|
||
Element getSourceElement() { | ||
exists(SubObject object | | ||
this = TReadWrite(object) and | ||
result = object.getRootIdentity() | ||
) | ||
or | ||
this = TStdFunctionCall(result) | ||
} | ||
} | ||
|
||
class WritingThread extends ThreadedFunction { | ||
NonReentrantOperation aWriteObject; | ||
Expr aWrite; | ||
|
||
WritingThread() { | ||
aWrite = aWriteObject.getAWrite() and | ||
this.calls*(aWrite.getEnclosingFunction()) and | ||
MichaelRFairhurst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
not aWrite instanceof LockProtectedControlFlowNode and | ||
not aWrite.getEnclosingFunction().getName().matches(["%init%", "%boot%", "%start%"]) | ||
} | ||
|
||
Expr getAWrite() { result = aWrite } | ||
} | ||
|
||
class ReadingThread extends ThreadedFunction { | ||
Expr aReadExpr; | ||
|
||
ReadingThread() { | ||
exists(NonReentrantOperation op | | ||
aReadExpr = op.getARead() and | ||
this.calls*(aReadExpr.getEnclosingFunction()) and | ||
not aReadExpr instanceof LockProtectedControlFlowNode | ||
) | ||
} | ||
|
||
Expr getARead() { result = aReadExpr } | ||
} | ||
|
||
predicate mayBeDataRace(Expr write, Expr read, NonReentrantOperation operation) { | ||
exists(WritingThread wt | | ||
wt.getAWrite() = write and | ||
write = operation.getAWrite() and | ||
exists(ReadingThread rt | | ||
read = rt.getARead() and | ||
read = operation.getARead() and | ||
( | ||
wt.isMultiplySpawned() or | ||
not wt = rt | ||
) | ||
) | ||
) | ||
} | ||
|
||
from | ||
WritingThread wt, ReadingThread rt, Expr write, Expr read, NonReentrantOperation operation, | ||
string message, string writeString, string readString | ||
where | ||
not isExcluded(write, Concurrency9Package::possibleDataRaceBetweenThreadsQuery()) and | ||
mayBeDataRace(write, read, operation) and | ||
wt = min(WritingThread f | f.getAWrite() = write | f order by f.getName()) and | ||
rt = min(ReadingThread f | f.getARead() = read | f order by f.getName()) and | ||
writeString = operation.getWriteString() and | ||
readString = operation.getReadString() and | ||
if wt.isMultiplySpawned() | ||
then | ||
message = | ||
"Threaded " + writeString + | ||
" $@ not synchronized, for example from thread function $@ spawned from a loop." | ||
MichaelRFairhurst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else | ||
message = | ||
"Threaded " + writeString + | ||
" $@, for example from thread function $@, not synchronized with $@, for example from thread function $@." | ||
MichaelRFairhurst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
select write, message, operation.getSourceElement(), operation.toString(), wt, wt.getName(), read, | ||
"concurrent " + readString, rt, rt.getName() |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.