Merge branch 'master' into users/raulga/c6324

This commit is contained in:
Raul Garcia
2018-12-14 15:46:38 -08:00
committed by GitHub
3718 changed files with 239651 additions and 37135 deletions

View File

@@ -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

View File

@@ -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

View 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)

View 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)

View File

@@ -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)

View File

@@ -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"

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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&amp;rep=rep1&amp;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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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
*/

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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)
{
// ...
// ...
}

View File

@@ -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"

View File

@@ -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;

View File

@@ -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"

View File

@@ -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]);
}
}

View File

@@ -4,6 +4,8 @@
* @kind problem
* @id cpp/use-number-constant
* @problem.severity recommendation
* @precision low
* @tags maintainability
*/
import cpp
import MagicConstants

View File

@@ -4,6 +4,8 @@
* @kind problem
* @id cpp/use-string-constant
* @problem.severity recommendation
* @precision low
* @tags maintainability
*/
import cpp
import MagicConstants

View File

@@ -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

View File

@@ -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)"

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View 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"

View File

@@ -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, _))
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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()

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -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>

View File

@@ -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();
...
}

View File

@@ -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>

View File

@@ -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

View 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;

View 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>

View 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()

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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
...
...
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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);

View File

@@ -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 &amp; Prevention</em>. SANS Institute InfoSec Reading Room. 2002.</li>
</references>
</qhelp>

View File

@@ -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));
}

View File

@@ -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 &amp; Prevention</em>. SANS Institute InfoSec Reading Room. 2002.</li>
</references>
</qhelp>

View File

@@ -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."

View File

@@ -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)

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -4,6 +4,8 @@
* @kind problem
* @id cpp/jpl-c/exit-nonterminating-loop
* @problem.severity warning
* @tags correctness
* external/jpl
*/
import cpp

View File

@@ -5,6 +5,8 @@
* @kind problem
* @id cpp/jpl-c/loop-bounds
* @problem.severity warning
* @tags correctness
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,10 @@
* @kind problem
* @id cpp/jpl-c/recursion
* @problem.severity warning
* @tags maintainability
* readability
* testability
* external/jpl
*/
import cpp

View File

@@ -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

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/thread-safety
* @problem.severity warning
* @tags correctness
* concurrency
* external/jpl
*/
import cpp

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/avoid-nested-semaphores
* @problem.severity warning
* @tags correctness
* concurrency
* external/jpl
*/
import Semaphores

View File

@@ -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

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/out-of-order-locks
* @problem.severity warning
* @tags correctness
* concurrency
* external/jpl
*/
import Semaphores

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/release-locks-when-acquired
* @problem.severity warning
* @tags correctness
* concurrency
* external/jpl
*/
import Semaphores

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/simple-control-flow-goto
* @problem.severity warning
* @tags maintainability
* readability
* external/jpl
*/
import cpp

View File

@@ -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

View File

@@ -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

View File

@@ -4,6 +4,8 @@
* @kind problem
* @id cpp/jpl-c/extern-decls-in-header
* @problem.severity warning
* @tags maintainability
* external/jpl
*/
import cpp

View File

@@ -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."

View File

@@ -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()

View File

@@ -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

View File

@@ -4,6 +4,9 @@
* @kind problem
* @id cpp/jpl-c/checking-return-values
* @problem.severity warning
* @tags correctness
* reliability
* external/jpl
*/
import cpp

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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