зеркало из https://github.com/github/codeql.git
C++: Autoformat everything
Some files that will change in #1736 have been spared. ./build -j4 target/jars/qlformat find ql/cpp/ql -name "*.ql" -print0 | xargs -0 target/jars/qlformat --input find ql/cpp/ql -name "*.qll" -print0 | xargs -0 target/jars/qlformat --input (cd ql && git checkout 'cpp/ql/src/semmle/code/cpp/ir/implementation/**/*SSA*.qll') buildutils-internal/scripts/pr-checks/sync-identical-files.py --latest
This commit is contained in:
Родитель
1784122929
Коммит
4ef5c9af62
|
@ -16,64 +16,56 @@ class SuppressionComment extends CppStyleComment {
|
|||
|
||||
SuppressionComment() {
|
||||
text = getContents().suffix(2) and
|
||||
( // match `lgtm[...]` anywhere in the comment
|
||||
(
|
||||
// match `lgtm[...]` anywhere in the comment
|
||||
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
|
||||
or
|
||||
// match `lgtm` at the start of the comment and after semicolon
|
||||
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _)
|
||||
.trim()
|
||||
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/** Gets the text in this comment, excluding the leading //. */
|
||||
string getText() {
|
||||
result = text
|
||||
}
|
||||
string getText() { result = text }
|
||||
|
||||
/** Gets the suppression annotation in this comment. */
|
||||
string getAnnotation() {
|
||||
result = annotation
|
||||
}
|
||||
string getAnnotation() { result = annotation }
|
||||
|
||||
/**
|
||||
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
|
||||
* to column `endcolumn` of line `endline` in file `filepath`.
|
||||
*/
|
||||
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
|
||||
* to column `endcolumn` of line `endline` in file `filepath`.
|
||||
*/
|
||||
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
|
||||
startcolumn = 1
|
||||
}
|
||||
|
||||
/** Gets the scope of this suppression. */
|
||||
SuppressionScope getScope() {
|
||||
result = this
|
||||
}
|
||||
SuppressionScope getScope() { result = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* The scope of an alert suppression comment.
|
||||
*/
|
||||
class SuppressionScope extends ElementBase {
|
||||
SuppressionScope() {
|
||||
this instanceof SuppressionComment
|
||||
}
|
||||
SuppressionScope() { this instanceof SuppressionComment }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
from SuppressionComment c
|
||||
select c, // suppression comment
|
||||
c.getText(), // text of suppression comment (excluding delimiters)
|
||||
c.getAnnotation(), // text of suppression annotation
|
||||
c.getScope() // scope of suppression
|
||||
|
||||
select c, // suppression comment
|
||||
c.getText(), // text of suppression comment (excluding delimiters)
|
||||
c.getAnnotation(), // text of suppression annotation
|
||||
c.getScope() // scope of suppression
|
||||
|
|
|
@ -42,9 +42,7 @@ newtype TVariableDeclarationInfo =
|
|||
*/
|
||||
class VariableDeclarationLine extends TVariableDeclarationInfo {
|
||||
Class c;
|
||||
|
||||
File f;
|
||||
|
||||
int line;
|
||||
|
||||
VariableDeclarationLine() {
|
||||
|
|
|
@ -42,7 +42,6 @@ predicate eraDate(int year, int month, int day) {
|
|||
year = 2019 and month = 5 and day = 1
|
||||
}
|
||||
|
||||
|
||||
predicate badStructInitialization(Element target, string message) {
|
||||
exists(
|
||||
StructLikeClass s, YearFieldAccess year, MonthFieldAccess month, DayFieldAccess day,
|
||||
|
|
|
@ -14,9 +14,7 @@
|
|||
import cpp
|
||||
|
||||
predicate declarationHasSideEffects(Variable v) {
|
||||
exists(Class c | c = v.getUnspecifiedType() |
|
||||
c.hasConstructor() or c.hasDestructor()
|
||||
)
|
||||
exists(Class c | c = v.getUnspecifiedType() | c.hasConstructor() or c.hasDestructor())
|
||||
}
|
||||
|
||||
from Variable v
|
||||
|
|
|
@ -26,20 +26,18 @@ class MinusOne extends NullValue {
|
|||
*/
|
||||
predicate mayCallFunction(Expr call, Function f) {
|
||||
call.(FunctionCall).getTarget() = f or
|
||||
call.(VariableCall).getVariable().getAnAssignedValue().
|
||||
getAChild*().(FunctionAccess).getTarget() = f
|
||||
call.(VariableCall).getVariable().getAnAssignedValue().getAChild*().(FunctionAccess).getTarget() =
|
||||
f
|
||||
}
|
||||
|
||||
predicate fopenCallOrIndirect(Expr e) {
|
||||
// direct fopen call
|
||||
fopenCall(e) and
|
||||
|
||||
// We are only interested in fopen calls that are
|
||||
// actually closed somehow, as FileNeverClosed
|
||||
// will catch those that aren't.
|
||||
fopenCallMayBeClosed(e)
|
||||
or
|
||||
|
||||
exists(ReturnStmt rtn |
|
||||
// indirect fopen call
|
||||
mayCallFunction(e, rtn.getEnclosingFunction()) and
|
||||
|
@ -86,7 +84,6 @@ class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassi
|
|||
exists(node.(AnalysedExpr).getNullSuccessor(v)) or
|
||||
fcloseCallOrIndirect(node, v) or
|
||||
assignedToFieldOrGlobal(v, node) or
|
||||
|
||||
// node may be used directly in query
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
@ -122,12 +119,10 @@ class FOpenReachability extends LocalScopeVariableReachabilityExt {
|
|||
}
|
||||
|
||||
override predicate isBarrier(
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next,
|
||||
LocalScopeVariable v)
|
||||
{
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable 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) |
|
||||
|
@ -172,6 +167,4 @@ where
|
|||
fopenVariableReaches(v, def, ret) and
|
||||
ret.getAChild*() = v.getAnAccess()
|
||||
)
|
||||
select
|
||||
def, "The file opened here may not be closed at $@.",
|
||||
ret, "this exit point"
|
||||
select def, "The file opened here may not be closed at $@.", ret, "this exit point"
|
||||
|
|
|
@ -18,20 +18,18 @@ import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
|||
*/
|
||||
predicate mayCallFunction(Expr call, Function f) {
|
||||
call.(FunctionCall).getTarget() = f or
|
||||
call.(VariableCall).getVariable().getAnAssignedValue().
|
||||
getAChild*().(FunctionAccess).getTarget() = f
|
||||
call.(VariableCall).getVariable().getAnAssignedValue().getAChild*().(FunctionAccess).getTarget() =
|
||||
f
|
||||
}
|
||||
|
||||
predicate allocCallOrIndirect(Expr e) {
|
||||
// direct alloc call
|
||||
isAllocationExpr(e) and
|
||||
|
||||
// We are only interested in alloc calls that are
|
||||
// actually freed somehow, as MemoryNeverFreed
|
||||
// will catch those that aren't.
|
||||
allocMayBeFreed(e)
|
||||
or
|
||||
|
||||
exists(ReturnStmt rtn |
|
||||
// indirect alloc call
|
||||
mayCallFunction(e, rtn.getEnclosingFunction()) and
|
||||
|
@ -64,7 +62,6 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
|
|||
newV.getAnAssignedValue() = reallocCall and
|
||||
node.(AnalysedExpr).getNonNullSuccessor(newV) = verified and
|
||||
// note: this case uses naive flow logic (getAnAssignedValue).
|
||||
|
||||
// special case: if the result of the 'realloc' is assigned to the
|
||||
// same variable, we don't descriminate properly between the old
|
||||
// and the new allocation; better to not consider this a free at
|
||||
|
@ -116,7 +113,6 @@ class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassi
|
|||
exists(node.(AnalysedExpr).getNullSuccessor(v)) or
|
||||
freeCallOrIndirect(node, v) or
|
||||
assignedToFieldOrGlobal(v, node) or
|
||||
|
||||
// node may be used directly in query
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
@ -152,12 +148,10 @@ class AllocReachability extends LocalScopeVariableReachabilityExt {
|
|||
}
|
||||
|
||||
override predicate isBarrier(
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next,
|
||||
LocalScopeVariable v)
|
||||
{
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable 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) |
|
||||
|
@ -202,6 +196,4 @@ where
|
|||
allocatedVariableReaches(v, def, ret) and
|
||||
ret.getAChild*() = v.getAnAccess()
|
||||
)
|
||||
select
|
||||
def, "The memory allocated here may not be released at $@.",
|
||||
ret, "this exit point"
|
||||
select def, "The memory allocated here may not be released at $@.", ret, "this exit point"
|
||||
|
|
|
@ -33,12 +33,10 @@ predicate sourceSized(FunctionCall fc, Expr src) {
|
|||
fc.getArgument(2) = size and
|
||||
src = v.getAnAccess() and
|
||||
size.getAChild+() = v.getAnAccess() and
|
||||
|
||||
// exception: `dest` is also referenced in the size argument
|
||||
not exists(Variable other |
|
||||
dest = other.getAnAccess() and size.getAChild+() = other.getAnAccess()
|
||||
) and
|
||||
|
||||
// exception: `src` and `dest` are both arrays of the same type and size
|
||||
not exists(ArrayType srctype, ArrayType desttype |
|
||||
dest.getType().getUnderlyingType() = desttype and
|
||||
|
|
|
@ -33,7 +33,6 @@ class BufferAccess extends ArrayExpr {
|
|||
staticBuffer(this.getArrayBase(), _, size) and
|
||||
size != 0
|
||||
) and
|
||||
|
||||
// exclude accesses in macro implementation of `strcmp`,
|
||||
// which are carefully controlled but can look dangerous.
|
||||
not exists(Macro m |
|
||||
|
|
|
@ -13,14 +13,11 @@ private import Options as CustomOptions
|
|||
|
||||
/**
|
||||
* Default predicates that specify information about the behavior of
|
||||
* the program being analyzed.
|
||||
* the program being analyzed.
|
||||
*/
|
||||
class Options extends string
|
||||
{
|
||||
Options() {
|
||||
this = "Options"
|
||||
}
|
||||
|
||||
class Options extends string {
|
||||
Options() { this = "Options" }
|
||||
|
||||
/**
|
||||
* Holds if we wish to override the "may return NULL" inference for this
|
||||
* call. If this holds, then rather than trying to infer whether this
|
||||
|
@ -60,7 +57,8 @@ class Options extends string
|
|||
* `noreturn` attribute.
|
||||
*/
|
||||
predicate exits(Function f) {
|
||||
f.getAnAttribute().hasName("noreturn") or
|
||||
f.getAnAttribute().hasName("noreturn")
|
||||
or
|
||||
exists(string name | f.hasGlobalName(name) |
|
||||
name = "exit" or
|
||||
name = "_exit" or
|
||||
|
@ -68,7 +66,8 @@ class Options extends string
|
|||
name = "__assert_fail" or
|
||||
name = "longjmp" or
|
||||
name = "__builtin_unreachable"
|
||||
) or
|
||||
)
|
||||
or
|
||||
CustomOptions::exits(f) // old Options.qll
|
||||
}
|
||||
|
||||
|
@ -108,14 +107,11 @@ class Options extends string
|
|||
fc.isInMacroExpansion()
|
||||
or
|
||||
// common way of sleeping using select:
|
||||
(fc.getTarget().hasGlobalName("select") and
|
||||
fc.getArgument(0).getValue() = "0")
|
||||
fc.getTarget().hasGlobalName("select") and
|
||||
fc.getArgument(0).getValue() = "0"
|
||||
or
|
||||
CustomOptions::okToIgnoreReturnValue(fc) // old Options.qll
|
||||
}
|
||||
}
|
||||
|
||||
Options getOptions()
|
||||
{
|
||||
any()
|
||||
}
|
||||
Options getOptions() { any() }
|
||||
|
|
|
@ -1,26 +1,36 @@
|
|||
/**
|
||||
* Provides heuristics to find "todo" and "fixme" comments (in all caps).
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
string getCommentTextCaptioned(Comment c, string caption) {
|
||||
(caption = "TODO" or caption = "FIXME") and
|
||||
exists (string commentContents, string commentBody, int offset, string interestingSuffix, int endOfLine, string dontCare, string captionedLine, string followingLine
|
||||
| commentContents = c.getContents()
|
||||
and commentContents.matches("%" + caption + "%")
|
||||
and // Add some '\n's so that any interesting line, and its
|
||||
// following line, will definitely begin and end with '\n'.
|
||||
commentBody = commentContents.regexpReplaceAll("(?s)^/\\*(.*)\\*/$|^//(.*)$", "\n$1$2\n\n")
|
||||
and 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.prefix(endOfLine).regexpReplaceAll("^[/*\\s]*" + caption + "\\s*:?", "").trim()
|
||||
and followingLine = interestingSuffix.prefix(interestingSuffix.indexOf("\n", 2, 0)).suffix(endOfLine).trim()
|
||||
and if captionedLine = ""
|
||||
then result = caption + " comment"
|
||||
else if followingLine = ""
|
||||
then result = caption + " comment: " + captionedLine
|
||||
else result = caption + " comment: " + captionedLine + " [...]"
|
||||
)
|
||||
(caption = "TODO" or caption = "FIXME") and
|
||||
exists(
|
||||
string commentContents, string commentBody, int offset, string interestingSuffix, int endOfLine,
|
||||
string dontCare, string captionedLine, string followingLine
|
||||
|
|
||||
commentContents = c.getContents() and
|
||||
commentContents.matches("%" + caption + "%") and
|
||||
// Add some '\n's so that any interesting line, and its
|
||||
// following line, will definitely begin and end with '\n'.
|
||||
commentBody = commentContents.regexpReplaceAll("(?s)^/\\*(.*)\\*/$|^//(.*)$", "\n$1$2\n\n") and
|
||||
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
|
||||
.prefix(endOfLine)
|
||||
.regexpReplaceAll("^[/*\\s]*" + caption + "\\s*:?", "")
|
||||
.trim() and
|
||||
followingLine = interestingSuffix
|
||||
.prefix(interestingSuffix.indexOf("\n", 2, 0))
|
||||
.suffix(endOfLine)
|
||||
.trim() and
|
||||
if captionedLine = ""
|
||||
then result = caption + " comment"
|
||||
else
|
||||
if followingLine = ""
|
||||
then result = caption + " comment: " + captionedLine
|
||||
else result = caption + " comment: " + captionedLine + " [...]"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,44 +6,42 @@ import cpp
|
|||
bindingset[line]
|
||||
private predicate looksLikeCode(string line) {
|
||||
exists(string trimmed |
|
||||
// trim leading and trailing whitespace, and HTML codes:
|
||||
// trim leading and trailing whitespace, and HTML codes:
|
||||
// * HTML entities in common notation (e.g. &gt; and &eacute;)
|
||||
// * HTML entities in decimal notation (e.g. a&#768;)
|
||||
// * HTML entities in hexadecimal notation (e.g. &#x705F;)
|
||||
trimmed = line.regexpReplaceAll("(?i)(^\\s+|&#?[a-z0-9]{1,31};|\\s+$)", "")
|
||||
|
|
||||
(
|
||||
// Match comment lines ending with '{', '}' or ';'
|
||||
trimmed.regexpMatch(".*[{};]") and
|
||||
(
|
||||
// Match comment lines ending with '{', '}' or ';'
|
||||
trimmed.regexpMatch(".*[{};]") and
|
||||
(
|
||||
// If this line looks like code because it ends with a closing
|
||||
// brace that's preceded by something other than whitespace ...
|
||||
trimmed.regexpMatch(".*.\\}")
|
||||
implies
|
||||
// ... then there has to be ") {" (or some variation)
|
||||
// on the line, suggesting it's a statement like `if`
|
||||
// or a function definition. Otherwise it's likely to be a
|
||||
// benign use of braces such as a JSON example or explanatory
|
||||
// pseudocode.
|
||||
trimmed.regexpMatch(".*(\\)|const|volatile|override|final|noexcept|&)\\s*\\{.*")
|
||||
)
|
||||
) or (
|
||||
// Match comment lines that look like preprocessor code
|
||||
trimmed.regexpMatch("#\\s*(include|define|undef|if|ifdef|ifndef|elif|else|endif|error|pragma)\\b.*")
|
||||
// If this line looks like code because it ends with a closing
|
||||
// brace that's preceded by something other than whitespace ...
|
||||
trimmed.regexpMatch(".*.\\}")
|
||||
implies
|
||||
// ... then there has to be ") {" (or some variation)
|
||||
// on the line, suggesting it's a statement like `if`
|
||||
// or a function definition. Otherwise it's likely to be a
|
||||
// benign use of braces such as a JSON example or explanatory
|
||||
// pseudocode.
|
||||
trimmed.regexpMatch(".*(\\)|const|volatile|override|final|noexcept|&)\\s*\\{.*")
|
||||
)
|
||||
) and (
|
||||
// Exclude lines that start with '>' or contain '@{' or '@}'.
|
||||
// To account for the code generated by protobuf, we also insist that the comment
|
||||
// does not begin with `optional` or `repeated` and end with a `;`, which would
|
||||
// normally be a quoted bit of literal `.proto` specification above the associated
|
||||
// declaration.
|
||||
// To account for emacs folding markers, we ignore any line containing
|
||||
// `{{{` or `}}}`.
|
||||
// Finally, some code tends to embed GUIDs in comments, so we also exclude those.
|
||||
not trimmed
|
||||
or
|
||||
// Match comment lines that look like preprocessor code
|
||||
trimmed
|
||||
.regexpMatch("#\\s*(include|define|undef|if|ifdef|ifndef|elif|else|endif|error|pragma)\\b.*")
|
||||
) and
|
||||
// Exclude lines that start with '>' or contain '@{' or '@}'.
|
||||
// To account for the code generated by protobuf, we also insist that the comment
|
||||
// does not begin with `optional` or `repeated` and end with a `;`, which would
|
||||
// normally be a quoted bit of literal `.proto` specification above the associated
|
||||
// declaration.
|
||||
// To account for emacs folding markers, we ignore any line containing
|
||||
// `{{{` or `}}}`.
|
||||
// Finally, some code tends to embed GUIDs in comments, so we also exclude those.
|
||||
not trimmed
|
||||
.regexpMatch("(>.*|.*[\\\\@][{}].*|(optional|repeated) .*;|.*(\\{\\{\\{|\\}\\}\\}).*|\\{[-0-9a-zA-Z]+\\})")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -76,7 +74,6 @@ private predicate preprocLine(File f, int line) {
|
|||
private int lineInFile(CppStyleComment c, File f) {
|
||||
f = c.getFile() and
|
||||
result = c.getLocation().getStartLine() and
|
||||
|
||||
// Ignore comments on the same line as a preprocessor directive.
|
||||
not preprocLine(f, result)
|
||||
}
|
||||
|
@ -119,12 +116,11 @@ class CommentBlock extends Comment {
|
|||
this instanceof CppStyleComment
|
||||
implies
|
||||
not exists(CppStyleComment pred, File f | lineInFile(pred, f) + 1 = lineInFile(this, f))
|
||||
) and (
|
||||
// Ignore comments on the same line as a preprocessor directive.
|
||||
not exists(Location l |
|
||||
l = this.getLocation() and
|
||||
preprocLine(l.getFile(), l.getStartLine())
|
||||
)
|
||||
) and
|
||||
// Ignore comments on the same line as a preprocessor directive.
|
||||
not exists(Location l |
|
||||
l = this.getLocation() and
|
||||
preprocLine(l.getFile(), l.getStartLine())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,26 +11,26 @@
|
|||
* @tags maintainability
|
||||
* documentation
|
||||
*/
|
||||
import cpp
|
||||
|
||||
import cpp
|
||||
|
||||
predicate isCommented(FunctionDeclarationEntry f) {
|
||||
exists(Comment c | c.getCommentedElement() = f)
|
||||
}
|
||||
|
||||
// Uses of 'f' in 'other'
|
||||
Call uses(File other, Function f) {
|
||||
result.getTarget() = f and result.getFile() = other
|
||||
}
|
||||
Call uses(File other, Function f) { result.getTarget() = f and result.getFile() = other }
|
||||
|
||||
from File callerFile, Function f, Call use, int numCalls
|
||||
where numCalls = strictcount(File other | exists(uses(other, f)) and other != f.getFile())
|
||||
and not isCommented(f.getADeclarationEntry())
|
||||
and not f instanceof Constructor
|
||||
and not f instanceof Destructor
|
||||
and not f.hasName("operator=")
|
||||
and f.getMetrics().getNumberOfLinesOfCode() >= 5
|
||||
and numCalls > 1
|
||||
and use = uses(callerFile, f)
|
||||
and callerFile != f.getFile()
|
||||
select f, "Functions called from other files should be documented (called from $@).", use, use.getFile().getRelativePath()
|
||||
where
|
||||
numCalls = strictcount(File other | exists(uses(other, f)) and other != f.getFile()) and
|
||||
not isCommented(f.getADeclarationEntry()) and
|
||||
not f instanceof Constructor and
|
||||
not f instanceof Destructor and
|
||||
not f.hasName("operator=") and
|
||||
f.getMetrics().getNumberOfLinesOfCode() >= 5 and
|
||||
numCalls > 1 and
|
||||
use = uses(callerFile, f) and
|
||||
callerFile != f.getFile()
|
||||
select f, "Functions called from other files should be documented (called from $@).", use,
|
||||
use.getFile().getRelativePath()
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
* documentation
|
||||
* external/cwe/cwe-546
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Documentation.CaptionedComments
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
* documentation
|
||||
* external/cwe/cwe-546
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Documentation.CaptionedComments
|
||||
|
||||
from Comment c, string message
|
||||
where message = getCommentTextCaptioned(c, "TODO")
|
||||
select c, message
|
||||
|
||||
|
|
|
@ -10,10 +10,14 @@
|
|||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from MetricFunction f, int n
|
||||
where n = f.getNumberOfLines() and n > 100 and
|
||||
f.getCommentRatio() <= 0.02 and
|
||||
not f.isMultiplyDefined()
|
||||
select f, "Poorly documented function: fewer than 2% comments for a function of " + n.toString() + " lines."
|
||||
where
|
||||
n = f.getNumberOfLines() and
|
||||
n > 100 and
|
||||
f.getCommentRatio() <= 0.02 and
|
||||
not f.isMultiplyDefined()
|
||||
select f,
|
||||
"Poorly documented function: fewer than 2% comments for a function of " + n.toString() + " lines."
|
||||
|
|
|
@ -11,20 +11,20 @@
|
|||
import cpp
|
||||
|
||||
predicate markedAsNonterminating(Loop l) {
|
||||
exists(Comment c | c.getContents().matches("%@non-terminating@%") |
|
||||
c.getCommentedElement() = l
|
||||
)
|
||||
exists(Comment c | c.getContents().matches("%@non-terminating@%") | c.getCommentedElement() = l)
|
||||
}
|
||||
|
||||
Stmt exitFrom(Loop l) {
|
||||
l.getAChild+() = result and
|
||||
(result instanceof ReturnStmt or
|
||||
exists(BreakStmt break | break = result |
|
||||
not l.getAChild*() = break.getTarget())
|
||||
(
|
||||
result instanceof ReturnStmt
|
||||
or
|
||||
exists(BreakStmt break | break = result | not l.getAChild*() = break.getTarget())
|
||||
)
|
||||
}
|
||||
|
||||
from Loop l, Stmt exit
|
||||
where markedAsNonterminating(l) and
|
||||
exit = exitFrom(l)
|
||||
where
|
||||
markedAsNonterminating(l) and
|
||||
exit = exitFrom(l)
|
||||
select exit, "$@ should not be exited.", l, "This permanent loop"
|
||||
|
|
|
@ -30,7 +30,8 @@ predicate upperBoundCheck(Loop loop, VariableAccess checked) {
|
|||
rop.getGreaterOperand().(VariableAccess).getTarget().isConst() or
|
||||
validVarForBound(loop, rop.getGreaterOperand().(VariableAccess).getTarget())
|
||||
) and
|
||||
not rop.getGreaterOperand() instanceof CharLiteral)
|
||||
not rop.getGreaterOperand() instanceof CharLiteral
|
||||
)
|
||||
}
|
||||
|
||||
predicate lowerBoundCheck(Loop loop, VariableAccess checked) {
|
||||
|
@ -43,20 +44,23 @@ predicate lowerBoundCheck(Loop loop, VariableAccess checked) {
|
|||
rop.getLesserOperand().(VariableAccess).getTarget().isConst() or
|
||||
validVarForBound(loop, rop.getLesserOperand().(VariableAccess).getTarget())
|
||||
) and
|
||||
not rop.getLesserOperand() instanceof CharLiteral)
|
||||
not rop.getLesserOperand() instanceof CharLiteral
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess getAnIncrement(Variable var) {
|
||||
result.getTarget() = var and
|
||||
(
|
||||
result.getParent() instanceof IncrementOperation
|
||||
or
|
||||
or
|
||||
exists(AssignAddExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() =
|
||||
any(AddExpr ae | ae.getAnOperand() = var.getAnAccess() and
|
||||
ae.getAnOperand().getValue().toInt() > 0))
|
||||
a.getRValue() = any(AddExpr ae |
|
||||
ae.getAnOperand() = var.getAnAccess() and
|
||||
ae.getAnOperand().getValue().toInt() > 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -64,62 +68,75 @@ VariableAccess getADecrement(Variable var) {
|
|||
result.getTarget() = var and
|
||||
(
|
||||
result.getParent() instanceof DecrementOperation
|
||||
or
|
||||
or
|
||||
exists(AssignSubExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() =
|
||||
any(SubExpr ae | ae.getLeftOperand() = var.getAnAccess() and
|
||||
ae.getRightOperand().getValue().toInt() > 0))
|
||||
a.getRValue() = any(SubExpr ae |
|
||||
ae.getLeftOperand() = var.getAnAccess() and
|
||||
ae.getRightOperand().getValue().toInt() > 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate inScope(Loop l, Stmt s) {
|
||||
l.getAChild*() = s
|
||||
}
|
||||
predicate inScope(Loop l, Stmt s) { l.getAChild*() = s }
|
||||
|
||||
predicate reachesNoInc(VariableAccess source, ControlFlowNode target) {
|
||||
(upperBoundCheck(_, source) and source.getASuccessor() = target) or
|
||||
exists(ControlFlowNode mid | reachesNoInc(source, mid) and not mid = getAnIncrement(source.getTarget()) |
|
||||
target = mid.getASuccessor() and
|
||||
inScope(source.getEnclosingStmt(), target.getEnclosingStmt()))
|
||||
upperBoundCheck(_, source) and source.getASuccessor() = target
|
||||
or
|
||||
exists(ControlFlowNode mid |
|
||||
reachesNoInc(source, mid) and not mid = getAnIncrement(source.getTarget())
|
||||
|
|
||||
target = mid.getASuccessor() and
|
||||
inScope(source.getEnclosingStmt(), target.getEnclosingStmt())
|
||||
)
|
||||
}
|
||||
|
||||
predicate reachesNoDec(VariableAccess source, ControlFlowNode target) {
|
||||
(lowerBoundCheck(_, source) and source.getASuccessor() = target) or
|
||||
exists(ControlFlowNode mid | reachesNoDec(source, mid) and not mid = getADecrement(source.getTarget()) |
|
||||
target = mid.getASuccessor() and
|
||||
inScope(source.getEnclosingStmt(), target.getEnclosingStmt()))
|
||||
}
|
||||
|
||||
predicate hasSafeBound(Loop l) {
|
||||
exists(VariableAccess bound | upperBoundCheck(l, bound) |
|
||||
not reachesNoInc(bound, bound)
|
||||
) or exists(VariableAccess bound | lowerBoundCheck(l, bound) |
|
||||
not reachesNoDec(bound, bound)
|
||||
) or exists(l.getControllingExpr().getValue())
|
||||
}
|
||||
|
||||
predicate markedAsNonterminating(Loop l) {
|
||||
exists(Comment c | c.getContents().matches("%@non-terminating@%") |
|
||||
c.getCommentedElement() = l
|
||||
lowerBoundCheck(_, source) and source.getASuccessor() = target
|
||||
or
|
||||
exists(ControlFlowNode mid |
|
||||
reachesNoDec(source, mid) and not mid = getADecrement(source.getTarget())
|
||||
|
|
||||
target = mid.getASuccessor() and
|
||||
inScope(source.getEnclosingStmt(), target.getEnclosingStmt())
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasSafeBound(Loop l) {
|
||||
exists(VariableAccess bound | upperBoundCheck(l, bound) | not reachesNoInc(bound, bound))
|
||||
or
|
||||
exists(VariableAccess bound | lowerBoundCheck(l, bound) | not reachesNoDec(bound, bound))
|
||||
or
|
||||
exists(l.getControllingExpr().getValue())
|
||||
}
|
||||
|
||||
predicate markedAsNonterminating(Loop l) {
|
||||
exists(Comment c | c.getContents().matches("%@non-terminating@%") | c.getCommentedElement() = l)
|
||||
}
|
||||
|
||||
from Loop loop, string msg
|
||||
where not hasSafeBound(loop) and
|
||||
not markedAsNonterminating(loop) and
|
||||
(
|
||||
(
|
||||
not upperBoundCheck(loop, _) and
|
||||
not lowerBoundCheck(loop, _) and
|
||||
msg = "This loop does not have a fixed bound."
|
||||
) or exists(VariableAccess bound | upperBoundCheck(loop, bound) and
|
||||
reachesNoInc(bound, bound) and
|
||||
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() + " is not always decremented in the loop body."
|
||||
)
|
||||
where
|
||||
not hasSafeBound(loop) and
|
||||
not markedAsNonterminating(loop) and
|
||||
(
|
||||
not upperBoundCheck(loop, _) and
|
||||
not lowerBoundCheck(loop, _) and
|
||||
msg = "This loop does not have a fixed bound."
|
||||
or
|
||||
exists(VariableAccess bound |
|
||||
upperBoundCheck(loop, bound) and
|
||||
reachesNoInc(bound, bound) and
|
||||
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() +
|
||||
" is not always decremented in the loop body."
|
||||
)
|
||||
)
|
||||
select loop, msg
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
import cpp
|
||||
|
||||
class RecursiveCall extends FunctionCall {
|
||||
RecursiveCall() {
|
||||
this.getTarget().calls*(this.getEnclosingFunction())
|
||||
}
|
||||
RecursiveCall() { this.getTarget().calls*(this.getEnclosingFunction()) }
|
||||
}
|
||||
|
||||
from RecursiveCall call, string msg
|
||||
where if (call.getTarget() = call.getEnclosingFunction()) then
|
||||
msg = "This call directly invokes its containing function $@."
|
||||
else
|
||||
msg = "The function " + call.getEnclosingFunction() + " is indirectly recursive via this call to $@."
|
||||
where
|
||||
if call.getTarget() = call.getEnclosingFunction()
|
||||
then msg = "This call directly invokes its containing function $@."
|
||||
else
|
||||
msg = "The function " + call.getEnclosingFunction() +
|
||||
" is indirectly recursive via this call to $@."
|
||||
select call, msg, call.getTarget(), call.getTarget().getName()
|
||||
|
|
|
@ -23,12 +23,17 @@ class Initialization extends Function {
|
|||
class Allocation extends FunctionCall {
|
||||
Allocation() {
|
||||
exists(string name | name = this.getTarget().getName() |
|
||||
name = "malloc" or name = "calloc" or name = "alloca" or
|
||||
name = "sbrk" or name = "valloc")
|
||||
name = "malloc" or
|
||||
name = "calloc" or
|
||||
name = "alloca" or
|
||||
name = "sbrk" or
|
||||
name = "valloc"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from Function f, Allocation a
|
||||
where not f instanceof Initialization and
|
||||
a.getEnclosingFunction() = f
|
||||
where
|
||||
not f instanceof Initialization and
|
||||
a.getEnclosingFunction() = f
|
||||
select a, "Dynamic memory allocation is only allowed during initialization."
|
||||
|
|
|
@ -14,8 +14,10 @@ import cpp
|
|||
class ForbiddenCall extends FunctionCall {
|
||||
ForbiddenCall() {
|
||||
exists(string name | name = this.getTarget().getName() |
|
||||
name = "task_delay" or name = "taskDelay" or
|
||||
name = "sleep" or name = "nanosleep" or
|
||||
name = "task_delay" or
|
||||
name = "taskDelay" or
|
||||
name = "sleep" or
|
||||
name = "nanosleep" or
|
||||
name = "clock_nanosleep"
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,20 +12,22 @@
|
|||
import Semaphores
|
||||
|
||||
LockOperation maybeLocked(Function f) {
|
||||
result.getEnclosingFunction() = f or
|
||||
exists(Function g | f.calls(g) |
|
||||
result = maybeLocked(g)
|
||||
)
|
||||
result.getEnclosingFunction() = f
|
||||
or
|
||||
exists(Function g | f.calls(g) | result = maybeLocked(g))
|
||||
}
|
||||
|
||||
predicate intraproc(LockOperation inner, string msg, LockOperation outer) {
|
||||
inner = outer.getAReachedNode() and outer.getLocked() != inner.getLocked() and
|
||||
inner = outer.getAReachedNode() and
|
||||
outer.getLocked() != inner.getLocked() and
|
||||
msg = "This lock operation is nested in a $@."
|
||||
}
|
||||
|
||||
predicate interproc(FunctionCall inner, string msg, LockOperation outer) {
|
||||
inner = outer.getAReachedNode() and
|
||||
exists(LockOperation lock | lock = maybeLocked(inner.getTarget()) and lock.getLocked() != outer.getLocked() |
|
||||
exists(LockOperation lock |
|
||||
lock = maybeLocked(inner.getTarget()) and lock.getLocked() != outer.getLocked()
|
||||
|
|
||||
msg = "This call may perform a " + lock.say() + " while under the effect of a $@."
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
import Semaphores
|
||||
|
||||
from FunctionCall call, string kind
|
||||
where (call instanceof SemaphoreCreation and kind = "semaphores") or
|
||||
(call instanceof LockingPrimitive and kind = "locking primitives")
|
||||
where
|
||||
call instanceof SemaphoreCreation and kind = "semaphores"
|
||||
or
|
||||
call instanceof LockingPrimitive and kind = "locking primitives"
|
||||
select call, "Use of " + kind + " should be avoided."
|
||||
|
|
|
@ -18,11 +18,15 @@ predicate lockOrder(LockOperation outer, LockOperation inner) {
|
|||
|
||||
int orderCount(Declaration outerLock, Declaration innerLock) {
|
||||
result = strictcount(LockOperation outer, LockOperation inner |
|
||||
outer.getLocked() = outerLock and inner.getLocked() = innerLock and
|
||||
lockOrder(outer, inner))
|
||||
outer.getLocked() = outerLock and
|
||||
inner.getLocked() = innerLock and
|
||||
lockOrder(outer, inner)
|
||||
)
|
||||
}
|
||||
|
||||
from LockOperation outer, LockOperation inner
|
||||
where lockOrder(outer, inner)
|
||||
and orderCount(outer.getLocked(), inner.getLocked()) <= orderCount(inner.getLocked(), outer.getLocked())
|
||||
where
|
||||
lockOrder(outer, inner) and
|
||||
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()
|
||||
|
|
|
@ -4,29 +4,31 @@
|
|||
|
||||
import cpp
|
||||
|
||||
|
||||
class SemaphoreCreation extends FunctionCall {
|
||||
SemaphoreCreation() {
|
||||
exists(string name | name = this.getTarget().getName() |
|
||||
name = "semBCreate" or name = "semMCreate" or name = "semCCreate" or
|
||||
name = "semBCreate" or
|
||||
name = "semMCreate" or
|
||||
name = "semCCreate" or
|
||||
name = "semRWCreate"
|
||||
)
|
||||
}
|
||||
|
||||
Variable getSemaphore() {
|
||||
result.getAnAccess() = this.getParent().(Assignment).getLValue()
|
||||
}
|
||||
Variable getSemaphore() { result.getAnAccess() = this.getParent().(Assignment).getLValue() }
|
||||
}
|
||||
|
||||
abstract class LockOperation extends FunctionCall {
|
||||
abstract UnlockOperation getMatchingUnlock();
|
||||
|
||||
abstract Declaration getLocked();
|
||||
|
||||
abstract string say();
|
||||
|
||||
ControlFlowNode getAReachedNode() {
|
||||
result = this or
|
||||
result = this
|
||||
or
|
||||
exists(ControlFlowNode mid | mid = getAReachedNode() |
|
||||
not(mid != this.getMatchingUnlock()) and
|
||||
not mid != this.getMatchingUnlock() and
|
||||
result = mid.getASuccessor()
|
||||
)
|
||||
}
|
||||
|
@ -39,24 +41,21 @@ abstract class UnlockOperation extends FunctionCall {
|
|||
class SemaphoreTake extends LockOperation {
|
||||
SemaphoreTake() {
|
||||
exists(string name | name = this.getTarget().getName() |
|
||||
name = "semTake" or
|
||||
name = "semTake"
|
||||
or
|
||||
// '_' is a wildcard, so this matches calls like
|
||||
// semBTakeScalable or semMTake_inline.
|
||||
name.matches("sem_Take%")
|
||||
)
|
||||
}
|
||||
|
||||
override Variable getLocked() {
|
||||
result.getAnAccess() = this.getArgument(0)
|
||||
}
|
||||
override Variable getLocked() { result.getAnAccess() = this.getArgument(0) }
|
||||
|
||||
override UnlockOperation getMatchingUnlock() {
|
||||
result.(SemaphoreGive).getLocked() = this.getLocked()
|
||||
}
|
||||
|
||||
override string say() {
|
||||
result = "semaphore take of " + getLocked().getName()
|
||||
}
|
||||
override string say() { result = "semaphore take of " + getLocked().getName() }
|
||||
}
|
||||
|
||||
class SemaphoreGive extends UnlockOperation {
|
||||
|
@ -67,14 +66,9 @@ class SemaphoreGive extends UnlockOperation {
|
|||
)
|
||||
}
|
||||
|
||||
Variable getLocked() {
|
||||
result.getAnAccess() = this.getArgument(0)
|
||||
}
|
||||
|
||||
override LockOperation getMatchingLock() {
|
||||
this = result.getMatchingUnlock()
|
||||
}
|
||||
Variable getLocked() { result.getAnAccess() = this.getArgument(0) }
|
||||
|
||||
override LockOperation getMatchingLock() { this = result.getMatchingUnlock() }
|
||||
}
|
||||
|
||||
class LockingPrimitive extends FunctionCall, LockOperation {
|
||||
|
@ -84,18 +78,16 @@ class LockingPrimitive extends FunctionCall, LockOperation {
|
|||
)
|
||||
}
|
||||
|
||||
override Function getLocked() {
|
||||
result = this.getTarget()
|
||||
}
|
||||
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()
|
||||
}
|
||||
override string say() { result = "call to " + getLocked().getName() }
|
||||
}
|
||||
|
||||
class UnlockingPrimitive extends FunctionCall, UnlockOperation {
|
||||
|
@ -105,11 +97,7 @@ class UnlockingPrimitive extends FunctionCall, UnlockOperation {
|
|||
)
|
||||
}
|
||||
|
||||
Function getLocked() {
|
||||
result = getMatchingLock().getLocked()
|
||||
}
|
||||
Function getLocked() { result = getMatchingLock().getLocked() }
|
||||
|
||||
override LockOperation getMatchingLock() {
|
||||
this = result.getMatchingUnlock()
|
||||
}
|
||||
override LockOperation getMatchingLock() { this = result.getMatchingUnlock() }
|
||||
}
|
||||
|
|
|
@ -15,8 +15,11 @@ import cpp
|
|||
class ForbiddenFunction extends Function {
|
||||
ForbiddenFunction() {
|
||||
exists(string name | name = this.getName() |
|
||||
name = "setjmp" or name = "longjmp" or
|
||||
name = "sigsetjmp" or name = "siglongjmp")
|
||||
name = "setjmp" or
|
||||
name = "longjmp" or
|
||||
name = "sigsetjmp" or
|
||||
name = "siglongjmp"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,15 +8,14 @@
|
|||
* readability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate hasInitializer(EnumConstant c) {
|
||||
c.getInitializer().fromSource()
|
||||
}
|
||||
predicate hasInitializer(EnumConstant c) { c.getInitializer().fromSource() }
|
||||
|
||||
/** Does this have an initializer that is not just a ref to another constant in the same enum? */
|
||||
predicate hasNonReferenceInitializer(EnumConstant c) {
|
||||
exists (Initializer init |
|
||||
exists(Initializer init |
|
||||
init = c.getInitializer() and
|
||||
init.fromSource() and
|
||||
not init.getExpr().(EnumConstantAccess).getTarget().getDeclaringEnum() = c.getDeclaringEnum()
|
||||
|
@ -24,14 +23,13 @@ predicate hasNonReferenceInitializer(EnumConstant c) {
|
|||
}
|
||||
|
||||
predicate hasReferenceInitializer(EnumConstant c) {
|
||||
exists (Initializer init |
|
||||
exists(Initializer init |
|
||||
init = c.getInitializer() and
|
||||
init.fromSource() and
|
||||
init.getExpr().(EnumConstantAccess).getTarget().getDeclaringEnum() = c.getDeclaringEnum()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// There exists another constant whose value is implicit, but it's
|
||||
// not the last one: the last value is okay to use to get the highest
|
||||
// enum value automatically. It can be followed by aliases though.
|
||||
|
@ -48,15 +46,16 @@ predicate enumThatHasConstantWithImplicitValue(Enum e) {
|
|||
}
|
||||
|
||||
from Enum e, int i
|
||||
where // e is at position i, and has an explicit value in the source - but
|
||||
// not just a reference to another enum constant
|
||||
hasNonReferenceInitializer(e.getEnumConstant(i)) and
|
||||
// but e is not the first or the last constant of the enum
|
||||
i != 0 and
|
||||
exists(e.getEnumConstant(i+1)) and
|
||||
// and there exists another constant whose value is implicit, but it's
|
||||
// not the last one: the last value is okay to use to get the highest
|
||||
// enum value automatically. It can be followed by aliases though.
|
||||
enumThatHasConstantWithImplicitValue(e)
|
||||
|
||||
select e, "In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized."
|
||||
where
|
||||
// e is at position i, and has an explicit value in the source - but
|
||||
// not just a reference to another enum constant
|
||||
hasNonReferenceInitializer(e.getEnumConstant(i)) and
|
||||
// but e is not the first or the last constant of the enum
|
||||
i != 0 and
|
||||
exists(e.getEnumConstant(i + 1)) and
|
||||
// and there exists another constant whose value is implicit, but it's
|
||||
// not the last one: the last value is okay to use to get the highest
|
||||
// enum value automatically. It can be followed by aliases though.
|
||||
enumThatHasConstantWithImplicitValue(e)
|
||||
select e,
|
||||
"In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized."
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
import cpp
|
||||
|
||||
from VariableDeclarationEntry v
|
||||
where v.getVariable() instanceof GlobalVariable and
|
||||
where
|
||||
v.getVariable() instanceof GlobalVariable and
|
||||
v.hasSpecifier("extern") and
|
||||
not v.getFile() instanceof HeaderFile
|
||||
select v, v.getName() + " should be declared only in a header file that is included as needed."
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
import cpp
|
||||
|
||||
from GlobalVariable v
|
||||
where forex(VariableAccess va | va.getTarget() = v | va.getFile() = v.getDefinitionLocation().getFile())
|
||||
and not v.hasSpecifier("static")
|
||||
and strictcount(v.getAnAccess().getEnclosingFunction()) > 1 // If = 1, variable should be function-scope.
|
||||
and not v.getADeclarationEntry().getFile() instanceof HeaderFile // intended to be accessed elsewhere
|
||||
select v, "The global variable " + v.getName() + " is not accessed outside of " + v.getFile().getBaseName()
|
||||
+ " and could be made static."
|
||||
where
|
||||
forex(VariableAccess va | va.getTarget() = v | va.getFile() = v.getDefinitionLocation().getFile()) and
|
||||
not v.hasSpecifier("static") and
|
||||
strictcount(v.getAnAccess().getEnclosingFunction()) > 1 and // If = 1, variable should be function-scope.
|
||||
not v.getADeclarationEntry().getFile() instanceof HeaderFile // intended to be accessed elsewhere
|
||||
select v,
|
||||
"The global variable " + v.getName() + " is not accessed outside of " + v.getFile().getBaseName() +
|
||||
" and could be made static."
|
||||
|
|
|
@ -12,8 +12,11 @@
|
|||
import cpp
|
||||
|
||||
from GlobalVariable v, Function f
|
||||
where v.getAnAccess().getEnclosingFunction() = f and
|
||||
strictcount(v.getAnAccess().getEnclosingFunction()) = 1 and
|
||||
forall(VariableAccess a | a = v.getAnAccess() | exists(a.getEnclosingFunction())) and
|
||||
not v.getADeclarationEntry().getFile() instanceof HeaderFile // intended to be accessed elsewhere
|
||||
select v, "The variable " + v.getName() + " is only accessed in $@ and should be scoped accordingly.", f, f.getName()
|
||||
where
|
||||
v.getAnAccess().getEnclosingFunction() = f and
|
||||
strictcount(v.getAnAccess().getEnclosingFunction()) = 1 and
|
||||
forall(VariableAccess a | a = v.getAnAccess() | exists(a.getEnclosingFunction())) and
|
||||
not v.getADeclarationEntry().getFile() instanceof HeaderFile // intended to be accessed elsewhere
|
||||
select v,
|
||||
"The variable " + v.getName() + " is only accessed in $@ and should be scoped accordingly.", f,
|
||||
f.getName()
|
||||
|
|
|
@ -8,27 +8,29 @@
|
|||
* readability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
class LocalVariableOrParameter extends Variable {
|
||||
LocalVariableOrParameter() {
|
||||
this instanceof LocalVariable or
|
||||
this instanceof LocalVariable
|
||||
or
|
||||
// A function declaration (i.e. "int foo(int bar);") doesn't usefully
|
||||
// shadow globals; the parameter should be on the version of the function
|
||||
// that has a body.
|
||||
exists(Parameter p | p = this |
|
||||
p.getFunction().getDefinitionLocation().getFile() = this.getFile() and
|
||||
exists(p.getFunction().getBlock()))
|
||||
p.getFunction().getDefinitionLocation().getFile() = this.getFile() and
|
||||
exists(p.getFunction().getBlock())
|
||||
)
|
||||
}
|
||||
|
||||
string type() {
|
||||
if this instanceof Parameter
|
||||
then result = "Parameter "
|
||||
else result = "Local variable "
|
||||
if this instanceof Parameter then result = "Parameter " else result = "Local variable "
|
||||
}
|
||||
}
|
||||
|
||||
from LocalVariableOrParameter lv, GlobalVariable gv
|
||||
where lv.getName() = gv.getName() and
|
||||
lv.getFile() = gv.getFile()
|
||||
where
|
||||
lv.getName() = gv.getName() and
|
||||
lv.getFile() = gv.getFile()
|
||||
select lv, lv.type() + lv.getName() + " hides the global variable $@.", gv, gv.getName()
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
|
||||
import cpp
|
||||
|
||||
/** In its full generality, the rule applies to all functions that
|
||||
/**
|
||||
* In its full generality, the rule applies to all functions that
|
||||
* return non-void, including things like 'printf' and 'close',
|
||||
* which are routinely not checked because the behavior on success
|
||||
* is the same as the behavior on failure. The recommendation is
|
||||
|
@ -27,13 +28,15 @@ predicate whitelist(Function f) {
|
|||
}
|
||||
|
||||
from FunctionCall c, string msg
|
||||
where not c.getTarget().getType() instanceof VoidType
|
||||
and not whitelist(c.getTarget())
|
||||
and
|
||||
(
|
||||
(c instanceof ExprInVoidContext and msg = "The return value of non-void function $@ is not checked.")
|
||||
or
|
||||
(definition(_, c.getParent()) and not definitionUsePair(_, c.getParent(), _) and
|
||||
msg = "$@'s return value is stored but not checked.")
|
||||
)
|
||||
where
|
||||
not c.getTarget().getType() instanceof VoidType and
|
||||
not whitelist(c.getTarget()) and
|
||||
(
|
||||
c instanceof ExprInVoidContext and
|
||||
msg = "The return value of non-void function $@ is not checked."
|
||||
or
|
||||
definition(_, c.getParent()) and
|
||||
not definitionUsePair(_, c.getParent(), _) and
|
||||
msg = "$@'s return value is stored but not checked."
|
||||
)
|
||||
select c, msg, c.getTarget() as f, f.getName()
|
||||
|
|
|
@ -12,16 +12,18 @@
|
|||
import JPL_C.Tasks
|
||||
|
||||
predicate flow(Parameter p, ControlFlowNode n) {
|
||||
(exists(p.getAnAccess()) and n = p.getFunction().getBlock()) or
|
||||
exists(ControlFlowNode mid | flow(p, mid) and not mid = p.getAnAccess() and n = mid.getASuccessor())
|
||||
exists(p.getAnAccess()) and n = p.getFunction().getBlock()
|
||||
or
|
||||
exists(ControlFlowNode mid |
|
||||
flow(p, mid) and not mid = p.getAnAccess() and n = mid.getASuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess firstAccess(Parameter p) {
|
||||
flow(p, result) and result = p.getAnAccess()
|
||||
}
|
||||
VariableAccess firstAccess(Parameter p) { flow(p, result) and result = p.getAnAccess() }
|
||||
|
||||
from Parameter p, VariableAccess va
|
||||
where va = firstAccess(p) and p.getFunction() instanceof PublicFunction and
|
||||
not exists(Expr e | e.isCondition() | e.getAChild*() = va)
|
||||
where
|
||||
va = firstAccess(p) and
|
||||
p.getFunction() instanceof PublicFunction and
|
||||
not exists(Expr e | e.isCondition() | e.getAChild*() = va)
|
||||
select va, "This use of parameter " + p.getName() + " has not been checked."
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
import semmle.code.cpp.commons.Assertions
|
||||
|
||||
from Assertion a, string value, string msg
|
||||
where value = a.getAsserted().getValue() and
|
||||
if value.toInt() = 0 then
|
||||
msg = "This assertion is always false."
|
||||
else
|
||||
msg = "This assertion is always true."
|
||||
where
|
||||
value = a.getAsserted().getValue() and
|
||||
if value.toInt() = 0
|
||||
then msg = "This assertion is always false."
|
||||
else msg = "This assertion is always true."
|
||||
select a.getAsserted(), msg
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
import semmle.code.cpp.commons.Assertions
|
||||
|
||||
from Function f
|
||||
where f.getMetrics().getNumberOfLinesOfCode() > 10 and
|
||||
where
|
||||
f.getMetrics().getNumberOfLinesOfCode() > 10 and
|
||||
not exists(Assertion a | a.getAsserted().getEnclosingFunction() = f)
|
||||
select f, "All functions of more than 10 lines should have at least one assertion."
|
||||
|
|
|
@ -13,11 +13,16 @@ import cpp
|
|||
|
||||
predicate allowedTypedefs(TypedefType t) {
|
||||
exists(string name | name = t.getName() |
|
||||
name = "I64" or name = "U64" or
|
||||
name = "I32" or name = "U32" or
|
||||
name = "I16" or name = "U16" or
|
||||
name = "I8" or name = "U8" or
|
||||
name = "F64" or name = "F32"
|
||||
name = "I64" or
|
||||
name = "U64" or
|
||||
name = "I32" or
|
||||
name = "U32" or
|
||||
name = "I16" or
|
||||
name = "U16" or
|
||||
name = "I8" or
|
||||
name = "U8" or
|
||||
name = "F64" or
|
||||
name = "F32"
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -25,7 +30,8 @@ predicate allowedTypedefs(TypedefType t) {
|
|||
* Gets a type which appears literally in the declaration of `d`.
|
||||
*/
|
||||
Type getAnImmediateUsedType(Declaration d) {
|
||||
d.isDefined() and (
|
||||
d.isDefined() and
|
||||
(
|
||||
result = d.(Function).getType() or
|
||||
result = d.(Variable).getType()
|
||||
)
|
||||
|
@ -48,7 +54,11 @@ predicate problematic(IntegralType t) {
|
|||
}
|
||||
|
||||
from Declaration d, Type usedType
|
||||
where usedType = getAUsedType*(getAnImmediateUsedType(d)) and problematic(usedType)
|
||||
where
|
||||
usedType = getAUsedType*(getAnImmediateUsedType(d)) and
|
||||
problematic(usedType) and
|
||||
// Ignore violations for which we do not have a valid location.
|
||||
and not(d.getLocation() instanceof UnknownLocation)
|
||||
select d, d.getName() + " uses the basic integral type " + usedType.getName() + " rather than a typedef with size and signedness."
|
||||
not d.getLocation() instanceof UnknownLocation
|
||||
select d,
|
||||
d.getName() + " uses the basic integral type " + usedType.getName() +
|
||||
" rather than a typedef with size and signedness."
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
import cpp
|
||||
|
||||
from BinaryOperation parent, BinaryOperation child
|
||||
where parent.getAnOperand() = child and not child.isParenthesised() and
|
||||
where
|
||||
parent.getAnOperand() = child and
|
||||
not child.isParenthesised() and
|
||||
(parent instanceof BinaryBitwiseOperation or child instanceof BinaryBitwiseOperation) and
|
||||
// Some benign cases...
|
||||
not (parent instanceof BitwiseAndExpr and child instanceof BitwiseAndExpr) and
|
||||
|
|
|
@ -46,10 +46,9 @@ predicate inherentlyUnsafe(Function f) {
|
|||
exists(Variable v | v.getAnAssignedValue().getEnclosingFunction() = f |
|
||||
v instanceof GlobalVariable or
|
||||
v.isStatic()
|
||||
) or
|
||||
exists(FunctionCall c | c.getEnclosingFunction() = f |
|
||||
inherentlyUnsafe(c.getTarget())
|
||||
)
|
||||
or
|
||||
exists(FunctionCall c | c.getEnclosingFunction() = f | inherentlyUnsafe(c.getTarget()))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,7 +58,9 @@ predicate inherentlyUnsafe(Function f) {
|
|||
* not inherently unsafe.
|
||||
*/
|
||||
predicate safeToCall(Function f) {
|
||||
forall(PointerType paramPointerType | paramPointerType = getAPointerType(f.getAParameter().getType()) |
|
||||
forall(PointerType paramPointerType |
|
||||
paramPointerType = getAPointerType(f.getAParameter().getType())
|
||||
|
|
||||
paramPointerType.getBaseType().isConst()
|
||||
) and
|
||||
not inherentlyUnsafe(f)
|
||||
|
@ -78,12 +79,16 @@ class BooleanExpression extends Expr {
|
|||
}
|
||||
|
||||
predicate hasSideEffect(Expr e) {
|
||||
e instanceof Assignment or
|
||||
e instanceof CrementOperation or
|
||||
e instanceof ExprCall or
|
||||
e instanceof Assignment
|
||||
or
|
||||
e instanceof CrementOperation
|
||||
or
|
||||
e instanceof ExprCall
|
||||
or
|
||||
exists(Function f | f = e.(FunctionCall).getTarget() and not safeFunctionWhitelist(f) |
|
||||
inherentlyUnsafe(f) or not safeToCall(f)
|
||||
) or
|
||||
)
|
||||
or
|
||||
hasSideEffect(e.getAChild())
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
import cpp
|
||||
|
||||
from PreprocessorDirective p
|
||||
where not p instanceof Include and
|
||||
not p instanceof Macro and
|
||||
not p instanceof PreprocessorIf and
|
||||
not p instanceof PreprocessorElif and
|
||||
not p instanceof PreprocessorElse and
|
||||
not p instanceof PreprocessorIfdef and
|
||||
not p instanceof PreprocessorIfndef and
|
||||
not p instanceof PreprocessorEndif
|
||||
where
|
||||
not p instanceof Include and
|
||||
not p instanceof Macro and
|
||||
not p instanceof PreprocessorIf and
|
||||
not p instanceof PreprocessorElif and
|
||||
not p instanceof PreprocessorElse and
|
||||
not p instanceof PreprocessorIfdef and
|
||||
not p instanceof PreprocessorIfndef and
|
||||
not p instanceof PreprocessorEndif
|
||||
select p, "This preprocessor directive is not allowed."
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
import cpp
|
||||
|
||||
from PreprocessorDirective i
|
||||
where (i instanceof PreprocessorIf or i instanceof PreprocessorIfdef or i instanceof PreprocessorIfndef)
|
||||
and not i.getFile() instanceof HeaderFile
|
||||
where
|
||||
(i instanceof PreprocessorIf or i instanceof PreprocessorIfdef or i instanceof PreprocessorIfndef) and
|
||||
not i.getFile() instanceof HeaderFile
|
||||
select i, "Use of conditional compilation must be kept to a minimum."
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
import cpp
|
||||
|
||||
from Macro m, string msg
|
||||
where (m.getHead().matches("%...%") and msg = "The macro " + m.getHead() + " is variadic, and hence not allowed.") or
|
||||
(m.getBody().matches("%##%") and msg = "The macro " + m.getHead() + " uses token pasting and is not allowed.")
|
||||
where
|
||||
m.getHead().matches("%...%") and
|
||||
msg = "The macro " + m.getHead() + " is variadic, and hence not allowed."
|
||||
or
|
||||
m.getBody().matches("%##%") and
|
||||
msg = "The macro " + m.getHead() + " uses token pasting and is not allowed."
|
||||
select m, msg
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
import cpp
|
||||
|
||||
int lineInBlock(File f) {
|
||||
exists(Block block, Location blockLocation | block.getFile() = f and blockLocation = block.getLocation()|
|
||||
result in [blockLocation.getStartLine()..blockLocation.getEndLine()]
|
||||
exists(Block block, Location blockLocation |
|
||||
block.getFile() = f and blockLocation = block.getLocation()
|
||||
|
|
||||
result in [blockLocation.getStartLine() .. blockLocation.getEndLine()]
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,43 +12,35 @@
|
|||
import cpp
|
||||
|
||||
class FileWithDirectives extends File {
|
||||
FileWithDirectives() {
|
||||
exists(Directive d | d.getFile() = this)
|
||||
}
|
||||
FileWithDirectives() { exists(Directive d | d.getFile() = this) }
|
||||
|
||||
int getDirectiveLine(Directive d) {
|
||||
d.getFile() = this and d.getLocation().getStartLine() = result
|
||||
}
|
||||
|
||||
int getDirectiveIndex(Directive d) {
|
||||
exists(int line | line = getDirectiveLine(d) |
|
||||
line = rank[result](getDirectiveLine(_))
|
||||
)
|
||||
exists(int line | line = getDirectiveLine(d) | line = rank[result](getDirectiveLine(_)))
|
||||
}
|
||||
|
||||
int depth(Directive d) {
|
||||
exists(int index | index = getDirectiveIndex(d) |
|
||||
(index = 1 and result = d.depthChange()) or
|
||||
exists(Directive prev | getDirectiveIndex(prev) = index-1 |
|
||||
index = 1 and result = d.depthChange()
|
||||
or
|
||||
exists(Directive prev | getDirectiveIndex(prev) = index - 1 |
|
||||
result = d.depthChange() + depth(prev)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Directive lastDirective() {
|
||||
getDirectiveIndex(result) = max(getDirectiveIndex(_))
|
||||
}
|
||||
Directive lastDirective() { getDirectiveIndex(result) = max(getDirectiveIndex(_)) }
|
||||
}
|
||||
|
||||
abstract class Directive extends PreprocessorDirective {
|
||||
abstract int depthChange();
|
||||
|
||||
abstract predicate mismatched();
|
||||
|
||||
int depth() {
|
||||
exists(FileWithDirectives f |
|
||||
f.depth(this) = result
|
||||
)
|
||||
}
|
||||
int depth() { exists(FileWithDirectives f | f.depth(this) = result) }
|
||||
}
|
||||
|
||||
class IfDirective extends Directive {
|
||||
|
@ -59,6 +51,7 @@ class IfDirective extends Directive {
|
|||
}
|
||||
|
||||
override int depthChange() { result = 1 }
|
||||
|
||||
override predicate mismatched() { none() }
|
||||
}
|
||||
|
||||
|
@ -69,24 +62,26 @@ class ElseDirective extends Directive {
|
|||
}
|
||||
|
||||
override int depthChange() { result = 0 }
|
||||
|
||||
override predicate mismatched() { depth() < 1 }
|
||||
}
|
||||
|
||||
class EndifDirective extends Directive {
|
||||
EndifDirective() {
|
||||
this instanceof PreprocessorEndif
|
||||
}
|
||||
EndifDirective() { this instanceof PreprocessorEndif }
|
||||
|
||||
override int depthChange() { result = -1 }
|
||||
|
||||
override predicate mismatched() { depth() < 0 }
|
||||
}
|
||||
|
||||
from FileWithDirectives f, Directive d, string msg
|
||||
where d.getFile() = f and
|
||||
if d.mismatched() then (
|
||||
msg = "'" + d + "' has no matching #if in file " + f.getBaseName() + "."
|
||||
) else (
|
||||
d = f.lastDirective() and d.depth() > 0 and msg = "File " + f.getBaseName() +
|
||||
" ends with " + d.depth() + " unterminated #if directives."
|
||||
where
|
||||
d.getFile() = f and
|
||||
if d.mismatched()
|
||||
then msg = "'" + d + "' has no matching #if in file " + f.getBaseName() + "."
|
||||
else (
|
||||
d = f.lastDirective() and
|
||||
d.depth() > 0 and
|
||||
msg = "File " + f.getBaseName() + " ends with " + d.depth() + " unterminated #if directives."
|
||||
)
|
||||
select d, msg
|
||||
|
|
|
@ -22,16 +22,16 @@ class OneLineStmt extends Stmt {
|
|||
}
|
||||
}
|
||||
|
||||
int numStmt(File f, int line) {
|
||||
result = strictcount(OneLineStmt o | o.onLine(f, line))
|
||||
}
|
||||
int numStmt(File f, int line) { result = strictcount(OneLineStmt o | o.onLine(f, line)) }
|
||||
|
||||
from File f, int line, OneLineStmt o, int cnt
|
||||
where numStmt(f, line) = cnt
|
||||
and cnt > 1
|
||||
and o.onLine(f, line)
|
||||
and o.getLocation().getStartColumn() =
|
||||
min(OneLineStmt other, int toMin
|
||||
| other.onLine(f, line) and toMin = other.getLocation().getStartColumn()
|
||||
| toMin)
|
||||
where
|
||||
numStmt(f, line) = cnt and
|
||||
cnt > 1 and
|
||||
o.onLine(f, line) and
|
||||
o.getLocation().getStartColumn() = min(OneLineStmt other, int toMin |
|
||||
other.onLine(f, line) and toMin = other.getLocation().getStartColumn()
|
||||
|
|
||||
toMin
|
||||
)
|
||||
select o, "This line contains " + cnt + " statements; only one is allowed."
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
import cpp
|
||||
|
||||
from DeclStmt d
|
||||
where exists(Variable v1, Variable v2 | v1 = d.getADeclaration() and v2 = d.getADeclaration() |
|
||||
where
|
||||
exists(Variable v1, Variable v2 | v1 = d.getADeclaration() and v2 = d.getADeclaration() |
|
||||
v1 != v2 and
|
||||
v1.getLocation().getStartLine() = v2.getLocation().getStartLine()
|
||||
)
|
||||
|
|
|
@ -26,6 +26,7 @@ string paramWarning(Function f) {
|
|||
}
|
||||
|
||||
from Function f, string msg
|
||||
where msg = lengthWarning(f) or
|
||||
msg = paramWarning(f)
|
||||
where
|
||||
msg = lengthWarning(f) or
|
||||
msg = paramWarning(f)
|
||||
select f, msg
|
||||
|
|
|
@ -15,7 +15,7 @@ string var(Variable v) {
|
|||
exists(int level | level = v.getType().getPointerIndirectionLevel() |
|
||||
level > 2 and
|
||||
result = "The type of " + v.getName() + " uses " + level +
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ string fun(Function f) {
|
|||
exists(int level | level = f.getType().getPointerIndirectionLevel() |
|
||||
level > 2 and
|
||||
result = "The return type of " + f.getName() + " uses " + level +
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
import cpp
|
||||
|
||||
from PointerDereferenceExpr e, int n
|
||||
where not e.getParent+() instanceof PointerDereferenceExpr
|
||||
and n = strictcount(PointerDereferenceExpr child | child.getParent+() = e)
|
||||
and n > 1
|
||||
where
|
||||
not e.getParent+() instanceof PointerDereferenceExpr and
|
||||
n = strictcount(PointerDereferenceExpr child | child.getParent+() = e) and
|
||||
n > 1
|
||||
select e, "This expression involves " + n + " levels of pointer dereference; 2 are allowed."
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
import cpp
|
||||
|
||||
from Macro m
|
||||
where forex(MacroInvocation mi | mi.getMacro() = m |
|
||||
where
|
||||
forex(MacroInvocation mi | mi.getMacro() = m |
|
||||
exists(PointerDereferenceExpr e, Location miLoc, Location eLoc | e = mi.getAGeneratedElement() |
|
||||
miLoc = mi.getLocation() and eLoc = e.getLocation() and
|
||||
miLoc = mi.getLocation() and
|
||||
eLoc = e.getLocation() and
|
||||
eLoc.getStartColumn() = miLoc.getStartColumn() and
|
||||
eLoc.getStartLine() = miLoc.getStartLine()
|
||||
)
|
||||
|
|
|
@ -20,7 +20,9 @@ predicate permissibleConversion(Type t) {
|
|||
}
|
||||
|
||||
from Expr e, Type converted
|
||||
where e.getType() instanceof FunctionPointerType and
|
||||
where
|
||||
e.getType() instanceof FunctionPointerType and
|
||||
e.getFullyConverted().getType() = converted and
|
||||
not permissibleConversion(converted)
|
||||
select e, "Function pointer converted to " + converted.getName() + ", which is not an integral type."
|
||||
select e,
|
||||
"Function pointer converted to " + converted.getName() + ", which is not an integral type."
|
||||
|
|
|
@ -12,8 +12,16 @@
|
|||
import cpp
|
||||
|
||||
int firstCodeLine(File f) {
|
||||
result = min(Declaration d, Location l, int toMin | (l = d.getLocation() and
|
||||
l.getFile() = f and not d.isInMacroExpansion()) and (toMin = l.getStartLine()) | toMin)
|
||||
result = min(Declaration d, Location l, int toMin |
|
||||
(
|
||||
l = d.getLocation() and
|
||||
l.getFile() = f and
|
||||
not d.isInMacroExpansion()
|
||||
) and
|
||||
toMin = l.getStartLine()
|
||||
|
|
||||
toMin
|
||||
)
|
||||
}
|
||||
|
||||
int badIncludeLine(File f, Include i) {
|
||||
|
@ -23,7 +31,9 @@ int badIncludeLine(File f, Include i) {
|
|||
}
|
||||
|
||||
from File f, Include i, int line
|
||||
where line = badIncludeLine(f, i) and
|
||||
where
|
||||
line = badIncludeLine(f, i) and
|
||||
line = min(badIncludeLine(f, _))
|
||||
select i, "'" + i.toString() + "' is preceded by code -- it should be moved above line " +
|
||||
firstCodeLine(f) + " in " + f.getBaseName() + "."
|
||||
select i,
|
||||
"'" + i.toString() + "' is preceded by code -- it should be moved above line " + firstCodeLine(f) +
|
||||
" in " + f.getBaseName() + "."
|
||||
|
|
|
@ -22,7 +22,8 @@ class Task extends Function {
|
|||
*/
|
||||
class PublicFunction extends Function {
|
||||
PublicFunction() {
|
||||
not this.isStatic() and (
|
||||
not this.isStatic() and
|
||||
(
|
||||
strictcount(Task t | t.calls+(this)) > 1 or
|
||||
not exists(Task t | t.getFile() = this.getFile())
|
||||
)
|
||||
|
|
|
@ -14,17 +14,21 @@
|
|||
* language-features
|
||||
* external/cwe/cwe-190
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from BitField bf
|
||||
where not bf.getUnspecifiedType().(IntegralType).isExplicitlySigned()
|
||||
and not bf.getUnspecifiedType().(IntegralType).isExplicitlyUnsigned()
|
||||
and not bf.getUnspecifiedType() instanceof Enum
|
||||
and not bf.getUnspecifiedType() instanceof BoolType
|
||||
where
|
||||
not bf.getUnspecifiedType().(IntegralType).isExplicitlySigned() and
|
||||
not bf.getUnspecifiedType().(IntegralType).isExplicitlyUnsigned() and
|
||||
not bf.getUnspecifiedType() instanceof Enum and
|
||||
not bf.getUnspecifiedType() instanceof BoolType and
|
||||
// At least for C programs on Windows, BOOL is a common typedef for a type
|
||||
// representing BoolType.
|
||||
and not bf.getType().hasName("BOOL")
|
||||
not bf.getType().hasName("BOOL") and
|
||||
// If this is true, then there cannot be unsigned sign extension or overflow.
|
||||
and not bf.getDeclaredNumBits() = bf.getType().getSize() * 8
|
||||
and not bf.isAnonymous()
|
||||
select bf, "Bit field " + bf.getName() + " of type " + bf.getUnderlyingType().getName() + " should have explicitly unsigned integral, explicitly signed integral, or enumeration type."
|
||||
not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and
|
||||
not bf.isAnonymous()
|
||||
select bf,
|
||||
"Bit field " + bf.getName() + " of type " + bf.getUnderlyingType().getName() +
|
||||
" should have explicitly unsigned integral, explicitly signed integral, or enumeration type."
|
||||
|
|
|
@ -13,8 +13,9 @@ private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
|||
* swapping the operands both ways round.
|
||||
*/
|
||||
private predicate addExpr(AddExpr plus, Expr a, Expr b) {
|
||||
(a = plus.getLeftOperand() and b = plus.getRightOperand()) or
|
||||
(b = plus.getLeftOperand() and a = plus.getRightOperand())
|
||||
a = plus.getLeftOperand() and b = plus.getRightOperand()
|
||||
or
|
||||
b = plus.getLeftOperand() and a = plus.getRightOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,12 +30,12 @@ private predicate addExpr(AddExpr plus, Expr a, Expr b) {
|
|||
* false.
|
||||
*/
|
||||
predicate badAdditionOverflowCheck(RelationalOperation cmp, AddExpr plus) {
|
||||
exists (Variable v, VariableAccess a1, VariableAccess a2, Expr b
|
||||
| addExpr(plus, a1, b) and
|
||||
exists(Variable v, VariableAccess a1, VariableAccess a2, Expr b |
|
||||
addExpr(plus, a1, b) and
|
||||
a1 = v.getAnAccess() and
|
||||
a2 = v.getAnAccess() and
|
||||
not exists (a1.getQualifier()) and // Avoid structure fields
|
||||
not exists (a2.getQualifier()) and // Avoid structure fields
|
||||
not exists(a1.getQualifier()) and // Avoid structure fields
|
||||
not exists(a2.getQualifier()) and // Avoid structure fields
|
||||
// Simple type-based check that the addition cannot overflow.
|
||||
exprMinVal(plus) <= exprMinVal(a1) + exprMinVal(b) and
|
||||
exprMaxVal(plus) > exprMaxVal(a1) and
|
||||
|
@ -43,5 +44,6 @@ predicate badAdditionOverflowCheck(RelationalOperation cmp, AddExpr plus) {
|
|||
exprMinVal(plus.getExplicitlyConverted()) <= exprMinVal(plus) and
|
||||
exprMaxVal(plus.getExplicitlyConverted()) >= exprMaxVal(plus) and
|
||||
cmp.getAnOperand() = plus and
|
||||
cmp.getAnOperand() = a2)
|
||||
cmp.getAnOperand() = a2
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
* correctness
|
||||
* types
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from EqualityOperation t, RemExpr lhs, Literal rhs
|
||||
where t.getLeftOperand() = lhs and
|
||||
t.getRightOperand() = rhs and
|
||||
lhs.getLeftOperand().getUnspecifiedType().(IntegralType).isSigned() and
|
||||
lhs.getRightOperand().getValue() = "2" and
|
||||
rhs.getValue() = "1"
|
||||
where
|
||||
t.getLeftOperand() = lhs and
|
||||
t.getRightOperand() = rhs and
|
||||
lhs.getLeftOperand().getUnspecifiedType().(IntegralType).isSigned() and
|
||||
lhs.getRightOperand().getValue() = "2" and
|
||||
rhs.getValue() = "1"
|
||||
select t, "Possibly invalid test for oddness. This will fail for negative numbers."
|
||||
|
|
|
@ -9,12 +9,14 @@
|
|||
* @tags reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from RelationalOperation e, BinaryBitwiseOperation lhs
|
||||
where lhs = e.getGreaterOperand() and
|
||||
lhs.getActualType().(IntegralType).isSigned() and
|
||||
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
|
||||
e.getLesserOperand().getValue() = "0" and
|
||||
not e.isAffectedByMacro()
|
||||
where
|
||||
lhs = e.getGreaterOperand() and
|
||||
lhs.getActualType().(IntegralType).isSigned() and
|
||||
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
|
||||
e.getLesserOperand().getValue() = "0" and
|
||||
not e.isAffectedByMacro()
|
||||
select e, "Potential unsafe sign check of a bitwise operation."
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
import cpp
|
||||
|
||||
from ComparisonOperation co, ComparisonOperation chco
|
||||
where co.getAChild() = chco and not chco.isParenthesised()
|
||||
|
|
|
@ -25,11 +25,15 @@ import PointlessSelfComparison
|
|||
* `parent = 2 * child`
|
||||
*/
|
||||
private predicate linearChild(Expr parent, Expr child, float multiplier) {
|
||||
(child = parent.(AddExpr).getAChild() and multiplier = 1.0) or
|
||||
(child = parent.(SubExpr).getLeftOperand() and multiplier = 1.0) or
|
||||
(child = parent.(SubExpr).getRightOperand() and multiplier = -1.0) or
|
||||
(child = parent.(UnaryPlusExpr).getOperand() and multiplier = 1.0) or
|
||||
(child = parent.(UnaryMinusExpr).getOperand() and multiplier = -1.0)
|
||||
child = parent.(AddExpr).getAChild() and multiplier = 1.0
|
||||
or
|
||||
child = parent.(SubExpr).getLeftOperand() and multiplier = 1.0
|
||||
or
|
||||
child = parent.(SubExpr).getRightOperand() and multiplier = -1.0
|
||||
or
|
||||
child = parent.(UnaryPlusExpr).getOperand() and multiplier = 1.0
|
||||
or
|
||||
child = parent.(UnaryMinusExpr).getOperand() and multiplier = -1.0
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,18 +45,19 @@ private predicate linearChild(Expr parent, Expr child, float multiplier) {
|
|||
* In this example, `x` has multiplier 4, `y` has multiplier -1, and `z`
|
||||
* has multiplier -3 (multipliers from the right hand child are negated).
|
||||
*/
|
||||
private predicate cmpLinearSubExpr(
|
||||
ComparisonOperation cmp, Expr child, float multiplier) {
|
||||
not convertedExprMightOverflow(child)
|
||||
and
|
||||
((child = cmp.getLeftOperand() and multiplier = 1.0)
|
||||
or
|
||||
(child = cmp.getRightOperand() and multiplier = -1.0)
|
||||
or
|
||||
exists (Expr parent, float m1, float m2
|
||||
| cmpLinearSubExpr(cmp, parent, m1) and
|
||||
linearChild(parent, child, m2) and
|
||||
multiplier = m1 * m2))
|
||||
private predicate cmpLinearSubExpr(ComparisonOperation cmp, Expr child, float multiplier) {
|
||||
not convertedExprMightOverflow(child) and
|
||||
(
|
||||
child = cmp.getLeftOperand() and multiplier = 1.0
|
||||
or
|
||||
child = cmp.getRightOperand() and multiplier = -1.0
|
||||
or
|
||||
exists(Expr parent, float m1, float m2 |
|
||||
cmpLinearSubExpr(cmp, parent, m1) and
|
||||
linearChild(parent, child, m2) and
|
||||
multiplier = m1 * m2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,9 +65,10 @@ private predicate cmpLinearSubExpr(
|
|||
* `child` is an access of variable `v`.
|
||||
*/
|
||||
private predicate cmpLinearSubVariable(
|
||||
ComparisonOperation cmp, Variable v, VariableAccess child, float multiplier) {
|
||||
ComparisonOperation cmp, Variable v, VariableAccess child, float multiplier
|
||||
) {
|
||||
v = child.getTarget() and
|
||||
not exists (child.getQualifier()) and
|
||||
not exists(child.getQualifier()) and
|
||||
cmpLinearSubExpr(cmp, child, multiplier)
|
||||
}
|
||||
|
||||
|
@ -75,23 +81,19 @@ private predicate cmpLinearSubVariable(
|
|||
* `v + x - v < y`
|
||||
* `v + x + v < y + 2*v`
|
||||
*/
|
||||
private predicate cancelingSubExprs(
|
||||
ComparisonOperation cmp, VariableAccess a1, VariableAccess a2) {
|
||||
exists (Variable v
|
||||
| exists (float m | m < 0 and cmpLinearSubVariable(cmp, v, a1, m)) and
|
||||
exists (float m | m > 0 and cmpLinearSubVariable(cmp, v, a2, m)))
|
||||
private predicate cancelingSubExprs(ComparisonOperation cmp, VariableAccess a1, VariableAccess a2) {
|
||||
exists(Variable v |
|
||||
exists(float m | m < 0 and cmpLinearSubVariable(cmp, v, a1, m)) and
|
||||
exists(float m | m > 0 and cmpLinearSubVariable(cmp, v, a2, m))
|
||||
)
|
||||
}
|
||||
|
||||
from ComparisonOperation cmp, VariableAccess a1, VariableAccess a2
|
||||
where
|
||||
cancelingSubExprs(cmp, a1, a2)
|
||||
|
||||
cancelingSubExprs(cmp, a1, a2) and
|
||||
// Most practical examples found by this query are instances of
|
||||
// BadAdditionOverflowCheck or PointlessSelfComparison.
|
||||
and not badAdditionOverflowCheck(cmp, _)
|
||||
and not pointlessSelfComparison(cmp)
|
||||
select
|
||||
cmp,
|
||||
"Comparison can be simplified by canceling $@ with $@.",
|
||||
a1, a1.getTarget().getName(),
|
||||
not badAdditionOverflowCheck(cmp, _) and
|
||||
not pointlessSelfComparison(cmp)
|
||||
select cmp, "Comparison can be simplified by canceling $@ with $@.", a1, a1.getTarget().getName(),
|
||||
a2, a2.getTarget().getName()
|
||||
|
|
|
@ -10,11 +10,15 @@
|
|||
* @tags reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from EqualityOperation ro, Expr left, Expr right
|
||||
where left = ro.getLeftOperand() and right = ro.getRightOperand() and
|
||||
ro.getAnOperand().getExplicitlyConverted().getType().getUnderlyingType() instanceof FloatingPointType and
|
||||
not ro.getAnOperand().isConstant() and // comparisons to constants generate too many false positives
|
||||
not left.(VariableAccess).getTarget() = right.(VariableAccess).getTarget() // skip self comparison
|
||||
where
|
||||
left = ro.getLeftOperand() and
|
||||
right = ro.getRightOperand() and
|
||||
ro.getAnOperand().getExplicitlyConverted().getType().getUnderlyingType() instanceof
|
||||
FloatingPointType and
|
||||
not ro.getAnOperand().isConstant() and // comparisons to constants generate too many false positives
|
||||
not left.(VariableAccess).getTarget() = right.(VariableAccess).getTarget() // skip self comparison
|
||||
select ro, "Equality test on floating point values may not behave as expected."
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
* external/cwe/cwe-197
|
||||
* external/cwe/cwe-681
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
|
||||
|
@ -28,9 +29,12 @@ import semmle.code.cpp.controlflow.SSA
|
|||
* controlled, so we consider it less likely to cause an overflow.
|
||||
*/
|
||||
predicate likelySmall(Expr e) {
|
||||
e.isConstant() or
|
||||
e.getType().getSize() <= 1 or
|
||||
e.(ArrayExpr).getArrayBase().getType().(ArrayType).getBaseType().isConst() or
|
||||
e.isConstant()
|
||||
or
|
||||
e.getType().getSize() <= 1
|
||||
or
|
||||
e.(ArrayExpr).getArrayBase().getType().(ArrayType).getBaseType().isConst()
|
||||
or
|
||||
exists(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = e and
|
||||
likelySmall(def.getDefiningValue(v))
|
||||
|
@ -41,9 +45,7 @@ predicate likelySmall(Expr e) {
|
|||
* Gets an operand of a multiply expression (we need the restriction
|
||||
* to multiply expressions to get the correct transitive closure).
|
||||
*/
|
||||
Expr getMulOperand(MulExpr me) {
|
||||
result = me.getAnOperand()
|
||||
}
|
||||
Expr getMulOperand(MulExpr me) { result = me.getAnOperand() }
|
||||
|
||||
/**
|
||||
* Gets the number of non-constant operands of a multiply expression,
|
||||
|
@ -56,53 +58,50 @@ Expr getMulOperand(MulExpr me) {
|
|||
*/
|
||||
int getEffectiveMulOperands(MulExpr me) {
|
||||
result = count(Expr op |
|
||||
op = getMulOperand*(me) and
|
||||
not op instanceof MulExpr and
|
||||
not likelySmall(op)
|
||||
)
|
||||
op = getMulOperand*(me) and
|
||||
not op instanceof MulExpr and
|
||||
not likelySmall(op)
|
||||
)
|
||||
}
|
||||
|
||||
from MulExpr me, Type t1, Type t2
|
||||
where t1 = me.getType().getUnderlyingType() and
|
||||
t2 = me.getConversion().getType().getUnderlyingType() and
|
||||
t1.getSize() < t2.getSize() and
|
||||
(
|
||||
(
|
||||
t1.getUnspecifiedType() instanceof IntegralType and
|
||||
t2.getUnspecifiedType() instanceof IntegralType
|
||||
) or (
|
||||
t1.getUnspecifiedType() instanceof FloatingPointType and
|
||||
t2.getUnspecifiedType() instanceof FloatingPointType
|
||||
)
|
||||
) and
|
||||
|
||||
// exclude explicit conversions
|
||||
me.getConversion().isCompilerGenerated() and
|
||||
|
||||
// require the multiply to have two non-constant operands
|
||||
// (the intuition here is that multiplying two unknowns is
|
||||
// much more likely to produce a result that needs significantly
|
||||
// more bits than the operands did, and thus requires a larger
|
||||
// type).
|
||||
getEffectiveMulOperands(me) >= 2 and
|
||||
|
||||
// exclude varargs promotions
|
||||
not exists(FunctionCall fc, int vararg |
|
||||
fc.getArgument(vararg) = me and
|
||||
vararg >= fc.getTarget().getNumberOfParameters()
|
||||
) and
|
||||
|
||||
// exclude cases where the type was made bigger by a literal
|
||||
// (compared to other cases such as assignment, this is more
|
||||
// likely to be a trivial accident rather than suggesting a
|
||||
// larger type is needed for the result).
|
||||
not exists(Expr other, Expr e |
|
||||
other = me.getParent().(BinaryOperation).getAnOperand() and
|
||||
not other = me and
|
||||
(
|
||||
e = other or
|
||||
e = other.(BinaryOperation).getAnOperand*()
|
||||
) and
|
||||
e.(Literal).getType().getSize() = t2.getSize()
|
||||
)
|
||||
select me, "Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '" + me.getFullyConverted().getType().toString() + "'."
|
||||
where
|
||||
t1 = me.getType().getUnderlyingType() and
|
||||
t2 = me.getConversion().getType().getUnderlyingType() and
|
||||
t1.getSize() < t2.getSize() and
|
||||
(
|
||||
t1.getUnspecifiedType() instanceof IntegralType and
|
||||
t2.getUnspecifiedType() instanceof IntegralType
|
||||
or
|
||||
t1.getUnspecifiedType() instanceof FloatingPointType and
|
||||
t2.getUnspecifiedType() instanceof FloatingPointType
|
||||
) and
|
||||
// exclude explicit conversions
|
||||
me.getConversion().isCompilerGenerated() and
|
||||
// require the multiply to have two non-constant operands
|
||||
// (the intuition here is that multiplying two unknowns is
|
||||
// much more likely to produce a result that needs significantly
|
||||
// more bits than the operands did, and thus requires a larger
|
||||
// type).
|
||||
getEffectiveMulOperands(me) >= 2 and
|
||||
// exclude varargs promotions
|
||||
not exists(FunctionCall fc, int vararg |
|
||||
fc.getArgument(vararg) = me and
|
||||
vararg >= fc.getTarget().getNumberOfParameters()
|
||||
) and
|
||||
// exclude cases where the type was made bigger by a literal
|
||||
// (compared to other cases such as assignment, this is more
|
||||
// likely to be a trivial accident rather than suggesting a
|
||||
// larger type is needed for the result).
|
||||
not exists(Expr other, Expr e |
|
||||
other = me.getParent().(BinaryOperation).getAnOperand() and
|
||||
not other = me and
|
||||
(
|
||||
e = other or
|
||||
e = other.(BinaryOperation).getAnOperand*()
|
||||
) and
|
||||
e.(Literal).getType().getSize() = t2.getSize()
|
||||
)
|
||||
select me,
|
||||
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
|
||||
+ me.getFullyConverted().getType().toString() + "'."
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.commons.Exclusions
|
||||
private import semmle.code.cpp.rangeanalysis.PointlessComparison
|
||||
|
@ -25,38 +26,40 @@ import UnsignedGEZero
|
|||
// So to reduce the number of false positives, we do not report a result if
|
||||
// the comparison is in a macro expansion. Similarly for template
|
||||
// instantiations.
|
||||
from
|
||||
ComparisonOperation cmp, SmallSide ss,
|
||||
float left, float right, boolean value,
|
||||
string reason
|
||||
from ComparisonOperation cmp, SmallSide ss, float left, float right, boolean value, string reason
|
||||
where
|
||||
not cmp.isInMacroExpansion() and
|
||||
not cmp.isFromTemplateInstantiation(_) and
|
||||
not functionContainsDisabledCode(cmp.getEnclosingFunction()) and
|
||||
reachablePointlessComparison(cmp, left, right, value, ss) and
|
||||
|
||||
// a comparison between an enum and zero is always valid because whether
|
||||
// the underlying type of an enum is signed is compiler-dependent
|
||||
not exists (Expr e, ConstantZero z
|
||||
| relOpWithSwap(cmp, e.getFullyConverted(), z, _, _) and
|
||||
e.getUnderlyingType() instanceof Enum) and
|
||||
|
||||
not exists(Expr e, ConstantZero z |
|
||||
relOpWithSwap(cmp, e.getFullyConverted(), z, _, _) and
|
||||
e.getUnderlyingType() instanceof Enum
|
||||
) and
|
||||
// Construct a reason for the message. Something like: x >= 5 and 3 >= y.
|
||||
exists (string cmpOp, string leftReason, string rightReason
|
||||
| ((ss = LeftIsSmaller() and cmpOp = " <= ") or
|
||||
(ss = RightIsSmaller() and cmpOp = " >= ")) and
|
||||
exists(string cmpOp, string leftReason, string rightReason |
|
||||
(
|
||||
ss = LeftIsSmaller() and cmpOp = " <= "
|
||||
or
|
||||
ss = RightIsSmaller() and cmpOp = " >= "
|
||||
) and
|
||||
leftReason = cmp.getLeftOperand().toString() + cmpOp + left.toString() and
|
||||
rightReason = right.toString() + cmpOp + cmp.getRightOperand().toString() and
|
||||
// If either of the operands is constant, then don't include it.
|
||||
(if cmp.getLeftOperand().isConstant()
|
||||
then if cmp.getRightOperand().isConstant()
|
||||
then none() // Both operands are constant so don't create a message.
|
||||
else reason = rightReason
|
||||
else if cmp.getRightOperand().isConstant()
|
||||
then reason = leftReason
|
||||
else reason = leftReason + " and " + rightReason)) and
|
||||
|
||||
(
|
||||
if cmp.getLeftOperand().isConstant()
|
||||
then
|
||||
if cmp.getRightOperand().isConstant()
|
||||
then none() // Both operands are constant so don't create a message.
|
||||
else reason = rightReason
|
||||
else
|
||||
if cmp.getRightOperand().isConstant()
|
||||
then reason = leftReason
|
||||
else reason = leftReason + " and " + rightReason
|
||||
)
|
||||
) and
|
||||
// Don't report results which have already been reported by UnsignedGEZero.
|
||||
not unsignedGEZero(cmp, _)
|
||||
select
|
||||
cmp, "Comparison is always " + value.toString() + " because " + reason + "."
|
||||
select cmp, "Comparison is always " + value.toString() + " because " + reason + "."
|
||||
|
|
|
@ -15,15 +15,15 @@ import cpp
|
|||
import PointlessSelfComparison
|
||||
|
||||
from ComparisonOperation cmp
|
||||
where pointlessSelfComparison(cmp)
|
||||
and not nanTest(cmp)
|
||||
and not overflowTest(cmp)
|
||||
and not exists(MacroInvocation mi |
|
||||
// cmp is in mi
|
||||
mi.getAnExpandedElement() = cmp and
|
||||
|
||||
// and cmp was apparently not passed in as a macro parameter
|
||||
cmp.getLocation().getStartLine() = mi.getLocation().getStartLine() and
|
||||
cmp.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
|
||||
)
|
||||
where
|
||||
pointlessSelfComparison(cmp) and
|
||||
not nanTest(cmp) and
|
||||
not overflowTest(cmp) and
|
||||
not exists(MacroInvocation mi |
|
||||
// cmp is in mi
|
||||
mi.getAnExpandedElement() = cmp and
|
||||
// and cmp was apparently not passed in as a macro parameter
|
||||
cmp.getLocation().getStartLine() = mi.getLocation().getStartLine() and
|
||||
cmp.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
|
||||
)
|
||||
select cmp, "Self comparison."
|
||||
|
|
|
@ -21,15 +21,16 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
|||
* `<=`, `>=`).
|
||||
*/
|
||||
predicate pointlessSelfComparison(ComparisonOperation cmp) {
|
||||
exists (Variable v, VariableAccess lhs, VariableAccess rhs
|
||||
| lhs = cmp.getLeftOperand() and
|
||||
exists(Variable v, VariableAccess lhs, VariableAccess rhs |
|
||||
lhs = cmp.getLeftOperand() and
|
||||
rhs = cmp.getRightOperand() and
|
||||
lhs = v.getAnAccess() and
|
||||
rhs = v.getAnAccess() and
|
||||
not exists (lhs.getQualifier()) and // Avoid structure fields
|
||||
not exists (rhs.getQualifier()) and // Avoid structure fields
|
||||
not exists(lhs.getQualifier()) and // Avoid structure fields
|
||||
not exists(rhs.getQualifier()) and // Avoid structure fields
|
||||
not convertedExprMightOverflow(lhs) and
|
||||
not convertedExprMightOverflow(rhs))
|
||||
not convertedExprMightOverflow(rhs)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,10 +45,10 @@ predicate pointlessSelfComparison(ComparisonOperation cmp) {
|
|||
*/
|
||||
predicate nanTest(EqualityOperation cmp) {
|
||||
pointlessSelfComparison(cmp) and
|
||||
exists (Type t
|
||||
| t = cmp.getLeftOperand().getUnspecifiedType()
|
||||
| t instanceof FloatingPointType or
|
||||
t instanceof TemplateParameter)
|
||||
exists(Type t | t = cmp.getLeftOperand().getUnspecifiedType() |
|
||||
t instanceof FloatingPointType or
|
||||
t instanceof TemplateParameter
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import UnsignedGEZero
|
||||
|
||||
|
|
|
@ -18,20 +18,16 @@ class ConstantZero extends Expr {
|
|||
class UnsignedGEZero extends GEExpr {
|
||||
UnsignedGEZero() {
|
||||
this.getRightOperand() instanceof ConstantZero and
|
||||
|
||||
// left operand was a signed or unsigned IntegralType before conversions
|
||||
// (not a pointer, checking a pointer >= 0 is an entirely different mistake)
|
||||
// (not an enum, as the fully converted type of an enum is compiler dependent
|
||||
// so checking an enum >= 0 is always reasonable)
|
||||
getLeftOperand().getUnderlyingType() instanceof IntegralType and
|
||||
|
||||
exists(Expr ue |
|
||||
// ue is some conversion of the left operand
|
||||
ue = getLeftOperand().getConversion*() and
|
||||
|
||||
// ue is unsigned
|
||||
ue.getUnderlyingType().(IntegralType).isUnsigned() and
|
||||
|
||||
// ue may be converted to zero or more strictly larger possibly signed types
|
||||
// before it is fully converted
|
||||
forall(Expr following | following = ue.getConversion+() |
|
||||
|
@ -45,7 +41,6 @@ predicate unsignedGEZero(UnsignedGEZero ugez, string msg) {
|
|||
not exists(MacroInvocation mi |
|
||||
// ugez is in mi
|
||||
mi.getAnExpandedElement() = ugez and
|
||||
|
||||
// and ugez was apparently not passed in as a macro parameter
|
||||
ugez.getLocation().getStartLine() = mi.getLocation().getStartLine() and
|
||||
ugez.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
|
||||
|
|
|
@ -16,8 +16,8 @@ import cpp
|
|||
* Gets a `do` ... `while` loop with a constant false condition.
|
||||
*/
|
||||
DoStmt getAFalseLoop() {
|
||||
result.getControllingExpr().getValue() = "0"
|
||||
and not result.getControllingExpr().isAffectedByMacro()
|
||||
result.getControllingExpr().getValue() = "0" and
|
||||
not result.getControllingExpr().isAffectedByMacro()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,22 +30,19 @@ DoStmt enclosingLoop(Stmt s) {
|
|||
exists(Stmt parent |
|
||||
parent = s.getParent() and
|
||||
(
|
||||
(
|
||||
parent instanceof Loop and
|
||||
result = parent
|
||||
) or (
|
||||
not parent instanceof Loop and
|
||||
not parent instanceof SwitchStmt and
|
||||
result = enclosingLoop(parent)
|
||||
)
|
||||
parent instanceof Loop and
|
||||
result = parent
|
||||
or
|
||||
not parent instanceof Loop and
|
||||
not parent instanceof SwitchStmt and
|
||||
result = enclosingLoop(parent)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from DoStmt loop, ContinueStmt continue
|
||||
where loop = getAFalseLoop()
|
||||
and loop = enclosingLoop(continue)
|
||||
select continue,
|
||||
"This 'continue' never re-runs the loop - the $@ is always false.",
|
||||
loop.getControllingExpr(),
|
||||
"loop condition"
|
||||
where
|
||||
loop = getAFalseLoop() and
|
||||
loop = enclosingLoop(continue)
|
||||
select continue, "This 'continue' never re-runs the loop - the $@ is always false.",
|
||||
loop.getControllingExpr(), "loop condition"
|
||||
|
|
|
@ -8,20 +8,21 @@
|
|||
* @precision high
|
||||
* @tags reliability
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Buffer
|
||||
|
||||
from Function f, FunctionCall c, int i, ArrayType argType, ArrayType paramType, int a, int b
|
||||
where f = c.getTarget() and
|
||||
argType = c.getArgument(i).getType() and
|
||||
paramType = f.getParameter(i).getType() and
|
||||
a = argType.getArraySize() and
|
||||
b = paramType.getArraySize() and
|
||||
argType.getBaseType().getSize() = paramType.getBaseType().getSize() and
|
||||
a < b and
|
||||
not memberMayBeVarSize(_, c.getArgument(i).(VariableAccess).getTarget()) and
|
||||
// filter out results for inconsistent declarations
|
||||
strictcount(f.getParameter(i).getType().getSize()) = 1
|
||||
select c.getArgument(i), "Array of size " + a +
|
||||
" passed to $@ which expects an array of size " + b + ".",
|
||||
f, f.getName()
|
||||
where
|
||||
f = c.getTarget() and
|
||||
argType = c.getArgument(i).getType() and
|
||||
paramType = f.getParameter(i).getType() and
|
||||
a = argType.getArraySize() and
|
||||
b = paramType.getArraySize() and
|
||||
argType.getBaseType().getSize() = paramType.getBaseType().getSize() and
|
||||
a < b and
|
||||
not memberMayBeVarSize(_, c.getArgument(i).(VariableAccess).getTarget()) and
|
||||
// filter out results for inconsistent declarations
|
||||
strictcount(f.getParameter(i).getType().getSize()) = 1
|
||||
select c.getArgument(i),
|
||||
"Array of size " + a + " passed to $@ which expects an array of size " + b + ".", f, f.getName()
|
||||
|
|
|
@ -20,26 +20,17 @@ import semmle.code.cpp.dataflow.DataFlow
|
|||
import DataFlow::PathGraph
|
||||
|
||||
class CastToPointerArithFlow extends DataFlow::Configuration {
|
||||
CastToPointerArithFlow() {
|
||||
this = "CastToPointerArithFlow"
|
||||
}
|
||||
CastToPointerArithFlow() { this = "CastToPointerArithFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
not node.asExpr() instanceof Conversion and
|
||||
introducesNewField(
|
||||
node.asExpr().getType().(DerivedType).getBaseType(),
|
||||
node.asExpr().getConversion*().getType().(DerivedType).getBaseType()
|
||||
|
||||
)
|
||||
introducesNewField(node.asExpr().getType().(DerivedType).getBaseType(),
|
||||
node.asExpr().getConversion*().getType().(DerivedType).getBaseType())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(PointerAddExpr pae |
|
||||
pae.getAnOperand() = node.asExpr()
|
||||
) or
|
||||
exists(ArrayExpr ae |
|
||||
ae.getArrayBase() = node.asExpr()
|
||||
)
|
||||
exists(PointerAddExpr pae | pae.getAnOperand() = node.asExpr()) or
|
||||
exists(ArrayExpr ae | ae.getArrayBase() = node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,12 +43,19 @@ predicate introducesNewField(Class derived, Class base) {
|
|||
exists(Field f |
|
||||
f.getDeclaringType() = derived and
|
||||
derived.getABaseClass+() = base
|
||||
) or
|
||||
)
|
||||
or
|
||||
introducesNewField(derived.getABaseClass(), 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()
|
||||
select sink, source, sink, "Pointer arithmetic here may be done with the wrong type because of the cast $@.", source, "here"
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
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"
|
||||
|
|
|
@ -6,18 +6,25 @@
|
|||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Expr e1, Cast e2, IntegralType it1, IntegralType it2
|
||||
where e2 = e1.getConversion() and
|
||||
e2.isImplicit() and
|
||||
it1 = e1.getUnderlyingType() and
|
||||
it2 = e2.getUnderlyingType() and
|
||||
(
|
||||
it1.isUnsigned() and it2.isSigned() and it1.getSize() >= it2.getSize()
|
||||
or it1.isSigned() and it2.isUnsigned()
|
||||
) and
|
||||
not (e1.isConstant() and 0 <= e1.getValue().toInt() and
|
||||
e1.getValue().toInt() <= ((it2.getSize()*8-1)*(2.log())).exp())
|
||||
and not e1.isConstant()
|
||||
select e1, "Conversion between signed and unsigned types "+it1.toString()+" and "+it2.toString()+"."
|
||||
where
|
||||
e2 = e1.getConversion() and
|
||||
e2.isImplicit() and
|
||||
it1 = e1.getUnderlyingType() and
|
||||
it2 = e2.getUnderlyingType() and
|
||||
(
|
||||
it1.isUnsigned() and it2.isSigned() and it1.getSize() >= it2.getSize()
|
||||
or
|
||||
it1.isSigned() and it2.isUnsigned()
|
||||
) and
|
||||
not (
|
||||
e1.isConstant() and
|
||||
0 <= e1.getValue().toInt() and
|
||||
e1.getValue().toInt() <= ((it2.getSize() * 8 - 1) * 2.log()).exp()
|
||||
) and
|
||||
not e1.isConstant()
|
||||
select e1,
|
||||
"Conversion between signed and unsigned types " + it1.toString() + " and " + it2.toString() + "."
|
||||
|
|
|
@ -14,10 +14,9 @@
|
|||
import cpp
|
||||
|
||||
from BitField fi, VariableAccess va
|
||||
|
||||
where fi.getNumBits() > va.getFullyConverted().getType().getSize() * 8
|
||||
and va.getExplicitlyConverted().getType().getSize() > va.getFullyConverted().getType().getSize()
|
||||
and va.getTarget() = fi
|
||||
and not va.getActualType() instanceof BoolType
|
||||
|
||||
where
|
||||
fi.getNumBits() > va.getFullyConverted().getType().getSize() * 8 and
|
||||
va.getExplicitlyConverted().getType().getSize() > va.getFullyConverted().getType().getSize() and
|
||||
va.getTarget() = fi and
|
||||
not va.getActualType() instanceof BoolType
|
||||
select va, "Implicit downcast of bitfield $@", fi, fi.toString()
|
||||
|
|
|
@ -44,15 +44,18 @@ predicate whitelistPow(FunctionCall fc) {
|
|||
fc.getTarget().getName() = "pow" or
|
||||
fc.getTarget().getName() = "powf" or
|
||||
fc.getTarget().getName() = "powl"
|
||||
) and exists(float value |
|
||||
) and
|
||||
exists(float value |
|
||||
value = fc.getArgument(0).getValue().toFloat() and
|
||||
(value.floor() - value).abs() < 0.001
|
||||
)
|
||||
}
|
||||
|
||||
predicate whiteListWrapped(FunctionCall fc) {
|
||||
whitelist(fc.getTarget()) or
|
||||
whitelistPow(fc) or
|
||||
whitelist(fc.getTarget())
|
||||
or
|
||||
whitelistPow(fc)
|
||||
or
|
||||
exists(Expr e, ReturnStmt rs |
|
||||
whiteListWrapped(e) and
|
||||
DataFlow::localExprFlow(e, rs.getExpr()) and
|
||||
|
@ -61,8 +64,11 @@ predicate whiteListWrapped(FunctionCall fc) {
|
|||
}
|
||||
|
||||
from FunctionCall c, FloatingPointType t1, IntegralType t2
|
||||
where t1 = c.getTarget().getType().getUnderlyingType() and
|
||||
t2 = c.getActualType() and
|
||||
c.hasImplicitConversion() and
|
||||
not whiteListWrapped(c)
|
||||
select c, "Return value of type " + t1.toString() + " is implicitly converted to " + t2.toString() + " here."
|
||||
where
|
||||
t1 = c.getTarget().getType().getUnderlyingType() and
|
||||
t2 = c.getActualType() and
|
||||
c.hasImplicitConversion() and
|
||||
not whiteListWrapped(c)
|
||||
select c,
|
||||
"Return value of type " + t1.toString() + " is implicitly converted to " + t2.toString() +
|
||||
" here."
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
* correctness
|
||||
* types
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate lossyPointerCast(Expr e, PointerType pt, IntegralType it) {
|
||||
|
@ -25,4 +26,4 @@ predicate lossyPointerCast(Expr e, PointerType pt, IntegralType it) {
|
|||
|
||||
from Expr e, PointerType pt, IntegralType it
|
||||
where lossyPointerCast(e, pt, it)
|
||||
select e, "Converted from " + pt.getName() + " to smaller type "+it.getName()
|
||||
select e, "Converted from " + pt.getName() + " to smaller type " + it.getName()
|
||||
|
|
|
@ -9,22 +9,26 @@
|
|||
* correctness
|
||||
* types
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate commonErrorCode(string value) {
|
||||
value = "0" or value = "1" or value = "-1"
|
||||
or value = "18446744073709551615" // 2^64-1, i.e. -1 as an unsigned int64
|
||||
or value = "4294967295" // 2^32-1, i.e. -1 as an unsigned int32
|
||||
or value = "3735928559" // 0xdeadbeef
|
||||
or value = "3735929054" // 0xdeadc0de
|
||||
or value = "3405691582" // 0xcafebabe
|
||||
value = "0" or
|
||||
value = "1" or
|
||||
value = "-1" or
|
||||
value = "18446744073709551615" or // 2^64-1, i.e. -1 as an unsigned int64
|
||||
value = "4294967295" or // 2^32-1, i.e. -1 as an unsigned int32
|
||||
value = "3735928559" or // 0xdeadbeef
|
||||
value = "3735929054" or // 0xdeadc0de
|
||||
value = "3405691582" // 0xcafebabe
|
||||
}
|
||||
|
||||
from Expr e
|
||||
where e.isConstant() and
|
||||
not commonErrorCode(e.getValue()) and
|
||||
e.getFullyConverted().getType() instanceof PointerType and
|
||||
not e.getType() instanceof ArrayType and
|
||||
not e.getType() instanceof PointerType and
|
||||
not e.isInMacroExpansion()
|
||||
where
|
||||
e.isConstant() and
|
||||
not commonErrorCode(e.getValue()) and
|
||||
e.getFullyConverted().getType() instanceof PointerType and
|
||||
not e.getType() instanceof ArrayType and
|
||||
not e.getType() instanceof PointerType and
|
||||
not e.isInMacroExpansion()
|
||||
select e, "Nonzero value " + e.getValueText() + " cast to pointer."
|
||||
|
|
|
@ -33,11 +33,9 @@ predicate flowsToExpr(Expr source, Expr sink, boolean pathMightOverflow) {
|
|||
* checking whether the current expression might overflow.
|
||||
*/
|
||||
predicate flowsToExprImpl(Expr source, Expr sink, boolean pathMightOverflow) {
|
||||
(
|
||||
source = sink and
|
||||
pathMightOverflow = false and
|
||||
source.(FunctionCall).getTarget().(Snprintf).returnsFullFormatLength()
|
||||
)
|
||||
source = sink and
|
||||
pathMightOverflow = false and
|
||||
source.(FunctionCall).getTarget().(Snprintf).returnsFullFormatLength()
|
||||
or
|
||||
exists(RangeSsaDefinition def, LocalScopeVariable v |
|
||||
flowsToDef(source, def, v, pathMightOverflow) and
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
* @tags reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given
|
||||
where ffc = fl.getUse()
|
||||
and expected = fl.getNumArgNeeded()
|
||||
and given = ffc.getNumFormatArgument()
|
||||
and expected < given
|
||||
and fl.specsAreKnown()
|
||||
select ffc, "Format expects "+expected.toString()+" arguments but given "+given.toString()
|
||||
where
|
||||
ffc = fl.getUse() and
|
||||
expected = fl.getNumArgNeeded() and
|
||||
given = ffc.getNumFormatArgument() and
|
||||
expected < given and
|
||||
fl.specsAreKnown()
|
||||
select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString()
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
* security
|
||||
* external/cwe/cwe-685
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given
|
||||
where ffc = fl.getUse()
|
||||
and expected = fl.getNumArgNeeded()
|
||||
and given = ffc.getNumFormatArgument()
|
||||
and expected > given
|
||||
and fl.specsAreKnown()
|
||||
select ffc, "Format expects "+expected.toString()+" arguments but given "+given.toString()
|
||||
where
|
||||
ffc = fl.getUse() and
|
||||
expected = fl.getNumArgNeeded() and
|
||||
given = ffc.getNumFormatArgument() and
|
||||
expected > given and
|
||||
fl.specsAreKnown()
|
||||
select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString()
|
||||
|
|
|
@ -16,17 +16,19 @@ import cpp
|
|||
|
||||
/**
|
||||
* Holds if the argument corresponding to the `pos` conversion specifier
|
||||
* of `ffc` is expected to have type `expected`.
|
||||
* of `ffc` is expected to have type `expected`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate formattingFunctionCallExpectedType(FormattingFunctionCall ffc, int pos, Type expected) {
|
||||
exists(FormattingFunction f, int i, FormatLiteral fl |
|
||||
ffc instanceof FormattingFunctionCall and
|
||||
ffc.getTarget() = f and
|
||||
f.getFormatParameterIndex() = i and
|
||||
ffc.getArgument(i) = fl and
|
||||
fl.getConversionType(pos) = expected
|
||||
)
|
||||
private predicate formattingFunctionCallExpectedType(
|
||||
FormattingFunctionCall ffc, int pos, Type expected
|
||||
) {
|
||||
exists(FormattingFunction f, int i, FormatLiteral fl |
|
||||
ffc instanceof FormattingFunctionCall and
|
||||
ffc.getTarget() = f and
|
||||
f.getFormatParameterIndex() = i and
|
||||
ffc.getArgument(i) = fl and
|
||||
fl.getConversionType(pos) = expected
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +52,9 @@ predicate formatArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr
|
|||
* `expected` and the corresponding argument `arg` has type `actual`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
predicate formatOtherArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual) {
|
||||
predicate formatOtherArgType(
|
||||
FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual
|
||||
) {
|
||||
exists(Expr argConverted |
|
||||
(arg = ffc.getMinFieldWidthArgument(pos) or arg = ffc.getPrecisionArgument(pos)) and
|
||||
argConverted = arg.getFullyConverted() and
|
||||
|
@ -63,14 +67,14 @@ predicate formatOtherArgType(FormattingFunctionCall ffc, int pos, Type expected,
|
|||
* A type that may be expected by a printf format parameter, or that may
|
||||
* be pointed to by such a type (e.g. `wchar_t`, from `wchar_t *`).
|
||||
*/
|
||||
class ExpectedType extends Type
|
||||
{
|
||||
class ExpectedType extends Type {
|
||||
ExpectedType() {
|
||||
exists(Type t |
|
||||
exists(Type t |
|
||||
(
|
||||
formatArgType(_, _, t, _, _) or
|
||||
formatOtherArgType(_, _, t, _, _)
|
||||
) and this = t.getUnspecifiedType()
|
||||
) and
|
||||
this = t.getUnspecifiedType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +82,7 @@ class ExpectedType extends Type
|
|||
/**
|
||||
* Holds if it is safe to display a value of type `actual` when `printf`
|
||||
* expects a value of type `expected`.
|
||||
*
|
||||
*
|
||||
* Note that variadic arguments undergo default argument promotions before
|
||||
* they reach `printf`, notably `bool`, `char`, `short` and `enum` types
|
||||
* are promoted to `int` (or `unsigned int`, as appropriate) and `float`s
|
||||
|
@ -89,69 +93,71 @@ predicate trivialConversion(ExpectedType expected, Type actual) {
|
|||
formatArgType(_, _, exp, _, act) and
|
||||
expected = exp.getUnspecifiedType() and
|
||||
actual = act.getUnspecifiedType()
|
||||
) and (
|
||||
(
|
||||
// allow a pointer type to be displayed with `%p`
|
||||
expected instanceof VoidPointerType and actual instanceof PointerType
|
||||
) or (
|
||||
// allow a function pointer type to be displayed with `%p`
|
||||
expected instanceof VoidPointerType and actual instanceof FunctionPointerType and expected.getSize() = actual.getSize()
|
||||
) or (
|
||||
// allow an `enum` type to be displayed with `%i`, `%c` etc
|
||||
expected instanceof IntegralType and actual instanceof Enum
|
||||
) or (
|
||||
// allow any `char *` type to be displayed with `%s`
|
||||
expected instanceof CharPointerType and actual instanceof CharPointerType
|
||||
) or (
|
||||
// allow `wchar_t *`, or any pointer to an integral type of the same size, to be displayed
|
||||
// with `%ws`
|
||||
expected.(PointerType).getBaseType().hasName("wchar_t") and
|
||||
exists(Wchar_t t |
|
||||
actual.getUnspecifiedType().(PointerType).getBaseType().(IntegralType).getSize() = t.getSize()
|
||||
)
|
||||
) or (
|
||||
// allow an `int` (or anything promoted to `int`) to be displayed with `%c`
|
||||
expected instanceof CharType and actual instanceof IntType
|
||||
) or (
|
||||
// allow an `int` (or anything promoted to `int`) to be displayed with `%wc`
|
||||
expected instanceof Wchar_t and actual instanceof IntType
|
||||
) or (
|
||||
expected instanceof UnsignedCharType and actual instanceof IntType
|
||||
) or (
|
||||
// allow any integral type of the same size
|
||||
// (this permits signedness changes)
|
||||
expected.(IntegralType).getSize() = actual.(IntegralType).getSize()
|
||||
) 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()
|
||||
) or (
|
||||
expected = actual
|
||||
) and
|
||||
(
|
||||
// allow a pointer type to be displayed with `%p`
|
||||
expected instanceof VoidPointerType and actual instanceof PointerType
|
||||
or
|
||||
// allow a function pointer type to be displayed with `%p`
|
||||
expected instanceof VoidPointerType and
|
||||
actual instanceof FunctionPointerType and
|
||||
expected.getSize() = actual.getSize()
|
||||
or
|
||||
// allow an `enum` type to be displayed with `%i`, `%c` etc
|
||||
expected instanceof IntegralType and actual instanceof Enum
|
||||
or
|
||||
// allow any `char *` type to be displayed with `%s`
|
||||
expected instanceof CharPointerType and actual instanceof CharPointerType
|
||||
or
|
||||
// allow `wchar_t *`, or any pointer to an integral type of the same size, to be displayed
|
||||
// with `%ws`
|
||||
expected.(PointerType).getBaseType().hasName("wchar_t") and
|
||||
exists(Wchar_t t |
|
||||
actual.getUnspecifiedType().(PointerType).getBaseType().(IntegralType).getSize() = t.getSize()
|
||||
)
|
||||
or
|
||||
// allow an `int` (or anything promoted to `int`) to be displayed with `%c`
|
||||
expected instanceof CharType and actual instanceof IntType
|
||||
or
|
||||
// allow an `int` (or anything promoted to `int`) to be displayed with `%wc`
|
||||
expected instanceof Wchar_t and actual instanceof IntType
|
||||
or
|
||||
expected instanceof UnsignedCharType and actual instanceof IntType
|
||||
or
|
||||
// allow any integral type of the same size
|
||||
// (this permits signedness changes)
|
||||
expected.(IntegralType).getSize() = actual.(IntegralType).getSize()
|
||||
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()
|
||||
or
|
||||
expected = actual
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the `int` type.
|
||||
*/
|
||||
int sizeof_IntType() {
|
||||
exists(IntType it | result = it.getSize())
|
||||
}
|
||||
int sizeof_IntType() { exists(IntType it | result = it.getSize()) }
|
||||
|
||||
from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual
|
||||
where (
|
||||
(
|
||||
formatArgType(ffc, n, expected, arg, actual) and
|
||||
not exists(Type anyExpected |
|
||||
formatArgType(ffc, n, anyExpected, arg, actual) and
|
||||
trivialConversion(anyExpected.getUnspecifiedType(), actual.getUnspecifiedType())
|
||||
)
|
||||
)
|
||||
or
|
||||
(
|
||||
formatOtherArgType(ffc, n, expected, arg, actual) and
|
||||
not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType()
|
||||
)
|
||||
)
|
||||
and not arg.isAffectedByMacro()
|
||||
select arg, "This argument should be of type '"+expected.getName()+"' but is of type '"+actual.getUnspecifiedType().getName() + "'"
|
||||
where
|
||||
(
|
||||
formatArgType(ffc, n, expected, arg, actual) and
|
||||
not exists(Type anyExpected |
|
||||
formatArgType(ffc, n, anyExpected, arg, actual) and
|
||||
trivialConversion(anyExpected.getUnspecifiedType(), actual.getUnspecifiedType())
|
||||
)
|
||||
or
|
||||
formatOtherArgType(ffc, n, expected, arg, actual) and
|
||||
not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType()
|
||||
) and
|
||||
not arg.isAffectedByMacro()
|
||||
select arg,
|
||||
"This argument should be of type '" + expected.getName() + "' but is of type '" +
|
||||
actual.getUnspecifiedType().getName() + "'"
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
* non-attributable
|
||||
* external/cwe/cwe-252
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate exclude(Function f) {
|
||||
|
@ -39,7 +40,7 @@ predicate checkExpr(Expr e, string operation, Variable v) {
|
|||
|
||||
predicate checkedFunctionCall(FunctionCall fc, string operation) {
|
||||
relevantFunctionCall(fc, _) and
|
||||
exists (Variable v, Expr check | v.getAnAssignedValue() = fc |
|
||||
exists(Variable v, Expr check | v.getAnAssignedValue() = fc |
|
||||
checkExpr(check, operation, v) and
|
||||
check != fc
|
||||
)
|
||||
|
@ -47,13 +48,11 @@ predicate checkedFunctionCall(FunctionCall fc, string operation) {
|
|||
|
||||
predicate relevantFunctionCall(FunctionCall fc, Function f) {
|
||||
fc.getTarget() = f and
|
||||
exists (Variable v | v.getAnAssignedValue() = fc) and
|
||||
exists(Variable v | v.getAnAssignedValue() = fc) and
|
||||
not okToIgnore(fc)
|
||||
}
|
||||
|
||||
predicate okToIgnore(FunctionCall fc) {
|
||||
fc.isInMacroExpansion()
|
||||
}
|
||||
predicate okToIgnore(FunctionCall fc) { fc.isInMacroExpansion() }
|
||||
|
||||
predicate functionStats(Function f, string operation, int used, int total, int percentage) {
|
||||
exists(PointerType pt | pt.getATypeNameUse() = f.getADeclarationEntry()) and
|
||||
|
@ -67,7 +66,9 @@ where
|
|||
relevantFunctionCall(unchecked, f) and
|
||||
not checkedFunctionCall(unchecked, operation) and
|
||||
functionStats(f, operation, _, _, percent) and
|
||||
percent >= 70
|
||||
and unchecked.getFile().getAbsolutePath().matches("%fbcode%")
|
||||
and not unchecked.getFile().getAbsolutePath().matches("%\\_build%")
|
||||
select unchecked, "After " + percent.toString() + "% of calls to " + f.getName() + " there is a call to " + operation + " on the return value. The call may be missing in this case."
|
||||
percent >= 70 and
|
||||
unchecked.getFile().getAbsolutePath().matches("%fbcode%") and
|
||||
not unchecked.getFile().getAbsolutePath().matches("%\\_build%")
|
||||
select unchecked,
|
||||
"After " + percent.toString() + "% of calls to " + f.getName() + " there is a call to " +
|
||||
operation + " on the return value. The call may be missing in this case."
|
||||
|
|
|
@ -14,11 +14,10 @@
|
|||
* non-attributable
|
||||
* external/cwe/cwe-476
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate assertMacro(Macro m) {
|
||||
m.getHead().toLowerCase().matches("%assert%")
|
||||
}
|
||||
predicate assertMacro(Macro m) { m.getHead().toLowerCase().matches("%assert%") }
|
||||
|
||||
predicate assertInvocation(File f, int line) {
|
||||
exists(MacroInvocation i, Location l | assertMacro(i.getMacro()) and l = i.getLocation() |
|
||||
|
@ -28,61 +27,92 @@ predicate assertInvocation(File f, int line) {
|
|||
|
||||
predicate nullCheckAssert(Expr e, Variable v, Declaration qualifier) {
|
||||
nullCheckInCondition(e, v, qualifier) and
|
||||
exists(File f, int i | e.getLocation().getStartLine() = i and e.getFile() = f and assertInvocation(f, i))
|
||||
exists(File f, int i |
|
||||
e.getLocation().getStartLine() = i and e.getFile() = f and assertInvocation(f, i)
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess qualifiedAccess(Variable v, Declaration qualifier) {
|
||||
result = v.getAnAccess() and
|
||||
(
|
||||
result.getQualifier().(VariableAccess).getTarget() = qualifier
|
||||
or exists(PointerDereferenceExpr e, VariableAccess va
|
||||
| result.getQualifier() = e
|
||||
| e.getOperand() = va and va.getTarget() = qualifier)
|
||||
or (not exists(result.getQualifier()) and qualifier = result.getEnclosingFunction())
|
||||
or (result.getQualifier() instanceof ThisExpr and qualifier = result.getEnclosingFunction())
|
||||
result.getQualifier().(VariableAccess).getTarget() = qualifier
|
||||
or
|
||||
exists(PointerDereferenceExpr e, VariableAccess va | result.getQualifier() = e |
|
||||
e.getOperand() = va and va.getTarget() = qualifier
|
||||
)
|
||||
or
|
||||
not exists(result.getQualifier()) and qualifier = result.getEnclosingFunction()
|
||||
or
|
||||
result.getQualifier() instanceof ThisExpr and qualifier = result.getEnclosingFunction()
|
||||
)
|
||||
}
|
||||
|
||||
predicate nullCheckInCondition(Expr e, Variable v, Declaration qualifier) {
|
||||
// if(v)
|
||||
exists(FunctionCall fc | relevantFunctionCall(fc, _) and fc = assignedValueForVariableAndQualifier(v, qualifier) |
|
||||
e = qualifiedAccess(v, qualifier)
|
||||
)
|
||||
or exists(AssignExpr a | a = e and a.getLValue() = qualifiedAccess(v, qualifier))
|
||||
// if(v == NULL), if(v != NULL), if(NULL != v), if(NULL == v)
|
||||
or exists(EqualityOperation eq | eq = e and nullCheckInCondition(eq.getAnOperand(), v, qualifier) and
|
||||
eq.getAnOperand().getValue() = "0")
|
||||
// if(v && something)
|
||||
or exists(LogicalAndExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier))
|
||||
// if(v || something)
|
||||
or exists(LogicalOrExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier))
|
||||
// if(!v)
|
||||
or exists(NotExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier))
|
||||
or exists(FunctionCall c | c = e and nullCheckInCondition(c.getAnArgument(), v, qualifier) and
|
||||
c.getTarget().getName() = "__builtin_expect")
|
||||
or exists(ConditionDeclExpr d | d = e and nullCheckInCondition(d.getVariableAccess(), v, qualifier))
|
||||
// if(v)
|
||||
exists(FunctionCall fc |
|
||||
relevantFunctionCall(fc, _) and fc = assignedValueForVariableAndQualifier(v, qualifier)
|
||||
|
|
||||
e = qualifiedAccess(v, qualifier)
|
||||
)
|
||||
or
|
||||
exists(AssignExpr a | a = e and a.getLValue() = qualifiedAccess(v, qualifier))
|
||||
or
|
||||
// if(v == NULL), if(v != NULL), if(NULL != v), if(NULL == v)
|
||||
exists(EqualityOperation eq |
|
||||
eq = e and
|
||||
nullCheckInCondition(eq.getAnOperand(), v, qualifier) and
|
||||
eq.getAnOperand().getValue() = "0"
|
||||
)
|
||||
or
|
||||
// if(v && something)
|
||||
exists(LogicalAndExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier))
|
||||
or
|
||||
// if(v || something)
|
||||
exists(LogicalOrExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier))
|
||||
or
|
||||
// if(!v)
|
||||
exists(NotExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier))
|
||||
or
|
||||
exists(FunctionCall c |
|
||||
c = e and
|
||||
nullCheckInCondition(c.getAnArgument(), v, qualifier) and
|
||||
c.getTarget().getName() = "__builtin_expect"
|
||||
)
|
||||
or
|
||||
exists(ConditionDeclExpr d | d = e and nullCheckInCondition(d.getVariableAccess(), v, qualifier))
|
||||
}
|
||||
|
||||
predicate hasNullCheck(Function enclosing, Variable v, Declaration qualifier) {
|
||||
exists(Expr exp | nullCheckInCondition(exp, v, qualifier) and exp.getEnclosingFunction() = enclosing |
|
||||
exists(ControlStructure s | exp = s.getControllingExpr())
|
||||
or exists(ConditionalExpr e | exp = e.getCondition())
|
||||
or exists(ReturnStmt s | exp = s.getExpr() and not exp instanceof VariableAccess)
|
||||
or exists(AssignExpr e | exp = e.getRValue() and not exp instanceof VariableAccess)
|
||||
or exists(AggregateLiteral al | exp = al.getAChild() and not exp instanceof VariableAccess)
|
||||
or exists(Variable other | exp = other.getInitializer().getExpr() and not exp instanceof VariableAccess)
|
||||
exists(Expr exp |
|
||||
nullCheckInCondition(exp, v, qualifier) and exp.getEnclosingFunction() = enclosing
|
||||
|
|
||||
exists(ControlStructure s | exp = s.getControllingExpr())
|
||||
or
|
||||
exists(ConditionalExpr e | exp = e.getCondition())
|
||||
or
|
||||
exists(ReturnStmt s | exp = s.getExpr() and not exp instanceof VariableAccess)
|
||||
or
|
||||
exists(AssignExpr e | exp = e.getRValue() and not exp instanceof VariableAccess)
|
||||
or
|
||||
exists(AggregateLiteral al | exp = al.getAChild() and not exp instanceof VariableAccess)
|
||||
or
|
||||
exists(Variable other |
|
||||
exp = other.getInitializer().getExpr() and not exp instanceof VariableAccess
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Expr assignedValueForVariableAndQualifier(Variable v, Declaration qualifier) {
|
||||
(result = v.getInitializer().getExpr() and qualifier = result.getEnclosingFunction())
|
||||
or exists(AssignExpr e | e.getLValue() = qualifiedAccess(v, qualifier) and result = e.getRValue())
|
||||
result = v.getInitializer().getExpr() and qualifier = result.getEnclosingFunction()
|
||||
or
|
||||
exists(AssignExpr e | e.getLValue() = qualifiedAccess(v, qualifier) and result = e.getRValue())
|
||||
}
|
||||
|
||||
|
||||
predicate checkedFunctionCall(FunctionCall fc) {
|
||||
relevantFunctionCall(fc, _) and
|
||||
exists (Variable v, Declaration qualifier | fc = assignedValueForVariableAndQualifier(v, qualifier) |
|
||||
exists(Variable v, Declaration qualifier |
|
||||
fc = assignedValueForVariableAndQualifier(v, qualifier)
|
||||
|
|
||||
hasNullCheck(fc.getEnclosingFunction(), v, qualifier)
|
||||
)
|
||||
}
|
||||
|
@ -91,27 +121,33 @@ predicate uncheckedFunctionCall(FunctionCall fc) {
|
|||
relevantFunctionCall(fc, _) and
|
||||
not checkedFunctionCall(fc) and
|
||||
not exists(File f, int line | f = fc.getFile() and line = fc.getLocation().getEndLine() |
|
||||
assertInvocation(f, line+1) or assertInvocation(f, line)
|
||||
assertInvocation(f, line + 1) or assertInvocation(f, line)
|
||||
) and
|
||||
not exists(Variable v, Declaration qualifier
|
||||
| fc = assignedValueForVariableAndQualifier(v, qualifier)
|
||||
| nullCheckAssert(_, v, qualifier)) and
|
||||
not exists(ControlStructure s
|
||||
| callResultNullCheckInCondition(s.getControllingExpr(), fc)) and
|
||||
not exists(FunctionCall other, Variable v, Declaration qualifier, Expr arg
|
||||
| fc = assignedValueForVariableAndQualifier(v, qualifier)
|
||||
| arg = other.getAnArgument() and
|
||||
nullCheckInCondition(arg, v, qualifier) and
|
||||
not arg instanceof VariableAccess)
|
||||
not exists(Variable v, Declaration qualifier |
|
||||
fc = assignedValueForVariableAndQualifier(v, qualifier)
|
||||
|
|
||||
nullCheckAssert(_, v, qualifier)
|
||||
) and
|
||||
not exists(ControlStructure s | callResultNullCheckInCondition(s.getControllingExpr(), fc)) and
|
||||
not exists(FunctionCall other, Variable v, Declaration qualifier, Expr arg |
|
||||
fc = assignedValueForVariableAndQualifier(v, qualifier)
|
||||
|
|
||||
arg = other.getAnArgument() and
|
||||
nullCheckInCondition(arg, v, qualifier) and
|
||||
not arg instanceof VariableAccess
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Declaration functionQualifier(FunctionCall fc) {
|
||||
fc.getQualifier().(VariableAccess).getTarget() = result
|
||||
or exists(PointerDereferenceExpr e, VariableAccess va
|
||||
| fc.getQualifier() = e and e.getOperand() = va and va.getTarget() = result)
|
||||
or (not exists(fc.getQualifier()) and result = fc.getEnclosingFunction())
|
||||
or (fc.getQualifier() instanceof ThisExpr and result = fc.getEnclosingFunction())
|
||||
fc.getQualifier().(VariableAccess).getTarget() = result
|
||||
or
|
||||
exists(PointerDereferenceExpr e, VariableAccess va |
|
||||
fc.getQualifier() = e and e.getOperand() = va and va.getTarget() = result
|
||||
)
|
||||
or
|
||||
not exists(fc.getQualifier()) and result = fc.getEnclosingFunction()
|
||||
or
|
||||
fc.getQualifier() instanceof ThisExpr and result = fc.getEnclosingFunction()
|
||||
}
|
||||
|
||||
predicate callTargetAndEnclosing(FunctionCall fc, Function target, Function enclosing) {
|
||||
|
@ -123,28 +159,38 @@ predicate callArgumentVariable(FunctionCall fc, Variable v, int i) {
|
|||
}
|
||||
|
||||
predicate callResultNullCheckInCondition(Expr e, FunctionCall fc) {
|
||||
// if(v)
|
||||
exists(FunctionCall other | e = other and
|
||||
relevantFunctionCall(fc,_) and not checkedFunctionCall(fc)
|
||||
and exists(Function called, Function enclosing |
|
||||
callTargetAndEnclosing(fc, called, enclosing) and
|
||||
callTargetAndEnclosing(other, called, enclosing)) and
|
||||
forall(Variable v, int i | callArgumentVariable(fc, v, i) | callArgumentVariable(other, v, i)) and
|
||||
(
|
||||
functionQualifier(fc) = functionQualifier(other)
|
||||
or
|
||||
(not exists(functionQualifier(fc)) and not exists(functionQualifier(other)))
|
||||
)
|
||||
// if(v)
|
||||
exists(FunctionCall other |
|
||||
e = other and
|
||||
relevantFunctionCall(fc, _) and
|
||||
not checkedFunctionCall(fc) and
|
||||
exists(Function called, Function enclosing |
|
||||
callTargetAndEnclosing(fc, called, enclosing) and
|
||||
callTargetAndEnclosing(other, called, enclosing)
|
||||
) and
|
||||
forall(Variable v, int i | callArgumentVariable(fc, v, i) | callArgumentVariable(other, v, i)) and
|
||||
(
|
||||
functionQualifier(fc) = functionQualifier(other)
|
||||
or
|
||||
not exists(functionQualifier(fc)) and not exists(functionQualifier(other))
|
||||
)
|
||||
// if(v == NULL), if(v != NULL), if(NULL != v), if(NULL == v)
|
||||
or exists(EqualityOperation eq | eq = e and callResultNullCheckInCondition(eq.getAnOperand(), fc) and
|
||||
eq.getAnOperand().getValue() = "0")
|
||||
// if(v && something)
|
||||
or exists(LogicalAndExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc))
|
||||
// if(v || something)
|
||||
or exists(LogicalOrExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc))
|
||||
// if(!v)
|
||||
or exists(NotExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc))
|
||||
)
|
||||
or
|
||||
// if(v == NULL), if(v != NULL), if(NULL != v), if(NULL == v)
|
||||
exists(EqualityOperation eq |
|
||||
eq = e and
|
||||
callResultNullCheckInCondition(eq.getAnOperand(), fc) and
|
||||
eq.getAnOperand().getValue() = "0"
|
||||
)
|
||||
or
|
||||
// if(v && something)
|
||||
exists(LogicalAndExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc))
|
||||
or
|
||||
// if(v || something)
|
||||
exists(LogicalOrExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc))
|
||||
or
|
||||
// if(!v)
|
||||
exists(NotExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc))
|
||||
}
|
||||
|
||||
predicate dereferenced(Variable v, Declaration qualifier, Function f) {
|
||||
|
@ -152,11 +198,13 @@ predicate dereferenced(Variable v, Declaration qualifier, Function f) {
|
|||
e.getOperand() = qualifiedAccess(v, qualifier) and
|
||||
e.getEnclosingFunction() = f and
|
||||
not exists(SizeofExprOperator s | s.getExprOperand() = e)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(FunctionCall c |
|
||||
c.getQualifier() = qualifiedAccess(v, qualifier) and
|
||||
c.getEnclosingFunction() = f
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va |
|
||||
va.getQualifier() = qualifiedAccess(v, qualifier) and
|
||||
va.getEnclosingFunction() = f
|
||||
|
@ -165,15 +213,15 @@ predicate dereferenced(Variable v, Declaration qualifier, Function f) {
|
|||
|
||||
predicate relevantFunctionCall(FunctionCall fc, Function f) {
|
||||
fc.getTarget() = f and
|
||||
exists (Variable v, Declaration qualifier
|
||||
| fc = assignedValueForVariableAndQualifier(v, qualifier)
|
||||
| dereferenced(v, qualifier, fc.getEnclosingFunction())) and
|
||||
exists(Variable v, Declaration qualifier |
|
||||
fc = assignedValueForVariableAndQualifier(v, qualifier)
|
||||
|
|
||||
dereferenced(v, qualifier, fc.getEnclosingFunction())
|
||||
) and
|
||||
not okToIgnore(fc)
|
||||
}
|
||||
|
||||
predicate okToIgnore(FunctionCall fc) {
|
||||
fc.isInMacroExpansion()
|
||||
}
|
||||
predicate okToIgnore(FunctionCall fc) { fc.isInMacroExpansion() }
|
||||
|
||||
predicate functionStats(Function f, int percentage) {
|
||||
exists(int used, int total |
|
||||
|
@ -190,4 +238,6 @@ where
|
|||
uncheckedFunctionCall(unchecked) and
|
||||
functionStats(f, percent) and
|
||||
percent >= 70
|
||||
select unchecked, "The result of this call to " + f.getName() + " is not checked for null, but " + percent + "% of calls to " + f.getName() + " check for null."
|
||||
select unchecked,
|
||||
"The result of this call to " + f.getName() + " is not checked for null, but " + percent +
|
||||
"% of calls to " + f.getName() + " check for null."
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
import cpp
|
||||
import semmle.code.cpp.commons.DateTime
|
||||
|
||||
predicate assignedYear(Struct s, YearFieldAccess year, int value)
|
||||
{
|
||||
predicate assignedYear(Struct s, YearFieldAccess year, int value) {
|
||||
exists(Operation yearAssignment |
|
||||
s.getAField().getAnAccess() = year and
|
||||
yearAssignment.getAnOperand() = year and
|
||||
|
@ -24,8 +23,7 @@ predicate assignedYear(Struct s, YearFieldAccess year, int value)
|
|||
)
|
||||
}
|
||||
|
||||
predicate assignedMonth(Struct s, MonthFieldAccess month, int value)
|
||||
{
|
||||
predicate assignedMonth(Struct s, MonthFieldAccess month, int value) {
|
||||
exists(Operation monthAssignment |
|
||||
s.getAField().getAnAccess() = month and
|
||||
monthAssignment.getAnOperand() = month and
|
||||
|
@ -33,8 +31,7 @@ predicate assignedMonth(Struct s, MonthFieldAccess month, int value)
|
|||
)
|
||||
}
|
||||
|
||||
predicate assignedDay(Struct s, DayFieldAccess day, int value)
|
||||
{
|
||||
predicate assignedDay(Struct s, DayFieldAccess day, int value) {
|
||||
exists(Operation dayAssignment |
|
||||
s.getAField().getAnAccess() = day and
|
||||
dayAssignment.getAnOperand() = day and
|
||||
|
@ -42,8 +39,7 @@ predicate assignedDay(Struct s, DayFieldAccess day, int value)
|
|||
)
|
||||
}
|
||||
|
||||
from
|
||||
StructLikeClass s, YearFieldAccess year, MonthFieldAccess month, DayFieldAccess day
|
||||
from StructLikeClass s, YearFieldAccess year, MonthFieldAccess month, DayFieldAccess day
|
||||
where
|
||||
assignedYear(s, year, 1989) and
|
||||
assignedMonth(s, month, 1) and
|
||||
|
|
|
@ -197,18 +197,14 @@ class StructTmLeapYearFieldAccess extends LeapYearFieldAccess {
|
|||
* `Function` that includes an operation that is checking for leap year.
|
||||
*/
|
||||
class ChecksForLeapYearFunction extends Function {
|
||||
ChecksForLeapYearFunction() {
|
||||
this = any(CheckForLeapYearOperation clyo).getEnclosingFunction()
|
||||
}
|
||||
ChecksForLeapYearFunction() { this = any(CheckForLeapYearOperation clyo).getEnclosingFunction() }
|
||||
}
|
||||
|
||||
/**
|
||||
* `FunctionCall` that includes an operation that is checking for leap year.
|
||||
*/
|
||||
class ChecksForLeapYearFunctionCall extends FunctionCall {
|
||||
ChecksForLeapYearFunctionCall() {
|
||||
this.getTarget() instanceof ChecksForLeapYearFunction
|
||||
}
|
||||
ChecksForLeapYearFunctionCall() { this.getTarget() instanceof ChecksForLeapYearFunction }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,26 +10,25 @@
|
|||
* correctness
|
||||
* external/cwe/cwe-481
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
|
||||
class UndefReachability extends LocalScopeVariableReachability {
|
||||
UndefReachability() {
|
||||
this = "UndefReachability"
|
||||
}
|
||||
|
||||
UndefReachability() { this = "UndefReachability" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
candidateVariable(v) and
|
||||
node = v.getParentScope() and
|
||||
not v instanceof Parameter and
|
||||
not v.hasInitializer()
|
||||
}
|
||||
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
|
||||
candidateVariable(v) and
|
||||
node = v.getAnAccess()
|
||||
}
|
||||
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
node.(AssignExpr).getLValue() = v.getAnAccess()
|
||||
}
|
||||
|
@ -41,27 +40,23 @@ abstract class BooleanControllingAssignment extends AssignExpr {
|
|||
|
||||
class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
BooleanControllingAssignmentInExpr() {
|
||||
this.getParent() instanceof UnaryLogicalOperation
|
||||
or this.getParent() instanceof BinaryLogicalOperation
|
||||
or exists(ConditionalExpr c | c.getCondition() = this)
|
||||
this.getParent() instanceof UnaryLogicalOperation or
|
||||
this.getParent() instanceof BinaryLogicalOperation or
|
||||
exists(ConditionalExpr c | c.getCondition() = this)
|
||||
}
|
||||
|
||||
override predicate isWhitelisted() {
|
||||
this.getConversion().(ParenthesisExpr).isParenthesised()
|
||||
}
|
||||
override predicate isWhitelisted() { this.getConversion().(ParenthesisExpr).isParenthesised() }
|
||||
}
|
||||
|
||||
class BooleanControllingAssignmentInStmt extends BooleanControllingAssignment {
|
||||
BooleanControllingAssignmentInStmt() {
|
||||
exists(IfStmt i | i.getCondition() = this)
|
||||
or exists(ForStmt f | f.getCondition() = this)
|
||||
or exists(WhileStmt w | w.getCondition() = this)
|
||||
or exists(DoStmt d | d.getCondition() = this)
|
||||
exists(IfStmt i | i.getCondition() = this) or
|
||||
exists(ForStmt f | f.getCondition() = this) or
|
||||
exists(WhileStmt w | w.getCondition() = this) or
|
||||
exists(DoStmt d | d.getCondition() = this)
|
||||
}
|
||||
|
||||
override predicate isWhitelisted() {
|
||||
this.isParenthesised()
|
||||
}
|
||||
override predicate isWhitelisted() { this.isParenthesised() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,6 +79,7 @@ predicate candidateVariable(Variable v) {
|
|||
}
|
||||
|
||||
from BooleanControllingAssignment ae, UndefReachability undef
|
||||
where candidateResult(ae)
|
||||
and not undef.reaches(_, ae.getLValue().(VariableAccess).getTarget(), ae.getLValue())
|
||||
where
|
||||
candidateResult(ae) and
|
||||
not undef.reaches(_, ae.getLValue().(VariableAccess).getTarget(), ae.getLValue())
|
||||
select ae, "Use of '=' where '==' may have been intended."
|
||||
|
|
|
@ -39,8 +39,9 @@ predicate nonShortCircuitLogic2(BinaryBitwiseOperation op) {
|
|||
}
|
||||
|
||||
from LogicalOperation o
|
||||
where o.getParent() instanceof BitwiseOperation and
|
||||
not nonShortCircuitLogic2(o.getParent()) and
|
||||
not o.getParent().isInMacroExpansion() and // It's ok if o itself is in a macro expansion.
|
||||
not o.getParent().(LShiftExpr).getLeftOperand() = o // Common pattern for producing bit masks: "(a && b) << 16".
|
||||
where
|
||||
o.getParent() instanceof BitwiseOperation and
|
||||
not nonShortCircuitLogic2(o.getParent()) and
|
||||
not o.getParent().isInMacroExpansion() and // It's ok if o itself is in a macro expansion.
|
||||
not o.getParent().(LShiftExpr).getLeftOperand() = o // Common pattern for producing bit masks: "(a && b) << 16".
|
||||
select o, "The result of this expression is Boolean, but it is used in a bitwise context."
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
* correctness
|
||||
* external/cwe/cwe-482
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ExprInVoidContext op
|
||||
where op instanceof EQExpr
|
||||
or
|
||||
op.(FunctionCall).getTarget().hasName("operator==")
|
||||
where
|
||||
op instanceof EQExpr
|
||||
or
|
||||
op.(FunctionCall).getTarget().hasName("operator==")
|
||||
select op, "This '==' operator has no effect. The assignment ('=') operator was probably intended."
|
||||
|
|
|
@ -21,8 +21,10 @@ predicate zeroComparison(EqualityOperation e) {
|
|||
|
||||
predicate inNullContext(AddressOfExpr e) {
|
||||
e.getFullyConverted().getUnderlyingType() instanceof BoolType
|
||||
or exists(ControlStructure c | c.getControllingExpr() = e)
|
||||
or exists(EqualityOperation cmp | zeroComparison(cmp) |
|
||||
or
|
||||
exists(ControlStructure c | c.getControllingExpr() = e)
|
||||
or
|
||||
exists(EqualityOperation cmp | zeroComparison(cmp) |
|
||||
e = cmp.getLeftOperand() or
|
||||
e = cmp.getRightOperand()
|
||||
)
|
||||
|
@ -34,11 +36,12 @@ FieldAccess chainedFields(FieldAccess fa) {
|
|||
}
|
||||
|
||||
from AddressOfExpr addrof, FieldAccess fa, Variable v, int offset
|
||||
where fa = addrof.getOperand()
|
||||
and inNullContext(addrof)
|
||||
and not addrof.isInMacroExpansion()
|
||||
and v.getAnAccess() = chainedFields(fa).getQualifier()
|
||||
and not v instanceof MemberVariable
|
||||
and offset = strictsum(chainedFields(fa).getTarget().getByteOffset())
|
||||
and offset != 0
|
||||
where
|
||||
fa = addrof.getOperand() and
|
||||
inNullContext(addrof) and
|
||||
not addrof.isInMacroExpansion() and
|
||||
v.getAnAccess() = chainedFields(fa).getQualifier() and
|
||||
not v instanceof MemberVariable and
|
||||
offset = strictsum(chainedFields(fa).getTarget().getByteOffset()) and
|
||||
offset != 0
|
||||
select addrof, "This will only be NULL if " + v.getName() + " == -" + offset + "."
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* correctness
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
|
@ -20,8 +21,10 @@ class PureExprInVoidContext extends ExprInVoidContext {
|
|||
// loop variable mentioned in the init stmt of a for
|
||||
predicate accessInInitOfForStmt(Expr e) {
|
||||
e instanceof Access and
|
||||
exists(ForStmt f, ExprStmt s | f.getInitialization() = s and
|
||||
s.getExpr() = e)
|
||||
exists(ForStmt f, ExprStmt s |
|
||||
f.getInitialization() = s and
|
||||
s.getExpr() = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,7 +32,8 @@ predicate accessInInitOfForStmt(Expr e) {
|
|||
* code excluded by the preprocessor.
|
||||
*/
|
||||
predicate functionContainsDisabledCodeRecursive(Function f) {
|
||||
functionContainsDisabledCode(f) or
|
||||
functionContainsDisabledCode(f)
|
||||
or
|
||||
// recurse into function calls
|
||||
exists(FunctionCall fc |
|
||||
fc.getEnclosingFunction() = f and
|
||||
|
@ -42,7 +46,8 @@ predicate functionContainsDisabledCodeRecursive(Function f) {
|
|||
* preprocessor branch that may have code in another arm
|
||||
*/
|
||||
predicate functionDefinedInIfDefRecursive(Function f) {
|
||||
functionDefinedInIfDef(f) or
|
||||
functionDefinedInIfDef(f)
|
||||
or
|
||||
// recurse into function calls
|
||||
exists(FunctionCall fc |
|
||||
fc.getEnclosingFunction() = f and
|
||||
|
@ -60,36 +65,41 @@ 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
|
||||
where // EQExprs are covered by CompareWhereAssignMeant.ql
|
||||
not peivc instanceof EQExpr and
|
||||
// as is operator==
|
||||
not peivc.(FunctionCall).getTarget().hasName("operator==") and
|
||||
not baseCall(peivc) and
|
||||
not accessInInitOfForStmt(peivc) and
|
||||
not peivc.isCompilerGenerated() and
|
||||
not peivc.getEnclosingFunction().isDefaulted() and
|
||||
not exists(Macro m | peivc = m.getAnInvocation().getAnExpandedElement()) and
|
||||
not peivc.isFromTemplateInstantiation(_) and
|
||||
parent = peivc.getParent() and
|
||||
not parent.isInMacroExpansion() and
|
||||
not parent instanceof PureExprInVoidContext and
|
||||
not peivc.getEnclosingFunction().isCompilerGenerated() and
|
||||
not peivc.getType() instanceof UnknownType and
|
||||
not functionContainsDisabledCodeRecursive(peivc.(FunctionCall).getTarget()) and
|
||||
not functionDefinedInIfDefRecursive(peivc.(FunctionCall).getTarget()) and
|
||||
if peivc instanceof FunctionCall then
|
||||
exists(Function target |
|
||||
target = peivc.(FunctionCall).getTarget() and
|
||||
info = target and
|
||||
info_text = target.getName() and
|
||||
tail = " (because $@ has no external side effects).")
|
||||
else
|
||||
(tail = "." and info = peivc and info_text = "")
|
||||
select peivc, "This expression has no effect" + tail,
|
||||
info, info_text
|
||||
from PureExprInVoidContext peivc, Locatable parent, Locatable info, string info_text, string tail
|
||||
where
|
||||
// EQExprs are covered by CompareWhereAssignMeant.ql
|
||||
not peivc instanceof EQExpr and
|
||||
// as is operator==
|
||||
not peivc.(FunctionCall).getTarget().hasName("operator==") and
|
||||
not baseCall(peivc) and
|
||||
not accessInInitOfForStmt(peivc) and
|
||||
not peivc.isCompilerGenerated() and
|
||||
not peivc.getEnclosingFunction().isDefaulted() and
|
||||
not exists(Macro m | peivc = m.getAnInvocation().getAnExpandedElement()) and
|
||||
not peivc.isFromTemplateInstantiation(_) and
|
||||
parent = peivc.getParent() and
|
||||
not parent.isInMacroExpansion() and
|
||||
not parent instanceof PureExprInVoidContext and
|
||||
not peivc.getEnclosingFunction().isCompilerGenerated() and
|
||||
not peivc.getType() instanceof UnknownType and
|
||||
not functionContainsDisabledCodeRecursive(peivc.(FunctionCall).getTarget()) and
|
||||
not functionDefinedInIfDefRecursive(peivc.(FunctionCall).getTarget()) and
|
||||
if peivc instanceof FunctionCall
|
||||
then
|
||||
exists(Function target |
|
||||
target = peivc.(FunctionCall).getTarget() and
|
||||
info = target and
|
||||
info_text = target.getName() and
|
||||
tail = " (because $@ has no external side effects)."
|
||||
)
|
||||
else (
|
||||
tail = "." and info = peivc and info_text = ""
|
||||
)
|
||||
select peivc, "This expression has no effect" + tail, info, info_text
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
* @tags reliability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate macroUse(Locatable l) {
|
||||
|
@ -28,12 +29,12 @@ predicate macroUseLocation(File f, int start, int end) {
|
|||
pragma[noopt]
|
||||
predicate emptyIf(IfStmt s, Block b, File f, int start, int end) {
|
||||
s instanceof IfStmt and
|
||||
not exists (s.getElse()) and
|
||||
not exists(s.getElse()) and
|
||||
b = s.getThen() and
|
||||
b instanceof Block and
|
||||
not exists(b.getAChild()) and
|
||||
f = b.getFile() and
|
||||
exists (Location l |
|
||||
exists(Location l |
|
||||
l = b.getLocation() and
|
||||
start = l.getStartLine() and
|
||||
end = l.getEndLine()
|
||||
|
@ -53,6 +54,7 @@ predicate query(IfStmt s, Block b) {
|
|||
}
|
||||
|
||||
from IfStmt s, Block b
|
||||
where query(s, b) and
|
||||
not b.isInMacroExpansion()
|
||||
where
|
||||
query(s, b) and
|
||||
not b.isInMacroExpansion()
|
||||
select s, "If-statement with an empty then-branch and no else-branch."
|
||||
|
|
|
@ -17,19 +17,19 @@ import cpp
|
|||
/**
|
||||
* It's common in some projects to use "a double negation" to normalize the boolean
|
||||
* result to either 1 or 0.
|
||||
* This predciate is intended to filter explicit usage of a double negation as it typically
|
||||
* indicates the explicit purpose to normalize the result for bit-wise or arithmetic purposes.
|
||||
* This predciate is intended to filter explicit usage of a double negation as it typically
|
||||
* indicates the explicit purpose to normalize the result for bit-wise or arithmetic purposes.
|
||||
*/
|
||||
predicate doubleNegationNormalization( NotExpr notexpr ){
|
||||
notexpr.getAnOperand() instanceof NotExpr
|
||||
}
|
||||
predicate doubleNegationNormalization(NotExpr notexpr) { notexpr.getAnOperand() instanceof NotExpr }
|
||||
|
||||
from BinaryBitwiseOperation binbitwop
|
||||
where exists( NotExpr notexpr |
|
||||
binbitwop.getAnOperand() = notexpr
|
||||
and not doubleNegationNormalization(notexpr)
|
||||
and ( binbitwop instanceof BitwiseAndExpr
|
||||
or binbitwop instanceof BitwiseOrExpr )
|
||||
)
|
||||
where
|
||||
exists(NotExpr notexpr |
|
||||
binbitwop.getAnOperand() = notexpr and
|
||||
not doubleNegationNormalization(notexpr) and
|
||||
(
|
||||
binbitwop instanceof BitwiseAndExpr or
|
||||
binbitwop instanceof BitwiseOrExpr
|
||||
)
|
||||
)
|
||||
select binbitwop, "Usage of a logical not (!) expression as a bitwise operator."
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
* correctness
|
||||
* external/cwe/cwe-478
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from EnumSwitch es, float missing, float total
|
||||
where not es.hasDefaultCase()
|
||||
and missing = count(es.getAMissingCase())
|
||||
and total = missing + count(es.getASwitchCase())
|
||||
and missing/total < 0.30
|
||||
select es, "Switch statement is missing case for "+es.getAMissingCase().getName()
|
||||
where
|
||||
not es.hasDefaultCase() and
|
||||
missing = count(es.getAMissingCase()) and
|
||||
total = missing + count(es.getASwitchCase()) and
|
||||
missing / total < 0.3
|
||||
select es, "Switch statement is missing case for " + es.getAMissingCase().getName()
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* correctness
|
||||
* external/cwe/cwe-480
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
|
@ -21,12 +22,10 @@ float candidateExpr(Expr e) {
|
|||
e = blo.getAnOperand() and
|
||||
e.isConstant() and
|
||||
result = e.getValue().toFloat() and
|
||||
|
||||
// exclusions
|
||||
not e.isFromTemplateInstantiation(_) and
|
||||
not e instanceof SizeofOperator and
|
||||
not inMacroExpansion(blo) and
|
||||
|
||||
// exclude values 0 and 1
|
||||
result != 0.0 and
|
||||
result != 1.0
|
||||
|
@ -36,15 +35,21 @@ float candidateExpr(Expr e) {
|
|||
from Expr e, float v, int l, string msg
|
||||
where
|
||||
v = candidateExpr(e) and
|
||||
|
||||
// before reporting an error, we check that the candidate is either a hex/octal
|
||||
// literal, or its value is a power of two.
|
||||
// literal, or its value is a power of two.
|
||||
l = v.log2().floor() and
|
||||
if v = 2.pow(l) then
|
||||
msg = "Operand to short-circuiting operator looks like a flag ("+v+" = 2 ^ "+l+"), may be typo for bitwise operator."
|
||||
else exists(string kind |
|
||||
((e instanceof HexLiteral and kind = "a hexadecimal literal") or
|
||||
(e instanceof OctalLiteral and kind = "an octal literal")) and
|
||||
msg = "Operand to short-circuiting operator is " + kind + ", and therefore likely a flag; a bitwise operator may be intended."
|
||||
)
|
||||
if v = 2.pow(l)
|
||||
then
|
||||
msg = "Operand to short-circuiting operator looks like a flag (" + v + " = 2 ^ " + l +
|
||||
"), may be typo for bitwise operator."
|
||||
else
|
||||
exists(string kind |
|
||||
(
|
||||
e instanceof HexLiteral and kind = "a hexadecimal literal"
|
||||
or
|
||||
e instanceof OctalLiteral and kind = "an octal literal"
|
||||
) and
|
||||
msg = "Operand to short-circuiting operator is " + kind +
|
||||
", and therefore likely a flag; a bitwise operator may be intended."
|
||||
)
|
||||
select e, msg
|
||||
|
|
|
@ -70,10 +70,8 @@ predicate isStringCopyUsedInLogicalOperationOrCondition(FunctionCall func, Expr
|
|||
|
||||
from FunctionCall func, Expr expr1, string msg
|
||||
where
|
||||
(
|
||||
isStringCopyCastedAsBoolean(func, expr1, msg) and
|
||||
not isStringCopyUsedInLogicalOperationOrCondition(func, _, _)
|
||||
)
|
||||
isStringCopyCastedAsBoolean(func, expr1, msg) and
|
||||
not isStringCopyUsedInLogicalOperationOrCondition(func, _, _)
|
||||
or
|
||||
isStringCopyUsedInLogicalOperationOrCondition(func, expr1, msg)
|
||||
select expr1, msg
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
* @precision high
|
||||
* @id cpp/inconsistent-loop-direction
|
||||
* @tags correctness
|
||||
* external/cwe/cwe-835
|
||||
* external/cwe/cwe-835
|
||||
* external/microsoft/6293
|
||||
* @msrc.severity important
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
@ -17,17 +18,20 @@ import semmle.code.cpp.dataflow.DataFlow
|
|||
/**
|
||||
* A `for` statement whose update is a crement operation on a variable.
|
||||
*/
|
||||
predicate candidateForStmt(ForStmt forStmt, Variable v, CrementOperation update, RelationalOperation rel) {
|
||||
predicate candidateForStmt(
|
||||
ForStmt forStmt, Variable v, CrementOperation update, RelationalOperation rel
|
||||
) {
|
||||
update = forStmt.getUpdate() and
|
||||
update.getAnOperand() = v.getAnAccess() and
|
||||
rel = forStmt.getCondition()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate candidateDecrForStmt(ForStmt forStmt, Variable v, VariableAccess lesserOperand, Expr terminalCondition) {
|
||||
predicate candidateDecrForStmt(
|
||||
ForStmt forStmt, Variable v, VariableAccess lesserOperand, Expr terminalCondition
|
||||
) {
|
||||
exists(DecrementOperation update, RelationalOperation rel |
|
||||
candidateForStmt(forStmt, v, update, rel) and
|
||||
|
||||
// condition is `v < terminalCondition`
|
||||
terminalCondition = rel.getGreaterOperand() and
|
||||
lesserOperand = rel.getLesserOperand() and
|
||||
|
@ -35,29 +39,30 @@ predicate candidateDecrForStmt(ForStmt forStmt, Variable v, VariableAccess lesse
|
|||
)
|
||||
}
|
||||
|
||||
predicate illDefinedDecrForStmt( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition ) {
|
||||
predicate illDefinedDecrForStmt(
|
||||
ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition
|
||||
) {
|
||||
exists(VariableAccess lesserOperand |
|
||||
// decrementing for loop
|
||||
candidateDecrForStmt(forstmt, v, lesserOperand, terminalCondition) and
|
||||
|
||||
// `initialCondition` is a value of `v` in the for loop
|
||||
v.getAnAssignedValue() = initialCondition and
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(lesserOperand)) and
|
||||
|
||||
// `initialCondition` < `terminalCondition`
|
||||
(
|
||||
( upperBound(initialCondition) < lowerBound(terminalCondition) )
|
||||
upperBound(initialCondition) < lowerBound(terminalCondition)
|
||||
or
|
||||
( forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue() )
|
||||
(forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate candidateIncrForStmt(ForStmt forStmt, Variable v, VariableAccess greaterOperand, Expr terminalCondition) {
|
||||
predicate candidateIncrForStmt(
|
||||
ForStmt forStmt, Variable v, VariableAccess greaterOperand, Expr terminalCondition
|
||||
) {
|
||||
exists(IncrementOperation update, RelationalOperation rel |
|
||||
candidateForStmt(forStmt, v, update, rel) and
|
||||
|
||||
// condition is `v > terminalCondition`
|
||||
terminalCondition = rel.getLesserOperand() and
|
||||
greaterOperand = rel.getGreaterOperand() and
|
||||
|
@ -65,71 +70,64 @@ predicate candidateIncrForStmt(ForStmt forStmt, Variable v, VariableAccess great
|
|||
)
|
||||
}
|
||||
|
||||
predicate illDefinedIncrForStmt( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition ) {
|
||||
predicate illDefinedIncrForStmt(
|
||||
ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition
|
||||
) {
|
||||
exists(VariableAccess greaterOperand |
|
||||
// incrementing for loop
|
||||
candidateIncrForStmt(forstmt, v, greaterOperand, terminalCondition) and
|
||||
|
||||
// `initialCondition` is a value of `v` in the for loop
|
||||
v.getAnAssignedValue() = initialCondition and
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(greaterOperand)) and
|
||||
|
||||
// `terminalCondition` < `initialCondition`
|
||||
(
|
||||
( upperBound(terminalCondition) < lowerBound(initialCondition) )
|
||||
upperBound(terminalCondition) < lowerBound(initialCondition)
|
||||
or
|
||||
( forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue() )
|
||||
(forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate illDefinedForStmtWrongDirection( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition
|
||||
, boolean isIncr ) {
|
||||
( illDefinedDecrForStmt( forstmt, v, initialCondition, terminalCondition) and isIncr = false )
|
||||
|
||||
predicate illDefinedForStmtWrongDirection(
|
||||
ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition, boolean isIncr
|
||||
) {
|
||||
illDefinedDecrForStmt(forstmt, v, initialCondition, terminalCondition) and isIncr = false
|
||||
or
|
||||
( illDefinedIncrForStmt( forstmt, v, initialCondition, terminalCondition) and isIncr = true)
|
||||
}
|
||||
|
||||
bindingset[b]
|
||||
private string forLoopdirection(boolean b){
|
||||
if( b = true ) then result = "upward"
|
||||
else result = "downward"
|
||||
illDefinedIncrForStmt(forstmt, v, initialCondition, terminalCondition) and isIncr = true
|
||||
}
|
||||
|
||||
bindingset[b]
|
||||
private string forLoopTerminalConditionRelationship(boolean b){
|
||||
if( b = true ) then result = "lower"
|
||||
else result = "higher"
|
||||
private string forLoopdirection(boolean b) {
|
||||
if b = true then result = "upward" else result = "downward"
|
||||
}
|
||||
|
||||
predicate illDefinedForStmt( ForStmt for, string message ) {
|
||||
exists(
|
||||
boolean isIncr,
|
||||
Variable v,
|
||||
Expr initialCondition,
|
||||
Expr terminalCondition |
|
||||
illDefinedForStmtWrongDirection(for, v, initialCondition, terminalCondition, isIncr)
|
||||
and
|
||||
if( for.conditionAlwaysFalse() ) then
|
||||
(
|
||||
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 "
|
||||
+ 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 "
|
||||
+ forLoopdirection(isIncr) + " from a value ("+ initialCondition +"), but the terminal condition is "
|
||||
+ forLoopTerminalConditionRelationship(isIncr) + " (" + terminalCondition + ")."
|
||||
)
|
||||
bindingset[b]
|
||||
private string forLoopTerminalConditionRelationship(boolean b) {
|
||||
if b = true then result = "lower" else result = "higher"
|
||||
}
|
||||
|
||||
predicate illDefinedForStmt(ForStmt for, string message) {
|
||||
exists(boolean isIncr, Variable v, Expr initialCondition, Expr terminalCondition |
|
||||
illDefinedForStmtWrongDirection(for, v, initialCondition, terminalCondition, isIncr) and
|
||||
if for.conditionAlwaysFalse()
|
||||
then
|
||||
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 " +
|
||||
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 " +
|
||||
forLoopdirection(isIncr) + " from a value (" + initialCondition +
|
||||
"), but the terminal condition is " + forLoopTerminalConditionRelationship(isIncr) +
|
||||
" (" + terminalCondition + ")."
|
||||
)
|
||||
}
|
||||
|
||||
from ForStmt forstmt, string message
|
||||
where illDefinedForStmt(forstmt, message)
|
||||
|
||||
from ForStmt forstmt, string message
|
||||
where illDefinedForStmt(forstmt, message)
|
||||
select forstmt, message
|
||||
|
|
|
@ -328,5 +328,4 @@ where
|
|||
not l.isTightlyBounded() and
|
||||
alloc = l.getAnAllocaCall() and
|
||||
alloc.getASuccessor*() = l.(Loop).getStmt()
|
||||
select alloc, "Stack allocation is inside a $@ loop.", l,
|
||||
l.toString()
|
||||
select alloc, "Stack allocation is inside a $@ loop.", l, l.toString()
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
import semmle.code.cpp.padding.Padding
|
||||
|
||||
from PaddedType t, ILP32 ilp32, LP64 lp64, int w32, int w64
|
||||
where w32 = t.wastedSpace(ilp32) - t.trailingPadding(ilp32) and
|
||||
where
|
||||
w32 = t.wastedSpace(ilp32) - t.trailingPadding(ilp32) and
|
||||
w64 = t.wastedSpace(lp64) - t.trailingPadding(lp64) and
|
||||
w64 > w32 and
|
||||
t.isPrecise()
|
||||
select t, t.getName() + " includes " + w32 + " bits of padding on ILP32, but " + w64 + " bits on LP64."
|
||||
select t,
|
||||
t.getName() + " includes " + w32 + " bits of padding on ILP32, but " + w64 + " bits on LP64."
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
import cpp
|
||||
import semmle.code.cpp.padding.Padding
|
||||
|
||||
/** Used to avoid reporting conflicts between a char
|
||||
/**
|
||||
* Used to avoid reporting conflicts between a char
|
||||
* pointer type with specified signedness and an unspecified
|
||||
* char pointer (whose signedness is compiler-dependent).
|
||||
*/
|
||||
|
@ -24,14 +25,16 @@ class SignedOrUnsignedCharPointerType extends CharPointerType {
|
|||
}
|
||||
|
||||
pragma[noopt]
|
||||
private predicate formattingFunctionCallExpectedType(FormattingFunctionCall ffc, int pos, Type expected) {
|
||||
exists(FormattingFunction f, int i, FormatLiteral fl |
|
||||
ffc.getTarget() = f and
|
||||
ffc instanceof FormattingFunctionCall and
|
||||
f.getFormatParameterIndex() = i and
|
||||
ffc.getArgument(i) = fl and
|
||||
fl.getConversionType(pos) = expected
|
||||
)
|
||||
private predicate formattingFunctionCallExpectedType(
|
||||
FormattingFunctionCall ffc, int pos, Type expected
|
||||
) {
|
||||
exists(FormattingFunction f, int i, FormatLiteral fl |
|
||||
ffc.getTarget() = f and
|
||||
ffc instanceof FormattingFunctionCall and
|
||||
f.getFormatParameterIndex() = i and
|
||||
ffc.getArgument(i) = fl and
|
||||
fl.getConversionType(pos) = expected
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
|
@ -42,7 +45,9 @@ predicate formatArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr
|
|||
}
|
||||
|
||||
pragma[noopt]
|
||||
predicate formatOtherArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual) {
|
||||
predicate formatOtherArgType(
|
||||
FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual
|
||||
) {
|
||||
(arg = ffc.getMinFieldWidthArgument(pos) or arg = ffc.getPrecisionArgument(pos)) and
|
||||
actual = arg.getActualType() and
|
||||
exists(IntType it | it instanceof IntType and it.isImplicitlySigned() and expected = it)
|
||||
|
@ -52,37 +57,38 @@ predicate trivialConversion(Type expected, Type actual) {
|
|||
formatArgType(_, _, expected, _, actual) and
|
||||
(
|
||||
expected instanceof VoidPointerType and actual instanceof PointerType
|
||||
or
|
||||
or
|
||||
expected instanceof IntegralType and actual instanceof Enum
|
||||
or
|
||||
or
|
||||
expected instanceof CharPointerType and actual instanceof SignedOrUnsignedCharPointerType
|
||||
or
|
||||
or
|
||||
expected instanceof SignedOrUnsignedCharPointerType and actual instanceof CharPointerType
|
||||
or
|
||||
or
|
||||
expected instanceof CharType and actual instanceof IntType
|
||||
or
|
||||
or
|
||||
expected instanceof UnsignedCharType and actual instanceof IntType
|
||||
or
|
||||
or
|
||||
expected.(IntegralType).getUnsigned() = actual.(IntegralType).getUnsigned()
|
||||
or
|
||||
or
|
||||
expected = actual
|
||||
)
|
||||
}
|
||||
|
||||
from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual, ILP32 ilp32, LP64 lp64, int size32, int size64
|
||||
where (
|
||||
(
|
||||
formatArgType(ffc, n, expected, arg, actual) and
|
||||
not trivialConversion(expected, actual)
|
||||
)
|
||||
or
|
||||
(
|
||||
formatOtherArgType(ffc, n, expected, arg, actual) and
|
||||
not actual instanceof IntType
|
||||
)
|
||||
)
|
||||
and not arg.isAffectedByMacro()
|
||||
and size32 = ilp32.paddedSize(actual) and size64 = lp64.paddedSize(actual)
|
||||
and size64 != size32
|
||||
select arg, "This argument should be of type '" + expected.getName() + "' but is of type '" + actual.getName() +
|
||||
"' (which changes size from " + size32 + " to " + size64 + " on 64-bit systems)."
|
||||
from
|
||||
FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual, ILP32 ilp32, LP64 lp64,
|
||||
int size32, int size64
|
||||
where
|
||||
(
|
||||
formatArgType(ffc, n, expected, arg, actual) and
|
||||
not trivialConversion(expected, actual)
|
||||
or
|
||||
formatOtherArgType(ffc, n, expected, arg, actual) and
|
||||
not actual instanceof IntType
|
||||
) and
|
||||
not arg.isAffectedByMacro() and
|
||||
size32 = ilp32.paddedSize(actual) and
|
||||
size64 = lp64.paddedSize(actual) and
|
||||
size64 != size32
|
||||
select arg,
|
||||
"This argument should be of type '" + expected.getName() + "' but is of type '" + actual.getName()
|
||||
+ "' (which changes size from " + size32 + " to " + size64 + " on 64-bit systems)."
|
||||
|
|
|
@ -10,15 +10,17 @@
|
|||
|
||||
import semmle.code.cpp.padding.Padding
|
||||
|
||||
from PaddedType t, Architecture arch, WideCharType wc, int holes, int size, int percentage, int optimum
|
||||
from
|
||||
PaddedType t, Architecture arch, WideCharType wc, int holes, int size, int percentage, int optimum
|
||||
where
|
||||
arch.pointerSize() = 64 and // Select 64-bit architecture
|
||||
arch.wideCharSize() = (wc.getSize() * 8) and // Select Windows(sizeof(wchar_t == 2)) or non-Windows(sizeof(wchar_t == 4))
|
||||
arch.pointerSize() = 64 and // Select 64-bit architecture
|
||||
arch.wideCharSize() = (wc.getSize() * 8) and // Select Windows(sizeof(wchar_t == 2)) or non-Windows(sizeof(wchar_t == 4))
|
||||
t.isPrecise() and
|
||||
optimum = t.optimalSize(arch) and
|
||||
size = arch.paddedSize(t) and
|
||||
holes = size - optimum and
|
||||
holes > 0 and
|
||||
percentage = (holes*100.0/(float)size).ceil()
|
||||
select t, t.getName() + " could be optimized to save " + holes + "/" + t.wastedSpace(arch) +
|
||||
" bits of padding (or " + percentage + "% of its size)."
|
||||
percentage = (holes * 100.0 / size.(float)).ceil()
|
||||
select t,
|
||||
t.getName() + " could be optimized to save " + holes + "/" + t.wastedSpace(arch) +
|
||||
" bits of padding (or " + percentage + "% of its size)."
|
||||
|
|
|
@ -14,29 +14,25 @@
|
|||
* Potentially overrunning write with float to string conversion
|
||||
* (`cpp/overrunning-write-with-float) instead.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Buffer
|
||||
|
||||
class SprintfCall extends FunctionCall {
|
||||
SprintfCall() {
|
||||
this.getTarget().hasName("sprintf") or this.getTarget().hasName("vsprintf")
|
||||
}
|
||||
SprintfCall() { this.getTarget().hasName("sprintf") or this.getTarget().hasName("vsprintf") }
|
||||
|
||||
int getBufferSize() {
|
||||
result = getBufferSize(this.getArgument(0), _)
|
||||
}
|
||||
int getBufferSize() { result = getBufferSize(this.getArgument(0), _) }
|
||||
|
||||
int getMaxConvertedLength() {
|
||||
result = this.getArgument(1).(FormatLiteral).getMaxConvertedLength()
|
||||
}
|
||||
|
||||
predicate isDangerous() {
|
||||
this.getMaxConvertedLength() > this.getBufferSize()
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,13 +21,14 @@ class StdString extends Class {
|
|||
StdString() {
|
||||
// `std::string` is usually a typedef and the actual class
|
||||
// is called something like `string std::__cxx11::basic_string`.
|
||||
exists (Type stdstring, Namespace std
|
||||
| stdstring.getName() = "string" and
|
||||
exists(Type stdstring, Namespace std |
|
||||
stdstring.getName() = "string" and
|
||||
this = stdstring.getUnspecifiedType() and
|
||||
// Make sure that the class is in the `std` namespace.
|
||||
std = this.getNamespace().getParentNamespace*() and
|
||||
std.getName() = "std" and
|
||||
std.getParentNamespace() instanceof GlobalNamespace)
|
||||
std.getParentNamespace() instanceof GlobalNamespace
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,17 +37,20 @@ class StdString extends Class {
|
|||
* allocated `std::string`.
|
||||
*/
|
||||
predicate refToStdString(Expr e, ConstructorCall source) {
|
||||
exists (StdString stdstring
|
||||
| stdstring.getAMemberFunction() = source.getTarget() and
|
||||
not exists(LocalVariable v
|
||||
| source = v.getInitializer().getExpr() and
|
||||
v.isStatic()) and
|
||||
e = source)
|
||||
exists(StdString stdstring |
|
||||
stdstring.getAMemberFunction() = source.getTarget() and
|
||||
not exists(LocalVariable v |
|
||||
source = v.getInitializer().getExpr() and
|
||||
v.isStatic()
|
||||
) and
|
||||
e = source
|
||||
)
|
||||
or
|
||||
// Indirect use.
|
||||
exists(Expr prev
|
||||
| refToStdString(prev, source) and
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(prev), DataFlow::exprNode(e)))
|
||||
exists(Expr prev |
|
||||
refToStdString(prev, source) and
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(prev), DataFlow::exprNode(e))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,12 +62,11 @@ predicate refToStdString(Expr e, ConstructorCall source) {
|
|||
* will also become invalid.
|
||||
*/
|
||||
predicate flowFunction(Function fcn, int argIndex) {
|
||||
(fcn.hasQualifiedName("", "_JNIEnv", "NewStringUTF") and argIndex = 0)
|
||||
fcn.hasQualifiedName("", "_JNIEnv", "NewStringUTF") and argIndex = 0
|
||||
or
|
||||
(fcn.hasQualifiedName("art", "JNI", "NewStringUTF") and argIndex = 1)
|
||||
fcn.hasQualifiedName("art", "JNI", "NewStringUTF") and argIndex = 1
|
||||
or
|
||||
(fcn.hasQualifiedName("art", "CheckJNI", "NewStringUTF") and argIndex = 1)
|
||||
|
||||
fcn.hasQualifiedName("art", "CheckJNI", "NewStringUTF") and argIndex = 1
|
||||
// Add other functions that behave like NewStringUTF here.
|
||||
}
|
||||
|
||||
|
@ -72,27 +75,28 @@ predicate flowFunction(Function fcn, int argIndex) {
|
|||
* `c_str` on a locally allocated `std::string`.
|
||||
*/
|
||||
predicate refToCStr(Expr e, ConstructorCall source) {
|
||||
exists (MemberFunction f, FunctionCall call
|
||||
| f.getName() = "c_str" and
|
||||
exists(MemberFunction f, FunctionCall call |
|
||||
f.getName() = "c_str" and
|
||||
call = e and
|
||||
call.getTarget() = f and
|
||||
refToStdString(call.getQualifier(), source))
|
||||
refToStdString(call.getQualifier(), source)
|
||||
)
|
||||
or
|
||||
// Indirect use.
|
||||
exists(Expr prev
|
||||
| refToCStr(prev, source) and
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(prev), DataFlow::exprNode(e)))
|
||||
exists(Expr prev |
|
||||
refToCStr(prev, source) and
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(prev), DataFlow::exprNode(e))
|
||||
)
|
||||
or
|
||||
// Some functions, such as `JNIEnv::NewStringUTF()` (from Java's JNI)
|
||||
// embed return a structure containing a reference to the C-style string.
|
||||
exists (Function f, int argIndex
|
||||
| flowFunction(f, argIndex) and
|
||||
exists(Function f, int argIndex |
|
||||
flowFunction(f, argIndex) and
|
||||
f = e.(Call).getTarget() and
|
||||
refToCStr(e.(Call).getArgument(argIndex), source))
|
||||
refToCStr(e.(Call).getArgument(argIndex), source)
|
||||
)
|
||||
}
|
||||
|
||||
from ReturnStmt r, ConstructorCall source
|
||||
where refToCStr(r.getExpr(), source)
|
||||
select
|
||||
r, "Return value may contain a dangling pointer to $@.",
|
||||
source, "this local std::string"
|
||||
select r, "Return value may contain a dangling pointer to $@.", source, "this local std::string"
|
||||
|
|
|
@ -32,8 +32,8 @@ predicate conservativeDataFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
|||
* data flow through such expressions.
|
||||
*/
|
||||
predicate hasNontrivialConversion(Expr e) {
|
||||
e instanceof Conversion and not
|
||||
(
|
||||
e instanceof Conversion and
|
||||
not (
|
||||
e instanceof Cast
|
||||
or
|
||||
e instanceof ParenthesisExpr
|
||||
|
@ -61,10 +61,7 @@ where
|
|||
exists(Expr pointerToLocal |
|
||||
variableAddressEscapesTree(va, pointerToLocal.getFullyConverted()) and
|
||||
not hasNontrivialConversion(pointerToLocal) and
|
||||
conservativeDataFlowStep+(
|
||||
DataFlow::exprNode(pointerToLocal),
|
||||
DataFlow::exprNode(r.getExpr())
|
||||
)
|
||||
conservativeDataFlowStep+(DataFlow::exprNode(pointerToLocal), DataFlow::exprNode(r.getExpr()))
|
||||
)
|
||||
)
|
||||
select r, "May return stack-allocated memory from $@.", va, va.toString()
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
* @id cpp/stack-address-escape
|
||||
* @tags reliability
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.StackAddress
|
||||
|
||||
|
@ -18,7 +19,7 @@ import semmle.code.cpp.dataflow.StackAddress
|
|||
* escape.
|
||||
*/
|
||||
predicate stackAddressEscapes(AssignExpr assignExpr, Expr source, boolean isLocal) {
|
||||
stackPointerFlowsToUse(assignExpr.getRValue(), _, source, isLocal) and
|
||||
stackPointerFlowsToUse(assignExpr.getRValue(), _, source, isLocal) and
|
||||
not stackReferenceFlowsToUse(assignExpr.getLValue(), _, _, _)
|
||||
}
|
||||
|
||||
|
@ -26,9 +27,11 @@ from Expr use, Expr source, boolean isLocal, string msg, string srcStr
|
|||
where
|
||||
stackAddressEscapes(use, source, isLocal) and
|
||||
if isLocal = true
|
||||
then (msg = "A stack address ($@) may be assigned to a non-local variable." and
|
||||
srcStr = "source")
|
||||
else (msg = "A stack address which arrived via a $@ may be assigned to a non-local variable." and
|
||||
srcStr = "parameter")
|
||||
select
|
||||
use, msg, source, srcStr
|
||||
then (
|
||||
msg = "A stack address ($@) may be assigned to a non-local variable." and
|
||||
srcStr = "source"
|
||||
) else (
|
||||
msg = "A stack address which arrived via a $@ may be assigned to a non-local variable." and
|
||||
srcStr = "parameter"
|
||||
)
|
||||
select use, msg, source, srcStr
|
||||
|
|
|
@ -13,16 +13,16 @@
|
|||
* external/cwe/cwe-119
|
||||
* external/cwe/cwe-251
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Buffer
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
predicate isSizePlus(Expr e, BufferSizeExpr baseSize, int plus)
|
||||
{
|
||||
(
|
||||
// baseSize
|
||||
e = baseSize and plus = 0
|
||||
) or exists(AddExpr ae, Expr operand1, Expr operand2, int plusSub |
|
||||
predicate isSizePlus(Expr e, BufferSizeExpr baseSize, int plus) {
|
||||
// baseSize
|
||||
e = baseSize and plus = 0
|
||||
or
|
||||
exists(AddExpr ae, Expr operand1, Expr operand2, int plusSub |
|
||||
// baseSize + n or n + baseSize
|
||||
ae = e and
|
||||
operand1 = ae.getAnOperand() and
|
||||
|
@ -30,7 +30,9 @@ predicate isSizePlus(Expr e, BufferSizeExpr baseSize, int plus)
|
|||
operand1 != operand2 and
|
||||
isSizePlus(operand1, baseSize, plusSub) and
|
||||
plus = plusSub + operand2.getValue().toInt()
|
||||
) or exists(SubExpr se, int plusSub |
|
||||
)
|
||||
or
|
||||
exists(SubExpr se, int plusSub |
|
||||
// baseSize - n
|
||||
se = e and
|
||||
isSizePlus(se.getLeftOperand(), baseSize, plusSub) and
|
||||
|
@ -38,45 +40,40 @@ predicate isSizePlus(Expr e, BufferSizeExpr baseSize, int plus)
|
|||
)
|
||||
}
|
||||
|
||||
predicate strncpyFunction(Function f, int argDest, int argSrc, int argLimit)
|
||||
{
|
||||
predicate strncpyFunction(Function f, int argDest, int argSrc, int argLimit) {
|
||||
exists(string name | name = f.getName() |
|
||||
(
|
||||
(
|
||||
name = "strcpy_s" or // strcpy_s(dst, max_amount, src)
|
||||
name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src)
|
||||
name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src)
|
||||
) and
|
||||
argDest = 0 and
|
||||
argSrc = 2 and
|
||||
argLimit = 1
|
||||
) or (
|
||||
(
|
||||
name = "strncpy" or // strncpy(dst, src, max_amount)
|
||||
name = "strncpy_l" or // strncpy_l(dst, src, max_amount, locale)
|
||||
name = "wcsncpy" or // wcsncpy(dst, src, max_amount)
|
||||
name = "_wcsncpy_l" or // _wcsncpy_l(dst, src, max_amount, locale)
|
||||
name = "_mbsncpy" or // _mbsncpy(dst, src, max_amount)
|
||||
name = "_mbsncpy_l" // _mbsncpy_l(dst, src, max_amount, locale)
|
||||
) and
|
||||
argDest = 0 and
|
||||
argSrc = 1 and
|
||||
argLimit = 2
|
||||
)
|
||||
name = "strcpy_s" or // strcpy_s(dst, max_amount, src)
|
||||
name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src)
|
||||
name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src)
|
||||
) and
|
||||
argDest = 0 and
|
||||
argSrc = 2 and
|
||||
argLimit = 1
|
||||
or
|
||||
(
|
||||
name = "strncpy" or // strncpy(dst, src, max_amount)
|
||||
name = "strncpy_l" or // strncpy_l(dst, src, max_amount, locale)
|
||||
name = "wcsncpy" or // wcsncpy(dst, src, max_amount)
|
||||
name = "_wcsncpy_l" or // _wcsncpy_l(dst, src, max_amount, locale)
|
||||
name = "_mbsncpy" or // _mbsncpy(dst, src, max_amount)
|
||||
name = "_mbsncpy_l" // _mbsncpy_l(dst, src, max_amount, locale)
|
||||
) and
|
||||
argDest = 0 and
|
||||
argSrc = 1 and
|
||||
argLimit = 2
|
||||
)
|
||||
}
|
||||
|
||||
string nthString (int num) {
|
||||
(
|
||||
num = 0 and
|
||||
result = "first"
|
||||
) or (
|
||||
num = 1 and
|
||||
result = "second"
|
||||
) or (
|
||||
num = 2 and
|
||||
result = "third"
|
||||
)
|
||||
string nthString(int num) {
|
||||
num = 0 and
|
||||
result = "first"
|
||||
or
|
||||
num = 1 and
|
||||
result = "second"
|
||||
or
|
||||
num = 2 and
|
||||
result = "third"
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,14 +85,16 @@ int arrayExprFixedSize(Expr e) {
|
|||
or
|
||||
result = e.(NewArrayExpr).getAllocatedType().(ArrayType).getSize()
|
||||
or
|
||||
exists (SsaDefinition def, LocalVariable v
|
||||
| not (e.getUnspecifiedType() instanceof ArrayType) and
|
||||
exists(SsaDefinition def, LocalVariable v |
|
||||
not e.getUnspecifiedType() instanceof ArrayType and
|
||||
e = def.getAUse(v) and
|
||||
result = arrayExprFixedSize(def.getDefiningValue(v)))
|
||||
result = arrayExprFixedSize(def.getDefiningValue(v))
|
||||
)
|
||||
}
|
||||
|
||||
from Function f, FunctionCall fc, int argDest, int argSrc, int argLimit, int charSize,
|
||||
Access copyDest, Access copySource, string name, string nth
|
||||
from
|
||||
Function f, FunctionCall fc, int argDest, int argSrc, int argLimit, int charSize, Access copyDest,
|
||||
Access copySource, string name, string nth
|
||||
where
|
||||
f = fc.getTarget() and
|
||||
strncpyFunction(f, argDest, argSrc, argLimit) and
|
||||
|
@ -104,22 +103,27 @@ where
|
|||
// Some of the functions operate on a larger char type, like `wchar_t`, so we
|
||||
// need to take this into account in the fixed size case.
|
||||
charSize = f.getParameter(argDest).getUnspecifiedType().(PointerType).getBaseType().getSize() and
|
||||
if exists(fc.getArgument(argLimit).getValue().toInt()) then (
|
||||
// Fixed sized case
|
||||
exists(int size |
|
||||
size = arrayExprFixedSize(copyDest) and
|
||||
size < charSize * fc.getArgument(argLimit).getValue().toInt() and
|
||||
size != 0 // if the array has zero size, something special is going on
|
||||
)
|
||||
) else exists (Access takenSizeOf, BufferSizeExpr sizeExpr, int plus |
|
||||
// Variable sized case
|
||||
sizeExpr = fc.getArgument(argLimit).getAChild*() and
|
||||
isSizePlus(fc.getArgument(argLimit), sizeExpr, plus) and
|
||||
plus >= 0 and
|
||||
takenSizeOf = sizeExpr.getArg() and
|
||||
globalValueNumber(copySource) = globalValueNumber(takenSizeOf) and // e.g. strncpy(x, y, strlen(y))
|
||||
globalValueNumber(copyDest) != globalValueNumber(takenSizeOf) // e.g. strncpy(y, y, strlen(y))
|
||||
)
|
||||
and name = fc.getTarget().getName()
|
||||
and nth = nthString(argLimit)
|
||||
select fc, "Potentially unsafe call to " + name + "; " + nth + " argument should be size of destination."
|
||||
(
|
||||
if exists(fc.getArgument(argLimit).getValue().toInt())
|
||||
then
|
||||
// Fixed sized case
|
||||
exists(int size |
|
||||
size = arrayExprFixedSize(copyDest) and
|
||||
size < charSize * fc.getArgument(argLimit).getValue().toInt() and
|
||||
size != 0 // if the array has zero size, something special is going on
|
||||
)
|
||||
else
|
||||
exists(Access takenSizeOf, BufferSizeExpr sizeExpr, int plus |
|
||||
// Variable sized case
|
||||
sizeExpr = fc.getArgument(argLimit).getAChild*() and
|
||||
isSizePlus(fc.getArgument(argLimit), sizeExpr, plus) and
|
||||
plus >= 0 and
|
||||
takenSizeOf = sizeExpr.getArg() and
|
||||
globalValueNumber(copySource) = globalValueNumber(takenSizeOf) and // e.g. strncpy(x, y, strlen(y))
|
||||
globalValueNumber(copyDest) != globalValueNumber(takenSizeOf) // e.g. strncpy(y, y, strlen(y))
|
||||
)
|
||||
) and
|
||||
name = fc.getTarget().getName() and
|
||||
nth = nthString(argLimit)
|
||||
select fc,
|
||||
"Potentially unsafe call to " + name + "; " + nth + " argument should be size of destination."
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче