mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge remote-tracking branch 'upstream/master' into MissingEnumCaseInSwitch-perf
This commit is contained in:
@@ -10,12 +10,25 @@ import cpp
|
||||
/**
|
||||
* An alert suppression comment.
|
||||
*/
|
||||
class SuppressionComment extends CppStyleComment {
|
||||
class SuppressionComment extends Comment {
|
||||
string annotation;
|
||||
string text;
|
||||
|
||||
SuppressionComment() {
|
||||
text = getContents().suffix(2) and
|
||||
(
|
||||
this instanceof CppStyleComment and
|
||||
// strip the beginning slashes
|
||||
text = getContents().suffix(2)
|
||||
or
|
||||
this instanceof CStyleComment and
|
||||
// strip both the beginning /* and the end */ the comment
|
||||
exists(string text0 |
|
||||
text0 = getContents().suffix(2) and
|
||||
text = text0.prefix(text0.length() - 2)
|
||||
) and
|
||||
// The /* */ comment must be a single-line comment
|
||||
not text.matches("%\n%")
|
||||
) and
|
||||
(
|
||||
// match `lgtm[...]` anywhere in the comment
|
||||
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description A function that uses more functions and variables from another file than functions and variables from its own file. This function might be better placed in the other file, to avoid exposing internals of the file it depends on.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/feature-envy
|
||||
* @tags maintainability
|
||||
* modularity
|
||||
@@ -25,7 +25,8 @@ predicate functionUsesFunction(Function source, Function f, File target) {
|
||||
}
|
||||
|
||||
predicate dependencyCount(Function source, File target, int res) {
|
||||
res = strictcount(Declaration d |
|
||||
res =
|
||||
strictcount(Declaration d |
|
||||
functionUsesVariable(source, d, target) or
|
||||
functionUsesFunction(source, d, target)
|
||||
)
|
||||
|
||||
@@ -38,14 +38,16 @@ where
|
||||
n = count(Function f | f.fromSource()).toString()
|
||||
or
|
||||
l = "Number of Lines Of Code" and
|
||||
n = sum(File f, int toSum |
|
||||
n =
|
||||
sum(File f, int toSum |
|
||||
f.fromSource() and toSum = f.getMetrics().getNumberOfLinesOfCode()
|
||||
|
|
||||
toSum
|
||||
).toString()
|
||||
or
|
||||
l = "Self-Containedness" and
|
||||
n = (
|
||||
n =
|
||||
(
|
||||
100 * sum(Class c | c.fromSource() | c.getMetrics().getEfferentSourceCoupling()) /
|
||||
sum(Class c | c.fromSource() | c.getMetrics().getEfferentCoupling())
|
||||
).toString() + "%"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Two files share too much information about each other (accessing many operations or variables in both directions). It would be better to invert some of the dependencies to reduce the coupling between the two files.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/file-intimacy
|
||||
* @tags maintainability
|
||||
* modularity
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Finds classes with many fields; they could probably be refactored by breaking them down into smaller classes, and using composition.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/class-many-fields
|
||||
* @tags maintainability
|
||||
* statistical
|
||||
@@ -80,11 +80,8 @@ class VariableDeclarationLine extends TVariableDeclarationInfo {
|
||||
* (that is, the first is 0, the second is 1 and so on).
|
||||
*/
|
||||
private int getRank() {
|
||||
line = rank[result](VariableDeclarationLine vdl, int l |
|
||||
vdl = TVariableDeclarationLine(c, f, l)
|
||||
|
|
||||
l
|
||||
)
|
||||
line =
|
||||
rank[result](VariableDeclarationLine vdl, int l | vdl = TVariableDeclarationLine(c, f, l) | l)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +130,8 @@ class VariableDeclarationGroup extends VariableDeclarationLine {
|
||||
* Gets the number of uniquely named `VariableDeclarationEntry`s in this group.
|
||||
*/
|
||||
int getCount() {
|
||||
result = count(VariableDeclarationLine l |
|
||||
result =
|
||||
count(VariableDeclarationLine l |
|
||||
l = getProximateNext*()
|
||||
|
|
||||
l.getAVDE().getVariable().getName()
|
||||
@@ -166,7 +164,8 @@ class ExtClass extends Class {
|
||||
|
||||
from ExtClass c, int n, VariableDeclarationGroup vdg, string suffix
|
||||
where
|
||||
n = strictcount(string fieldName |
|
||||
n =
|
||||
strictcount(string fieldName |
|
||||
exists(Field f |
|
||||
f.getDeclaringType() = c and
|
||||
fieldName = f.getName() and
|
||||
|
||||
@@ -50,7 +50,8 @@ class BlockOrNonChild extends Element {
|
||||
|
||||
private int getNonContiguousStartRankIn(AffectedFile file) {
|
||||
// When using `rank` with `order by`, the ranks may not be contiguous.
|
||||
this = rank[result](BlockOrNonChild boc, int startLine, int startCol |
|
||||
this =
|
||||
rank[result](BlockOrNonChild boc, int startLine, int startCol |
|
||||
boc.getLocation().hasLocationInfo(file.getAbsolutePath(), startLine, startCol, _, _)
|
||||
|
|
||||
boc order by startLine, startCol
|
||||
@@ -58,13 +59,15 @@ class BlockOrNonChild extends Element {
|
||||
}
|
||||
|
||||
int getStartRankIn(AffectedFile file) {
|
||||
this.getNonContiguousStartRankIn(file) = rank[result](int rnk |
|
||||
this.getNonContiguousStartRankIn(file) =
|
||||
rank[result](int rnk |
|
||||
exists(BlockOrNonChild boc | boc.getNonContiguousStartRankIn(file) = rnk)
|
||||
)
|
||||
}
|
||||
|
||||
int getNonContiguousEndRankIn(AffectedFile file) {
|
||||
this = rank[result](BlockOrNonChild boc, int endLine, int endCol |
|
||||
this =
|
||||
rank[result](BlockOrNonChild boc, int endLine, int endCol |
|
||||
boc.getLocation().hasLocationInfo(file.getAbsolutePath(), _, _, endLine, endCol)
|
||||
|
|
||||
boc order by endLine, endCol
|
||||
@@ -79,9 +82,8 @@ predicate emptyBlockContainsNonchild(Block b) {
|
||||
emptyBlock(_, b) and
|
||||
exists(BlockOrNonChild c, AffectedFile file |
|
||||
c.(BlockOrNonChild).getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and
|
||||
c.(BlockOrNonChild).getNonContiguousEndRankIn(file) < b
|
||||
.(BlockOrNonChild)
|
||||
.getNonContiguousEndRankIn(file)
|
||||
c.(BlockOrNonChild).getNonContiguousEndRankIn(file) <
|
||||
b.(BlockOrNonChild).getNonContiguousEndRankIn(file)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id cpp/japanese-era/exact-era-date
|
||||
* @precision medium
|
||||
* @tags reliability
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
* reliability
|
||||
* japanese-era
|
||||
*/
|
||||
|
||||
|
||||
@@ -307,7 +307,8 @@ predicate nonTrivialValue(string value, Literal literal) {
|
||||
}
|
||||
|
||||
predicate valueOccurrenceCount(string value, int n) {
|
||||
n = strictcount(Location loc |
|
||||
n =
|
||||
strictcount(Location loc |
|
||||
exists(Literal lit | lit.getLocation() = loc | nonTrivialValue(value, lit)) and
|
||||
// Exclude generated files (they do not have the same maintainability
|
||||
// concerns as ordinary source files)
|
||||
@@ -338,7 +339,8 @@ predicate check(Literal lit, string value, int n, File f) {
|
||||
}
|
||||
|
||||
predicate checkWithFileCount(string value, int overallCount, int fileCount, File f) {
|
||||
fileCount = strictcount(Location loc |
|
||||
fileCount =
|
||||
strictcount(Location loc |
|
||||
exists(Literal lit | lit.getLocation() = loc | check(lit, value, overallCount, f))
|
||||
)
|
||||
}
|
||||
@@ -364,7 +366,8 @@ predicate firstOccurrence(Literal lit, string value, int n) {
|
||||
predicate magicConstant(Literal e, string msg) {
|
||||
exists(string value, int n |
|
||||
firstOccurrence(e, value, n) and
|
||||
msg = "Magic constant: literal '" + value + "' is repeated " + n.toString() +
|
||||
msg =
|
||||
"Magic constant: literal '" + value + "' is repeated " + n.toString() +
|
||||
" times and should be encapsulated in a constant."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,13 +28,15 @@ import cpp
|
||||
// design question and carries has no safety risk.
|
||||
predicate generatedCopyAssignment(CopyConstructor cc, string msg) {
|
||||
cc.getDeclaringType().hasImplicitCopyAssignmentOperator() and
|
||||
msg = "No matching copy assignment operator in class " + cc.getDeclaringType().getName() +
|
||||
msg =
|
||||
"No matching copy assignment operator in class " + cc.getDeclaringType().getName() +
|
||||
". It is good practice to match a copy constructor with a " + "copy assignment operator."
|
||||
}
|
||||
|
||||
predicate generatedCopyConstructor(CopyAssignmentOperator ca, string msg) {
|
||||
ca.getDeclaringType().hasImplicitCopyConstructor() and
|
||||
msg = "No matching copy constructor in class " + ca.getDeclaringType().getName() +
|
||||
msg =
|
||||
"No matching copy constructor in class " + ca.getDeclaringType().getName() +
|
||||
". It is good practice to match a copy assignment operator with a " + "copy constructor."
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ predicate testAndBranch(Expr e, Stmt branch) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate choice(LocalScopeVariable v, Stmt branch, string value) {
|
||||
predicate choice(StackVariable v, Stmt branch, string value) {
|
||||
exists(AnalysedExpr e |
|
||||
testAndBranch(e, branch) and
|
||||
(
|
||||
@@ -33,7 +33,7 @@ predicate choice(LocalScopeVariable v, Stmt branch, string value) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate guarded(LocalScopeVariable v, Stmt loopstart, AnalysedExpr child) {
|
||||
predicate guarded(StackVariable v, Stmt loopstart, AnalysedExpr child) {
|
||||
choice(v, loopstart, _) and
|
||||
loopstart.getChildStmt*() = child.getEnclosingStmt() and
|
||||
(definition(v, child) or exists(child.getNullSuccessor(v)))
|
||||
@@ -47,9 +47,7 @@ predicate addressLeak(Variable v, Stmt leak) {
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
LocalScopeVariable v, Stmt branch, AnalysedExpr cond, string context, string test,
|
||||
string testresult
|
||||
from StackVariable v, Stmt branch, AnalysedExpr cond, string context, string test, string testresult
|
||||
where
|
||||
choice(v, branch, context) and
|
||||
forall(ControlFlowNode def | definition(v, def) and definitionReaches(def, cond) |
|
||||
|
||||
@@ -23,14 +23,14 @@ predicate closeCall(FunctionCall fc, Variable v) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate openDefinition(LocalScopeVariable v, ControlFlowNode def) {
|
||||
predicate openDefinition(StackVariable v, ControlFlowNode def) {
|
||||
exists(Expr expr | exprDefinition(v, def, expr) and allocateDescriptorCall(expr))
|
||||
}
|
||||
|
||||
predicate openReaches(ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(LocalScopeVariable v | openDefinition(v, def) and node = def.getASuccessor())
|
||||
exists(StackVariable v | openDefinition(v, def) and node = def.getASuccessor())
|
||||
or
|
||||
exists(LocalScopeVariable v, ControlFlowNode mid |
|
||||
exists(StackVariable v, ControlFlowNode mid |
|
||||
openDefinition(v, def) and
|
||||
openReaches(def, mid) and
|
||||
not errorSuccessor(v, mid) and
|
||||
@@ -40,7 +40,7 @@ predicate openReaches(ControlFlowNode def, ControlFlowNode node) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Assignment assign) {
|
||||
predicate assignedToFieldOrGlobal(StackVariable v, Assignment assign) {
|
||||
exists(Variable external |
|
||||
assign.getRValue() = v.getAnAccess() and
|
||||
assign.getLValue().(VariableAccess).getTarget() = external and
|
||||
@@ -48,7 +48,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Assignment assign) {
|
||||
)
|
||||
}
|
||||
|
||||
from LocalScopeVariable v, ControlFlowNode def, ReturnStmt ret
|
||||
from StackVariable v, ControlFlowNode def, ReturnStmt ret
|
||||
where
|
||||
openDefinition(v, def) and
|
||||
openReaches(def, ret) and
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds calls to <code>open</code> or <code>socket</code> where there is no corresponding <code>close</code> call in the program analyzed.
|
||||
This rule finds calls to <code>socket</code> where there is no corresponding <code>close</code> call in the program analyzed.
|
||||
Leaving descriptors open will cause a resource leak that will persist even after the program terminates.
|
||||
</p>
|
||||
|
||||
@@ -14,7 +14,7 @@ Leaving descriptors open will cause a resource leak that will persist even after
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Ensure that all file or socket descriptors allocated by the program are freed before it terminates.</p>
|
||||
<p>Ensure that all socket descriptors allocated by the program are freed before it terminates.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Open descriptor never closed
|
||||
* @description Functions that always return before closing the socket or file they opened leak resources.
|
||||
* @description Functions that always return before closing the socket they opened leak resources.
|
||||
* @kind problem
|
||||
* @id cpp/descriptor-never-closed
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import FileClosed
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/**
|
||||
* Extend the NullValue class used by Nullness.qll to include simple -1 as a 'null' value
|
||||
@@ -68,18 +68,18 @@ predicate fcloseCallOrIndirect(FunctionCall fc, Variable v) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate fopenDefinition(LocalScopeVariable v, ControlFlowNode def) {
|
||||
predicate fopenDefinition(StackVariable v, ControlFlowNode def) {
|
||||
exists(Expr expr | exprDefinition(v, def, expr) and fopenCallOrIndirect(expr))
|
||||
}
|
||||
|
||||
class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassignment {
|
||||
class FOpenVariableReachability extends StackVariableReachabilityWithReassignment {
|
||||
FOpenVariableReachability() { this = "FOpenVariableReachability" }
|
||||
|
||||
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
|
||||
fopenDefinition(v, node)
|
||||
}
|
||||
|
||||
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
|
||||
// node may be used in fopenReaches
|
||||
exists(node.(AnalysedExpr).getNullSuccessor(v)) or
|
||||
fcloseCallOrIndirect(node, v) or
|
||||
@@ -88,15 +88,13 @@ class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassi
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
definitionBarrier(v, node)
|
||||
}
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) { definitionBarrier(v, node) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value from fopen at `def` is still held in Variable `v` upon entering `node`.
|
||||
*/
|
||||
predicate fopenVariableReaches(LocalScopeVariable v, ControlFlowNode def, ControlFlowNode node) {
|
||||
predicate fopenVariableReaches(StackVariable v, ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(FOpenVariableReachability r |
|
||||
// reachability
|
||||
r.reachesTo(def, _, node, v)
|
||||
@@ -107,25 +105,23 @@ predicate fopenVariableReaches(LocalScopeVariable v, ControlFlowNode def, Contro
|
||||
)
|
||||
}
|
||||
|
||||
class FOpenReachability extends LocalScopeVariableReachabilityExt {
|
||||
class FOpenReachability extends StackVariableReachabilityExt {
|
||||
FOpenReachability() { this = "FOpenReachability" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
fopenDefinition(v, node)
|
||||
}
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) { fopenDefinition(v, node) }
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) {
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
||||
override predicate isBarrier(
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v
|
||||
) {
|
||||
isSource(source, v) and
|
||||
next = node.getASuccessor() and
|
||||
// the file (stored in any variable `v0`) opened at `source` is closed or
|
||||
// assigned to a global at node, or NULL checked on the edge node -> next.
|
||||
exists(LocalScopeVariable v0 | fopenVariableReaches(v0, source, node) |
|
||||
exists(StackVariable v0 | fopenVariableReaches(v0, source, node) |
|
||||
node.(AnalysedExpr).getNullSuccessor(v0) = next or
|
||||
fcloseCallOrIndirect(node, v0) or
|
||||
assignedToFieldOrGlobal(v0, node)
|
||||
@@ -142,11 +138,11 @@ predicate fopenReaches(ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(FOpenReachability r | r.reaches(def, _, node))
|
||||
}
|
||||
|
||||
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
|
||||
// assigned to anything except a LocalScopeVariable
|
||||
predicate assignedToFieldOrGlobal(StackVariable v, Expr e) {
|
||||
// assigned to anything except a StackVariable
|
||||
// (typically a field or global, but for example also *ptr = v)
|
||||
e.(Assignment).getRValue() = v.getAnAccess() and
|
||||
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable
|
||||
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof StackVariable
|
||||
or
|
||||
exists(Expr midExpr, Function mid, int arg |
|
||||
// indirect assignment
|
||||
@@ -163,7 +159,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
|
||||
from ControlFlowNode def, ReturnStmt ret
|
||||
where
|
||||
fopenReaches(def, ret) and
|
||||
not exists(LocalScopeVariable v |
|
||||
not exists(StackVariable v |
|
||||
fopenVariableReaches(v, def, ret) and
|
||||
ret.getAChild*() = v.getAnAccess()
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from LocalScopeVariable v, ControlFlowNode def, VariableAccess checked, VariableAccess unchecked
|
||||
from StackVariable v, ControlFlowNode def, VariableAccess checked, VariableAccess unchecked
|
||||
where
|
||||
checked = v.getAnAccess() and
|
||||
dereferenced(checked) and
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import cpp
|
||||
|
||||
predicate negativeCheck(LocalScopeVariable v, ComparisonOperation op) {
|
||||
predicate negativeCheck(StackVariable v, ComparisonOperation op) {
|
||||
exists(int varindex, string constant, Literal lit |
|
||||
op.getChild(varindex) = v.getAnAccess() and
|
||||
op.getChild(1 - varindex) = lit and
|
||||
@@ -38,7 +38,7 @@ predicate negativeCheck(LocalScopeVariable v, ComparisonOperation op) {
|
||||
)
|
||||
}
|
||||
|
||||
from LocalScopeVariable v, ArrayExpr dangerous, Expr check
|
||||
from StackVariable v, ArrayExpr dangerous, Expr check
|
||||
where
|
||||
useUsePair(v, dangerous.getArrayOffset(), check.getAChild()) and
|
||||
negativeCheck(v, check) and
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
private predicate freed(Expr e) {
|
||||
exists(FunctionCall fc, Expr arg |
|
||||
freeCall(fc, arg) and
|
||||
arg = e
|
||||
)
|
||||
or
|
||||
exists(DeleteExpr de | de.getExpr() = e)
|
||||
or
|
||||
exists(DeleteArrayExpr de | de.getExpr() = e)
|
||||
e = any(DeallocationExpr de).getFreedExpr()
|
||||
or
|
||||
exists(ExprCall c |
|
||||
// cautiously assume that any ExprCall could be a freeCall.
|
||||
@@ -22,7 +15,4 @@ class FreedExpr extends PointsToExpr {
|
||||
override predicate interesting() { freed(this) }
|
||||
}
|
||||
|
||||
predicate allocMayBeFreed(Expr alloc) {
|
||||
isAllocationExpr(alloc) and
|
||||
anythingPointsTo(alloc)
|
||||
}
|
||||
predicate allocMayBeFreed(AllocationExpr alloc) { anythingPointsTo(alloc) }
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import MemoryFreed
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/**
|
||||
* 'call' is either a direct call to f, or a possible call to f
|
||||
@@ -24,7 +24,7 @@ predicate mayCallFunction(Expr call, Function f) {
|
||||
|
||||
predicate allocCallOrIndirect(Expr e) {
|
||||
// direct alloc call
|
||||
isAllocationExpr(e) and
|
||||
e.(AllocationExpr).requiresDealloc() and
|
||||
// We are only interested in alloc calls that are
|
||||
// actually freed somehow, as MemoryNeverFreed
|
||||
// will catch those that aren't.
|
||||
@@ -53,8 +53,7 @@ predicate allocCallOrIndirect(Expr e) {
|
||||
* can cause memory leaks.
|
||||
*/
|
||||
predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) {
|
||||
reallocCall.getTarget().hasGlobalOrStdName("realloc") and
|
||||
reallocCall.getArgument(0) = v.getAnAccess() and
|
||||
reallocCall.(AllocationExpr).getReallocPtr() = v.getAnAccess() and
|
||||
(
|
||||
exists(Variable newV, ControlFlowNode node |
|
||||
// a realloc followed by a null check at 'node' (return the non-null
|
||||
@@ -71,23 +70,19 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
|
||||
or
|
||||
// a realloc(ptr, 0), which always succeeds and frees
|
||||
// (return the realloc itself)
|
||||
reallocCall.getArgument(1).getValue() = "0" and
|
||||
reallocCall.(AllocationExpr).getReallocPtr().getValue() = "0" and
|
||||
verified = reallocCall
|
||||
)
|
||||
}
|
||||
|
||||
predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
|
||||
// direct free call
|
||||
freeCall(n, v.getAnAccess()) and
|
||||
not n.(FunctionCall).getTarget().hasGlobalOrStdName("realloc")
|
||||
n.(DeallocationExpr).getFreedExpr() = v.getAnAccess() and
|
||||
not exists(n.(AllocationExpr).getReallocPtr())
|
||||
or
|
||||
// verified realloc call
|
||||
verifiedRealloc(_, v, n)
|
||||
or
|
||||
n.(DeleteExpr).getExpr() = v.getAnAccess()
|
||||
or
|
||||
n.(DeleteArrayExpr).getExpr() = v.getAnAccess()
|
||||
or
|
||||
exists(FunctionCall midcall, Function mid, int arg |
|
||||
// indirect free call
|
||||
n.(Call).getArgument(arg) = v.getAnAccess() and
|
||||
@@ -97,18 +92,18 @@ predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate allocationDefinition(LocalScopeVariable v, ControlFlowNode def) {
|
||||
predicate allocationDefinition(StackVariable v, ControlFlowNode def) {
|
||||
exists(Expr expr | exprDefinition(v, def, expr) and allocCallOrIndirect(expr))
|
||||
}
|
||||
|
||||
class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassignment {
|
||||
class AllocVariableReachability extends StackVariableReachabilityWithReassignment {
|
||||
AllocVariableReachability() { this = "AllocVariableReachability" }
|
||||
|
||||
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
|
||||
allocationDefinition(v, node)
|
||||
}
|
||||
|
||||
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
|
||||
// node may be used in allocationReaches
|
||||
exists(node.(AnalysedExpr).getNullSuccessor(v)) or
|
||||
freeCallOrIndirect(node, v) or
|
||||
@@ -117,15 +112,13 @@ class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassi
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
definitionBarrier(v, node)
|
||||
}
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) { definitionBarrier(v, node) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value from allocation `def` is still held in Variable `v` upon entering `node`.
|
||||
*/
|
||||
predicate allocatedVariableReaches(LocalScopeVariable v, ControlFlowNode def, ControlFlowNode node) {
|
||||
predicate allocatedVariableReaches(StackVariable v, ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(AllocVariableReachability r |
|
||||
// reachability
|
||||
r.reachesTo(def, _, node, v)
|
||||
@@ -136,25 +129,25 @@ predicate allocatedVariableReaches(LocalScopeVariable v, ControlFlowNode def, Co
|
||||
)
|
||||
}
|
||||
|
||||
class AllocReachability extends LocalScopeVariableReachabilityExt {
|
||||
class AllocReachability extends StackVariableReachabilityExt {
|
||||
AllocReachability() { this = "AllocReachability" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) {
|
||||
allocationDefinition(v, node)
|
||||
}
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) {
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
||||
override predicate isBarrier(
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v
|
||||
) {
|
||||
isSource(source, v) and
|
||||
next = node.getASuccessor() and
|
||||
// the memory (stored in any variable `v0`) allocated at `source` is freed or
|
||||
// assigned to a global at node, or NULL checked on the edge node -> next.
|
||||
exists(LocalScopeVariable v0 | allocatedVariableReaches(v0, source, node) |
|
||||
exists(StackVariable v0 | allocatedVariableReaches(v0, source, node) |
|
||||
node.(AnalysedExpr).getNullSuccessor(v0) = next or
|
||||
freeCallOrIndirect(node, v0) or
|
||||
assignedToFieldOrGlobal(v0, node)
|
||||
@@ -171,11 +164,11 @@ predicate allocationReaches(ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(AllocReachability r | r.reaches(def, _, node))
|
||||
}
|
||||
|
||||
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
|
||||
// assigned to anything except a LocalScopeVariable
|
||||
predicate assignedToFieldOrGlobal(StackVariable v, Expr e) {
|
||||
// assigned to anything except a StackVariable
|
||||
// (typically a field or global, but for example also *ptr = v)
|
||||
e.(Assignment).getRValue() = v.getAnAccess() and
|
||||
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable
|
||||
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof StackVariable
|
||||
or
|
||||
exists(Expr midExpr, Function mid, int arg |
|
||||
// indirect assignment
|
||||
@@ -192,7 +185,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
|
||||
from ControlFlowNode def, ReturnStmt ret
|
||||
where
|
||||
allocationReaches(def, ret) and
|
||||
not exists(LocalScopeVariable v |
|
||||
not exists(StackVariable v |
|
||||
allocatedVariableReaches(v, def, ret) and
|
||||
ret.getAChild*() = v.getAnAccess()
|
||||
)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
import MemoryFreed
|
||||
|
||||
from Expr alloc
|
||||
where isAllocationExpr(alloc) and not allocMayBeFreed(alloc)
|
||||
from AllocationExpr alloc
|
||||
where
|
||||
alloc.requiresDealloc() and
|
||||
not allocMayBeFreed(alloc)
|
||||
select alloc, "This memory is never freed"
|
||||
|
||||
@@ -43,7 +43,7 @@ class FunctionWithNegativeReturn extends Function {
|
||||
predicate dangerousUse(IntegralReturnValue val, Expr use) {
|
||||
exists(ArrayExpr ae | ae.getArrayOffset() = val and use = val)
|
||||
or
|
||||
exists(LocalScopeVariable v, ControlFlowNode def, ArrayExpr ae |
|
||||
exists(StackVariable v, ControlFlowNode def, ArrayExpr ae |
|
||||
exprDefinition(v, def, val) and
|
||||
use = ae.getArrayOffset() and
|
||||
not boundsChecked(v, use) and
|
||||
@@ -54,7 +54,7 @@ predicate dangerousUse(IntegralReturnValue val, Expr use) {
|
||||
val = use and
|
||||
use.getType().getUnderlyingType() instanceof PointerType
|
||||
or
|
||||
exists(LocalScopeVariable v, ControlFlowNode def, AddExpr add |
|
||||
exists(StackVariable v, ControlFlowNode def, AddExpr add |
|
||||
exprDefinition(v, def, val) and
|
||||
definitionUsePair(v, def, use) and
|
||||
add.getAnOperand() = use and
|
||||
|
||||
@@ -60,7 +60,7 @@ predicate allocExprOrIndirect(Expr alloc, string kind) {
|
||||
pragma[nomagic]
|
||||
private predicate allocReachesVariable(Variable v, Expr alloc, string kind) {
|
||||
exists(Expr mid |
|
||||
not v instanceof LocalScopeVariable and
|
||||
not v instanceof StackVariable and
|
||||
v.getAnAssignedValue() = mid and
|
||||
allocReaches0(mid, alloc, kind)
|
||||
)
|
||||
@@ -76,7 +76,7 @@ private predicate allocReaches0(Expr e, Expr alloc, string kind) {
|
||||
allocExprOrIndirect(alloc, kind) and
|
||||
e = alloc
|
||||
or
|
||||
exists(SsaDefinition def, LocalScopeVariable v |
|
||||
exists(SsaDefinition def, StackVariable v |
|
||||
// alloc via SSA
|
||||
allocReaches0(def.getAnUltimateDefiningValue(v), alloc, kind) and
|
||||
e = def.getAUse(v)
|
||||
|
||||
@@ -11,26 +11,16 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
class MallocCall extends FunctionCall {
|
||||
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
|
||||
|
||||
Expr getAllocatedSize() {
|
||||
if this.getArgument(0) instanceof VariableAccess
|
||||
then
|
||||
exists(LocalScopeVariable v, ControlFlowNode def |
|
||||
definitionUsePair(v, def, this.getArgument(0)) and
|
||||
exprDefinition(v, def, result)
|
||||
)
|
||||
else result = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Allocation
|
||||
|
||||
predicate spaceProblem(FunctionCall append, string msg) {
|
||||
exists(MallocCall malloc, StrlenCall strlen, AddExpr add, FunctionCall insert, Variable buffer |
|
||||
exists(
|
||||
AllocationExpr malloc, StrlenCall strlen, AddExpr add, FunctionCall insert, Variable buffer
|
||||
|
|
||||
add.getAChild() = strlen and
|
||||
exists(add.getAChild().getValue()) and
|
||||
malloc.getAllocatedSize() = add and
|
||||
DataFlow::localExprFlow(add, malloc.getSizeExpr()) and
|
||||
buffer.getAnAccess() = strlen.getStringExpr() and
|
||||
(
|
||||
insert.getTarget().hasGlobalOrStdName("strcpy") or
|
||||
@@ -43,7 +33,8 @@ predicate spaceProblem(FunctionCall append, string msg) {
|
||||
malloc.getASuccessor+() = insert and
|
||||
insert.getArgument(1) = buffer.getAnAccess() and
|
||||
insert.getASuccessor+() = append and
|
||||
msg = "This buffer only contains enough room for '" + buffer.getName() + "' (copied on line " +
|
||||
msg =
|
||||
"This buffer only contains enough room for '" + buffer.getName() + "' (copied on line " +
|
||||
insert.getLocation().getStartLine().toString() + ")"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
|
||||
loop.getStmt().getAChild*() = bufaccess.getEnclosingStmt() and
|
||||
loop.limit() >= bufaccess.bufferSize() and
|
||||
loop.counter().getAnAccess() = bufaccess.getArrayOffset() and
|
||||
msg = "Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
|
||||
msg =
|
||||
"Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
|
||||
loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " +
|
||||
bufaccess.bufferSize().toString() + " elements."
|
||||
)
|
||||
@@ -106,8 +107,9 @@ predicate wrongBufferSize(Expr error, string msg) {
|
||||
statedSize = min(call.statedSizeValue()) and
|
||||
statedSize > bufsize and
|
||||
error = call.statedSizeExpr() and
|
||||
msg = "Potential buffer-overflow: '" + buf.getName() + "' has size " + bufsize.toString() +
|
||||
" not " + statedSize + "."
|
||||
msg =
|
||||
"Potential buffer-overflow: '" + buf.getName() + "' has size " + bufsize.toString() + " not " +
|
||||
statedSize + "."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -121,8 +123,9 @@ predicate outOfBounds(BufferAccess bufaccess, string msg) {
|
||||
or
|
||||
access = size and not exists(AddressOfExpr addof | bufaccess = addof.getOperand())
|
||||
) and
|
||||
msg = "Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf +
|
||||
"[" + access.toString() + "]' is accessed here."
|
||||
msg =
|
||||
"Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf + "[" +
|
||||
access.toString() + "]' is accessed here."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,10 @@ class ReturnPointsToExpr extends PointsToExpr {
|
||||
ReturnStmt getReturnStmt() { result.getExpr().getFullyConverted() = this }
|
||||
}
|
||||
|
||||
from ReturnPointsToExpr ret, LocalVariable local, float confidence
|
||||
from ReturnPointsToExpr ret, StackVariable local, float confidence
|
||||
where
|
||||
ret.pointsTo() = local and
|
||||
ret.getReturnStmt().getEnclosingFunction() = local.getFunction() and
|
||||
not local.isStatic() and
|
||||
confidence = ret.confidence() and
|
||||
confidence > 0.01
|
||||
select ret,
|
||||
|
||||
@@ -23,7 +23,8 @@ predicate important(Function f, string message) {
|
||||
predicate dubious(Function f, string message) {
|
||||
not important(f, _) and
|
||||
exists(Options opts, int used, int total, int percentage |
|
||||
used = count(FunctionCall fc |
|
||||
used =
|
||||
count(FunctionCall fc |
|
||||
fc.getTarget() = f and not opts.okToIgnoreReturnValue(fc) and not unused(fc)
|
||||
) and
|
||||
total = count(FunctionCall fc | fc.getTarget() = f and not opts.okToIgnoreReturnValue(fc)) and
|
||||
|
||||
@@ -20,11 +20,10 @@ class ScopeUtilityClass extends Class {
|
||||
Call getAUse() { result = this.getAConstructor().getACallToThisFunction() }
|
||||
}
|
||||
|
||||
from LocalScopeVariable v, ControlFlowNode def
|
||||
from StackVariable v, ControlFlowNode def
|
||||
where
|
||||
definition(v, def) and
|
||||
not definitionUsePair(v, def, _) and
|
||||
not v.isStatic() and
|
||||
not v.getAnAccess().isAddressOfAccess() and
|
||||
// parameter initializers are not in the call-graph at the moment
|
||||
not v.(Parameter).getInitializer().getExpr() = def and
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/** `e` is an expression that frees the memory pointed to by `v`. */
|
||||
predicate isFreeExpr(Expr e, LocalScopeVariable v) {
|
||||
predicate isFreeExpr(Expr e, StackVariable v) {
|
||||
exists(VariableAccess va | va.getTarget() = v |
|
||||
exists(FunctionCall fc | fc = e |
|
||||
fc.getTarget().hasGlobalOrStdName("free") and
|
||||
@@ -27,7 +27,7 @@ predicate isFreeExpr(Expr e, LocalScopeVariable v) {
|
||||
}
|
||||
|
||||
/** `e` is an expression that (may) dereference `v`. */
|
||||
predicate isDerefExpr(Expr e, LocalScopeVariable v) {
|
||||
predicate isDerefExpr(Expr e, StackVariable v) {
|
||||
v.getAnAccess() = e and dereferenced(e)
|
||||
or
|
||||
isDerefByCallExpr(_, _, e, v)
|
||||
@@ -39,27 +39,27 @@ predicate isDerefExpr(Expr e, LocalScopeVariable v) {
|
||||
* or a source code function that dereferences the relevant
|
||||
* parameter.
|
||||
*/
|
||||
predicate isDerefByCallExpr(Call c, int i, VariableAccess va, LocalScopeVariable v) {
|
||||
predicate isDerefByCallExpr(Call c, int i, VariableAccess va, StackVariable v) {
|
||||
v.getAnAccess() = va and
|
||||
va = c.getAnArgumentSubExpr(i) and
|
||||
not c.passesByReference(i, va) and
|
||||
(c.getTarget().hasEntryPoint() implies isDerefExpr(_, c.getTarget().getParameter(i)))
|
||||
}
|
||||
|
||||
class UseAfterFreeReachability extends LocalScopeVariableReachability {
|
||||
class UseAfterFreeReachability extends StackVariableReachability {
|
||||
UseAfterFreeReachability() { this = "UseAfterFree" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { isFreeExpr(node, v) }
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) { isFreeExpr(node, v) }
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { isDerefExpr(node, v) }
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) { isDerefExpr(node, v) }
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
definitionBarrier(v, node) or
|
||||
isFreeExpr(node, v)
|
||||
}
|
||||
}
|
||||
|
||||
from UseAfterFreeReachability r, LocalScopeVariable v, Expr free, Expr e
|
||||
from UseAfterFreeReachability r, StackVariable v, Expr free, Expr e
|
||||
where r.reaches(free, v, e)
|
||||
select e, "Memory pointed to by '" + v.getName().toString() + "' may have been previously freed $@",
|
||||
free, "here"
|
||||
|
||||
@@ -18,14 +18,13 @@ string getCommentTextCaptioned(Comment c, string caption) {
|
||||
dontCare = commentBody.regexpFind("\\n[/* \\t\\x0B\\f\\r]*" + caption, _, offset) and
|
||||
interestingSuffix = commentBody.suffix(offset) and
|
||||
endOfLine = interestingSuffix.indexOf("\n", 1, 0) and
|
||||
captionedLine = interestingSuffix
|
||||
captionedLine =
|
||||
interestingSuffix
|
||||
.prefix(endOfLine)
|
||||
.regexpReplaceAll("^[/*\\s]*" + caption + "\\s*:?", "")
|
||||
.trim() and
|
||||
followingLine = interestingSuffix
|
||||
.prefix(interestingSuffix.indexOf("\n", 2, 0))
|
||||
.suffix(endOfLine)
|
||||
.trim() and
|
||||
followingLine =
|
||||
interestingSuffix.prefix(interestingSuffix.indexOf("\n", 2, 0)).suffix(endOfLine).trim() and
|
||||
if captionedLine = ""
|
||||
then result = caption + " comment"
|
||||
else
|
||||
|
||||
@@ -56,7 +56,8 @@ VariableAccess getAnIncrement(Variable var) {
|
||||
exists(AssignAddExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() = any(AddExpr ae |
|
||||
a.getRValue() =
|
||||
any(AddExpr ae |
|
||||
ae.getAnOperand() = var.getAnAccess() and
|
||||
ae.getAnOperand().getValue().toInt() > 0
|
||||
)
|
||||
@@ -72,7 +73,8 @@ VariableAccess getADecrement(Variable var) {
|
||||
exists(AssignSubExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() = any(SubExpr ae |
|
||||
a.getRValue() =
|
||||
any(SubExpr ae |
|
||||
ae.getLeftOperand() = var.getAnAccess() and
|
||||
ae.getRightOperand().getValue().toInt() > 0
|
||||
)
|
||||
@@ -128,14 +130,16 @@ where
|
||||
exists(VariableAccess bound |
|
||||
upperBoundCheck(loop, bound) and
|
||||
reachesNoInc(bound, bound) and
|
||||
msg = "The loop counter " + bound.getTarget().getName() +
|
||||
msg =
|
||||
"The loop counter " + bound.getTarget().getName() +
|
||||
" is not always incremented in the loop body."
|
||||
)
|
||||
or
|
||||
exists(VariableAccess bound |
|
||||
lowerBoundCheck(loop, bound) and
|
||||
reachesNoDec(bound, bound) and
|
||||
msg = "The loop counter " + bound.getTarget().getName() +
|
||||
msg =
|
||||
"The loop counter " + bound.getTarget().getName() +
|
||||
" is not always decremented in the loop body."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -21,6 +21,7 @@ where
|
||||
if call.getTarget() = call.getEnclosingFunction()
|
||||
then msg = "This call directly invokes its containing function $@."
|
||||
else
|
||||
msg = "The function " + call.getEnclosingFunction() +
|
||||
msg =
|
||||
"The function " + call.getEnclosingFunction() +
|
||||
" is indirectly recursive via this call to $@."
|
||||
select call, msg, call.getTarget(), call.getTarget().getName()
|
||||
|
||||
@@ -17,7 +17,8 @@ predicate lockOrder(LockOperation outer, LockOperation inner) {
|
||||
}
|
||||
|
||||
int orderCount(Declaration outerLock, Declaration innerLock) {
|
||||
result = strictcount(LockOperation outer, LockOperation inner |
|
||||
result =
|
||||
strictcount(LockOperation outer, LockOperation inner |
|
||||
outer.getLocked() = outerLock and
|
||||
inner.getLocked() = innerLock and
|
||||
lockOrder(outer, inner)
|
||||
@@ -27,6 +28,6 @@ int orderCount(Declaration outerLock, Declaration innerLock) {
|
||||
from LockOperation outer, LockOperation inner
|
||||
where
|
||||
lockOrder(outer, inner) and
|
||||
orderCount(outer.getLocked(), inner.getLocked()) <= orderCount(inner.getLocked(),
|
||||
outer.getLocked())
|
||||
orderCount(outer.getLocked(), inner.getLocked()) <=
|
||||
orderCount(inner.getLocked(), outer.getLocked())
|
||||
select inner, "Out-of-order locks: A " + inner.say() + " usually precedes a $@.", outer, outer.say()
|
||||
|
||||
@@ -81,10 +81,8 @@ class LockingPrimitive extends FunctionCall, LockOperation {
|
||||
override Function getLocked() { result = this.getTarget() }
|
||||
|
||||
override UnlockOperation getMatchingUnlock() {
|
||||
result.(UnlockingPrimitive).getTarget().getName() = this
|
||||
.getTarget()
|
||||
.getName()
|
||||
.replaceAll("Lock", "Unlock")
|
||||
result.(UnlockingPrimitive).getTarget().getName() =
|
||||
this.getTarget().getName().replaceAll("Lock", "Unlock")
|
||||
}
|
||||
|
||||
override string say() { result = "call to " + getLocked().getName() }
|
||||
|
||||
@@ -29,7 +29,8 @@ where
|
||||
numStmt(f, line) = cnt and
|
||||
cnt > 1 and
|
||||
o.onLine(f, line) and
|
||||
o.getLocation().getStartColumn() = min(OneLineStmt other, int toMin |
|
||||
o.getLocation().getStartColumn() =
|
||||
min(OneLineStmt other, int toMin |
|
||||
other.onLine(f, line) and toMin = other.getLocation().getStartColumn()
|
||||
|
|
||||
toMin
|
||||
|
||||
@@ -14,7 +14,8 @@ import cpp
|
||||
string var(Variable v) {
|
||||
exists(int level | level = v.getType().getPointerIndirectionLevel() |
|
||||
level > 2 and
|
||||
result = "The type of " + v.getName() + " uses " + level +
|
||||
result =
|
||||
"The type of " + v.getName() + " uses " + level +
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
)
|
||||
}
|
||||
@@ -22,7 +23,8 @@ string var(Variable v) {
|
||||
string fun(Function f) {
|
||||
exists(int level | level = f.getType().getPointerIndirectionLevel() |
|
||||
level > 2 and
|
||||
result = "The return type of " + f.getName() + " uses " + level +
|
||||
result =
|
||||
"The return type of " + f.getName() + " uses " + level +
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
import cpp
|
||||
|
||||
int firstCodeLine(File f) {
|
||||
result = min(Declaration d, Location l, int toMin |
|
||||
result =
|
||||
min(Declaration d, Location l, int toMin |
|
||||
(
|
||||
l = d.getLocation() and
|
||||
l.getFile() = f and
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
/**
|
||||
* Holds if `e` is either:
|
||||
@@ -57,13 +59,122 @@ Expr getMulOperand(MulExpr me) { result = me.getAnOperand() }
|
||||
* ```
|
||||
*/
|
||||
int getEffectiveMulOperands(MulExpr me) {
|
||||
result = count(Expr op |
|
||||
result =
|
||||
count(Expr op |
|
||||
op = getMulOperand*(me) and
|
||||
not op instanceof MulExpr and
|
||||
not likelySmall(op)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* As SimpleRangeAnalysis does not support reasoning about multiplication
|
||||
* we create a tiny abstract interpreter for handling multiplication, which
|
||||
* we invoke only after weeding out of all of trivial cases that we do
|
||||
* not care about. By default, the maximum and minimum values are computed
|
||||
* using SimpleRangeAnalysis.
|
||||
*/
|
||||
class AnalyzableExpr extends Expr {
|
||||
float maxValue() { result = upperBound(this.getFullyConverted()) }
|
||||
|
||||
float minValue() { result = lowerBound(this.getFullyConverted()) }
|
||||
}
|
||||
|
||||
class ParenAnalyzableExpr extends AnalyzableExpr, ParenthesisExpr {
|
||||
override float maxValue() { result = this.getExpr().(AnalyzableExpr).maxValue() }
|
||||
|
||||
override float minValue() { result = this.getExpr().(AnalyzableExpr).minValue() }
|
||||
}
|
||||
|
||||
class MulAnalyzableExpr extends AnalyzableExpr, MulExpr {
|
||||
override float maxValue() {
|
||||
exists(float x1, float y1, float x2, float y2 |
|
||||
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
result = (x1 * y1).maximum(x1 * y2).maximum(x2 * y1).maximum(x2 * y2)
|
||||
)
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
exists(float x1, float x2, float y1, float y2 |
|
||||
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
result = (x1 * y1).minimum(x1 * y2).minimum(x2 * y1).minimum(x2 * y2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AddAnalyzableExpr extends AnalyzableExpr, AddExpr {
|
||||
override float maxValue() {
|
||||
result =
|
||||
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() +
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
result =
|
||||
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() +
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
|
||||
}
|
||||
}
|
||||
|
||||
class SubAnalyzableExpr extends AnalyzableExpr, SubExpr {
|
||||
override float maxValue() {
|
||||
result =
|
||||
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() -
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
result =
|
||||
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() -
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
|
||||
}
|
||||
}
|
||||
|
||||
class VarAnalyzableExpr extends AnalyzableExpr, VariableAccess {
|
||||
VarAnalyzableExpr() { this.getTarget() instanceof StackVariable }
|
||||
|
||||
override float maxValue() {
|
||||
exists(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = this and
|
||||
// if there is a defining expression, use that for
|
||||
// computing the maximum value. Otherwise, assign the
|
||||
// variable the largest possible value it can hold
|
||||
if exists(def.getDefiningValue(v))
|
||||
then result = def.getDefiningValue(v).(AnalyzableExpr).maxValue()
|
||||
else result = upperBound(this)
|
||||
)
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
exists(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = this and
|
||||
if exists(def.getDefiningValue(v))
|
||||
then result = def.getDefiningValue(v).(AnalyzableExpr).minValue()
|
||||
else result = lowerBound(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` is not an instance of `IntegralType`,
|
||||
* or if `me` cannot be proven to not overflow
|
||||
*/
|
||||
predicate overflows(MulExpr me, Type t) {
|
||||
t instanceof IntegralType
|
||||
implies
|
||||
(
|
||||
me.(MulAnalyzableExpr).maxValue() > exprMaxVal(me)
|
||||
or
|
||||
me.(MulAnalyzableExpr).minValue() < exprMinVal(me)
|
||||
)
|
||||
}
|
||||
|
||||
from MulExpr me, Type t1, Type t2
|
||||
where
|
||||
t1 = me.getType().getUnderlyingType() and
|
||||
@@ -101,7 +212,11 @@ where
|
||||
e = other.(BinaryOperation).getAnOperand*()
|
||||
) and
|
||||
e.(Literal).getType().getSize() = t2.getSize()
|
||||
)
|
||||
) and
|
||||
// only report if we cannot prove that the result of the
|
||||
// multiplication will be less (resp. greater) than the
|
||||
// maximum (resp. minimum) number we can compute.
|
||||
overflows(me, t1)
|
||||
select me,
|
||||
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
|
||||
+ me.getFullyConverted().getType().toString() + "'."
|
||||
|
||||
@@ -52,10 +52,7 @@ predicate introducesNewField(Class derived, Class base) {
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, CastToPointerArithFlow cfg
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
source.getNode().asExpr().getFullyConverted().getUnspecifiedType() = sink
|
||||
.getNode()
|
||||
.asExpr()
|
||||
.getFullyConverted()
|
||||
.getUnspecifiedType()
|
||||
source.getNode().asExpr().getFullyConverted().getUnspecifiedType() =
|
||||
sink.getNode().asExpr().getFullyConverted().getUnspecifiedType()
|
||||
select sink, source, sink,
|
||||
"Pointer arithmetic here may be done with the wrong type because of the cast $@.", source, "here"
|
||||
|
||||
@@ -37,7 +37,7 @@ predicate flowsToExprImpl(Expr source, Expr sink, boolean pathMightOverflow) {
|
||||
pathMightOverflow = false and
|
||||
source.(FunctionCall).getTarget().(Snprintf).returnsFullFormatLength()
|
||||
or
|
||||
exists(RangeSsaDefinition def, LocalScopeVariable v |
|
||||
exists(RangeSsaDefinition def, StackVariable v |
|
||||
flowsToDef(source, def, v, pathMightOverflow) and
|
||||
sink = def.getAUse(v)
|
||||
)
|
||||
@@ -63,9 +63,7 @@ predicate flowsToExprImpl(Expr source, Expr sink, boolean pathMightOverflow) {
|
||||
* `pathMightOverflow` is true if there is an arithmetic operation
|
||||
* on the path that might overflow.
|
||||
*/
|
||||
predicate flowsToDef(
|
||||
Expr source, RangeSsaDefinition def, LocalScopeVariable v, boolean pathMightOverflow
|
||||
) {
|
||||
predicate flowsToDef(Expr source, RangeSsaDefinition def, StackVariable v, boolean pathMightOverflow) {
|
||||
// Might the current definition overflow?
|
||||
exists(boolean otherMightOverflow | flowsToDefImpl(source, def, v, otherMightOverflow) |
|
||||
if defMightOverflow(def, v)
|
||||
@@ -86,7 +84,7 @@ predicate flowsToDef(
|
||||
* the path. But it is a good way to reduce the number of false positives.
|
||||
*/
|
||||
predicate flowsToDefImpl(
|
||||
Expr source, RangeSsaDefinition def, LocalScopeVariable v, boolean pathMightOverflow
|
||||
Expr source, RangeSsaDefinition def, StackVariable v, boolean pathMightOverflow
|
||||
) {
|
||||
// Assignment or initialization: `e = v;`
|
||||
exists(Expr e |
|
||||
|
||||
@@ -130,11 +130,8 @@ predicate trivialConversion(ExpectedType expected, Type actual) {
|
||||
or
|
||||
// allow a pointer to any integral type of the same size
|
||||
// (this permits signedness changes)
|
||||
expected.(PointerType).getBaseType().(IntegralType).getSize() = actual
|
||||
.(PointerType)
|
||||
.getBaseType()
|
||||
.(IntegralType)
|
||||
.getSize()
|
||||
expected.(PointerType).getBaseType().(IntegralType).getSize() =
|
||||
actual.(PointerType).getBaseType().(IntegralType).getSize()
|
||||
or
|
||||
expected = actual
|
||||
)
|
||||
|
||||
@@ -25,10 +25,16 @@ predicate assertInvocation(File f, int line) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate nullCheckAssert(Expr e, Variable v, Declaration qualifier) {
|
||||
nullCheckInCondition(e, v, qualifier) and
|
||||
class InterestingExpr extends Expr {
|
||||
InterestingExpr() { nullCheckInCondition(this, _, _) }
|
||||
}
|
||||
|
||||
predicate nullCheckAssert(InterestingExpr e, Variable v, Declaration qualifier) {
|
||||
exists(File f, int i |
|
||||
e.getLocation().getStartLine() = i and e.getFile() = f and assertInvocation(f, i)
|
||||
e.getLocation().getStartLine() = i and
|
||||
e.getFile() = f and
|
||||
assertInvocation(f, i) and
|
||||
nullCheckInCondition(e, v, qualifier)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @id cpp/leap-year/adding-365-days-per-year
|
||||
* @precision medium
|
||||
* @tags leap-year
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* @id cpp/leap-year/unchecked-after-arithmetic-year-modification
|
||||
* @precision medium
|
||||
* @tags leap-year
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @id cpp/leap-year/unchecked-return-value-for-time-conversion-function
|
||||
* @precision medium
|
||||
* @tags leap-year
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id cpp/leap-year/unsafe-array-for-days-of-the-year
|
||||
* @precision medium
|
||||
* @precision low
|
||||
* @tags security
|
||||
* leap-year
|
||||
*/
|
||||
|
||||
@@ -12,24 +12,24 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
class UndefReachability extends LocalScopeVariableReachability {
|
||||
class UndefReachability extends StackVariableReachability {
|
||||
UndefReachability() { this = "UndefReachability" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) {
|
||||
candidateVariable(v) and
|
||||
node = v.getParentScope() and
|
||||
not v instanceof Parameter and
|
||||
not v.hasInitializer()
|
||||
}
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) {
|
||||
candidateVariable(v) and
|
||||
node = v.getAnAccess()
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
node.(AssignExpr).getLValue() = v.getAnAccess()
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,12 @@ abstract class BooleanControllingAssignment extends AssignExpr {
|
||||
abstract predicate isWhitelisted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an operand of a logical operation expression (we need the restriction
|
||||
* to BinaryLogicalOperation expressions to get the correct transitive closure).
|
||||
*/
|
||||
Expr getComparisonOperand(BinaryLogicalOperation op) { result = op.getAnOperand() }
|
||||
|
||||
class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
BooleanControllingAssignmentInExpr() {
|
||||
this.getParent() instanceof UnaryLogicalOperation or
|
||||
@@ -45,7 +51,18 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
exists(ConditionalExpr c | c.getCondition() = this)
|
||||
}
|
||||
|
||||
override predicate isWhitelisted() { this.getConversion().(ParenthesisExpr).isParenthesised() }
|
||||
override predicate isWhitelisted() {
|
||||
this.getConversion().(ParenthesisExpr).isParenthesised()
|
||||
or
|
||||
// whitelist this assignment if all comparison operations in the expression that this
|
||||
// assignment is part of, are not parenthesized. In that case it seems like programmer
|
||||
// is fine with unparenthesized comparison operands to binary logical operators, and
|
||||
// the parenthesis around this assignment was used to call it out as an assignment.
|
||||
this.isParenthesised() and
|
||||
forex(ComparisonOperation op | op = getComparisonOperand*(this.getParent+()) |
|
||||
not op.isParenthesised()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanControllingAssignmentInStmt extends BooleanControllingAssignment {
|
||||
@@ -65,7 +82,8 @@ class BooleanControllingAssignmentInStmt extends BooleanControllingAssignment {
|
||||
*/
|
||||
predicate candidateResult(BooleanControllingAssignment ae) {
|
||||
ae.getRValue().isConstant() and
|
||||
not ae.isWhitelisted()
|
||||
not ae.isWhitelisted() and
|
||||
not ae.getRValue() instanceof StringLiteral
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,5 +99,6 @@ predicate candidateVariable(Variable v) {
|
||||
from BooleanControllingAssignment ae, UndefReachability undef
|
||||
where
|
||||
candidateResult(ae) and
|
||||
not ae.isFromUninstantiatedTemplate(_) and
|
||||
not undef.reaches(_, ae.getLValue().(VariableAccess).getTarget(), ae.getLValue())
|
||||
select ae, "Use of '=' where '==' may have been intended."
|
||||
|
||||
@@ -15,7 +15,10 @@ import cpp
|
||||
|
||||
from ExprInVoidContext op
|
||||
where
|
||||
op instanceof EQExpr
|
||||
or
|
||||
op.(FunctionCall).getTarget().hasName("operator==")
|
||||
not op.isUnevaluated() and
|
||||
(
|
||||
op instanceof EQExpr
|
||||
or
|
||||
op.(FunctionCall).getTarget().hasName("operator==")
|
||||
)
|
||||
select op, "This '==' operator has no effect. The assignment ('=') operator was probably intended."
|
||||
|
||||
@@ -65,11 +65,8 @@ predicate functionDefinedInIfDefRecursive(Function f) {
|
||||
* break encapsulation.
|
||||
*/
|
||||
predicate baseCall(FunctionCall call) {
|
||||
call.getNameQualifier().getQualifyingElement() = call
|
||||
.getEnclosingFunction()
|
||||
.getDeclaringType()
|
||||
.(Class)
|
||||
.getABaseClass+()
|
||||
call.getNameQualifier().getQualifyingElement() =
|
||||
call.getEnclosingFunction().getDeclaringType().(Class).getABaseClass+()
|
||||
}
|
||||
|
||||
from PureExprInVoidContext peivc, Locatable parent, Locatable info, string info_text, string tail
|
||||
@@ -84,8 +81,10 @@ where
|
||||
not peivc.getEnclosingFunction().isDefaulted() and
|
||||
not exists(Macro m | peivc = m.getAnInvocation().getAnExpandedElement()) and
|
||||
not peivc.isFromTemplateInstantiation(_) and
|
||||
not peivc.isFromUninstantiatedTemplate(_) and
|
||||
parent = peivc.getParent() and
|
||||
not parent.isInMacroExpansion() and
|
||||
not peivc.isUnevaluated() and
|
||||
not parent instanceof PureExprInVoidContext and
|
||||
not peivc.getEnclosingFunction().isCompilerGenerated() and
|
||||
not peivc.getType() instanceof UnknownType and
|
||||
|
||||
@@ -35,7 +35,8 @@ predicate booleanLiteral(Literal l) {
|
||||
string boolLiteralInLogicalOp(Literal literal) {
|
||||
booleanLiteral(literal) and
|
||||
literal.getParent() instanceof BinaryLogicalOperation and
|
||||
result = "Literal value " + literal.getValueText() +
|
||||
result =
|
||||
"Literal value " + literal.getValueText() +
|
||||
" is used in a logical expression; simplify or use a constant."
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ where
|
||||
l = v.log2().floor() and
|
||||
if v = 2.pow(l)
|
||||
then
|
||||
msg = "Operand to short-circuiting operator looks like a flag (" + v + " = 2 ^ " + l +
|
||||
msg =
|
||||
"Operand to short-circuiting operator looks like a flag (" + v + " = 2 ^ " + l +
|
||||
"), may be typo for bitwise operator."
|
||||
else
|
||||
exists(string kind |
|
||||
@@ -49,7 +50,8 @@ where
|
||||
or
|
||||
e instanceof OctalLiteral and kind = "an octal literal"
|
||||
) and
|
||||
msg = "Operand to short-circuiting operator is " + kind +
|
||||
msg =
|
||||
"Operand to short-circuiting operator is " + kind +
|
||||
", and therefore likely a flag; a bitwise operator may be intended."
|
||||
)
|
||||
select e, msg
|
||||
|
||||
@@ -63,7 +63,8 @@ predicate isStringCopyUsedInLogicalOperationOrCondition(FunctionCall func, Expr
|
||||
func = ce.getCondition()
|
||||
)
|
||||
) and
|
||||
msg = "Return value of " + func.getTarget().getName() +
|
||||
msg =
|
||||
"Return value of " + func.getTarget().getName() +
|
||||
" used directly in a conditional expression."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -111,17 +111,20 @@ predicate illDefinedForStmt(ForStmt for, string message) {
|
||||
illDefinedForStmtWrongDirection(for, v, initialCondition, terminalCondition, isIncr) and
|
||||
if for.conditionAlwaysFalse()
|
||||
then
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
message =
|
||||
"Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
forLoopdirection(isIncr) + " from a value (" + initialCondition +
|
||||
"), but the terminal condition is always false."
|
||||
else
|
||||
if for.conditionAlwaysTrue()
|
||||
then
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
message =
|
||||
"Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
forLoopdirection(isIncr) + " from a value (" + initialCondition +
|
||||
"), but the terminal condition is always true."
|
||||
else
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
message =
|
||||
"Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
forLoopdirection(isIncr) + " from a value (" + initialCondition +
|
||||
"), but the terminal condition is " + forLoopTerminalConditionRelationship(isIncr) +
|
||||
" (" + terminalCondition + ")."
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
import semmle.code.cpp.commons.NullTermination
|
||||
|
||||
/**
|
||||
@@ -22,10 +22,10 @@ DeclStmt declWithNoInit(LocalVariable v) {
|
||||
not exists(v.getInitializer())
|
||||
}
|
||||
|
||||
class ImproperNullTerminationReachability extends LocalScopeVariableReachabilityWithReassignment {
|
||||
class ImproperNullTerminationReachability extends StackVariableReachabilityWithReassignment {
|
||||
ImproperNullTerminationReachability() { this = "ImproperNullTerminationReachability" }
|
||||
|
||||
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
|
||||
node = declWithNoInit(v)
|
||||
or
|
||||
exists(Call c, VariableAccess va |
|
||||
@@ -36,12 +36,12 @@ class ImproperNullTerminationReachability extends LocalScopeVariableReachability
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
|
||||
node.(VariableAccess).getTarget() = v and
|
||||
variableMustBeNullTerminated(node)
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
exprDefinition(v, node, _) or
|
||||
mayAddNullTerminator(node, v.getAnAccess()) or
|
||||
isSinkActual(node, v) // only report first use
|
||||
|
||||
@@ -122,7 +122,7 @@ class MallocSizeExpr extends BufferAccess, FunctionCall {
|
||||
|
||||
override Expr getPointer() { none() }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(1) }
|
||||
override Expr getAccessedLength() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
class NetworkFunctionCall extends FunctionCall {
|
||||
|
||||
@@ -30,9 +30,9 @@ class SprintfCall extends FunctionCall {
|
||||
predicate isDangerous() { this.getMaxConvertedLength() > this.getBufferSize() }
|
||||
|
||||
string getDescription() {
|
||||
result = "This conversion may yield a string of length " +
|
||||
this.getMaxConvertedLength().toString() + ", which exceeds the allocated buffer size of " +
|
||||
this.getBufferSize().toString()
|
||||
result =
|
||||
"This conversion may yield a string of length " + this.getMaxConvertedLength().toString() +
|
||||
", which exceeds the allocated buffer size of " + this.getBufferSize().toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,10 +42,8 @@ predicate hasNontrivialConversion(Expr e) {
|
||||
hasNontrivialConversion(e.getConversion())
|
||||
}
|
||||
|
||||
from LocalScopeVariable var, VariableAccess va, ReturnStmt r
|
||||
from StackVariable var, VariableAccess va, ReturnStmt r
|
||||
where
|
||||
not var.isStatic() and
|
||||
not var.isThreadLocal() and
|
||||
not var.getUnspecifiedType() instanceof ReferenceType and
|
||||
not r.isFromUninstantiatedTemplate(_) and
|
||||
va = var.getAnAccess() and
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/**
|
||||
* Auxiliary predicate: Types that don't require initialization
|
||||
@@ -40,23 +40,17 @@ DeclStmt declWithNoInit(LocalVariable v) {
|
||||
result.getADeclaration() = v and
|
||||
not exists(v.getInitializer()) and
|
||||
/* The type of the variable is not stack-allocated. */
|
||||
not allocatedType(v.getType()) and
|
||||
/* The variable is not static (otherwise it is zeroed). */
|
||||
not v.isStatic() and
|
||||
/* The variable is not extern (otherwise it is zeroed). */
|
||||
not v.hasSpecifier("extern")
|
||||
not allocatedType(v.getType())
|
||||
}
|
||||
|
||||
class UninitialisedLocalReachability extends LocalScopeVariableReachability {
|
||||
class UninitialisedLocalReachability extends StackVariableReachability {
|
||||
UninitialisedLocalReachability() { this = "UninitialisedLocal" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
node = declWithNoInit(v)
|
||||
}
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) { node = declWithNoInit(v) }
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { useOfVarActual(v, node) }
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) { useOfVarActual(v, node) }
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
// only report the _first_ possibly uninitialized use
|
||||
useOfVarActual(v, node) or
|
||||
definitionBarrier(v, node)
|
||||
|
||||
@@ -3,9 +3,20 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols.</p>
|
||||
<p>Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols may expose the software to known vulnerabilities or permit weak encryption algorithms to be used. Disabling the minimum-recommended protocols is also flagged.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>When using the TLS or SSLv23 protocol, set the <code>no_tlsv1</code> and <code>no_tlsv1_1</code> options, but do not set <code>no_tlsv1_2</code>. When using the SSLv23 protocol, also set the <code>no_sslv3</code> option.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, the <code>no_tlsv1_1</code> option has not been set. Use of TLS 1.1 is not recommended.</p>
|
||||
<sample src="TlsSettingsMisconfigurationBad.cpp"/>
|
||||
<p>In the corrected example, the <code>no_tlsv1</code> and <code>no_tlsv1_1</code> options have both been set, ensuring the use of TLS 1.2 or later.</p>
|
||||
<sample src="TlsSettingsMisconfigurationGood.cpp"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Boost_asio TLS Settings Misconfiguration
|
||||
* @description Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols
|
||||
* @description Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols, or disabling minimum-recommended protocols.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id cpp/boost/tls_settings_misconfiguration
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
void useTLS_bad()
|
||||
{
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::tls);
|
||||
ctx.set_options(boost::asio::ssl::context::no_tlsv1); // BAD: missing no_tlsv1_1
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
void useTLS_good()
|
||||
{
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::tls);
|
||||
ctx.set_options(boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1); // GOOD
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -4,13 +4,22 @@
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using boost::asio library but specifying a deprecated hardcoded protocol.</p>
|
||||
<p>Using a deprecated hardcoded protocol instead of negotiting would lock your application to a protocol that has known vulnerabilities or weaknesses.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Only use modern protocols such as TLS 1.2 or TLS 1.3.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, the <code>sslv2</code> protocol is specified. This protocol is out of date and its use is not recommended.</p>
|
||||
<sample src="UseOfDeprecatedHardcodedProtocolBad.cpp"/>
|
||||
<p>In the corrected example, the <code>tlsv13</code> protocol is used instead.</p>
|
||||
<sample src="UseOfDeprecatedHardcodedProtocolGood.cpp"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
void useProtocol_bad()
|
||||
{
|
||||
boost::asio::ssl::context ctx_sslv2(boost::asio::ssl::context::sslv2); // BAD: outdated protocol
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
void useProtocol_good()
|
||||
{
|
||||
boost::asio::ssl::context cxt_tlsv13(boost::asio::ssl::context::tlsv13);
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -28,7 +28,8 @@ class NullInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
bool = any(CompareInstruction cmp |
|
||||
bool =
|
||||
any(CompareInstruction cmp |
|
||||
exists(NullInstruction null |
|
||||
cmp.getLeft() = null and cmp.getRight() = checked
|
||||
or
|
||||
@@ -40,7 +41,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
)
|
||||
)
|
||||
or
|
||||
bool = any(ConvertInstruction convert |
|
||||
bool =
|
||||
any(ConvertInstruction convert |
|
||||
checked = convert.getUnary() and
|
||||
convert.getResultType() instanceof BoolType and
|
||||
checked.getResultType() instanceof PointerType
|
||||
|
||||
@@ -17,7 +17,9 @@ where
|
||||
hasSuperfluousConstReturn(f) and
|
||||
if f.hasSpecifier("const") or f.isStatic()
|
||||
then
|
||||
message = "The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed."
|
||||
message =
|
||||
"The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed."
|
||||
else
|
||||
message = "The 'const' modifier has no effect on return types. For a const function, the 'const' should go after the parameter list."
|
||||
message =
|
||||
"The 'const' modifier has no effect on return types. For a const function, the 'const' should go after the parameter list."
|
||||
select f, message
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/* '#include <stdlib.h>' was forgotton */
|
||||
|
||||
int main(void) {
|
||||
/* 'int malloc()' assumed */
|
||||
unsigned char *p = malloc(100);
|
||||
*p = 'a';
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>A function is called without a prior function declaration or definition.
|
||||
When this happens, the compiler generates an implicit declaration of the function,
|
||||
specifying an integer return type and no parameters.
|
||||
If the implicit declaration does not match the true signature of the function, the
|
||||
function may behave unpredictably.</p>
|
||||
|
||||
<p>This may indicate a misspelled function name, or that the required header containing
|
||||
the function declaration has not been included.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Provide an explicit declaration of the function before invoking it.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="ImplicitFunctionDeclaration.c" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL31-C.+Declare+identifiers+before+using+them">DCL31-C. Declare identifiers before using them</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @name Implicit function declaration
|
||||
* @description An implicitly declared function is assumed to take no
|
||||
* arguments and return an integer. If this assumption does not hold, it
|
||||
* may lead to unpredictable behavior.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/implicit-function-declaration
|
||||
* @tags correctness
|
||||
* maintainability
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import MistypedFunctionArguments
|
||||
import TooFewArguments
|
||||
import TooManyArguments
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
predicate locInfo(Locatable e, File file, int line, int col) {
|
||||
e.getFile() = file and
|
||||
e.getLocation().getStartLine() = line and
|
||||
e.getLocation().getStartColumn() = col
|
||||
}
|
||||
|
||||
predicate sameLocation(FunctionDeclarationEntry fde, FunctionCall fc) {
|
||||
exists(File file, int line, int col |
|
||||
locInfo(fde, file, line, col) and
|
||||
locInfo(fc, file, line, col)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
from FunctionDeclarationEntry fdeIm, FunctionCall fc
|
||||
where
|
||||
isCompiledAsC(fdeIm.getFile()) and
|
||||
not isFromMacroDefinition(fc) and
|
||||
fdeIm.isImplicit() and
|
||||
sameLocation(fdeIm, fc) and
|
||||
not mistypedFunctionArguments(fc, _, _) and
|
||||
not tooFewArguments(fc, _) and
|
||||
not tooManyArguments(fc, _)
|
||||
select fc, "Function call implicitly declares '" + fdeIm.getName() + "'."
|
||||
@@ -12,95 +12,10 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate arithTypesMatch(Type arg, Type parm) {
|
||||
arg = parm
|
||||
or
|
||||
arg.getSize() = parm.getSize() and
|
||||
(
|
||||
arg instanceof IntegralOrEnumType and
|
||||
parm instanceof IntegralOrEnumType
|
||||
or
|
||||
arg instanceof FloatingPointType and
|
||||
parm instanceof FloatingPointType
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate nestedPointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||
// arithmetic types
|
||||
arithTypesMatch(arg, parm)
|
||||
or
|
||||
// conversion to/from pointers to void is allowed
|
||||
arg instanceof VoidType
|
||||
or
|
||||
parm instanceof VoidType
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate pointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||
nestedPointerArgTypeMayBeUsed(arg, parm)
|
||||
or
|
||||
// nested pointers
|
||||
nestedPointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
nestedPointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate argTypeMayBeUsed(Type arg, Type parm) {
|
||||
// arithmetic types
|
||||
arithTypesMatch(arg, parm)
|
||||
or
|
||||
// pointers to compatible types
|
||||
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
// C11 arrays
|
||||
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
// This predicate holds whenever expression `arg` may be used to initialize
|
||||
// function parameter `parm` without need for run-time conversion.
|
||||
pragma[inline]
|
||||
predicate argMayBeUsed(Expr arg, Parameter parm) {
|
||||
argTypeMayBeUsed(arg.getFullyConverted().getUnspecifiedType(), parm.getUnspecifiedType())
|
||||
}
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
}
|
||||
import MistypedFunctionArguments
|
||||
|
||||
from FunctionCall fc, Function f, Parameter p
|
||||
where
|
||||
f = fc.getTarget() and
|
||||
p = f.getAParameter() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
p.getIndex() < fc.getNumberOfArguments() and
|
||||
// Parameter p and its corresponding call argument must have mismatched types
|
||||
not argMayBeUsed(fc.getArgument(p.getIndex()), p)
|
||||
where mistypedFunctionArguments(fc, f, p)
|
||||
select fc, "Calling $@: argument $@ of type $@ is incompatible with parameter $@.", f, f.toString(),
|
||||
fc.getArgument(p.getIndex()) as arg, arg.toString(),
|
||||
arg.getExplicitlyConverted().getUnspecifiedType() as atype, atype.toString(), p, p.getTypedName()
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Provides the implementation of the MistypedFunctionArguments query. The
|
||||
* query is implemented as a library, so that we can avoid producing
|
||||
* duplicate results in other similar queries.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
private predicate arithTypesMatch(Type arg, Type parm) {
|
||||
arg = parm
|
||||
or
|
||||
arg.getSize() = parm.getSize() and
|
||||
(
|
||||
arg instanceof IntegralOrEnumType and
|
||||
parm instanceof IntegralOrEnumType
|
||||
or
|
||||
arg instanceof FloatingPointType and
|
||||
parm instanceof FloatingPointType
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate nestedPointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||
// arithmetic types
|
||||
arithTypesMatch(arg, parm)
|
||||
or
|
||||
// conversion to/from pointers to void is allowed
|
||||
arg instanceof VoidType
|
||||
or
|
||||
parm instanceof VoidType
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate pointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||
nestedPointerArgTypeMayBeUsed(arg, parm)
|
||||
or
|
||||
// nested pointers
|
||||
nestedPointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
nestedPointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate argTypeMayBeUsed(Type arg, Type parm) {
|
||||
// arithmetic types
|
||||
arithTypesMatch(arg, parm)
|
||||
or
|
||||
// pointers to compatible types
|
||||
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
// C11 arrays
|
||||
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
// This predicate holds whenever expression `arg` may be used to initialize
|
||||
// function parameter `parm` without need for run-time conversion.
|
||||
pragma[inline]
|
||||
private predicate argMayBeUsed(Expr arg, Parameter parm) {
|
||||
argTypeMayBeUsed(arg.getFullyConverted().getUnspecifiedType(), parm.getUnspecifiedType())
|
||||
}
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
private predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
private predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
predicate mistypedFunctionArguments(FunctionCall fc, Function f, Parameter p) {
|
||||
f = fc.getTarget() and
|
||||
p = f.getAParameter() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
p.getIndex() < fc.getNumberOfArguments() and
|
||||
// Parameter p and its corresponding call argument must have mismatched types
|
||||
not argMayBeUsed(fc.getArgument(p.getIndex()), p)
|
||||
}
|
||||
@@ -15,31 +15,8 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
}
|
||||
import TooFewArguments
|
||||
|
||||
from FunctionCall fc, Function f
|
||||
where
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
// There is an explicit declaration of the function whose parameter count is larger
|
||||
// than the number of call arguments
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
fde.getNumberOfParameters() > fc.getNumberOfArguments()
|
||||
)
|
||||
where tooFewArguments(fc, f)
|
||||
select fc, "This call has fewer arguments than required by $@.", f, f.toString()
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Provides the implementation of the TooFewArguments query. The
|
||||
* query is implemented as a library, so that we can avoid producing
|
||||
* duplicate results in other similar queries.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
private predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
private predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
predicate tooFewArguments(FunctionCall fc, Function f) {
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
// There is an explicit declaration of the function whose parameter count is larger
|
||||
// than the number of call arguments
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
fde.getNumberOfParameters() > fc.getNumberOfArguments()
|
||||
)
|
||||
}
|
||||
@@ -12,35 +12,8 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
// or implicitly declared (i.e., lacking a prototype)
|
||||
predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.isImplicit() and
|
||||
not fde.hasVoidParamList() and
|
||||
fde.getNumberOfParameters() = 0 and
|
||||
not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
}
|
||||
import TooManyArguments
|
||||
|
||||
from FunctionCall fc, Function f
|
||||
where
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
exists(f.getBlock()) and
|
||||
// There must not exist a declaration with the number of parameters
|
||||
// at least as large as the number of call arguments
|
||||
not exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
fde.getNumberOfParameters() >= fc.getNumberOfArguments()
|
||||
)
|
||||
where tooManyArguments(fc, f)
|
||||
select fc, "This call has more arguments than required by $@.", f, f.toString()
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Provides the implementation of the TooManyArguments query. The
|
||||
* query is implemented as a library, so that we can avoid producing
|
||||
* duplicate results in other similar queries.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
// or implicitly declared (i.e., lacking a prototype)
|
||||
private predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.isImplicit() and
|
||||
not fde.hasVoidParamList() and
|
||||
fde.getNumberOfParameters() = 0 and
|
||||
not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
private predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
predicate tooManyArguments(FunctionCall fc, Function f) {
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
exists(f.getBlock()) and
|
||||
// There must not exist a declaration with the number of parameters
|
||||
// at least as large as the number of call arguments
|
||||
not exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
fde.getNumberOfParameters() >= fc.getNumberOfArguments()
|
||||
)
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import cpp
|
||||
from Class c, int n
|
||||
where
|
||||
c.fromSource() and
|
||||
n = c.getMetrics().getNumberOfMembers() +
|
||||
n =
|
||||
c.getMetrics().getNumberOfMembers() +
|
||||
sum(Function f | c.getACanonicalMemberFunction() = f | f.getMetrics().getNumberOfLinesOfCode())
|
||||
select c, n order by n desc
|
||||
|
||||
@@ -15,16 +15,15 @@ import cpp
|
||||
from Class c, int ccLoc, int loc
|
||||
where
|
||||
c.fromSource() and
|
||||
ccLoc = sum(Function f |
|
||||
ccLoc =
|
||||
sum(Function f |
|
||||
c.getACanonicalMemberFunction() = f and
|
||||
f.getMetrics().getCyclomaticComplexity() > 18
|
||||
|
|
||||
f.getMetrics().getNumberOfLinesOfCode()
|
||||
) and
|
||||
loc = sum(Function f |
|
||||
c.getACanonicalMemberFunction() = f
|
||||
|
|
||||
f.getMetrics().getNumberOfLinesOfCode()
|
||||
) + c.getMetrics().getNumberOfMembers() and
|
||||
loc =
|
||||
sum(Function f | c.getACanonicalMemberFunction() = f | f.getMetrics().getNumberOfLinesOfCode()) +
|
||||
c.getMetrics().getNumberOfMembers() and
|
||||
loc != 0
|
||||
select c, (ccLoc * 100).(float) / loc as n order by n desc
|
||||
|
||||
@@ -76,7 +76,8 @@ class Library extends LibraryT {
|
||||
* `sourceFile` is not in `destLib`).
|
||||
*/
|
||||
predicate libDependencies(File sourceFile, Library destLib, int num) {
|
||||
num = strictcount(Element source, Element dest, File destFile |
|
||||
num =
|
||||
strictcount(Element source, Element dest, File destFile |
|
||||
// dependency from source -> dest.
|
||||
dependsOnSimple(source, dest) and
|
||||
sourceFile = source.getFile() and
|
||||
@@ -101,7 +102,7 @@ predicate libDependencies(File sourceFile, Library destLib, int num) {
|
||||
predicate encodedDependencies(File source, string encodedDependency, int num) {
|
||||
exists(Library destLib |
|
||||
libDependencies(source, destLib, num) and
|
||||
encodedDependency = "/" + source.getRelativePath() + "<|>" + destLib.getName() + "<|>" +
|
||||
destLib.getVersion()
|
||||
encodedDependency =
|
||||
"/" + source.getRelativePath() + "<|>" + destLib.getName() + "<|>" + destLib.getVersion()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -85,7 +85,8 @@ private predicate closeWithDepth(int depth, File f, PreprocessorDirective close,
|
||||
predicate length(PreprocessorDirective open, int length) {
|
||||
exists(int depth, File f, int start, int end |
|
||||
openWithDepth(depth, f, open, start) and
|
||||
end = min(PreprocessorDirective endif, int closeLine |
|
||||
end =
|
||||
min(PreprocessorDirective endif, int closeLine |
|
||||
closeWithDepth(depth, f, endif, closeLine) and
|
||||
closeLine > start
|
||||
|
|
||||
|
||||
@@ -19,7 +19,8 @@ where
|
||||
if loc > 0
|
||||
then
|
||||
// Weighted average of complexity by function length
|
||||
complexity = sum(FunctionDeclarationEntry fde |
|
||||
complexity =
|
||||
sum(FunctionDeclarationEntry fde |
|
||||
fde.getFile() = f
|
||||
|
|
||||
fde.getNumberOfLines() * fde.getCyclomaticComplexity()
|
||||
|
||||
@@ -17,7 +17,8 @@ import external.CodeDuplication
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = count(int line |
|
||||
n =
|
||||
count(int line |
|
||||
exists(DuplicateBlock d | d.sourceFile() = f |
|
||||
line in [d.sourceStartLine() .. d.sourceEndLine()]
|
||||
) and
|
||||
|
||||
@@ -15,7 +15,8 @@ import cpp
|
||||
from File f, int n
|
||||
where
|
||||
f.fromSource() and
|
||||
n = count(Comment c |
|
||||
n =
|
||||
count(Comment c |
|
||||
c.getFile() = f and
|
||||
(
|
||||
c.getContents().matches("%TODO%") or
|
||||
|
||||
@@ -20,7 +20,8 @@ predicate callToOperator(FunctionCall fc) {
|
||||
from Function f, int n, int o
|
||||
where
|
||||
strictcount(f.getEntryPoint()) = 1 and
|
||||
o = count(FunctionCall c |
|
||||
o =
|
||||
count(FunctionCall c |
|
||||
c.getEnclosingFunction() = f and
|
||||
not c.isInMacroExpansion() and
|
||||
not c.isCompilerGenerated() and
|
||||
|
||||
@@ -16,7 +16,8 @@ import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = sum(Commit entry, int churn |
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentChurnForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
|
||||
@@ -16,7 +16,8 @@ import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = sum(Commit entry, int churn |
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentAdditionsForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
|
||||
@@ -16,7 +16,8 @@ import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = sum(Commit entry, int churn |
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentDeletionsForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
|
||||
@@ -25,7 +25,8 @@ predicate inRange(Commit first, Commit second) {
|
||||
}
|
||||
|
||||
int recommitsForFile(File f) {
|
||||
result = count(Commit recommit |
|
||||
result =
|
||||
count(Commit recommit |
|
||||
f = recommit.getAnAffectedFile() and
|
||||
exists(Commit prev | inRange(prev, recommit))
|
||||
)
|
||||
|
||||
@@ -15,7 +15,8 @@ import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = count(Commit e |
|
||||
n =
|
||||
count(Commit e |
|
||||
e.getAnAffectedFile() = f and
|
||||
e.daysToNow() <= 180 and
|
||||
not artificialChange(e)
|
||||
|
||||
@@ -147,7 +147,8 @@ library class SALElement extends Element {
|
||||
exists(Location loc |
|
||||
loc = this.(FunctionDeclarationEntry).getBlock().getLocation()
|
||||
or
|
||||
this = any(VariableDeclarationEntry vde |
|
||||
this =
|
||||
any(VariableDeclarationEntry vde |
|
||||
vde.isDefinition() and
|
||||
loc = vde.getVariable().getInitializer().getLocation()
|
||||
)
|
||||
@@ -194,7 +195,8 @@ private predicate salAnnotationPos(SALPosition pos) {
|
||||
* ordering positions lexicographically by their start line and start column.
|
||||
*/
|
||||
private SALPosition salRelevantPositionAt(File file, int idx) {
|
||||
result = rank[idx](SALPosition pos, int line, int col |
|
||||
result =
|
||||
rank[idx](SALPosition pos, int line, int col |
|
||||
pos = MkSALPosition(file, line, col)
|
||||
|
|
||||
pos order by line, col
|
||||
|
||||
@@ -21,6 +21,7 @@ where
|
||||
if call.getTarget() = call.getEnclosingFunction()
|
||||
then msg = "This call directly invokes its containing function $@."
|
||||
else
|
||||
msg = "The function " + call.getEnclosingFunction() +
|
||||
msg =
|
||||
"The function " + call.getEnclosingFunction() +
|
||||
" is indirectly recursive via this call to $@."
|
||||
select call, msg, call.getTarget(), call.getTarget().getName()
|
||||
|
||||
@@ -58,7 +58,8 @@ VariableAccess getAnIncrement(Variable var) {
|
||||
exists(AssignAddExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() = any(AddExpr ae |
|
||||
a.getRValue() =
|
||||
any(AddExpr ae |
|
||||
ae.getAnOperand() = var.getAnAccess() and
|
||||
ae.getAnOperand().getValue().toInt() > 0
|
||||
)
|
||||
@@ -74,7 +75,8 @@ VariableAccess getADecrement(Variable var) {
|
||||
exists(AssignSubExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() = any(SubExpr ae |
|
||||
a.getRValue() =
|
||||
any(SubExpr ae |
|
||||
ae.getLeftOperand() = var.getAnAccess() and
|
||||
ae.getRightOperand().getValue().toInt() > 0
|
||||
)
|
||||
@@ -125,14 +127,16 @@ where
|
||||
exists(VariableAccess bound |
|
||||
upperBoundCheck(loop, bound) and
|
||||
reachesNoInc(bound, bound) and
|
||||
msg = "The loop counter " + bound.getTarget().getName() +
|
||||
msg =
|
||||
"The loop counter " + bound.getTarget().getName() +
|
||||
" is not always incremented in the loop body."
|
||||
)
|
||||
or
|
||||
exists(VariableAccess bound |
|
||||
lowerBoundCheck(loop, bound) and
|
||||
reachesNoDec(bound, bound) and
|
||||
msg = "The loop counter " + bound.getTarget().getName() +
|
||||
msg =
|
||||
"The loop counter " + bound.getTarget().getName() +
|
||||
" is not always decremented in the loop body."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -23,7 +23,8 @@ class MacroFunctionCall extends MacroInvocation {
|
||||
}
|
||||
|
||||
int logicalLength(FunctionDeclarationEntry f) {
|
||||
result = count(Stmt s |
|
||||
result =
|
||||
count(Stmt s |
|
||||
s.getEnclosingFunction() = f.getFunction() and
|
||||
s.getFile() = f.getFile() and
|
||||
not s instanceof Block and
|
||||
|
||||
@@ -39,7 +39,8 @@ where
|
||||
numStmt(f, line) = cnt and
|
||||
cnt > 1 and
|
||||
o.onLine(f, line) and
|
||||
o.getLocation().getStartColumn() = min(OneLineStmt other, int toMin |
|
||||
o.getLocation().getStartColumn() =
|
||||
min(OneLineStmt other, int toMin |
|
||||
other.onLine(f, line) and toMin = other.getLocation().getStartColumn()
|
||||
|
|
||||
toMin
|
||||
|
||||
@@ -23,7 +23,8 @@ class MacroFunctionCall extends MacroInvocation {
|
||||
}
|
||||
|
||||
int logicalLength(FunctionDeclarationEntry f) {
|
||||
result = count(Stmt s |
|
||||
result =
|
||||
count(Stmt s |
|
||||
s.getEnclosingFunction() = f.getFunction() and
|
||||
s.getFile() = f.getFile() and
|
||||
not s instanceof Block and
|
||||
@@ -34,7 +35,8 @@ int logicalLength(FunctionDeclarationEntry f) {
|
||||
}
|
||||
|
||||
int assertionCount(FunctionDeclarationEntry f) {
|
||||
result = count(Assertion a |
|
||||
result =
|
||||
count(Assertion a |
|
||||
a.getAsserted().getEnclosingFunction() = f.getFunction() and a.getFile() = f.getFile()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -34,22 +34,24 @@ where
|
||||
) and
|
||||
if accessType = 1
|
||||
then
|
||||
message = "This '" + ba.getName() + "' operation accesses " +
|
||||
plural(accessSize, " byte", " bytes") + " but the $@ is only " +
|
||||
plural(bufferSize, " byte", " bytes") + "."
|
||||
message =
|
||||
"This '" + ba.getName() + "' operation accesses " + plural(accessSize, " byte", " bytes") +
|
||||
" but the $@ is only " + plural(bufferSize, " byte", " bytes") + "."
|
||||
else
|
||||
if accessType = 2
|
||||
then
|
||||
message = "This '" + ba.getName() + "' operation may access " +
|
||||
plural(accessSize, " byte", " bytes") + " but the $@ is only " +
|
||||
plural(bufferSize, " byte", " bytes") + "."
|
||||
message =
|
||||
"This '" + ba.getName() + "' operation may access " + plural(accessSize, " byte", " bytes") +
|
||||
" but the $@ is only " + plural(bufferSize, " byte", " bytes") + "."
|
||||
else (
|
||||
if accessSize > 0
|
||||
then
|
||||
message = "This array indexing operation accesses byte offset " + (accessSize - 1) +
|
||||
message =
|
||||
"This array indexing operation accesses byte offset " + (accessSize - 1) +
|
||||
" but the $@ is only " + plural(bufferSize, " byte", " bytes") + "."
|
||||
else
|
||||
message = "This array indexing operation accesses a negative index " +
|
||||
message =
|
||||
"This array indexing operation accesses a negative index " +
|
||||
((accessSize / ba.getActualType().getSize()) - 1) + " on the $@."
|
||||
)
|
||||
select ba, message, bufferAlloc, bufferDesc
|
||||
|
||||
@@ -16,7 +16,7 @@ private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
predicate hasUpperBound(VariableAccess offsetExpr) {
|
||||
exists(BasicBlock controlled, LocalScopeVariable offsetVar, SsaDefinition def |
|
||||
exists(BasicBlock controlled, StackVariable offsetVar, SsaDefinition def |
|
||||
controlled.contains(offsetExpr) and
|
||||
linearBoundControls(controlled, def, offsetVar) and
|
||||
offsetExpr = def.getAUse(offsetVar)
|
||||
@@ -24,7 +24,7 @@ predicate hasUpperBound(VariableAccess offsetExpr) {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate linearBoundControls(BasicBlock controlled, SsaDefinition def, LocalScopeVariable offsetVar) {
|
||||
predicate linearBoundControls(BasicBlock controlled, SsaDefinition def, StackVariable offsetVar) {
|
||||
exists(GuardCondition guard, boolean branch |
|
||||
guard.controls(controlled, branch) and
|
||||
cmpWithLinearBound(guard, def.getAUse(offsetVar), Lesser(), branch)
|
||||
|
||||
@@ -16,28 +16,32 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.models.implementations.Memcpy
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Allocation
|
||||
|
||||
class MallocCall extends FunctionCall {
|
||||
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
|
||||
|
||||
Expr getAllocatedSize() {
|
||||
if this.getArgument(0) instanceof VariableAccess
|
||||
then
|
||||
exists(LocalScopeVariable v, ControlFlowNode def |
|
||||
definitionUsePair(v, def, this.getArgument(0)) and
|
||||
exprDefinition(v, def, result)
|
||||
predicate terminationProblem(AllocationExpr malloc, string msg) {
|
||||
// malloc(strlen(...))
|
||||
exists(StrlenCall strlen | DataFlow::localExprFlow(strlen, malloc.getSizeExpr())) and
|
||||
// flows to a call that implies this is a null-terminated string
|
||||
exists(ArrayFunction af, FunctionCall fc, int arg |
|
||||
DataFlow::localExprFlow(malloc, fc.getArgument(arg)) and
|
||||
fc.getTarget() = af and
|
||||
(
|
||||
// flows into null terminated string argument
|
||||
af.hasArrayWithNullTerminator(arg)
|
||||
or
|
||||
// flows into likely null terminated string argument (such as `strcpy`, `strcat`)
|
||||
af.hasArrayWithUnknownSize(arg)
|
||||
or
|
||||
// flows into string argument to a formatting function (such as `printf`)
|
||||
exists(int n, FormatLiteral fl |
|
||||
fc.getArgument(arg) = fc.(FormattingFunctionCall).getConversionArgument(n) and
|
||||
fl = fc.(FormattingFunctionCall).getFormat() and
|
||||
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
|
||||
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
|
||||
not fl.hasPrecision(n) // exclude: `%.*s`
|
||||
)
|
||||
else result = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
predicate terminationProblem(MallocCall malloc, string msg) {
|
||||
malloc.getAllocatedSize() instanceof StrlenCall and
|
||||
not exists(FunctionCall fc, MemcpyFunction memcpy, int ix |
|
||||
DataFlow::localExprFlow(malloc, fc.getArgument(ix)) and
|
||||
fc.getTarget() = memcpy and
|
||||
memcpy.hasArrayOutput(ix)
|
||||
)
|
||||
) and
|
||||
msg = "This allocation does not include space to null-terminate the string."
|
||||
}
|
||||
|
||||
@@ -16,12 +16,19 @@ import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" }
|
||||
|
||||
predicate isRandCallOrParent(Expr e) {
|
||||
isRandCall(e) or
|
||||
isRandCallOrParent(e.getAChild())
|
||||
}
|
||||
|
||||
predicate isRandValue(Expr e) {
|
||||
e.(FunctionCall).getTarget().getName() = "rand"
|
||||
isRandCall(e)
|
||||
or
|
||||
exists(MacroInvocation mi |
|
||||
e = mi.getExpr() and
|
||||
e.getAChild*().(FunctionCall).getTarget().getName() = "rand"
|
||||
isRandCallOrParent(e)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +56,12 @@ where
|
||||
// very noisy on codebases that started as 32-bit
|
||||
small.getExplicitlyConverted().getType().getSize() < 4 and
|
||||
// Ignore cases where integer promotion has occurred on /, -, or >> expressions.
|
||||
not getComparisonSize(large.(DivExpr).getLeftOperand().getExplicitlyConverted()) <= getComparisonSize(small) and
|
||||
not getComparisonSize(large.(SubExpr).getLeftOperand().getExplicitlyConverted()) <= getComparisonSize(small) and
|
||||
not getComparisonSize(large.(RShiftExpr).getLeftOperand().getExplicitlyConverted()) <= getComparisonSize(small) and
|
||||
not getComparisonSize(large.(DivExpr).getLeftOperand().getExplicitlyConverted()) <=
|
||||
getComparisonSize(small) and
|
||||
not getComparisonSize(large.(SubExpr).getLeftOperand().getExplicitlyConverted()) <=
|
||||
getComparisonSize(small) and
|
||||
not getComparisonSize(large.(RShiftExpr).getLeftOperand().getExplicitlyConverted()) <=
|
||||
getComparisonSize(small) and
|
||||
// ignore loop-invariant smaller variables
|
||||
loopVariant(small, l)
|
||||
select rel,
|
||||
|
||||
@@ -59,13 +59,15 @@ where
|
||||
(
|
||||
exists(BinaryLogicalOperation blop | blop.getAnOperand() = e1 |
|
||||
e1.getType().(TypedefType).hasName("HRESULT") and
|
||||
msg = "Usage of a type " + e1.getType().toString() +
|
||||
msg =
|
||||
"Usage of a type " + e1.getType().toString() +
|
||||
" as an argument of a binary logical operation"
|
||||
)
|
||||
or
|
||||
exists(UnaryLogicalOperation ulop | ulop.getAnOperand() = e1 |
|
||||
e1.getType().(TypedefType).hasName("HRESULT") and
|
||||
msg = "Usage of a type " + e1.getType().toString() +
|
||||
msg =
|
||||
"Usage of a type " + e1.getType().toString() +
|
||||
" as an argument of a unary logical operation"
|
||||
) and
|
||||
not isHresultBooleanConverted(e1)
|
||||
|
||||
@@ -82,7 +82,7 @@ FunctionCall stat(Expr path, Expr buf) {
|
||||
predicate referenceTo(Expr source, Expr use) {
|
||||
source = use
|
||||
or
|
||||
exists(SsaDefinition def, LocalScopeVariable v |
|
||||
exists(SsaDefinition def, StackVariable v |
|
||||
def.getAnUltimateDefiningValue(v) = source and def.getAUse(v) = use
|
||||
)
|
||||
}
|
||||
@@ -109,9 +109,7 @@ where
|
||||
)
|
||||
) and
|
||||
// checkUse and opUse refer to the same SSA variable
|
||||
exists(SsaDefinition def, LocalScopeVariable v |
|
||||
def.getAUse(v) = checkUse and def.getAUse(v) = opUse
|
||||
) and
|
||||
exists(SsaDefinition def, StackVariable v | def.getAUse(v) = checkUse and def.getAUse(v) = opUse) and
|
||||
// opUse looks like an operation on a filename
|
||||
fc = filenameOperation(opUse) and
|
||||
// the return value of check is used (possibly with one step of
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user