mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge branch 'master' into users/raulga/c6324
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Arithmetic/IntMultToLong.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
|
||||
# Consistent Use
|
||||
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
|
||||
+ semmlecode-cpp-queries/Likely Bugs/InconsistentCheckReturnNull.ql: /Correctness/Consistent Use
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
|
||||
# Consistent Use
|
||||
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
|
||||
+ semmlecode-cpp-queries/Likely Bugs/InconsistentCheckReturnNull.ql: /Correctness/Consistent Use
|
||||
|
||||
3
cpp/config/suites/security/cwe-253
Normal file
3
cpp/config/suites/security/cwe-253
Normal file
@@ -0,0 +1,3 @@
|
||||
# CWE-253: Incorrect Check of Function Return Value
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /CWE/CWE-253
|
||||
@name Cast between HRESULT and a Boolean type (CWE-253)
|
||||
3
cpp/config/suites/security/cwe-428
Normal file
3
cpp/config/suites/security/cwe-428
Normal file
@@ -0,0 +1,3 @@
|
||||
# CWE-428: Unquoted Search Path or Element
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql: /CWE/CWE-428
|
||||
@name NULL application name with an unquoted path in call to CreateProcess (CWE-428)
|
||||
@@ -1,3 +1,5 @@
|
||||
# CWE-732: Incorrect Permission Assignment for Critical Resource
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql: /CWE/CWE-732
|
||||
@name File created without restricting permissions (CWE-732)
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql: /CWE/CWE-732
|
||||
@name Setting a DACL to NULL in a SECURITY_DESCRIPTOR (CWE-732)
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
@import "cwe-170"
|
||||
@import "cwe-190"
|
||||
@import "cwe-242"
|
||||
@import "cwe-253"
|
||||
@import "cwe-290"
|
||||
@import "cwe-311"
|
||||
@import "cwe-327"
|
||||
@import "cwe-367"
|
||||
@import "cwe-416"
|
||||
@import "cwe-428"
|
||||
@import "cwe-457"
|
||||
@import "cwe-468"
|
||||
@import "cwe-676"
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query shows graph of class inheritance hierarchy</p>
|
||||
|
||||
<p />
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Class hierarchies
|
||||
* @description Shows classes and their base classes.
|
||||
* @description Shows an inheritance hierarchy for classes and their base classes.
|
||||
* @kind graph
|
||||
* @id cpp/architecture/class-hierarchies
|
||||
* @graph.layout organic
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query shows coupling between classes.</p>
|
||||
|
||||
<p>Red, large boxes are hub types that depend on many other classes
|
||||
and are depended on by many other classes.</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Hub classes
|
||||
* @description Shows coupling between classes; red, large boxes are hub types that depend on many other classes
|
||||
* @description Shows coupling between classes. Large, red, boxes are hub types that depend on many other classes
|
||||
* and are depended on by many other classes.
|
||||
* @kind treemap
|
||||
* @id cpp/architecture/hub-classes
|
||||
|
||||
@@ -5,44 +5,30 @@
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query shows the distribution of inheritance depth across all types, i.e. classes. Library types are ignored.</p>
|
||||
<p>This query shows the distribution of inheritance depth across all types, that is, classes. Library types are ignored.</p>
|
||||
|
||||
<p>The result of this query is a line graph showing, for each number <i>n</i>, how many types have an inheritance depth of <i>n</i>, where
|
||||
the inheritance depth of a type is the length of a longest path in the inheritance hierarchy from top class to the type.</p>
|
||||
|
||||
<p>When hovering the mouse pointer over a specific depth value, the number of types having this inheritance depth is displayed.</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
|
||||
<recommendation>
|
||||
<p>The depth of a type is an indication of how deeply nested a type is in a given design.
|
||||
Very deep types can be an indication of over-engineering, whereas a system with predominantly shallow types
|
||||
may not be exploiting object-orientation to the full.</p>
|
||||
</recommendation>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
<li>
|
||||
Shyam R. Chidamber and Chris F. Kemerer.
|
||||
<a href="http://www.pitt.edu/~ckemerer/CK%20research%20papers/MetricForOOD_ChidamberKemerer94.pdf">A Metrics Suite for Object Oriented Design
|
||||
</a>.
|
||||
Shyam R. Chidamber and Chris F. Kemerer,
|
||||
<i><a href="http://www.pitt.edu/~ckemerer/CK%20research%20papers/MetricForOOD_ChidamberKemerer94.pdf">A Metrics Suite for Object Oriented Design
|
||||
</a></i>.
|
||||
IEEE Transactions on Software Engineering,
|
||||
20(6), pages 476-493, June 1994.
|
||||
20(6), pages 476-493, June 1994.</li>
|
||||
|
||||
|
||||
|
||||
<a href="http://www.dmst.aueb.gr/dds/index.en.html">Diomides D. Spinnelis</a>.
|
||||
<a href="http://www.spinellis.gr/codequality/">Code Quality: The Open Source Perspective</a>.
|
||||
Addison-Wesley 2007.
|
||||
|
||||
|
||||
|
||||
<a href="http://www.dmst.aueb.gr/dds/index.en.html">Diomides D. Spinnelis</a>.
|
||||
<a href="http://www.spinellis.gr/sw/ckjm/">ckjm - Chidamber and Kemerer Java Metrics</a>.
|
||||
(implementation of CK metrics), 2006.
|
||||
|
||||
|
||||
|
||||
</li></references></qhelp>
|
||||
<li>
|
||||
Lutz Prechelt, Barbara Unger, Michael Philippsen, and Walter Tich, <i><a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.159.2229&rep=rep1&type=pdf">A Controlled Experiment on Inheritance Depth as a Cost Factor for Code Maintenance
|
||||
</a></i>.
|
||||
Journal of Systems and Software, 65 (2):115-126, 2003.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Inheritance depth distribution
|
||||
* @description Shows distribution of inheritance depth across all classes.
|
||||
* @description Shows the distribution of inheritance depth across all classes.
|
||||
* @kind chart
|
||||
* @id cpp/architecture/inheritance-depth-distribution
|
||||
* @chart.type line
|
||||
|
||||
@@ -7,19 +7,15 @@
|
||||
<overview>
|
||||
<p>This query shows namespaces that cyclically depend
|
||||
on one another.</p>
|
||||
|
||||
<p />
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p>If there are cyclic dependencies between packages, they cannot be developed and tested independently. It is thus preferable to
|
||||
eliminate such cycles from the program.</p>
|
||||
|
||||
<recommendation>
|
||||
<p>If there are cyclic dependencies between packages, they cannot be developed and tested independently.
|
||||
It is better to eliminate such cycles from the program.</p>
|
||||
</recommendation>
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
<li>Robert Martin's <a href="https://drive.google.com/file/d/0BwhCYaYDn8EgOGM2ZGFhNmYtNmE4ZS00OGY5LWFkZTYtMjE0ZGNjODQ0MjEx/view">Acyclic Dependencies Principle</a>.
|
||||
</li></references></qhelp>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -5,18 +5,15 @@
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query finds classes that belong to no namespace</p>
|
||||
|
||||
<p>This query finds classes that belong to no namespace.</p>
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p>If there are too many classes that belong to no namespace, consider creating namespaces to get a better project structure.</p>
|
||||
|
||||
<recommendation>
|
||||
<p>If there are many classes that belong to no namespace, consider defining namespaces to create a better project structure.</p>
|
||||
</recommendation>
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
<li>C++ reference: <a href="https://en.cppreference.com/w/cpp/language/namespace">Namespaces</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Global namespace classes
|
||||
* @description Finds classes that belong to no namespace
|
||||
* @description Finds classes that belong to no namespace.
|
||||
* @kind table
|
||||
* @id cpp/architecture/global-namespace-classes
|
||||
*/
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query finds namespace dependencies and draws hierarchical graph.</p>
|
||||
|
||||
<p />
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Namespace dependencies
|
||||
* @description Shows dependencies between namespaces.
|
||||
* @description Shows dependencies between namespaces as a hierarchical graph.
|
||||
* @kind graph
|
||||
* @id cpp/architecture/namespace-dependencies
|
||||
* @graph.layout hierarchical
|
||||
|
||||
@@ -15,20 +15,9 @@ functions, and the total number of source code resp. comment lines.</p>
|
||||
depends on third-party libraries: low self-containedness means that many dependencies
|
||||
are to library classes (as opposed to source classes within the same application).</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
|
||||
<p>The results of this query are purely informative and more useful for getting an overall impression of the application than for
|
||||
identifying particular defects.</p>
|
||||
identifying particular problems with the code.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -4,5 +4,5 @@ void fillRect(int x, int y, int w, int h,
|
||||
int r2, int g2, int b2, int a2,
|
||||
gradient_type grad, unsigned int flags, bool border)
|
||||
{
|
||||
// ...
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ class AffectedFile extends File {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A block, or an element we might find textually within a block that is
|
||||
* not a child of it in the AST.
|
||||
*/
|
||||
class BlockOrNonChild extends Element {
|
||||
BlockOrNonChild() {
|
||||
( this instanceof Block
|
||||
@@ -68,6 +72,9 @@ class BlockOrNonChild extends Element {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A block that contains a non-child element.
|
||||
*/
|
||||
predicate emptyBlockContainsNonchild(Block b) {
|
||||
emptyBlock(_, b) and
|
||||
exists(BlockOrNonChild c, AffectedFile file |
|
||||
@@ -78,7 +85,27 @@ predicate emptyBlockContainsNonchild(Block b) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A block that is entirely on one line, which also contains a comment. Chances
|
||||
* are the comment is intended to refer to the block.
|
||||
*/
|
||||
predicate lineComment(Block b) {
|
||||
emptyBlock(_, b) and
|
||||
exists(Location bLocation, File f, int line |
|
||||
bLocation = b.getLocation() and
|
||||
f = bLocation.getFile() and
|
||||
line = bLocation.getStartLine() and
|
||||
line = bLocation.getEndLine() and
|
||||
exists(Comment c, Location cLocation |
|
||||
cLocation = c.getLocation() and
|
||||
cLocation.getFile() = f and
|
||||
cLocation.getStartLine() = line
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from ControlStructure s, Block eb
|
||||
where emptyBlock(s, eb)
|
||||
and not emptyBlockContainsNonchild(eb)
|
||||
and not lineComment(eb)
|
||||
select eb, "Empty block without comment"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
int find(int start, char *str, char goal)
|
||||
{
|
||||
int len = strlen(str);
|
||||
//Potential buffer overflow
|
||||
//Potential buffer overflow
|
||||
for (int i = start; str[i] != 0 && i < len; i++) {
|
||||
if (str[i] == goal)
|
||||
return i;
|
||||
@@ -12,7 +12,7 @@ int find(int start, char *str, char goal)
|
||||
int findRangeCheck(int start, char *str, char goal)
|
||||
{
|
||||
int len = strlen(str);
|
||||
//Range check protects against buffer overflow
|
||||
//Range check protects against buffer overflow
|
||||
for (int i = start; i < len && str[i] != 0 ; i++) {
|
||||
if (str[i] == goal)
|
||||
return i;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/offset-use-before-range-check
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-120
|
||||
@@ -13,10 +14,29 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from Variable v, LogicalAndExpr andexpr, ArrayExpr access, LTExpr rangecheck
|
||||
where access.getArrayOffset() = v.getAnAccess()
|
||||
and andexpr.getLeftOperand().getAChild() = access
|
||||
and andexpr.getRightOperand() = rangecheck
|
||||
and rangecheck.getLeftOperand() = v.getAnAccess()
|
||||
and not access.isInMacroExpansion()
|
||||
predicate beforeArrayAccess(Variable v, ArrayExpr access, Expr before) {
|
||||
exists(LogicalAndExpr andexpr |
|
||||
access.getArrayOffset() = v.getAnAccess() and
|
||||
andexpr.getRightOperand().getAChild*() = access and
|
||||
andexpr.getLeftOperand() = before
|
||||
)
|
||||
}
|
||||
|
||||
predicate afterArrayAccess(Variable v, ArrayExpr access, Expr after) {
|
||||
exists(LogicalAndExpr andexpr |
|
||||
access.getArrayOffset() = v.getAnAccess() and
|
||||
andexpr.getLeftOperand().getAChild*() = access and
|
||||
andexpr.getRightOperand() = after
|
||||
)
|
||||
}
|
||||
|
||||
from Variable v, ArrayExpr access, LTExpr rangecheck
|
||||
where
|
||||
afterArrayAccess(v, access, rangecheck) and
|
||||
rangecheck.getLeftOperand() = v.getAnAccess() and
|
||||
not access.isInMacroExpansion() and
|
||||
not exists(LTExpr altcheck |
|
||||
beforeArrayAccess(v, access, altcheck) and
|
||||
altcheck.getLeftOperand() = v.getAnAccess()
|
||||
)
|
||||
select access, "This use of offset '" + v.getName() + "' should follow the $@.", rangecheck, "range check"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
void sanitize(Fields[] record) {
|
||||
//The number of fields here can be put in a const
|
||||
for (fieldCtr = 0; field < 7; field++) {
|
||||
sanitize(fields[fieldCtr]);
|
||||
}
|
||||
for (fieldCtr = 0; field < 7; field++) {
|
||||
sanitize(fields[fieldCtr]);
|
||||
}
|
||||
}
|
||||
|
||||
#define NUM_FIELDS 7
|
||||
|
||||
void process(Fields[] record) {
|
||||
//This avoids using a magic constant by using the macro instead
|
||||
for (fieldCtr = 0; field < NUM_FIELDS; field++) {
|
||||
process(fields[fieldCtr]);
|
||||
}
|
||||
//This avoids using a magic constant by using the macro instead
|
||||
for (fieldCtr = 0; field < NUM_FIELDS; field++) {
|
||||
process(fields[fieldCtr]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* @kind problem
|
||||
* @id cpp/use-number-constant
|
||||
* @problem.severity recommendation
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
*/
|
||||
import cpp
|
||||
import MagicConstants
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* @kind problem
|
||||
* @id cpp/use-string-constant
|
||||
* @problem.severity recommendation
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
*/
|
||||
import cpp
|
||||
import MagicConstants
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
* to enforce invariants that should hold for the whole hierarchy.
|
||||
* @kind problem
|
||||
* @id cpp/nvi
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
* to enforce invariants that should hold for the whole hierarchy.
|
||||
* This is especially problematic in classes with many
|
||||
* dependencies or dependents.
|
||||
* @kind table
|
||||
* @kind problem
|
||||
* @id cpp/nvi-hub
|
||||
* @problem.severity recommendation
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
@@ -19,4 +22,4 @@ where f.hasSpecifier("public") and
|
||||
fclass = f.getDeclaringType() and
|
||||
hubIndex = fclass.getMetrics().getAfferentCoupling() * fclass.getMetrics().getEfferentCoupling() and
|
||||
hubIndex > 100
|
||||
select f.getFile(), f, "Avoid having public virtual methods (NVI idiom)"
|
||||
select f, "Avoid having public virtual methods (NVI idiom)"
|
||||
|
||||
@@ -8,17 +8,13 @@
|
||||
<p>This query finds classes that define a destructor, a copy constructor, or a copy assignment operator, but not all three of them. The compiler generates default implementations for these functions, and since they deal with similar concerns it is likely that if the default implementation of one of them is not satisfactory, then neither are those of the others.</p>
|
||||
|
||||
<p>The query flags any such class with a warning, and also display the list of generated warnings in the result view.</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
|
||||
<recommendation>
|
||||
<p>Explicitly define the missing functions.</p>
|
||||
</recommendation>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
<li><a href="http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)">Wikipedia article</a>
|
||||
|
||||
</li></references></qhelp>
|
||||
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)">Rule of three (C++ programming)</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
* the included elements are used.
|
||||
* @kind problem
|
||||
* @id cpp/unused-includes
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
* useless-code
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//start of file
|
||||
static void f() { //static function f() is unused in the file
|
||||
//...
|
||||
//...
|
||||
}
|
||||
static void g() {
|
||||
//...
|
||||
//...
|
||||
}
|
||||
void public_func() { //non-static function public_func is not called in file,
|
||||
//but could be visible in other files
|
||||
//...
|
||||
g(); //call to g()
|
||||
//...
|
||||
//...
|
||||
g(); //call to g()
|
||||
//...
|
||||
}
|
||||
//end of file
|
||||
|
||||
@@ -25,4 +25,5 @@ where v.isStatic()
|
||||
and not v instanceof MemberVariable
|
||||
and not declarationHasSideEffects(v)
|
||||
and not v.getAnAttribute().hasName("used")
|
||||
and not v.getAnAttribute().hasName("unused")
|
||||
select v, "Static variable " + v.getName() + " is never read"
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
|
||||
import cpp
|
||||
import CPython.Extensions
|
||||
|
||||
/* A call to an argument parsing function */
|
||||
class PyArgParseTupleCall extends FunctionCall {
|
||||
|
||||
PyArgParseTupleCall() {
|
||||
this.getTarget().hasGlobalName("PyArg_Parse") or
|
||||
this.getTarget().hasGlobalName("PyArg_ParseTuple") or
|
||||
this.getTarget().hasGlobalName("PyArg_VaParse") or
|
||||
this.getTarget().hasGlobalName("PyArg_ParseTupleAndKeywords") or
|
||||
this.getTarget().hasGlobalName("PyArg_VaParseAndKeywords")
|
||||
}
|
||||
|
||||
private int getFormatIndex() {
|
||||
exists(Function f | f = this.getTarget() |
|
||||
(f.hasGlobalName("PyArg_Parse") or f.hasGlobalName("PyArg_ParseTuple") or f.hasGlobalName("PyArg_VaParse")) and result = 1
|
||||
or
|
||||
(f.hasGlobalName("PyArg_ParseTupleAndKeywords") or f.hasGlobalName("PyArg_VaParseAndKeywords")) and result = 2
|
||||
)
|
||||
}
|
||||
|
||||
private string getFormatString() {
|
||||
result = this.getArgument(this.getFormatIndex()).(StringLiteral).getValue()
|
||||
}
|
||||
|
||||
string getArgumentFormat() {
|
||||
exists(string fmt | fmt = this.getFormatString() |
|
||||
exists(int i | fmt.charAt(i) = ";" or fmt.charAt(i) = ":" | result = fmt.prefix(i))
|
||||
or
|
||||
not exists(int i | fmt.charAt(i) = ";" or fmt.charAt(i) = ":") and result = fmt
|
||||
)
|
||||
}
|
||||
|
||||
string getPyArgumentType(int index) {
|
||||
parse_format_string(this.getArgumentFormat(), index, _, result) and result != "typed"
|
||||
or
|
||||
exists(int cindex, PythonClass cls | parse_format_string(this.getArgumentFormat(), index, cindex, "typed") |
|
||||
cls.getAnAccess() = this.getArgument(this.getFormatIndex() * 2 + cindex).(AddressOfExpr).getOperand() and
|
||||
result = cls.getTpName()
|
||||
)
|
||||
or
|
||||
exists(int cindex | parse_format_string(this.getArgumentFormat(), index, cindex, "typed") and
|
||||
not exists(PythonClass cls | cls.getAnAccess() = this.getArgument(this.getFormatIndex() * 2 + cindex).(AddressOfExpr).getOperand())
|
||||
and result = "object"
|
||||
)
|
||||
}
|
||||
|
||||
predicate pyArgumentIsOptional(int index) {
|
||||
exists(string suffix | split_format_string(this.getArgumentFormat(), _, _, suffix, index, _) |
|
||||
suffix.charAt(0) = "|")
|
||||
}
|
||||
|
||||
predicate pyArgumentIsKwOnly(int index) {
|
||||
exists(string suffix | split_format_string(this.getArgumentFormat(), _, _, suffix, index, _) |
|
||||
suffix.charAt(0) = "$")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PyUnpackTupleCall extends FunctionCall {
|
||||
|
||||
PyUnpackTupleCall() {
|
||||
this.getTarget().hasGlobalName("PyArg_UnpackTuple")
|
||||
}
|
||||
|
||||
int getMinSize() {
|
||||
result = this.getArgument(2).getValue().toInt()
|
||||
}
|
||||
|
||||
int getMaxSize() {
|
||||
result = this.getArgument(3).getValue().toInt()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
predicate limiting_format(string text, string limit) {
|
||||
text = "t#" and limit = "read-only"
|
||||
or
|
||||
(text = "B" or text = "H" or text = "I" or text = "k" or text = "K") and limit = "non-negative"
|
||||
or
|
||||
(text = "c" or text = "C") and limit = "length-one"
|
||||
}
|
||||
|
||||
predicate format_string(string text, string type, int cargs) {
|
||||
tuple_format(text, type, cargs) or simple_format(text, type, cargs)
|
||||
}
|
||||
|
||||
private
|
||||
predicate simple_format(string text, string type, int cargs) {
|
||||
text = "s" and (type = "str" or type = "unicode") and cargs = 1
|
||||
or
|
||||
text = "s#" and (type = "str" or type = "unicode") and cargs = 2
|
||||
or
|
||||
text = "s*" and (type = "str" or type = "unicode") and cargs = 1
|
||||
or
|
||||
text = "z" and (type = "str" or type = "unicode" or type = "NoneType") and cargs = 1
|
||||
or
|
||||
text = "z#" and (type = "str" or type = "unicode" or type = "NoneType" or type = "buffer") and cargs = 2
|
||||
or
|
||||
text = "z*" and (type = "str" or type = "unicode" or type = "NoneType" or type = "buffer") and cargs = 1
|
||||
or
|
||||
text = "u" and type = "unicode" and cargs = 1
|
||||
or
|
||||
text = "u#" and type = "unicode" and cargs = 2
|
||||
or
|
||||
text = "O" and type = "object" and cargs = 1
|
||||
or
|
||||
text = "p" and type = "object" and cargs = 1
|
||||
or
|
||||
text = "O&" and type = "object" and cargs = 2
|
||||
or
|
||||
text = "O!" and type = "typed" and cargs = 2
|
||||
or
|
||||
(text = "b" or text = "h" or text = "i" or text = "l" or text = "L" or text = "n") and type = "int" and cargs = 1
|
||||
or
|
||||
(text = "B" or text = "H" or text = "I" or text = "k" or text = "K") and type = "int" and cargs = 1
|
||||
or
|
||||
text = "c" and (type = "bytes" or type = "bytearray") and cargs = 1
|
||||
or
|
||||
text = "C" and type = "unicode" and cargs = 1
|
||||
or
|
||||
text = "D" and type = "complex" and cargs = 1
|
||||
or
|
||||
(text = "f" or text = "d") and type = "float" and cargs = 1
|
||||
or
|
||||
text = "S" and type = "str" and cargs = 1
|
||||
or
|
||||
text = "U" and type = "unicode" and cargs = 1
|
||||
or
|
||||
text = "t#" and type = "buffer" and cargs = 2
|
||||
or
|
||||
text = "w" and type = "buffer" and cargs = 1
|
||||
or
|
||||
text = "w#" and type = "buffer" and cargs = 2
|
||||
or
|
||||
text = "w*" and type = "buffer" and cargs = 1
|
||||
or
|
||||
(text = "es" or text = "et") and (type = "str" or type = "unicode" or type = "buffer") and cargs = 2
|
||||
or
|
||||
(text = "es#" or text = "et#") and (type = "str" or type = "unicode" or type = "buffer") and cargs = 3
|
||||
or
|
||||
text = "y" and type = "bytes" and cargs = 1
|
||||
or
|
||||
text = "y*" and (type = "bytes" or type = "bytearray" or type = "buffer") and cargs = 1
|
||||
or
|
||||
text = "y#" and (type = "bytes" or type = "bytearray" or type = "buffer") and cargs = 2
|
||||
}
|
||||
|
||||
private
|
||||
predicate tuple_format(string text, string type, int cargs) {
|
||||
type = "tuple" and
|
||||
exists(PyArgParseTupleCall call | exists(call.getArgumentFormat().indexOf(text)))
|
||||
and
|
||||
exists(string body | text = "(" + body + ")" | tuple_body(body, _, cargs))
|
||||
}
|
||||
|
||||
private
|
||||
predicate tuple_body(string body, int pyargs, int cargs) {
|
||||
body = "" and cargs = 0 and pyargs = 0
|
||||
or
|
||||
(exists(PyArgParseTupleCall call | exists(call.getArgumentFormat().indexOf(body))) and
|
||||
exists(string p, int pargs, string s, int sargs, int pyargsm1 | pyargs = pyargsm1+1 and tuple_body(p, pyargsm1, pargs) and
|
||||
format_string(s, _, sargs) and body = p + s and cargs = pargs + sargs)
|
||||
)
|
||||
}
|
||||
|
||||
predicate format_token(string token, int delta, int cdelta) {
|
||||
format_string(token, _, cdelta) and delta = 1
|
||||
or
|
||||
token = "|" and delta = 0 and cdelta = 0
|
||||
or
|
||||
token = "$" and delta = 0 and cdelta = 0
|
||||
}
|
||||
|
||||
predicate split_format_string(string full, string prefix, string text, string suffix, int index, int cindex) {
|
||||
exists(PyArgParseTupleCall call | call.getArgumentFormat() = full) and
|
||||
full = prefix + text + suffix and
|
||||
(suffix = "" or exists(string s | suffix.prefix(s.length()) = s | format_token(s, _, _))) and
|
||||
format_token(text, _, _) and
|
||||
(prefix = "" and index = 0 and cindex = 0 and suffix = full.suffix(text.length())
|
||||
or
|
||||
exists(string prefixm1, string suffixm1, string textm1, int im1, int cim1, int prev, int cprev |
|
||||
full = prefixm1 + textm1 + suffixm1 and
|
||||
split_format_string(full, prefixm1, textm1, suffixm1, im1, cim1) and
|
||||
format_token(textm1, prev, cprev) and
|
||||
index = im1+prev and
|
||||
cindex = cim1+cprev and
|
||||
prefix = prefixm1 + textm1 and
|
||||
suffix = suffixm1.suffix(text.length()) and
|
||||
text = suffixm1.prefix(text.length())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate parse_format_string(string full, int index, int cindex, string type) {
|
||||
exists(string prefix, string text, string suffix | split_format_string(full, prefix, text, suffix, index, cindex) and format_string(text, type, _))
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @name Parameter type trap file generator
|
||||
* @description Generate trap files (in CSV form) describing CPython extension function parameter types.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/argument-type-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
from TypedPythonExtensionFunction func, int arg, PythonClass cls
|
||||
where func.getArgumentType(arg) = cls
|
||||
select "ext_argtype", func.getTrapID(), arg, cls.getTrapID()
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* @name py_cobject_sources() trap file generator
|
||||
* @description Generate trap files (in CSV form) for CPython objects.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/c-object-sources-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
from CObject c
|
||||
select "py_cobject_sources", c.getTrapID(), 1
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @name py_cobject() trap file generator
|
||||
* @description Generate trap files (in CSV form) for CPython objects.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/c-object-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
|
||||
from CObject c
|
||||
select "py_cobjects", c.getTrapID()
|
||||
@@ -1,893 +0,0 @@
|
||||
import cpp
|
||||
import CPython.ArgParse
|
||||
|
||||
|
||||
/* Root class of all 'C' objects */
|
||||
abstract class CObject extends Element {
|
||||
|
||||
abstract string getTrapID();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A Python class is an instance of PyTypeObject.
|
||||
*/
|
||||
class PythonClass extends Variable, CObject {
|
||||
|
||||
PythonClass() {
|
||||
getType().hasName("PyTypeObject")
|
||||
}
|
||||
|
||||
/** Gets the function table (if any) associated with the class. */
|
||||
PythonFunctionTable getFunctionTable() {
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = getInitializer().getExpr()
|
||||
and tt.hasName("PyTypeObject")
|
||||
and f.hasName("tp_methods")
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and result.getAnAccess() = l.getFieldExpr(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the getset table (if any) associated with the class. */
|
||||
PythonGetSetTable getGetSetTable() {
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = getInitializer().getExpr()
|
||||
and tt.hasName("PyTypeObject")
|
||||
and f.hasName("tp_getset")
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and result.getAnAccess() = l.getFieldExpr(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the Python module (if any) containing this class. */
|
||||
PythonModule getModule() {
|
||||
result = getFile()
|
||||
}
|
||||
|
||||
Expr getSlot(string name) {
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = getInitializer().getExpr()
|
||||
and tt.hasName("PyTypeObject")
|
||||
and f.hasName(name)
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = result
|
||||
)
|
||||
}
|
||||
|
||||
string getTpName() {
|
||||
exists(StringLiteral lit |
|
||||
lit = this.getSlot("tp_name") |
|
||||
result = lit.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
Expr getSizeOf() {
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = getInitializer().getExpr()
|
||||
and tt.hasName("PyTypeObject")
|
||||
and f.hasName("tp_basicsize")
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = result
|
||||
)
|
||||
}
|
||||
|
||||
override string getTrapID() {
|
||||
/* This needs to be kept in sync with extractor-python/semmle/passes/type.py */
|
||||
result = "C_type$" + this.getTpName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A call to a Py_InitModule function. These functions register a Python module.
|
||||
*/
|
||||
class Py_InitModuleCall extends FunctionCall {
|
||||
Py_InitModuleCall() {
|
||||
// Py_InitModule itself is actually a macro, ultimately defined to be something like Py_InitModule4_64.
|
||||
getTarget().getName().matches("Py\\_Init%")
|
||||
}
|
||||
|
||||
/** Gets the name of the module being registered. */
|
||||
string getModuleName() {
|
||||
result = getArgument(0).(StringLiteral).getValue()
|
||||
}
|
||||
|
||||
/** Gets the function table associated with this Py_InitModule call. */
|
||||
PythonFunctionTable getFunctionTable() {
|
||||
exists(VariableAccess va |
|
||||
va = getArgument(1)
|
||||
and result = va.getTarget()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A Python module, represented by the file containing an initialising call for it.
|
||||
*/
|
||||
class PythonModule extends File {
|
||||
PythonModule() {
|
||||
exists(PythonModuleDefinition def | def.getFile() = this)
|
||||
or
|
||||
exists(FunctionCall c | c.getFile() = this |
|
||||
c.getTarget().getName().matches("Py\\_InitModule%")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a Python class that is in this module. */
|
||||
PythonClass getAClass() {
|
||||
result.getFile() = this
|
||||
}
|
||||
|
||||
/** Gets the function table associated with the module. */
|
||||
PythonFunctionTable getFunctionTable() {
|
||||
result = this.getDefinition().getFunctionTable()
|
||||
or
|
||||
exists(FunctionCall c | c.getFile() = this |
|
||||
c.getTarget().getName().matches("Py\\_InitModule%") and
|
||||
c.getAnArgument() = result.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the Py_InitModule call that was used to register the module. */
|
||||
//private
|
||||
PythonModuleDefinition getDefinition() {
|
||||
result.getFile() = this
|
||||
}
|
||||
|
||||
/** Gets the name of the module. */
|
||||
string getModuleName() {
|
||||
result = this.getDefinition().getModuleName()
|
||||
or
|
||||
exists(FunctionCall c |c.getFile() = this |
|
||||
c.getTarget().getName().matches("Py\\_InitModule%") and
|
||||
c.getArgument(0).getValue() = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The function table for a Python module.
|
||||
*/
|
||||
class PythonFunctionTable extends Variable {
|
||||
|
||||
PythonFunctionTable() {
|
||||
not(this instanceof Parameter)
|
||||
and exists(ArrayType at | at = getType().getUnspecifiedType() and at.getBaseType().hasName("PyMethodDef"))
|
||||
}
|
||||
|
||||
/** Gets an entry in the table. */
|
||||
PythonFunctionTableEntry getATableEntry() {
|
||||
result = getInitializer().getExpr().getAChild()
|
||||
and exists(result.getRegisteredFunctionName())
|
||||
}
|
||||
|
||||
/** Gets the class (if any) for which this is the function table. */
|
||||
PythonClass getClass() {
|
||||
result.getFunctionTable() = this
|
||||
or
|
||||
exists(FunctionAccess getattr, Call find_method |
|
||||
result.getSlot("tp_getattr") = getattr |
|
||||
find_method.getEnclosingFunction() = getattr.getTarget() and
|
||||
find_method.getArgument(0) = this.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the module (if any) for which this is the function table. */
|
||||
PythonModule getModule() {
|
||||
result.getFunctionTable() = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The getset table for a Python module or type
|
||||
*/
|
||||
class PythonGetSetTable extends Variable {
|
||||
|
||||
PythonGetSetTable() {
|
||||
not(this instanceof Parameter) and
|
||||
exists(ArrayType at | at = getType() and at.getBaseType().hasName("PyGetSetDef"))
|
||||
}
|
||||
|
||||
/** Gets the class (if any) for which this is the function table. */
|
||||
PythonClass getClass() {
|
||||
result.getGetSetTable() = this
|
||||
}
|
||||
|
||||
/** Gets an entry in the table. */
|
||||
PythonGetSetTableEntry getATableEntry() {
|
||||
result = getInitializer().getExpr().getAChild()
|
||||
and exists(result.getRegisteredPropertyName())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PythonModuleDefinition extends Variable {
|
||||
|
||||
PythonModuleDefinition() {
|
||||
not(this instanceof Parameter)
|
||||
and exists(Type moddef_t | moddef_t = this.getType() and moddef_t.hasName("PyModuleDef"))
|
||||
}
|
||||
|
||||
/** Gets the function table (if any) associated with the class. */
|
||||
PythonFunctionTable getFunctionTable() {
|
||||
exists(ClassAggregateLiteral l, Type moddef_t, Field f |
|
||||
l = this.getInitializer().getExpr()
|
||||
and moddef_t.hasName("PyModuleDef")
|
||||
and f.hasName("m_methods")
|
||||
and f.getDeclaringType() = moddef_t
|
||||
and result.getAnAccess() = l.getFieldExpr(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the function table (if any) associated with the class. */
|
||||
string getModuleName() {
|
||||
exists(ClassAggregateLiteral l, Type moddef_t, Field f |
|
||||
l = this.getInitializer().getExpr()
|
||||
and moddef_t.hasName("PyModuleDef")
|
||||
and f.hasName("m_name")
|
||||
and f.getDeclaringType() = moddef_t
|
||||
and result = l.getFieldExpr(f).getValue()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A special (__xxx__) method implemented in C
|
||||
*/
|
||||
class PythonSpecialMethod extends Function {
|
||||
|
||||
PythonSpecialMethod() {
|
||||
class_special_methods(_, _, this)
|
||||
}
|
||||
|
||||
PythonClass getClass() {
|
||||
class_special_methods(result, _, this)
|
||||
}
|
||||
|
||||
string getPyName() {
|
||||
class_special_methods(_, result, this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
predicate class_special_methods(PythonClass cls, string name, Function method) {
|
||||
|
||||
exists(string slot, FunctionAccess fa |
|
||||
special_methods(name, slot) and cls.getSlot(slot) = fa and fa.getTarget() = method
|
||||
or
|
||||
number_methods(name, slot) and
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = cls.getSlot("tp_as_number")
|
||||
and tt.hasName("PyNumberMethods")
|
||||
and f.hasName(slot)
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = fa
|
||||
and fa.getTarget() = method
|
||||
)
|
||||
or
|
||||
sequence_methods(name, slot) and
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = cls.getSlot("tp_as_sequence")
|
||||
and tt.hasName("PySequenceMethods")
|
||||
and f.hasName(slot)
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = fa
|
||||
and fa.getTarget() = method
|
||||
)
|
||||
or
|
||||
mapping_methods(name, slot) and
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = cls.getSlot("tp_as_mapping")
|
||||
and tt.hasName("PyMappingMethods")
|
||||
and f.hasName(slot)
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = fa
|
||||
and fa.getTarget() = method
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
predicate special_methods(string name, string slot_name) {
|
||||
name = "__getattr__" and slot_name = "tp_getattr"
|
||||
or
|
||||
name = "__hash__" and slot_name = "tp_hash"
|
||||
or
|
||||
name = "__call__" and slot_name = "tp_call"
|
||||
or
|
||||
name = "__str__" and slot_name = "tp_str"
|
||||
or
|
||||
name = "__getattribute__" and slot_name = "tp_getattro"
|
||||
or
|
||||
name = "__setattro__" and slot_name = "tp_setattro"
|
||||
or
|
||||
name = "__iter__" and slot_name = "tp_iter"
|
||||
or
|
||||
name = "__descr_get__" and slot_name = "tp_descr_get"
|
||||
or
|
||||
name = "__descr_set__" and slot_name = "tp_descr_set"
|
||||
}
|
||||
|
||||
predicate number_methods(string name, string slot_name) {
|
||||
name = "__add__" and slot_name = "nb_add"
|
||||
or
|
||||
name = "__sub__" and slot_name = "nb_subtract"
|
||||
or
|
||||
name = "__mul__" and slot_name = "nb_multiply"
|
||||
or
|
||||
name = "__mod__" and slot_name = "nb_remainder"
|
||||
or
|
||||
name = "__pow__" and slot_name = "nb_power"
|
||||
or
|
||||
name = "__neg__" and slot_name = "nb_negative"
|
||||
or
|
||||
name = "__pos__" and slot_name = "nb_positive"
|
||||
or
|
||||
name = "__abs__" and slot_name = "nb_absolute"
|
||||
or
|
||||
name = "__bool__" and slot_name = "nb_bool"
|
||||
or
|
||||
name = "__bool__" and slot_name = "nb_bool"
|
||||
or
|
||||
name = "__invert__" and slot_name = "nb_invert"
|
||||
or
|
||||
name = "__lshift__" and slot_name = "nb_lshift"
|
||||
or
|
||||
name = "__rshift__" and slot_name = "nb_rshift"
|
||||
or
|
||||
name = "__and__" and slot_name = "nb_and"
|
||||
or
|
||||
name = "__xor__" and slot_name = "nb_xor"
|
||||
or
|
||||
name = "__or__" and slot_name = "nb_or"
|
||||
or
|
||||
name = "__int__" and slot_name = "nb_int"
|
||||
or
|
||||
name = "__long__" and slot_name = "nb_long"
|
||||
or
|
||||
name = "__float__" and slot_name = "nb_float"
|
||||
or
|
||||
name = "__iadd__" and slot_name = "nb_inplace_add"
|
||||
or
|
||||
name = "__isub__" and slot_name = "nb_inplace_subtract"
|
||||
or
|
||||
name = "__imul__" and slot_name = "nb_inplace_multiply"
|
||||
or
|
||||
name = "__imod__" and slot_name = "nb_inplace_remainder"
|
||||
or
|
||||
name = "__ilshift__" and slot_name = "nb_inplace_lshift"
|
||||
or
|
||||
name = "__irshift__" and slot_name = "nb_inplace_rshift"
|
||||
or
|
||||
name = "__iand__" and slot_name = "nb_inplace_and"
|
||||
or
|
||||
name = "__ixor__" and slot_name = "nb_inplace_xor"
|
||||
or
|
||||
name = "__ior__" and slot_name = "nb_inplace_or"
|
||||
or
|
||||
name = "__index__" and slot_name = "nb_index"
|
||||
}
|
||||
|
||||
predicate sequence_methods(string name, string slot_name) {
|
||||
name = "__len__" and slot_name = "sq_length"
|
||||
or
|
||||
name = "__add__" and slot_name = "sq_concat"
|
||||
or
|
||||
name = "__mul__" and slot_name = "sq_repeat"
|
||||
or
|
||||
name = "__getitem__" and slot_name = "sq_item"
|
||||
or
|
||||
name = "__setitem__" and slot_name = "sq_ass_item"
|
||||
or
|
||||
name = "__contains__" and slot_name = "sq_contains"
|
||||
or
|
||||
name = "__iadd__" and slot_name = "sq_inplace_concat"
|
||||
or
|
||||
name = "__imul__" and slot_name = "sq_inplace_repeat"
|
||||
}
|
||||
|
||||
predicate mapping_methods(string name, string slot_name) {
|
||||
name = "__len__" and slot_name = "mp_length"
|
||||
or
|
||||
name = "__getitem__" and slot_name = "mp_subscript"
|
||||
or
|
||||
name = "__setitem__" and slot_name = "mp_ass_subscript"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
An entry in the getset table for a Python class.
|
||||
This is the C code item that corresponds 1-to-1 with the Python-level property
|
||||
*/
|
||||
class PythonGetSetTableEntry extends AggregateLiteral {
|
||||
|
||||
PythonGetSetTableEntry() {
|
||||
this.getUnderlyingType().hasName("PyGetSetDef")
|
||||
and
|
||||
this.getChild(0) instanceof StringLiteral
|
||||
}
|
||||
|
||||
Function getGetter() {
|
||||
exists(FunctionAccess fa | fa = getChild(1) and result = fa.getTarget())
|
||||
}
|
||||
|
||||
Function getSetter() {
|
||||
exists(FunctionAccess fa | fa = getChild(2) and result = fa.getTarget())
|
||||
}
|
||||
|
||||
StringLiteral getRegisteredPropertyName() {
|
||||
result = this.getChild(0)
|
||||
}
|
||||
|
||||
PythonGetSetTable getTable() {
|
||||
result.getATableEntry() = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
An entry in the function table for a Python class or module.
|
||||
This is the C code item that corresponds 1-to-1 with the Python-level function.
|
||||
*/
|
||||
class PythonFunctionTableEntry extends AggregateLiteral {
|
||||
|
||||
PythonFunctionTableEntry() {
|
||||
this.getUnderlyingType().hasName("PyMethodDef")
|
||||
and
|
||||
this.getChild(0) instanceof StringLiteral
|
||||
}
|
||||
|
||||
/** Gets the doc string to be associated with the function being registered. */
|
||||
string getDocString() {
|
||||
result = getChild(3).(StringLiteral).getValueText()
|
||||
}
|
||||
|
||||
/** Gets the flags for the function being registered. */
|
||||
int getFlags() {
|
||||
result = getChild(2).getValue().toInt()
|
||||
}
|
||||
|
||||
/** Gets the function being registered. */
|
||||
Function getFunction() {
|
||||
exists(FunctionAccess fa | fa = getChild(1) and result = fa.getTarget())
|
||||
}
|
||||
|
||||
/** Gets the module containing the function table. */
|
||||
PythonModule getModule() {
|
||||
result = getTable().getModule()
|
||||
}
|
||||
|
||||
/** Gets the name with which the function should be referenced from Python. */
|
||||
StringLiteral getRegisteredFunctionName() {
|
||||
result = this.getChild(0)
|
||||
}
|
||||
|
||||
/** Gets the function table containing this entry. */
|
||||
PythonFunctionTable getTable() {
|
||||
result.getATableEntry() = this
|
||||
}
|
||||
|
||||
/** Gets a flag associated with this function. */
|
||||
string getAFlag() {
|
||||
exists(int f | f = this.getFlags() |
|
||||
(f % 2 = 1 and result = "METH_VARARGS")
|
||||
or ((f / 2) % 2 = 1 and result = "METH_KEYWORDS")
|
||||
or ((f / 4) % 2 = 1 and result = "METH_NOARGS")
|
||||
or ((f / 8) % 2 = 1 and result = "METH_O")
|
||||
or ((f / 16) % 2 = 1 and result = "METH_CLASS")
|
||||
or ((f / 32) % 2 = 1 and result = "METH_STATIC")
|
||||
or ((f / 64) % 2 = 1 and result = "METH_COEXIST")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
library class PythonBuildReturnCall extends FunctionCall {
|
||||
PythonBuildReturnCall() {
|
||||
exists(string name | name = getTarget().getName() |
|
||||
name = "Py_BuildValue"
|
||||
or name = "Py_VaBuildValue"
|
||||
)
|
||||
}
|
||||
|
||||
string getFormatString() {
|
||||
result = getArgument(0).(StringLiteral).getValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
An extension function for Python (written in C).
|
||||
*/
|
||||
class PythonExtensionFunction extends Function {
|
||||
PythonExtensionFunction() {
|
||||
exists(PythonFunctionTableEntry e | e.getFunction() = this)
|
||||
and exists(getAParameter())
|
||||
}
|
||||
|
||||
/** Gets a function table entry registering this function. */
|
||||
PythonFunctionTableEntry getATableEntry() {
|
||||
result.getFunction() = this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TypedPythonExtensionProperty extends PythonGetSetTableEntry, CObject {
|
||||
PythonClass getPropertyType() {
|
||||
result = py_return_type(this.getGetter())
|
||||
}
|
||||
|
||||
private string trapClass() {
|
||||
result = this.getClass().getTrapID()
|
||||
}
|
||||
|
||||
override string getTrapID() {
|
||||
result = this.trapClass() + "$" + this.getPyName()
|
||||
}
|
||||
|
||||
string getPyName() {
|
||||
result = this.getRegisteredPropertyName().getValue()
|
||||
}
|
||||
|
||||
/** Gets the class containing this function. */
|
||||
PythonClass getClass() {
|
||||
result = this.getTable().getClass()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* An extension function for Python (written in C); Python facing aspect */
|
||||
abstract class TypedPythonExtensionFunction extends PythonFunctionTableEntry, CObject {
|
||||
|
||||
override Location getLocation() {
|
||||
result = this.getRegisteredFunctionName().getLocation()
|
||||
}
|
||||
|
||||
override
|
||||
string toString() {
|
||||
result = "MethodDef " + this.getRegisteredFunctionName().getValue()
|
||||
}
|
||||
|
||||
abstract PythonClass getArgumentType(int index);
|
||||
|
||||
abstract predicate argumentIsOptional(int index);
|
||||
|
||||
abstract predicate argumentIsKwOnly(int index);
|
||||
|
||||
PythonExtensionFunction getCode() {
|
||||
result.getATableEntry() = this
|
||||
}
|
||||
|
||||
predicate isMethod() {
|
||||
exists(this.getTable().getClass()) and not this.getAFlag() = "METH_STATIC"
|
||||
}
|
||||
|
||||
int c_index(int index) {
|
||||
index in [0..20] and result in [-1..20]
|
||||
and
|
||||
(if this.isMethod() then
|
||||
result = index - 1
|
||||
else
|
||||
result = index
|
||||
)
|
||||
}
|
||||
|
||||
string getPyName() {
|
||||
result = this.getRegisteredFunctionName().getValue()
|
||||
}
|
||||
|
||||
PythonClass getReturnType() {
|
||||
result = py_return_type(this.getCode())
|
||||
}
|
||||
|
||||
|
||||
/** Gets the module containing this function. */
|
||||
override PythonModule getModule() {
|
||||
result = getTable().getModule()
|
||||
}
|
||||
|
||||
/** Gets the class containing this function. */
|
||||
PythonClass getClass() {
|
||||
result = getTable().getClass()
|
||||
}
|
||||
|
||||
//private
|
||||
string trapModule() {
|
||||
result = this.getModule().getModuleName()
|
||||
}
|
||||
|
||||
//private
|
||||
string trapClass() {
|
||||
result = this.getClass().getTrapID()
|
||||
}
|
||||
|
||||
/* A globally unique name for use in trap files.
|
||||
*/
|
||||
override string getTrapID() {
|
||||
result = "C_builtin_function_or_method$" + this.trapModule() + "." + this.getPyName()
|
||||
or
|
||||
result = this.trapClass() + "$" + this.getPyName()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
predicate src_dest_pair(Element src, ControlFlowNode dest) {
|
||||
exists(LocalScopeVariable v, ControlFlowNode def |
|
||||
definitionUsePair(v, def, dest) and
|
||||
exprDefinition(v, def, src) and
|
||||
not exists(AddressOfExpr addr | addr.getOperand() = v.getAnAccess())
|
||||
)
|
||||
or
|
||||
exists(Parameter p | dest = p.getAnAccess() and not definitionUsePair(_, _, dest) and src = p)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate local_flows_to(Element src, ControlFlowNode dest) {
|
||||
not unreachable(src) and not unreachable(dest) and
|
||||
(src = dest
|
||||
or
|
||||
src_dest_pair(src, dest)
|
||||
or
|
||||
exists(Element mid | local_flows_to(src, mid) and src_dest_pair(mid, dest))
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass py_return_type(Function f) {
|
||||
exists(ReturnStmt ret |
|
||||
ret.getEnclosingFunction() = f and
|
||||
result = python_type(ret.getExpr()) and
|
||||
not ret.getExpr().getValue() = "0"
|
||||
)
|
||||
or
|
||||
exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_NONE" and result.getTpName() = "NoneType")
|
||||
or
|
||||
exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_TRUE" and result.getTpName() = "bool")
|
||||
or
|
||||
exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_FALSE" and result.getTpName() = "bool")
|
||||
}
|
||||
|
||||
PythonClass python_type_from_size(Expr e) {
|
||||
exists(Type t, string name |
|
||||
t = e.getUnderlyingType().(PointerType).getBaseType() and name = t.getName() and name.matches("Py\\_%Object") |
|
||||
exists(PythonClass cls | cls.getSizeOf().getValueText() = "sizeof(" + t.getName() + ")" |
|
||||
result = cls and not result.getTpName() = "int" and not result.getTpName() = "bool"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate py_bool(Expr e) {
|
||||
exists(MacroInvocation mi, string name |
|
||||
mi.getExpr() = e and name = mi.getMacroName() |
|
||||
name = "Py_False" or name = "Py_True"
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass python_type_from_name(Expr e) {
|
||||
exists(Type t, string name |
|
||||
t = e.getUnderlyingType().(PointerType).getBaseType() and name = t.getName() |
|
||||
name = "PyBytesObject" and result.getTpName() = "bytes"
|
||||
or
|
||||
name = "PyTupleObject" and result.getTpName() = "tuple"
|
||||
or
|
||||
name = "PyLongObject" and result.getTpName() = "int" and not py_bool(e)
|
||||
or
|
||||
name = "PyIntObject" and result.getTpName() = "int" and not py_bool(e)
|
||||
or
|
||||
name = "PyStringObject" and result.getTpName() = "str" and cpython_major_version() = 2
|
||||
or
|
||||
name = "PyStringObject" and result.getTpName() = "bytes" and cpython_major_version() = 3
|
||||
or
|
||||
name = "PyUnicodeObject" and result.getTpName() = "unicode" and cpython_major_version() = 2
|
||||
or
|
||||
name = "PyUnicodeObject" and result.getTpName() = "str" and cpython_major_version() = 3
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass python_type(Expr e) {
|
||||
result = python_type_from_size(e)
|
||||
or
|
||||
result = python_type_from_name(e)
|
||||
or
|
||||
call_to_new(e, result)
|
||||
or
|
||||
exists(Element src | result = python_type(src) and local_flows_to(src, e))
|
||||
or
|
||||
result = type_from_build_value(e)
|
||||
or
|
||||
result = type_from_call(e)
|
||||
or
|
||||
py_bool(e) and result.getTpName() = "bool"
|
||||
or
|
||||
call_to_type(e, result)
|
||||
or
|
||||
exists(MacroInvocation mi |
|
||||
mi.getExpr() = e and mi.getMacroName() = "Py_None" |
|
||||
result.getTpName() = "NoneType"
|
||||
)
|
||||
}
|
||||
|
||||
predicate build_value_function(Function f) {
|
||||
f.getName().regexpMatch("_?Py_(Va)?BuildValue(_SizeT)?")
|
||||
}
|
||||
|
||||
PythonClass type_from_build_value(Expr e) {
|
||||
exists(FunctionCall c |
|
||||
c = e |
|
||||
build_value_function(c.getTarget()) and
|
||||
result = type_from_build_value_code(c.getArgument(0).getValue())
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass type_from_call(Expr e) {
|
||||
exists(Function f |
|
||||
not build_value_function(f) and
|
||||
/* Do not type to infer return type of the interpreter */
|
||||
not f.getName().matches("PyEval%") and
|
||||
f = e.(FunctionCall).getTarget() and
|
||||
result = py_return_type(f)
|
||||
)
|
||||
or
|
||||
exists(Function f |
|
||||
f = e.(FunctionCall).getTarget() and
|
||||
result.getTpName() = "dict"
|
||||
|
|
||||
f.hasName("PyEval_GetBuiltins") or
|
||||
f.hasName("PyEval_GetGlobals") or
|
||||
f.hasName("PyEval_GetLocals")
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass type_from_build_value_code(string s) {
|
||||
exists(FunctionCall c | c.getArgument(0).getValue() = s)
|
||||
and
|
||||
(result = type_from_simple_code(s)
|
||||
or
|
||||
result.getTpName() = "dict" and s.charAt(0) = "{"
|
||||
or
|
||||
result.getTpName() = "tuple" and not exists(type_from_simple_code(s)) and not s.charAt(0) = "{"
|
||||
)
|
||||
}
|
||||
|
||||
private PythonClass theBytesClass() {
|
||||
cpython_major_version() = 2 and result.getTpName() = "str"
|
||||
or
|
||||
cpython_major_version() = 3 and result.getTpName() = "bytes"
|
||||
}
|
||||
|
||||
private PythonClass theUnicodeClass() {
|
||||
cpython_major_version() = 2 and result.getTpName() = "unicode"
|
||||
or
|
||||
cpython_major_version() = 3 and result.getTpName() = "str"
|
||||
}
|
||||
|
||||
PythonClass type_from_simple_code(string s) {
|
||||
(s = "s" or s = "s#" or s = "z" or s = "z#") and result.getTpName() = "str"
|
||||
or
|
||||
(s = "u" or s = "u#" or s = "U" or s = "U#" or s = "C") and result = theUnicodeClass()
|
||||
or
|
||||
(s = "y" or s = "y#" or s = "c") and result = theBytesClass()
|
||||
or
|
||||
(s = "i" or s = "I" or s = "b" or s = "B" or s = "h" or s = "H" or
|
||||
s = "k" or s = "K" or s = "l" or s = "L" or s = "n"
|
||||
) and result.getTpName() = "int"
|
||||
or
|
||||
(s = "f" or s = "d") and result.getTpName() = "float"
|
||||
or
|
||||
s = "D" and result.getTpName() = "complex"
|
||||
or
|
||||
(s = "O" or s = "O&" or s = "N") and result.getTpName() = "object"
|
||||
}
|
||||
|
||||
predicate call_to_new(FunctionCall call, PythonClass cls) {
|
||||
exists(string name |
|
||||
name = call.getTarget().getName() |
|
||||
name = "_PyObject_New" or
|
||||
name = "_PyObject_GC_New" or
|
||||
name = "_PyObject_NewVar" or
|
||||
name = "_PyObject_GC_NewVar"
|
||||
) and
|
||||
exists(AddressOfExpr addr |
|
||||
addr = call.getArgument(0) |
|
||||
addr.getAddressable() = cls
|
||||
)
|
||||
}
|
||||
|
||||
predicate call_to_type(FunctionCall call, PythonClass cls) {
|
||||
exists(AddressOfExpr aoe |
|
||||
call.getTarget().getName().matches("PyObject\\_Call%") and
|
||||
call.getArgument(0) = aoe and
|
||||
aoe.getAddressable() = cls
|
||||
)
|
||||
}
|
||||
|
||||
predicate pyargs_function(PythonFunctionTableEntry func, PyArgParseTupleCall call) {
|
||||
func.getFunction().getParameter(1).getAnAccess() = call.getArgument(0)
|
||||
}
|
||||
|
||||
|
||||
class PyArgsFunction extends TypedPythonExtensionFunction {
|
||||
|
||||
PyArgsFunction() {
|
||||
this.getAFlag() = "METH_VARARGS"
|
||||
}
|
||||
|
||||
PyArgParseTupleCall getParseArgsCall() {
|
||||
strictcount(PyArgParseTupleCall other | this.getCode().getParameter(1).getAnAccess() = other.getArgument(0)) = 1 and
|
||||
pyargs_function(this, result)
|
||||
}
|
||||
|
||||
override PythonClass getArgumentType(int index) {
|
||||
this.isMethod() and index = 0 and result = this.getTable().getClass()
|
||||
or
|
||||
result.getTpName() = this.getParseArgsCall().getPyArgumentType(this.c_index(index))
|
||||
}
|
||||
|
||||
override predicate argumentIsOptional(int index) {
|
||||
this.getParseArgsCall().pyArgumentIsOptional(this.c_index(index))
|
||||
}
|
||||
|
||||
override predicate argumentIsKwOnly(int index) {
|
||||
this.getParseArgsCall().pyArgumentIsKwOnly(this.c_index(index))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PyOFunction extends TypedPythonExtensionFunction {
|
||||
|
||||
PyOFunction() {
|
||||
this.getAFlag() = "METH_O"
|
||||
}
|
||||
|
||||
override PythonClass getArgumentType(int index) {
|
||||
this.isMethod() and index = 0 and result = this.getTable().getClass()
|
||||
or
|
||||
/* TO DO -- Better analysis */
|
||||
this.c_index(index) = 0 and result.getTpName() = "object"
|
||||
}
|
||||
|
||||
override predicate argumentIsOptional(int index) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate argumentIsKwOnly(int index) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
class PyNoArgFunction extends TypedPythonExtensionFunction {
|
||||
|
||||
PyNoArgFunction() {
|
||||
this.getAFlag() = "METH_NOARGS"
|
||||
}
|
||||
|
||||
override PythonClass getArgumentType(int index) {
|
||||
this.isMethod() and index = 0 and result = this.getTable().getClass()
|
||||
}
|
||||
|
||||
override predicate argumentIsOptional(int index) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate argumentIsKwOnly(int index) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
int cpython_major_version() {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getMacroName() = "PY_MAJOR_VERSION" |
|
||||
result = mi.getExpr().getValue().toInt()
|
||||
)
|
||||
}
|
||||
|
||||
int cpython_minor_version() {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getMacroName() = "PY_MINOR_VERSION" |
|
||||
result = mi.getExpr().getValue().toInt()
|
||||
)
|
||||
}
|
||||
|
||||
string cpython_version() {
|
||||
result = cpython_major_version().toString() + "." + cpython_minor_version().toString()
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @name Parameter return trap file generator
|
||||
* @description Generate trap files (in CSV form) describing CPython extension functions return one of their parameters.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/parameter-return-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import CPython.Extensions
|
||||
|
||||
predicate argument_flows_to_return(Function func, int arg) {
|
||||
exists(Parameter p | p = func.getParameter(arg) |
|
||||
forall(ReturnStmt ret | ret.getEnclosingFunction() = func |
|
||||
local_flows_to(p, ret.getExpr()))
|
||||
)
|
||||
}
|
||||
|
||||
from TypedPythonExtensionFunction func, PythonExtensionFunction code, int arg
|
||||
where func.getCode() = code and argument_flows_to_return(code, arg)
|
||||
select "ext_argreturn", func.getTrapID(), arg
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @name Property type trap file generator
|
||||
* @description Generate trap files (in CSV form) describing CPython extension property types.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/property-type-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
|
||||
from TypedPythonExtensionProperty p, PythonClass cls
|
||||
where cls = p.getPropertyType()
|
||||
select "ext_proptype", p.getTrapID(), cls.getTrapID()
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @name Return type trap file generator
|
||||
* @description Generate trap files (in CSV form) describing CPython extension function return types.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/return-type-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
|
||||
from TypedPythonExtensionFunction func, PythonClass cls
|
||||
where cls = func.getReturnType()
|
||||
select "ext_rettype", func.getTrapID(), cls.getTrapID()
|
||||
@@ -5,23 +5,26 @@
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds branching statements with conditions that always evaluate to the same value.
|
||||
More likely than not these conditions indicate a defect in the branching condition or are an artifact left behind after debugging.</p>
|
||||
<p>This query finds branching statements with conditions that always evaluate to the same value.
|
||||
It is likely that these conditions indicate an error in the branching condition.
|
||||
Alternatively, the conditions may have been left behind after debugging.</p>
|
||||
|
||||
<include src="aliasAnalysisWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Check the branch condition for defects, and verify that it isn't a remnant from debugging.</p>
|
||||
|
||||
<p>Check the branch condition for logic errors. Check whether it is still required.</p>
|
||||
</recommendation>
|
||||
<example><sample src="DeadCodeCondition.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<p>This example shows two branch conditions that always evaluate to the same value.
|
||||
The two conditions and their associated branches should be deleted.
|
||||
This will simplify the code and make it easier to maintain.</p>
|
||||
|
||||
<sample src="DeadCodeCondition.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C++ Coding Standard <a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect or is never executed</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -2,7 +2,7 @@ class C {
|
||||
public:
|
||||
void g() {
|
||||
...
|
||||
//f() was previously used but is now commented, orphaning f()
|
||||
//f() was previously used but is now commented-out, orphaning f()
|
||||
//f();
|
||||
...
|
||||
}
|
||||
|
||||
@@ -3,28 +3,31 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds functions that are non-public, non-virtual and are never called. Dead functions are often deprecated pieces of code, and should be removed
|
||||
as they may increase object code size, decrease code comprehensibility, and create the possibility of misuse.</p>
|
||||
<p>This query highlights functions that are non-public, non-virtual, and are never called.
|
||||
Dead functions are often deprecated pieces of code, and should be removed.
|
||||
If left in the code base they increase object code size, decrease code comprehensibility, and create the possibility of misuse.</p>
|
||||
|
||||
<p>
|
||||
<code>public</code> and <code>protected</code> functions are not considered by the check, as they could be part of the program's
|
||||
API and could be used by external programs.
|
||||
<code>public</code> and <code>protected</code> functions are ignored by this query.
|
||||
This type of function may be part of the program's API and could be used by external programs.
|
||||
</p>
|
||||
|
||||
<include src="callGraphWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Consider removing the function.</p>
|
||||
|
||||
<p>Verify that the function is genuinely unused and consider removing it.</p>
|
||||
</recommendation>
|
||||
<example><sample src="DeadCodeFunction.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<p>The example below includes a function <code>f</code> that is no longer used and should be deleted.</p>
|
||||
<sample src="DeadCodeFunction.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C++ Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect or is never executed</a>.</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Function is never called
|
||||
* @description A function is never called, and should be considered for removal. Unused functions may increase object size, decrease readability and create the possibility of misuse.
|
||||
* @description Unused functions may increase object size, decrease readability, and create the possibility of misuse.
|
||||
* @kind problem
|
||||
* @id cpp/dead-code-function
|
||||
* @problem.severity warning
|
||||
|
||||
7
cpp/ql/src/Critical/DeadCodeGoto.cpp
Normal file
7
cpp/ql/src/Critical/DeadCodeGoto.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
goto err1;
|
||||
free(pointer); // BAD: this line is unreachable
|
||||
err1: return -1;
|
||||
|
||||
free(pointer); // GOOD: this line is reachable
|
||||
goto err2;
|
||||
err2: return -1;
|
||||
28
cpp/ql/src/Critical/DeadCodeGoto.qhelp
Normal file
28
cpp/ql/src/Critical/DeadCodeGoto.qhelp
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Code immediately following a <code>goto</code> or <code>break</code> statement will not be executed,
|
||||
unless there is a label or switch case. When the code is necessary, this leads to logical errors or
|
||||
resource leaks. If the code is unnecessary, it may confuse readers.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
If the unreachable code is necessary, move the <code>goto</code> or <code>break</code> statement to
|
||||
after the code. Otherwise, delete the unreachable code.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="DeadCodeGoto.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
The CERT C Secure Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect or is never executed</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
33
cpp/ql/src/Critical/DeadCodeGoto.ql
Normal file
33
cpp/ql/src/Critical/DeadCodeGoto.ql
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Dead code due to goto or break statement
|
||||
* @description A goto or break statement is followed by unreachable code.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/dead-code-goto
|
||||
* @tags maintainability
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
Stmt getNextRealStmt(Block b, int i) {
|
||||
result = b.getStmt(i + 1) and
|
||||
not result instanceof EmptyStmt
|
||||
or
|
||||
b.getStmt(i + 1) instanceof EmptyStmt and
|
||||
result = getNextRealStmt(b, i + 1)
|
||||
}
|
||||
|
||||
from JumpStmt js, Block b, int i, Stmt s
|
||||
where b.getStmt(i) = js
|
||||
and s = getNextRealStmt(b, i)
|
||||
// the next statement isn't jumped to
|
||||
and not s instanceof LabelStmt
|
||||
and not s instanceof SwitchCase
|
||||
// the next statement isn't breaking out of a switch
|
||||
and not s.(BreakStmt).getBreakable() instanceof SwitchStmt
|
||||
// the next statement isn't a loop that can be jumped into
|
||||
and not exists (LabelStmt ls | s.(Loop).getStmt().getAChild*() = ls)
|
||||
and not exists (SwitchCase sc | s.(Loop).getStmt().getAChild*() = sc)
|
||||
select js, "This statement makes $@ unreachable.", s, s.toString()
|
||||
@@ -6,23 +6,25 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule looks at functions that return file or socket descriptors, but may return an error value before actually closing the resource.
|
||||
This can occur when an operation performed on the open descriptor fails, and the function returns with an error before closing the open resource. An improperly handled error could cause the function to leak resource descriptors.
|
||||
This query looks at functions that return file or socket descriptors, but may return an error value before actually closing the resource.
|
||||
This can occur when an operation performed on the open descriptor fails, and the function returns with an error before it closes the open resource. An improperly handled error could cause the function to leak resource descriptors. Failing to close resources in the function that opened them also makes it more difficult to detect leaks.
|
||||
</p>
|
||||
|
||||
<include src="dataFlowWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Ensure that the function frees all the resources it acquired when an error occurs.</p>
|
||||
|
||||
<p>When an error occurs, ensure that the function frees all the resources it holds open.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the example below, the <code>sockfd</code> socket may remain open if an error is triggered.
|
||||
The code should be updated to ensure that the socket is always closed when when the function ends.
|
||||
</p>
|
||||
<sample src="DescriptorMayNotBeClosed.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C++ Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR57-CPP.+Do+not+leak+resources+when+handling+exceptions">ERR57-CPP. Do not leak resources when handling exceptions</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Open descriptor may not be closed
|
||||
* @description A function may return before closing a socket or file that was opened in the function. Closing resources in the same function that opened them ties the lifetime of the resource to that of the function call, making it easier to avoid and detect resource leaks.
|
||||
* @description Failing to close resources in the function that opened them makes it difficult to avoid and detect resource leaks.
|
||||
* @kind problem
|
||||
* @id cpp/descriptor-may-not-be-closed
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -6,20 +6,26 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds calls to <code>open</code> or <code>socket</code> with no corresponding <code>close</code> call in the entire program.
|
||||
This rule finds calls to <code>open</code> or <code>socket</code> where there is no corresponding <code>close</code> call in the program analyzed.
|
||||
Leaving descriptors open will cause a resource leak that will persist even after the program terminates.
|
||||
</p>
|
||||
|
||||
<include src="aliasAnalysisWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Ensure that all file or socket descriptors allocated by the program are freed before it terminates.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="DescriptorNeverClosed.cpp" />
|
||||
|
||||
|
||||
<example>
|
||||
<p>In the example below, the <code>sockfd</code> socket remains open when the <code>main</code> program finishes.
|
||||
The code should be updated to ensure that the socket is always closed when the program terminates.
|
||||
</p>
|
||||
|
||||
<sample src="DescriptorNeverClosed.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C++ Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR57-CPP.+Do+not+leak+resources+when+handling+exceptions">ERR57-CPP. Do not leak resources when handling exceptions</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Open descriptor never closed
|
||||
* @description A function always returns before closing a socket or file that was opened in the function. Closing resources in the same function that opened them ties the lifetime of the resource to that of the function call, making it easier to avoid and detect resource leaks.
|
||||
* @description Functions that always return before closing the socket or file they opened leak resources.
|
||||
* @kind problem
|
||||
* @id cpp/descriptor-never-closed
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -5,26 +5,28 @@
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds calls to functions that use a global variable which happen before the variable was initialized.
|
||||
<p>This rule finds calls to functions that use a global variable before the variable has been initialized.
|
||||
Not all compilers generate code that zero-out memory, especially when optimizations are enabled or the compiler
|
||||
is not compliant with the latest language standards. Accessing uninitialized memory will lead to undefined results.
|
||||
</p>
|
||||
|
||||
<include src="dataFlowWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Initialize the global variable. If no constant can be used for initialization, ensure that all accesses to the variable occur after
|
||||
the initialization code is executed.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="GlobalUseBeforeInit.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In the example below, <code>callCtr</code> is wrongly used before it has been initialized.
|
||||
</p>
|
||||
<sample src="GlobalUseBeforeInit.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C++ Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/EXP53-CPP.+Do+not+read+uninitialized+memory">EXP53-CPP. Do not read uninitialized memory</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Global variable used before initialization
|
||||
* @description A function that uses a global variable has been called before the variable has been initialized. Not all compilers zero-out memory for variables, especially when optimizations are enabled, or if the compiler is not compliant with the latest language standards. Using an uninitialized variable leads to undefined results.
|
||||
* @name Global variable may be used before initialization
|
||||
* @description Using an uninitialized variable may lead to undefined results.
|
||||
* @kind problem
|
||||
* @id cpp/global-use-before-init
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
void* f() {
|
||||
block = malloc(BLOCK_SIZE);
|
||||
block = (MyBlock *)malloc(sizeof(MyBlock));
|
||||
if (block) { //correct: block is checked for nullness here
|
||||
block->id = NORMAL_BLOCK_ID;
|
||||
}
|
||||
//...
|
||||
/* make sure data-portion is null-terminated */
|
||||
block[BLOCK_SIZE - 1] = '\0'; //wrong: block not checked for nullness here
|
||||
block->data[BLOCK_SIZE - 1] = '\0'; //wrong: block not checked for nullness here
|
||||
return block;
|
||||
}
|
||||
|
||||
@@ -5,23 +5,30 @@
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds pointer dereferences that do not check the pointer for nullness, while the same pointer is checked for nullness in other
|
||||
places in the code. It is most likely that the nullness check was omitted, and that a NULL pointer dereference can occur.
|
||||
Dereferencing a null pointer and attempting to modify its contents can lead to anything from a segfault to corrupting
|
||||
important system data (i.e. the interrupt table in some architectures).
|
||||
<p>This query finds pointer dereferences that do not first check the pointer for nullness,
|
||||
even though the same pointer is checked for nullness in other
|
||||
parts of the code. It is likely that the nullness check was accidentally omitted, and that a null pointer dereference can occur.
|
||||
Dereferencing a null pointer and attempting to modify its contents can lead to anything from a segmentation fault to corrupting
|
||||
important system data (including the interrupt table in some architectures).
|
||||
</p>
|
||||
|
||||
<include src="pointsToWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Make the nullness check on the pointer consistent across all dereferences.</p>
|
||||
<p>Use a nullness check consistently in all cases where a pointer is dereferenced.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="InconsistentNullnessTesting.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<p>
|
||||
This code shows two examples where a pointer is dereferenced.
|
||||
The first example checks that the pointer is not null before dereferencing it.
|
||||
The second example fails to perform a nullness check, leading to a potential vulnerability in the code.
|
||||
</p>
|
||||
<sample src="InconsistentNullnessTesting.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C++ Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/MEM10-C.+Define+and+use+a+pointer+validation+function">MEM10-C. Define and use a pointer validation function</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Inconsistent null check of pointer
|
||||
* @description A dereferenced pointer is not checked for nullness in the given location, but is checked in other locations. Dereferencing a NULL pointer leads to undefined results.
|
||||
* @description A dereferenced pointer is not checked for nullness in this location, but it is checked in other locations. Dereferencing a null pointer leads to undefined results.
|
||||
* @kind problem
|
||||
* @id cpp/inconsistent-nullness-testing
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -5,23 +5,28 @@
|
||||
|
||||
|
||||
<overview>
|
||||
<p>The rule finds code that initializes a global variable (i.e. uses it as an L-value) but is never called from <code>main</code>.
|
||||
<p>The query finds code that initializes a global variable (that is, uses it as an L-value) but is never called from <code>main</code>.
|
||||
Unless there is another entry point that triggers the initialization, the code should be modified so that the variable is initialized.
|
||||
Uninitialized variables may contain any value, as not all compilers generate code that zero-out memory, especially when
|
||||
optimizations are enabled or the compiler is not compliant with the latest language standards.
|
||||
</p>
|
||||
|
||||
<include src="callGraphWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Examine the code and ensure that the initialization is always run.</p>
|
||||
<p>Examine the code and ensure that the variable is always initialized before it is used.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="InitialisationNotRun.cpp" />
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<p>In the example below, the code that triggers the initialization of <code>g_storage</code> is not run from <code>main</code>.
|
||||
Unless the variable is initialized by another method, the call on line 10 may dereference a null pointer.
|
||||
</p>
|
||||
|
||||
<sample src="InitialisationNotRun.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>C++ reference: <a href="https://en.cppreference.com/book/uninitialized">uninitialized variables</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Initialization code not run
|
||||
* @description A global variable has initialization code, but that code is never run (i.e. is called directly or indirectly from main()). Accessing uninitialized variables leads to undefined results.
|
||||
* @description Not running initialization code may lead to unexpected behavior.
|
||||
* @kind problem
|
||||
* @id cpp/initialization-not-run
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
typedef struct Names {
|
||||
char first[100];
|
||||
char last[100];
|
||||
char first[100];
|
||||
char last[100];
|
||||
} Names;
|
||||
|
||||
int doFoo(Names n) { //wrong: n is passed by value (meaning the entire structure
|
||||
//is copied onto the stack, instead of just a pointer)
|
||||
...
|
||||
...
|
||||
}
|
||||
|
||||
int doBar(Names &n) { //better, only a reference is passed
|
||||
...
|
||||
...
|
||||
}
|
||||
|
||||
@@ -6,29 +6,39 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds integer values that are first used to index an array and
|
||||
This query finds integer values that are first used to index an array and
|
||||
subsequently tested for being negative. If it is relevant to perform this test
|
||||
at all then it should probably happen <em>before</em> the indexing, not
|
||||
at all then it should happen <em>before</em> the indexing, not
|
||||
after. Otherwise, if the value is negative then the program will have failed
|
||||
before performing the test.
|
||||
</p>
|
||||
<include src="dataFlowWarning.qhelp" />
|
||||
|
||||
<include src="dataFlowWarning.qhelp" />
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
See if the value needs checking before being used as array index. Double-check
|
||||
See if the value needs to be checked before being used as array index. Double-check
|
||||
if the value is derived from user input. If the value clearly cannot be
|
||||
negative then the negativity test is redundant and can be removed.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The example below includes two functions that use the value <code>recordIdx</code> to
|
||||
index an array and a test to verify that the value is positive.
|
||||
The test is made after <code>records</code> is indexed for <code>printRecord</code> and
|
||||
before <code>records</code> is indexed for <code>processRecord</code>.
|
||||
Unless the value of <code>recordIdx</code> cannot be negative, the test should be
|
||||
updated to run before <em>both</em> times the array is indexed.
|
||||
If the value cannot be negative, the test should be removed.
|
||||
</p>
|
||||
|
||||
<sample src="LateNegativeTest.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>cplusplus.com: <a href="http://www.cplusplus.com/doc/tutorial/pointers/">Pointers</a>.</li>
|
||||
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* @name Pointer offset used before it is checked
|
||||
* @description A value is used as a pointer offset before it is tested for
|
||||
* being positive/negative. This may mean that an unsuitable
|
||||
* pointer offset will be used before the test occurs.
|
||||
* @description Accessing a pointer or array using an offset before
|
||||
* checking if the value is positive
|
||||
* may result in unexpected behavior.
|
||||
* @kind problem
|
||||
* @id cpp/late-negative-test
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Record records[SIZE] = ...;
|
||||
|
||||
int f() {
|
||||
int recordIdx = 0;
|
||||
recordIdx = readUserInput(); //recordIdx is returned from a function
|
||||
int recordIdx = 0;
|
||||
recordIdx = readUserInput(); //recordIdx is returned from a function
|
||||
// there is no check so it could be negative
|
||||
doFoo(&(records[recordIdx])); //but is not checked before use as an array offset
|
||||
doFoo(&(records[recordIdx])); //but is not checked before use as an array offset
|
||||
}
|
||||
|
||||
|
||||
@@ -6,33 +6,35 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds pointer arithmetic expressions that use a value returned from a function before the value is checked to be positive.
|
||||
Most pointer arithmetic and almost all array element accesses use a positive value for offsets. A negative value is more likely than not
|
||||
This query finds pointer arithmetic expressions that use a value returned from a function without checking that the value is positive.
|
||||
Most pointer arithmetic and almost all array element accesses use a positive value for offsets. A negative value is likely to be
|
||||
a defect in the returning function. Checking pointer offsets (particularly if they derive from user input) is necessary to avoid
|
||||
buffer overruns.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The rules only looks at return values of functions that may return a negative value (not all functions).
|
||||
The query looks only at the return values of functions that may return a negative value (not all functions).
|
||||
</p>
|
||||
|
||||
|
||||
<include src="dataFlowWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Check the function and see whether it needs to check the value to be positive.
|
||||
Review the function. Determine whether it needs to check that the value is positive before performing pointer arithmetic.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The example below shows an example of this problem. There is no check to ensure that the value of <code>recordIdx</code>
|
||||
is positive and safe to use as an array offset.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="MissingNegativityTest.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>cplusplus.com: <a href="http://www.cplusplus.com/doc/tutorial/pointers/">Pointers</a>.</li>
|
||||
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @name Unchecked return value used as offset
|
||||
* @description A return value from a function is used as a pointer offset before it is checked for being positive/negative. Integer values used as pointer offsets should be checked, especially if they are derived from user input.
|
||||
* @description Using a return value as a pointer offset without checking that the value is positive
|
||||
* may lead to buffer overruns.
|
||||
* @kind problem
|
||||
* @id cpp/missing-negativity-test
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -6,23 +6,27 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds pointer dereferences that use a pointer returned from a function which may return NULL. Always
|
||||
This query finds pointer dereferences that use a pointer returned from a function which may return NULL. Always
|
||||
check your pointers for NULL-ness before dereferencing them. Dereferencing a null pointer and attempting to
|
||||
modify its contents can lead to anything from a segfault to corrupting important system data
|
||||
(i.e. the interrupt table in some architectures).
|
||||
modify its contents can lead to anything from a segmentation fault to corruption of important system data
|
||||
(for example, the interrupt table in some architectures).
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Add a null check before dereferencing the pointer, or modify the function so that it always returns a non-null value.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="MissingNullTest.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<p>In this example, the function is not protected from dereferencing a null pointer. It should be updated to ensure that
|
||||
this cannot happen.
|
||||
</p>
|
||||
<sample src="MissingNullTest.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointerss">EXP34-C. Do not dereference null pointers</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Returned pointer not checked
|
||||
* @description A value returned from a function that may return null is not tested to determine whether or not it is null. Dereferencing NULL pointers lead to undefined behavior.
|
||||
* @description Dereferencing an untested value from a function that can return null may lead to undefined behavior.
|
||||
* @kind problem
|
||||
* @id cpp/missing-null-test
|
||||
* @problem.severity recommendation
|
||||
|
||||
@@ -6,22 +6,23 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds variables that are used before they are initialized. Values of uninitialized variables are
|
||||
This query finds variables that are used before they are initialized. Values of uninitialized variables are
|
||||
undefined, as not all compilers zero out memory, especially when optimizations are enabled or the compiler
|
||||
is not compliant with the latest language standards.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Initialize the variable before accessing it.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="NotInitialised.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<sample src="NotInitialised.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>C++ reference: <a href="https://en.cppreference.com/book/uninitialized">uninitialized variables</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Variable not initialized before use
|
||||
* @description A variable is used before initialized. The value of a variable is undefined before initialization, and its use should be avoided.
|
||||
* @description Using an uninitialized variable may lead to undefined results.
|
||||
* @kind problem
|
||||
* @id cpp/not-initialised
|
||||
* @problem.severity error
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
void f(char* string) {
|
||||
// wrong: allocates space for characters, put not zero terminator
|
||||
// wrong: allocates space for characters, but not zero terminator
|
||||
char* buf = malloc(strlen(string));
|
||||
strcpy(buf, string);
|
||||
|
||||
|
||||
@@ -6,36 +6,43 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds <code>malloc</code> that use a <code>strlen</code> for the size but to not take the
|
||||
zero terminator into consideration, and <code>strcat/strncat</code> calls that are done on buffers that do
|
||||
not have the sufficient size to contain the new string.
|
||||
</p>
|
||||
This query finds calls to:</p>
|
||||
<ul>
|
||||
<li><code>malloc</code> that use a <code>strlen</code> for the buffer size and do not take the
|
||||
zero terminator into consideration.</li>
|
||||
<li><code>strcat</code> or <code>strncat</code> that use buffers that are too small to contain the new string.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
The indicated expression will cause a buffer overflow due to a buffer that is of insufficient size to contain
|
||||
the data being copied. Buffer overflows can result to anything from a segfault to a security vulnerability (particularly
|
||||
The highlighted expression will cause a buffer overflow because the buffer is too small to contain
|
||||
the data being copied. Buffer overflows can result to anything from a segmentation fault to a security vulnerability (particularly
|
||||
if the array is on stack-allocated memory).
|
||||
</p>
|
||||
|
||||
<include src="aliasAnalysisWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Increase the size of the buffer being allocated.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="OverflowCalculated.cpp" />
|
||||
|
||||
|
||||
<example>
|
||||
<p>This example includes three annotated calls that copy a string into a buffer.
|
||||
The first call to <code>malloc</code> creates a buffer that's the
|
||||
same size as the string, leaving no space for the zero terminator
|
||||
and causing an overflow. The second call to <code>malloc</code>
|
||||
correctly calculates the required buffer size. The call to
|
||||
<code>strcat</code> appends an additional string to the same buffer
|
||||
causing a second overflow.</p>
|
||||
|
||||
<sample src="OverflowCalculated.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<references>
|
||||
<li><a href="http://cwe.mitre.org/data/definitions/131.html">CWE-131: Incorrect Calculation of Buffer Size</a></li>
|
||||
<li>I. Gerg. <em>An Overview and Example of the Buffer-Overflow Exploit</em>. IANewsletter vol 7 no 4. 2005.</li>
|
||||
<li>M. Donaldson. <em>Inside the Buffer Overflow Attack: Mechanism, Method & Prevention</em>. SANS Institute InfoSec Reading Room. 2002.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
char param[SIZE];
|
||||
char param[20];
|
||||
char *arg1;
|
||||
|
||||
char arg1[10];
|
||||
char arg2[20];
|
||||
arg1 = argv[1];
|
||||
|
||||
//wrong: only uses the size of the source (argv[1]) when using strncpy
|
||||
strncpy(param, argv[1], strlen(arg1));
|
||||
strncpy(param, arg1, strlen(arg1));
|
||||
|
||||
//correct: uses the size of the destination array as well
|
||||
strncpy(param, argv[1], min(strlen(arg1, sizeof(param) -1)));
|
||||
strncpy(param, arg1, min(strlen(arg1), sizeof(param) -1));
|
||||
}
|
||||
|
||||
@@ -3,27 +3,31 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The bounded copy functions <code>memcpy</code>, <code>memmove</code>, <code>strncpy</code>, <code>strncat</code> accept a size argument. You should call these functions with a size argument that is derived from the size of the destination buffer. Using a size argument that is derived from the source buffer may cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.</p>
|
||||
|
||||
|
||||
<p>The bounded copy functions <code>memcpy</code>, <code>memmove</code>, <code>strncpy</code>, <code>strncat</code> accept a size argument.
|
||||
You should call these functions with a size argument that is derived from the size of the destination buffer.
|
||||
Using a size argument that is derived from the source buffer may cause a buffer overflow.
|
||||
Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Check the highlighted function calls carefully, and ensure that the size parameter is derived from the size of the destination buffer,
|
||||
<p>Check the highlighted function calls carefully.
|
||||
Ensure that the size parameter is derived from the size of the destination buffer, and
|
||||
not the source buffer.</p>
|
||||
|
||||
<include src="aliasAnalysisWarning.qhelp" />
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="OverflowDestination.cpp" />
|
||||
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The code below shows an example where <code>strncpy</code> is called incorrectly, without checking the size of the destination buffer.
|
||||
In the second example the call has been updated to include the size of the destination buffer.</p>
|
||||
|
||||
<sample src="OverflowDestination.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li><a href="http://cwe.mitre.org/data/definitions/119.html">CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer</a></li>
|
||||
<references>
|
||||
<li>I. Gerg. <em>An Overview and Example of the Buffer-Overflow Exploit</em>. IANewsletter vol 7 no 4. 2005.</li>
|
||||
<li>M. Donaldson. <em>Inside the Buffer Overflow Attack: Mechanism, Method & Prevention</em>. SANS Institute InfoSec Reading Room. 2002.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,30 +1,42 @@
|
||||
/**
|
||||
* @name Copy function using source size
|
||||
* @description Calling a copy operation with a size derived from the source
|
||||
* buffer instead of the destination buffer may result in a buffer overflow
|
||||
* buffer instead of the destination buffer may result in a buffer overflow.
|
||||
* @kind problem
|
||||
* @id cpp/overflow-destination
|
||||
* @problem.severity warning
|
||||
* @precision low
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-119
|
||||
* external/cwe/cwe-131
|
||||
*/
|
||||
import cpp
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
predicate sourceSized(FunctionCall fc)
|
||||
/**
|
||||
* Holds if `fc` is a call to a copy operation where the size argument contains
|
||||
* a reference to the source argument. For example:
|
||||
* ```
|
||||
* memcpy(dest, src, sizeof(src));
|
||||
* ```
|
||||
*/
|
||||
predicate sourceSized(FunctionCall fc, Expr src)
|
||||
{
|
||||
exists(string name |
|
||||
(name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and
|
||||
fc.getTarget().hasQualifiedName(name))
|
||||
and
|
||||
exists(Expr dest, Expr src, Expr size, Variable v |
|
||||
exists(Expr dest, Expr size, Variable v |
|
||||
fc.getArgument(0) = dest and fc.getArgument(1) = src and 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
|
||||
src.getType().getUnderlyingType() = srctype and
|
||||
@@ -32,48 +44,7 @@ predicate sourceSized(FunctionCall fc)
|
||||
desttype.getArraySize() = srctype.getArraySize()))
|
||||
}
|
||||
|
||||
class VulnerableArgument extends PointsToExpr
|
||||
{
|
||||
VulnerableArgument() { sourceSized(this.getParent()) }
|
||||
override predicate interesting() { sourceSized(this.getParent()) }
|
||||
}
|
||||
|
||||
predicate taintingFunction(Function f, int buf)
|
||||
{
|
||||
(f.hasQualifiedName("read") and buf = 1) or
|
||||
(f.hasQualifiedName("fgets") and buf = 0) or
|
||||
(f.hasQualifiedName("fread") and buf = 0)
|
||||
}
|
||||
|
||||
// Taint `argv[i]`, for all i, but also `*argv`, etc.
|
||||
predicate commandLineArg(Expr e)
|
||||
{
|
||||
exists(Function f, Parameter argv, VariableAccess access |
|
||||
f.hasQualifiedName("main") and f.getParameter(1) = argv and
|
||||
argv.getAnAccess() = access and access.isRValue() and
|
||||
pointer(access, e))
|
||||
}
|
||||
|
||||
predicate tainted(Expr e)
|
||||
{
|
||||
exists(FunctionCall fc, int arg |
|
||||
taintingFunction(fc.getTarget(), arg) and
|
||||
e = fc.getArgument(arg))
|
||||
or
|
||||
e.(FunctionCall).getTarget().hasQualifiedName("getenv")
|
||||
or
|
||||
commandLineArg(e)
|
||||
}
|
||||
|
||||
class TaintedArgument extends PointsToExpr
|
||||
{
|
||||
TaintedArgument() { tainted(this) }
|
||||
override predicate interesting() { tainted(this) }
|
||||
}
|
||||
|
||||
from FunctionCall fc, VulnerableArgument vuln, TaintedArgument tainted
|
||||
where sourceSized(fc)
|
||||
and fc.getArgument(1) = vuln
|
||||
and vuln.pointsTo() = tainted.pointsTo()
|
||||
and vuln.confidence() > 0.01
|
||||
from FunctionCall fc, Expr vuln, Expr taintSource
|
||||
where sourceSized(fc, vuln)
|
||||
and tainted(taintSource, vuln)
|
||||
select fc, "To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size."
|
||||
|
||||
@@ -82,22 +82,31 @@ class CallWithBufferSize extends FunctionCall
|
||||
Expr buffer() {
|
||||
exists(int i |
|
||||
bufferAndSizeFunction(this.getTarget(), i, _) and
|
||||
result = this.getArgument(i))
|
||||
result = this.getArgument(i)
|
||||
)
|
||||
}
|
||||
Expr statedSize() {
|
||||
Expr statedSizeExpr() {
|
||||
exists(int i |
|
||||
bufferAndSizeFunction(this.getTarget(), _, i) and
|
||||
result = this.getArgument(i))
|
||||
result = this.getArgument(i)
|
||||
)
|
||||
}
|
||||
int statedSizeValue() {
|
||||
exists(Expr statedSizeSrc |
|
||||
DataFlow::localFlow(DataFlow::exprNode(statedSizeSrc), DataFlow::exprNode(statedSizeExpr())) and
|
||||
result = statedSizeSrc.getValue().toInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate wrongBufferSize(Expr error, string msg) {
|
||||
exists(CallWithBufferSize call, int bufsize, Variable buf |
|
||||
exists(CallWithBufferSize call, int bufsize, Variable buf, int statedSize |
|
||||
staticBuffer(call.buffer(), buf, bufsize) and
|
||||
call.statedSize().getValue().toInt() > bufsize and
|
||||
error = call.statedSize() and
|
||||
statedSize = min(call.statedSizeValue()) and
|
||||
statedSize > bufsize and
|
||||
error = call.statedSizeExpr() and
|
||||
msg = "Potential buffer-overflow: '" + buf.getName() +
|
||||
"' has size " + bufsize.toString() + " not " + call.statedSize().getValue() + ".")
|
||||
"' has size " + bufsize.toString() + " not " + statedSize + ".")
|
||||
}
|
||||
|
||||
predicate outOfBounds(BufferAccess bufaccess, string msg)
|
||||
|
||||
@@ -6,27 +6,32 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds return statements that return pointers to an object allocated on the stack.
|
||||
This query finds return statements that return pointers to an object allocated on the stack.
|
||||
The lifetime of a stack allocated memory location only lasts until the function returns, and
|
||||
the contents of that memory become undefined after that. Clearly, using a pointer to stack
|
||||
memory after the function has already returned will have undefined results.
|
||||
</p>
|
||||
|
||||
<include src="pointsToWarning.qhelp" />
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Do not return pointers to stack memory locations. Instead, create an output parameter, or create a heap-allocated
|
||||
buffer, copy the contents of the stack allocated memory to that buffer and return that instead.
|
||||
buffer. You can then copy the contents of the stack-allocated memory to the heap-allocated buffer and return that location instead.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="ReturnStackAllocatedObject.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<p>The example below the reference to <code>myRecord</code> is useful only while the containing function is running.
|
||||
If you need to access the object outside this function, either create an output parameter with its value, or copy the object into
|
||||
heap-allocated memory.
|
||||
</p>
|
||||
<sample src="ReturnStackAllocatedObject.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>cplusplus.com: <a href="http://www.cplusplus.com/doc/tutorial/pointers/">Pointers</a>.</li>
|
||||
<li>The craft of coding: <a href="https://craftofcoding.wordpress.com/2015/12/07/memory-in-c-the-stack-the-heap-and-static/">Memory in C - the stack, the heap, and static</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
/**
|
||||
* @name Pointer to stack object used as return value
|
||||
* @description A function has returned a pointer to an object allocated on
|
||||
* the stack. The lifetime of stack allocated memory ends when the
|
||||
* stack frame of the function that allocated it is popped off the
|
||||
* stack. Any pointer to memory in a function call's stack frame
|
||||
* will be a dangling pointer after the function returns.
|
||||
* @description Using a pointer to stack memory after the function has returned gives undefined results.
|
||||
* @kind problem
|
||||
* @id cpp/return-stack-allocated-object
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -6,23 +6,24 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds variables that are assigned a value but are never read. This is usually an indication of a variable that has been orphaned
|
||||
This query finds variables that are assigned a value but are never read. This is usually an indication of a variable that has been orphaned
|
||||
due to changes in code, or a defect in the code due to the omission of the unused variable. The unused variables should be
|
||||
removed to avoid misuse.
|
||||
</p>
|
||||
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Examine the code to see if the variable should really be unused, and remove it if it is.
|
||||
Examine the code to see if the variable is no longer needed. If it is unnecessary, remove the variable.
|
||||
Otherwise, update the relevant code to use the variable.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="Unused.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
<example>
|
||||
<sample src="Unused.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC13-C.+Detect+and+remove+unused+values">MSC13-C. Detect and remove unused values</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Variable is assigned a value that is never read
|
||||
* @description A variable is assigned a value but is never read. These variables are usually orphaned due to changes in code and can be removed, or may indicate a bug in the code that is caused by an omission of the unused variable.
|
||||
* @description Assigning a value to a variable that is not used may indicate an error in the code.
|
||||
* @kind problem
|
||||
* @id cpp/unused-variable
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -119,7 +119,7 @@ class CommentBlock extends Comment {
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
|
||||
this.lastComment().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn)
|
||||
this.lastComment().getLocation().hasLocationInfo(_, _, _, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/exit-nonterminating-loop
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/loop-bounds
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/recursion
|
||||
* @problem.severity warning
|
||||
* @tags maintainability
|
||||
* readability
|
||||
* testability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
* @description Dynamic memory allocation (using malloc() or calloc()) should be confined to the initialization routines of a program.
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/heap-memory
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @tags resources
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/thread-safety
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* concurrency
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/avoid-nested-semaphores
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* concurrency
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import Semaphores
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
* @description The use of semaphores or locks to access shared data should be avoided.
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/avoid-semaphores
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @tags concurrency
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import Semaphores
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/out-of-order-locks
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* concurrency
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import Semaphores
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/release-locks-when-acquired
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* concurrency
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import Semaphores
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/simple-control-flow-goto
|
||||
* @problem.severity warning
|
||||
* @tags maintainability
|
||||
* readability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/simple-control-flow-jmp
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* portability
|
||||
* readability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
* @description In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized. An exception is the pattern to use the last element of an enumerator list to get the number of possible values.
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/enum-initialization
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @tags maintainability
|
||||
* readability
|
||||
* external/jpl
|
||||
*/
|
||||
import cpp
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/extern-decls-in-header
|
||||
* @problem.severity warning
|
||||
* @tags maintainability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
* @description Global variables that are not accessed outside their own file should be made static to promote information hiding.
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/limited-scope-file
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
* modularity
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
@@ -12,5 +16,6 @@ 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."
|
||||
|
||||
@@ -3,12 +3,17 @@
|
||||
* @description Global and file-scope variables that are accessed by only one function should be scoped within that function.
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/limited-scope-function
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from GlobalVariable v, Function f
|
||||
where v.getAnAccess().getEnclosingFunction() = f and
|
||||
strictcount(v.getAnAccess().getEnclosingFunction()) = 1
|
||||
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()
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
* @description A local variable or parameter that hides a global variable of the same name.
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/limited-scope-local-hides-global
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @tags maintainability
|
||||
* readability
|
||||
* external/jpl
|
||||
*/
|
||||
import cpp
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/checking-return-values
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* reliability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/checking-parameter-values
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* reliability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import JPL_C.Tasks
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/use-of-assertions-constant
|
||||
* @problem.severity warning
|
||||
* @tags maintainability
|
||||
* reliability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.commons.Assertions
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
* @description All functions of more than 10 lines should have at least one assertion.
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/use-of-assertions-density
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @tags maintainability
|
||||
* reliability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.commons.Assertions
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/use-of-assertions-non-boolean
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.commons.Assertions
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/use-of-assertions-side-effect
|
||||
* @problem.severity warning
|
||||
* @tags correctness
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.commons.Assertions
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user