QL code and tests for C#/C++/JavaScript.

This commit is contained in:
Pavel Avgustinov
2018-08-02 17:53:23 +01:00
commit b55526aa58
10684 changed files with 581163 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This rule finds blocks of code that have too many complex statements,
such as branching statements (<code>if</code>, <code>switch</code>), and loops (<code>for</code>, <code>while</code>).
</p>
<p>
Blocks with too many consecutive statements are candidates for refactoring.
Only complex statements are counted here (eg. for, while, switch ...).
The top-level logic will be clearer if each complex statement is extracted to a function.
</p>
</overview>
<recommendation>
<p>It is often the case that each consecutive complex statement performs a dedicated separate task. It is a very common case that each complex statement is actually commented with a description of the task. Extract each such task into its own function for improved readability and to promote reuse.</p>
</recommendation>
<references>
<li>
M. Fowler. <em>Refactoring</em> Addison-Wesley, 1999.
</li>
<li>
<a href="en.wikipedia.org/wiki/Code_refactoring">Wikipedia: Code refactoring</a>
</li>
<li>
Microsoft Patterns &amp; Practices Team. <a href="http://msdn.microsoft.com/en-us/library/ee658117.aspx">Architectural Patterns and Styles</a> <em>Microsoft Application Architecture Guide, 2nd Edition.</em> Microsoft Press, 2009.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,27 @@
/**
* @name Block with too many statements
* @description Blocks with too many consecutive statements are candidates for refactoring. Only complex statements are counted here (eg. for, while, switch ...). The top-level logic will be clearer if each complex statement is extracted to a function.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/complex-block
* @tags testability
* readability
* maintainability
*/
import cpp
class ComplexStmt extends Stmt {
ComplexStmt() {
exists(Block body | body = this.(Loop ).getStmt() or
body = this.(SwitchStmt).getStmt()
| strictcount(body.getAStmt+()) > 6)
and not exists (this.getGeneratingMacro())
}
}
from Block b, int n, ComplexStmt complexStmt
where n = strictcount(ComplexStmt s | s = b.getAStmt()) and n > 3
and complexStmt = b.getAStmt()
select b, "Block with too many statements (" + n.toString() + " complex statements in the block). Complex statements at: $@", complexStmt, complexStmt.toString()

View File

@@ -0,0 +1,13 @@
//This condition is too complex and can be improved by using local variables
bool accept_message =
(message_type == CONNECT && _state != CONNECTED) ||
(message_type == DISCONNECT && _state == CONNECTED) ||
(message_type == DATA && _state == CONNECTED);
//This condition is acceptable, as all the logical operators are of the same type (&&)
bool valid_connect =
message_type == CONNECT &&
_state != CONNECTED &&
time_since_prev_connect > MAX_CONNECT_INTERVAL &&
message_length <= MAX_PACKET_SIZE &&
checksum(message) == get_checksum_field(message);

View File

@@ -0,0 +1,43 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This rule finds boolean expressions that have more than 5 consecutive operators that are not of the same type (e.g. alternating <code>&amp;&amp;</code> and <code>||</code> operators).
Long chains of operators of the same type are not flagged as violations of this rule.
</p>
<p>
Complex boolean expressions are hard to read. Consequently, when modifying such expressions
there is an increased risk of introducing defects.
Naming intermediate results as local variables will make the logic easier to read and understand.
</p>
</overview>
<recommendation>
<p>Use local variables or macros to represent intermediate values to make the condition easier to understand.</p>
</recommendation>
<example><sample src="ComplexCondition.cpp" />
</example>
<references>
<li>
<a href="http://www.cplusplus.com/doc/tutorial/operators/">Operators</a>
</li>
<li>
<a href="http://geosoft.no/development/cppstyle.html#Conditionals">Conditionals</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,33 @@
/**
* @name Complex condition
* @description Boolean expressions that are too deeply nested are hard to read and understand. Consider naming intermediate results as local variables.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/complex-condition
* @tags testability
* readability
* maintainability
* statistical
* non-attributable
*/
import cpp
predicate logicalOp(string op) {
op = "&&" or op = "||"
}
predicate nontrivialLogicalOperator(Operation e) {
exists(string op |
op = e.getOperator() and
logicalOp(op) and
not (op = e.getParent().(Operation).getOperator())
)
and not e.isInMacroExpansion()
}
from Expr e, int operators
where not (e.getParent() instanceof Expr)
and operators = count(Operation op | op.getParent*() = e and nontrivialLogicalOperator(op))
and operators > 5
select e, "Complex condition: too many logical operations in this expression."

View File

@@ -0,0 +1,40 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The C++ <code>throw</code> expression can take several forms. One form throws a new exception, whereas the
other re-throws the current exception. In the latter case, if there is no current exception, then the program
will be terminated. Presence of a re-throw outside of an exception handling context is often caused by the
programmer not knowing what kind of exception to throw.</p>
</overview>
<recommendation>
<p>The <code>throw</code> expression should be changed to throw a particular type of exception.</p>
</recommendation>
<example>
<sample language="cpp">
void bad() {
/* ... */
if(error_condition)
throw;
}
void good() {
/* ... */
if(error_condition)
throw std::exception("Something went wrong.");
}
</sample>
</example>
<references>
<li>Open Standards: <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf">Standard for Programming Language C++, draft n3337</a> [except.throw], clause 9, page 380.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,25 @@
/**
* @name Accidental rethrow
* @description When there is nothing to rethrow, attempting to rethrow an exception will terminate the program.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/rethrow-no-exception
* @tags reliability
* correctness
* exceptions
*/
import cpp
predicate isInCatch(Expr e) {
e.getEnclosingStmt().getParent*() instanceof CatchBlock or // Lexically enclosing catch blocks will cause there to be a current exception,
exists(Function f | f = e.getEnclosingFunction() |
isInCatch(f.getACallToThisFunction()) or // as will dynamically enclosing catch blocks.
f.getName().toLowerCase().matches("%exception%") // We assume that rethrows are intended when the function is called *exception*.
)
}
from ReThrowExpr e
where not isInCatch(e)
select e, "As there is no current exception, this rethrow expression will terminate the program."

View File

@@ -0,0 +1,51 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Catching an exception by value will create a new local variable which is a copy of the originally thrown object.
Creating the copy is slightly wasteful, but not catastrophic. More worrisome is the fact that if the type being
caught is a strict supertype of the originally thrown type, then the copy might not contain as much information
as the original exception.</p>
</overview>
<recommendation>
<p>The parameter to the <code>catch</code> block should have its type changed from <code>T</code> to <code>T&amp;</code>
or <code>const T&amp;</code>.</p>
</recommendation>
<example>
<sample language="cpp">
void bad() {
try {
/* ... */
}
catch(std::exception a_copy_of_the_thrown_exception) {
// Do something with a_copy_of_the_thrown_exception
}
}
void good() {
try {
/* ... */
}
catch(const std::exception&amp; the_thrown_exception) {
// Do something with the_thrown_exception
}
}
</sample>
</example>
<references>
<li>C++ FAQ: <a href="https://isocpp.org/wiki/faq/exceptions#what-to-throw">
What should I throw?</a>, <a href="https://isocpp.org/wiki/faq/exceptions#what-to-catch">
What should I catch?</a>.</li>
<li>Wikibooks: <a href="http://en.wikibooks.org/wiki/C%2B%2B_Programming/Exception_Handling#Throwing_objects">
Throwing objects</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,17 @@
/**
* @name Catching by value
* @description Catching an exception by value will create a copy of the thrown exception, thereby potentially slicing the original exception object.
* @kind problem
* @problem.severity warning
* @precision very-high
* @id cpp/catch-by-value
* @tags efficiency
* correctness
* exceptions
*/
import cpp
from CatchBlock cb, Class caughtType
where caughtType = cb.getParameter().getType().getUnderlyingType().getUnspecifiedType()
select cb, "This should catch a " + caughtType.getName() + " by (const) reference rather than by value."

View File

@@ -0,0 +1,46 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Modern C++ code and frameworks should not throw or catch pointers. Older frameworks, such as Microsoft's MFC,
do throw and catch pointers. Said pointers will generally point to an exception object allocated on the heap,
and therefore need to be freed when they are caught. Failure to free them will result in a memory leak.</p>
</overview>
<recommendation>
<p>The <code>catch</code> block should be augmented to delete the exception pointer.</p>
</recommendation>
<example>
<sample language="cpp">
void bad() {
try {
/* ... */
}
catch(CException* e) {
e->ReportError();
}
}
void good() {
try {
/* ... */
}
catch(CException* e) {
e->ReportError();
e->Delete();
}
}
</sample>
</example>
<references>
<li>MSDN Library for MFC: <a href="http://msdn.microsoft.com/en-us/library/0e5twxsh(v=vs.110).aspx">Exceptions: Catching and Deleting Exceptions</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,48 @@
/**
* @name Leaky catch
* @description If an exception is allocated on the heap, then it should be deleted when caught.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/catch-missing-free
* @tags efficiency
* correctness
* exceptions
* external/cwe/cwe-401
*/
import cpp
predicate doesRethrow(Function f) {
exists(ReThrowExpr e | e.getEnclosingFunction() = f |
not e.getEnclosingStmt().getParent*() instanceof CatchBlock
)
or
exists(FunctionCall fc | fc.getEnclosingFunction() = f |
doesRethrow(fc.getTarget())
)
}
predicate deletesException(Expr expr, Parameter exception) {
expr.getEnclosingBlock().getParent*().(CatchBlock).getParameter() = exception and (
exists(FunctionCall fc | fc = expr |
// Calling a delete function on the exception will free it (MFC's CException has a Delete function).
(fc.getQualifier() = exception.getAnAccess() and fc.getTarget().getName().toLowerCase().matches("%delete%")) or
// Passing the exception to a function might free it.
(fc.getAnArgument() = exception.getAnAccess()) or
// Calling a function which rethrows the current exception might cause the exception to be freed.
doesRethrow(fc.getTarget())
) or
// Calling operator delete on the exception will free it.
exists(DeleteExpr d | d = expr |
d.getExpr() = exception.getAnAccess()
)
)
}
from CatchBlock cb
where cb.getParameter().getType().getUnderlyingType() instanceof PointerType
and not exists(Expr e | e.getEnclosingBlock().getParent*() = cb |
deletesException(e, cb.getParameter())
)
select cb, "This catch block does not free the caught exception, thereby leaking memory."

View File

@@ -0,0 +1,44 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>As C++ is not a garbage collected language, exceptions should not be dynamically allocated. Dynamically
allocating an exception puts an onus on every <code>catch</code> site to ensure that the memory is freed.</p>
<p>As a special case, it is permissible to throw anything derived from Microsoft MFC's <code>CException</code>
class as a pointer. This is for historical reasons; modern code and modern frameworks should not throw
pointer values.</p>
</overview>
<recommendation>
<p>The <code>new</code> keyword immediately following the <code>throw</code> keyword should be removed. Any
<code>catch</code> sites which previously caught the pointer should be changed to catch by reference or
<code>const</code> reference.</p>
</recommendation>
<example>
<sample language="cpp">
void bad() {
throw new std::exception("This is how not to throw an exception");
}
void good() {
throw std::exception("This is how to throw an exception");
}
</sample>
</example>
<references>
<li>C++ FAQ: <a href="https://isocpp.org/wiki/faq/exceptions#what-to-throw">
What should I throw?</a>, <a href="https://isocpp.org/wiki/faq/exceptions#what-to-catch">
What should I catch?</a>.</li>
<li>Wikibooks: <a href="http://en.wikibooks.org/wiki/C%2B%2B_Programming/Exception_Handling#Throwing_objects">
Throwing objects</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Throwing pointers
* @description Exceptions should be objects rather than pointers to objects.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/throwing-pointer
* @tags efficiency
* correctness
* exceptions
*/
import cpp
from ThrowExpr throw, NewExpr new, Type t
where new.getParent() = throw
// Microsoft MFC's CException hierarchy should be thrown (and caught) as pointers
and t = new.getAllocatedType()
and not t.getUnderlyingType().(Class).getABaseClass*().hasName("CException")
select throw, "This should throw a " + t.toString() + " rather than a pointer to one."

View File

@@ -0,0 +1,6 @@
void f(int i) {
for (int i = 0; i < 10; ++i) { //the loop variable hides the parameter to f()
...
}
}

View File

@@ -0,0 +1,30 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds declarations of local variables that hide parameters of the surrounding function. Such declarations
create variables with the same name but different scopes. This makes it hard to understand which variable is actually
being used in an expression.</p>
</overview>
<recommendation>
<p>Consider changing the name of either the variable or the parameter to keep them distinct.</p>
</recommendation>
<example>
<sample src="DeclarationHidesParameter.cpp" />
</example>
<references>
<li>
B. Stroustrup. <em>The C++ Programming Language Special Edition</em> p 82. Addison Wesley. 2000.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,36 @@
/**
* @name Declaration hides parameter
* @description A local variable hides a parameter. This may be confusing. Consider renaming one of them.
* @kind problem
* @problem.severity recommendation
* @precision very-high
* @id cpp/declaration-hides-parameter
* @tags maintainability
* readability
*/
import cpp
/* Names of parameters in the implementation of a function.
Notice that we need to exclude parameter names used in prototype
declarations and only include the ones from the actual definition.
We also exclude names from functions that have multiple definitions.
This should not happen in a single application but since we
have a system wide view it is likely to happen for instance for
the main function. */
ParameterDeclarationEntry functionParameterNames(Function f, string name) {
exists(FunctionDeclarationEntry fe |
result.getFunctionDeclarationEntry() = fe
and fe.getFunction() = f
and fe.getLocation() = f.getDefinitionLocation()
and strictcount(f.getDefinitionLocation()) = 1
and result.getName() = name
)
}
from Function f, LocalVariable lv, ParameterDeclarationEntry pde
where f = lv.getFunction() and
pde = functionParameterNames(f, lv.getName()) and
not lv.isInMacroExpansion()
select lv, "Local variable '"+ lv.getName() +"' hides a $@.",
pde, "parameter of the same name"

View File

@@ -0,0 +1,12 @@
void f() {
int i = 10;
for (int i = 0; i < 10; i++) { //the loop counter hides the variable
...
}
{
int i = 12; //this variable hides the variable in the outer block
...
}
}

View File

@@ -0,0 +1,30 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds declarations of local variables that hide a local variable from a surrounding scope. Such declarations
create variables with the same name but different scopes. This makes it difficult to know which variable is actually
used in an expression.</p>
</overview>
<recommendation>
<p>Consider changing the name of either variable to keep them distinct.</p>
</recommendation>
<example>
<sample src="DeclarationHidesVariable.cpp" />
</example>
<references>
<li>
B. Stroustrup. <em>The C++ Programming Language Special Edition</em> p 82. Addison Wesley. 2000.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,21 @@
/**
* @name Declaration hides variable
* @description A local variable hides another local variable from a surrounding scope. This may be confusing. Consider renaming one of the variables.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/declaration-hides-variable
* @tags maintainability
* readability
*/
import cpp
import Best_Practices.Hiding.Shadowing
from LocalVariable lv1, LocalVariable lv2
where shadowing(lv1, lv2) and
not lv1.getParentScope().(Block).isInMacroExpansion() and
not lv2.getParentScope().(Block).isInMacroExpansion()
select lv1, "Variable " + lv1.getName() +
" hides another variable of the same name (on $@).",
lv2, "line " + lv2.getLocation().getStartLine().toString()

View File

@@ -0,0 +1,12 @@
int i = 10;
void f() {
for (int i = 0; i < 10; i++) { //the loop counter hides the global variable i
...
}
{
int i = 12; //this variable hides the global variable i
...
}
}

View File

@@ -0,0 +1,29 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds declarations of local variables or parameters that hide a global variable.
Such declarations create variables with the same name but different scopes. This makes it
difficult to know which variable is actually used in an expression.</p>
</overview>
<recommendation>
<p>Consider changing the name of either variable to keep them distinct.</p>
</recommendation>
<example>
<sample src="LocalVariableHidesGlobalVariable.cpp" />
</example>
<references>
<li>
B. Stroustrup. <em>The C++ Programming Language Special Edition</em> p 82. Addison Wesley. 2000.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,36 @@
/**
* @name Local variable hides global variable
* @description A local variable or parameter that hides a global variable of the same name. This may be confusing. Consider renaming one of the variables.
* @kind problem
* @problem.severity warning
* @precision very-high
* @id cpp/local-variable-hides-global-variable
* @tags maintainability
* readability
*/
import cpp
class LocalVariableOrParameter extends VariableDeclarationEntry {
LocalVariableOrParameter() {
this.getVariable() instanceof LocalScopeVariable and
(
// we only need to report parameters hiding globals when the clash is with the parameter
// name as used in the function definition. The parameter name used in any other function
// declaration is harmless.
this instanceof ParameterDeclarationEntry
implies
exists(this.(ParameterDeclarationEntry).getFunctionDeclarationEntry().getBlock())
)
}
string type() {
if this.getVariable() instanceof Parameter
then result = "Parameter "
else result = "Local variable "
}
}
from LocalVariableOrParameter lv, GlobalVariable gv
where lv.getName() = gv.getName() and
lv.getFile() = gv.getFile()
select lv, lv.type() + gv.getName() + " hides $@ with the same name.", gv, "a global variable"

View File

@@ -0,0 +1,33 @@
import cpp
predicate ancestorScope(Element b1, Element b2) {
b1.getParentScope+() = b2
}
pragma[noopt]
predicate localVariablesSameNameInNestedScopes(LocalVariable lv1, LocalVariable lv2) {
exists(Element b1, Element b2
| b1 = lv1.getParentScope() and
not b1 instanceof Namespace and
lv1 instanceof LocalVariable and
ancestorScope(b1, b2) and
not b2 instanceof Namespace and
b2 = lv2.getParentScope() and
lv2 instanceof LocalVariable and
lv1.getName() = lv2.getName())
}
predicate shadowing(LocalVariable lv1, LocalVariable lv2) {
localVariablesSameNameInNestedScopes(lv1, lv2) and
exists(Location l1, Location l2 |
l1 = lv1.getLocation() and
l2 = lv2.getLocation() and
(
// variables declared later in parent scope are not shadowed
l2.getEndLine() < l1.getStartLine()
or (l2.getEndLine() = l1.getStartLine() and
l2.getEndColumn() <= l1.getStartColumn())
)
)
}

View File

@@ -0,0 +1,9 @@
void f(int i) {
if (i == 10); //empty then block
... //won't be part of the if statement
if (i == 12) {
...
} else { //empty else block, most likely a mistake
}
}

View File

@@ -0,0 +1,26 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds empty blocks that occur as a branch of a conditional or as a loop body.
This may indicate badly maintained code or a defect due to an unhandled case. It is common to find commented-out code in the empty body. Commented-out code is discouraged and is a source of defects and maintainability issues.</p>
</overview>
<recommendation>
<p>If the conditional or loop is useless, remove it.</p>
<p>If only the else-branch of an <code>if</code> statement is empty, omit it. If the then-branch is empty, invert the sense of the condition.</p>
</recommendation>
<example>
<sample src="EmptyBlock.cpp" />
</example>
</qhelp>

View File

@@ -0,0 +1,84 @@
/**
* @name Empty branch of conditional
* @description An empty block after a conditional can be a sign of an omission
* and can decrease maintainability of the code. Such blocks
* should contain an explanatory comment to aid future
* maintainers.
* @kind problem
* @problem.severity recommendation
* @precision very-high
* @id cpp/empty-block
* @tags reliability
* readability
*/
import cpp
predicate emptyBlock(ControlStructure s, Block b) {
b = s.getAChild() and
not exists(b.getAChild()) and
not b.isInMacroExpansion() and
not s instanceof Loop
}
class AffectedFile extends File {
AffectedFile() {
exists(Block b |
emptyBlock(_, b) and
this = b.getFile()
)
}
}
class BlockOrNonChild extends Element {
BlockOrNonChild() {
( this instanceof Block
or
this instanceof Comment
or
this instanceof PreprocessorDirective
or
this instanceof MacroInvocation
) and
this.getFile() instanceof AffectedFile
}
private int getNonContiguousStartRankIn(AffectedFile file) {
// When using `rank` with `order by`, the ranks may not be contiguous.
this = rank[result](BlockOrNonChild boc, int startLine, int startCol |
boc.getLocation()
.hasLocationInfo(file.getAbsolutePath(), startLine, startCol, _, _)
| boc
order by startLine, startCol
)
}
int getStartRankIn(AffectedFile file) {
this.getNonContiguousStartRankIn(file) = rank[result](int rnk |
exists(BlockOrNonChild boc | boc.getNonContiguousStartRankIn(file) = rnk)
)
}
int getNonContiguousEndRankIn(AffectedFile file) {
this = rank[result](BlockOrNonChild boc, int endLine, int endCol |
boc.getLocation()
.hasLocationInfo(file.getAbsolutePath(), _, _, endLine, endCol)
| boc
order by endLine, endCol
)
}
}
predicate emptyBlockContainsNonchild(Block b) {
emptyBlock(_, b) and
exists(BlockOrNonChild c, AffectedFile file |
c.(BlockOrNonChild).getStartRankIn(file) =
1 + b.(BlockOrNonChild).getStartRankIn(file) and
c.(BlockOrNonChild).getNonContiguousEndRankIn(file) <
b.(BlockOrNonChild).getNonContiguousEndRankIn(file)
)
}
from ControlStructure s, Block eb
where emptyBlock(s, eb)
and not emptyBlockContainsNonchild(eb)
select eb, "Empty block without comment"

View File

@@ -0,0 +1,24 @@
int find(int start, char *str, char goal)
{
int len = strlen(str);
//Potential buffer overflow
for (int i = start; str[i] != 0 && i < len; i++) {
if (str[i] == goal)
return i;
}
return -1;
}
int findRangeCheck(int start, char *str, char goal)
{
int len = strlen(str);
//Range check protects against buffer overflow
for (int i = start; i < len && str[i] != 0 ; i++) {
if (str[i] == goal)
return i;
}
return -1;
}

View File

@@ -0,0 +1,33 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The program contains an and-expression where the array access is defined before the range check. Consequently the array is accessed without any bounds checking. The range check does not protect the program from segmentation faults caused by attempts to read beyond the end of a buffer.</p>
</overview>
<recommendation>
<p>Update the and-expression so that the range check precedes the array offset. This will ensure that the bounds are checked before the array is accessed.</p>
</recommendation>
<example>
<p>The <code>find</code> function can read past the end of the buffer pointed to by <code>str</code> if <code>start</code> is longer than or equal to the length of the buffer (or longer than <code>len</code>, depending on the contents of the buffer).</p>
<sample src="OffsetUseBeforeRangeCheck.c" />
<p>Update the and-expression so that the range check precedes the array offset (for example, the <code>findRangeCheck</code> function).</p>
</example>
<references>
<li>cplusplus.com: <a href="http://www.cplusplus.com/reference/array/array/">
C++: array</a>.</li>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Bounds_checking">
Bounds checking</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,22 @@
/**
* @name Array offset used before range check
* @description Accessing an array offset before checking the range means that
* the program may attempt to read beyond the end of a buffer
* @kind problem
* @id cpp/offset-use-before-range-check
* @problem.severity warning
* @tags reliability
* security
* external/cwe/cwe-120
* external/cwe/cwe-125
*/
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()
select access, "This use of offset '" + v.getName() + "' should follow the $@.", rangecheck, "range check"

View File

@@ -0,0 +1,43 @@
static int idctr = 0;
//Basic connection with id
class Connection {
public:
int connId;
virtual void print_info() {
cout << "id: " << connId << "\n";
}
Connection() {
connId = idctr++;
}
};
//Adds counters, and an overriding print_info
class MeteredConnection : public Connection {
public:
int txCtr;
int rxCtr;
MeteredConnection() {
txCtr = 0;
rxCtr = 0;
}
virtual void print_info() {
cout << "id: " << connId << "\n" << "tx/rx: " << txCtr << "/" << rxCtr << "\n";
}
};
int main(int argc, char* argv[]) {
Connection conn;
MeteredConnection m_conn;
Connection curr_conn = conn;
curr_conn.print_info();
curr_conn = m_conn; //Wrong: Derived MetricConnection assigned to Connection
//variable, will slice off the counters and the overriding print_info
curr_conn.print_info(); //Will not print the counters.
Connection* curr_pconn = &conn;
curr_pconn->print_info();
curr_pconn = &m_conn; //Correct: Pointer assigned to address of the MetricConnection.
//Counters and virtual functions remain intact.
curr_pconn->print_info(); //Will call the correct method MeteredConnection::print_info
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query finds assignments of a non-reference instance of a derived type to a variable of the base type where the derived type has more fields than the base.
These assignments slice off all the fields added by the derived type, and can cause unexpected state when accessed as the derived type.</p>
</overview>
<recommendation>
<p>Change the type of the variable at the left-hand side of the assignment to the subclass.</p>
</recommendation>
<example>
<sample src="Slicing.cpp" />
</example>
<references>
<li>
Wikipedia: <a href="http://en.wikipedia.org/wiki/Object_slicing">Object slicing</a>.
</li>
<li>
DevX.com: <a href="http://www.devx.com/tips/Tip/14570">Slicing in C++</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,24 @@
/**
* @name Slicing
* @description Assigning a non-reference instance of a derived type to a variable of the base type slices off all members added by the derived class, and can cause an unexpected state.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/slicing
* @tags reliability
* correctness
* types
*/
import cpp
//see http://www.cs.ualberta.ca/~hoover/Courses/201/201-New-Notes/lectures/section/slice.htm
//Does not find anything in rivers (unfortunately)
from AssignExpr e, Class lhsType, Class rhsType
where e.getLValue().getType() = lhsType
and e.getRValue().getType() = rhsType
and rhsType.getABaseClass+() = lhsType
and exists(Declaration m | rhsType.getAMember() = m and
not m.(VirtualFunction).isPure()) //add additional checks for concrete members in in-between supertypes
select e, "This assignment expression slices from type $@ to $@",
rhsType, rhsType.getName(),
lhsType, lhsType.getName()

View File

@@ -0,0 +1,327 @@
import cpp
import semmle.code.cpp.AutogeneratedFile
/*
*
* Counting nontrivial literal occurrences
*
*/
predicate trivialPositiveIntValue(string s) {
s="0" or s="1" or s="2" or s="3" or s="4" or s="5" or s="6" or s="7" or s="8" or
s="9" or s="10" or s="11" or s="12" or s="13" or s="14" or s="15" or s="16" or s="17" or
s="18" or s="19" or s="20"
or
s="16" or s="24" or s="32" or s="64" or s="128" or s="256" or s="512" or s="1024" or
s="2048" or s="4096" or s="16384" or s="32768" or s="65536" or
s="1048576" or s="2147483648" or s="4294967296"
or
s="15" or s="31" or s="63" or s="127" or s="255" or s="511" or s="1023" or
s="2047" or s="4095" or s="16383" or s="32767" or s="65535" or
s="1048577" or s="2147483647" or s="4294967295"
or
s = "0x00000001" or s = "0x00000002" or s = "0x00000004" or s = "0x00000008" or s = "0x00000010" or s = "0x00000020" or s = "0x00000040" or s = "0x00000080" or s = "0x00000100" or s = "0x00000200" or s = "0x00000400" or s = "0x00000800" or s = "0x00001000" or s = "0x00002000" or s = "0x00004000" or s = "0x00008000" or s = "0x00010000" or s = "0x00020000" or s = "0x00040000" or s = "0x00080000" or s = "0x00100000" or s = "0x00200000" or s = "0x00400000" or s = "0x00800000" or s = "0x01000000" or s = "0x02000000" or s = "0x04000000" or s = "0x08000000" or s = "0x10000000" or s = "0x20000000" or s = "0x40000000" or s = "0x80000000" or
s = "0x00000001" or s = "0x00000003" or s = "0x00000007" or s = "0x0000000f" or s = "0x0000001f" or s = "0x0000003f" or s = "0x0000007f" or s = "0x000000ff" or s = "0x000001ff" or s = "0x000003ff" or s = "0x000007ff" or s = "0x00000fff" or s = "0x00001fff" or s = "0x00003fff" or s = "0x00007fff" or s = "0x0000ffff" or s = "0x0001ffff" or s = "0x0003ffff" or s = "0x0007ffff" or s = "0x000fffff" or s = "0x001fffff" or s = "0x003fffff" or s = "0x007fffff" or s = "0x00ffffff" or s = "0x01ffffff" or s = "0x03ffffff" or s = "0x07ffffff" or s = "0x0fffffff" or s = "0x1fffffff" or s = "0x3fffffff" or s = "0x7fffffff" or s = "0xffffffff" or
s = "0x0001" or s = "0x0002" or s = "0x0004" or s = "0x0008" or s = "0x0010" or s = "0x0020" or s = "0x0040" or s = "0x0080" or s = "0x0100" or s = "0x0200" or s = "0x0400" or s = "0x0800" or s = "0x1000" or s = "0x2000" or s = "0x4000" or s = "0x8000" or
s = "0x0001" or s = "0x0003" or s = "0x0007" or s = "0x000f" or s = "0x001f" or s = "0x003f" or s = "0x007f" or s = "0x00ff" or s = "0x01ff" or s = "0x03ff" or s = "0x07ff" or s = "0x0fff" or s = "0x1fff" or s = "0x3fff" or s = "0x7fff" or s = "0xffff" or
s = "0x01" or s = "0x02" or s = "0x04" or s = "0x08" or s = "0x10" or s = "0x20" or s = "0x40" or s = "0x80" or
s = "0x01" or s = "0x03" or s = "0x07" or s = "0x0f" or s = "0x1f" or s = "0x3f" or s = "0x7f" or s = "0xff" or
s = "0x00"
or
s = "10" or s = "100" or s = "1000" or s = "10000" or s = "100000" or s = "1000000" or s = "10000000" or s = "100000000" or s = "1000000000"
}
predicate trivialIntValue(string s) {
trivialPositiveIntValue(s) or
exists(string pos | trivialPositiveIntValue(pos) and s = "-" + pos)
}
predicate trivialLongValue(string s) {
exists(string v | trivialIntValue(v) and s = v + "L")
}
predicate intTrivial(Literal lit) {
exists(string v | trivialIntValue(v) and v = lit.getValue())
}
predicate longTrivial(Literal lit) {
exists(string v | trivialLongValue(v) and v = lit.getValue())
}
predicate powerOfTen(float f) {
f = 10 or f = 100 or f = 1000 or f = 10000 or f = 100000 or f = 1000000 or f = 10000000 or f = 100000000 or f = 1000000000
}
predicate floatTrivial(Literal lit) {
lit.getType() instanceof FloatingPointType and
exists(string value, float f |
lit.getValue() = value and
f = value.toFloat() and
(f.abs() <= 20.0 or powerOfTen(f))
)
}
predicate charLiteral(Literal lit) {
lit instanceof CharLiteral
}
Type literalType(Literal literal) {
result = literal.getType()
}
predicate stringType(DerivedType t) {
t.getBaseType() instanceof CharType
or
exists(SpecifiedType constCharType |
t.getBaseType() = constCharType and
constCharType.isConst() and
constCharType.getBaseType() instanceof CharType
)
}
predicate numberType(Type t) {
t instanceof FloatingPointType or t instanceof IntegralType
}
predicate stringLiteral(Literal literal) {
literal instanceof StringLiteral
}
predicate stringTrivial(Literal lit) {
stringLiteral(lit) and
lit.getValue().length() < 8
}
predicate joiningStringTrivial(Literal lit) {
// We want to be more lenient with string literals that are being
// joined together, because replacing sentence fragments with named
// constants could actually result in code that is harder to
// understand (which is against the spirit of these queries).
stringLiteral(lit) and
exists(FunctionCall fc |
(
fc.getTarget().getName() = "operator+" or
fc.getTarget().getName() = "operator<<"
) and
fc.getAnArgument().getAChild*() = lit
) and
lit.getValue().length() < 16
}
predicate small(Literal lit) {
lit.getValue().length() <= 1
}
predicate trivial(Literal lit) {
charLiteral(lit) or
intTrivial(lit) or
floatTrivial(lit) or
stringTrivial(lit) or
joiningStringTrivial(lit) or
longTrivial(lit) or
small(lit)
}
private predicate isReferenceTo(Variable ref, Variable to) {
exists(VariableAccess a |
ref.getInitializer().getExpr().getConversion().(ReferenceToExpr).getExpr() = a and a.getTarget() = to)
}
private predicate variableNotModifiedAfterInitializer(Variable v) {
not exists(VariableAccess a | a.getTarget() = v and a.isModified()) and
not exists(AddressOfExpr e | e.getAddressable() = v) and
forall(Variable v2 |
isReferenceTo(v2, v) |
variableNotModifiedAfterInitializer(v2))
}
predicate literalIsConstantInitializer(Literal literal, Variable f) {
f.getInitializer().getExpr() = literal and
variableNotModifiedAfterInitializer(f) and
not f instanceof Parameter
}
predicate literalIsEnumInitializer(Literal literal) {
exists(EnumConstant ec | ec.getInitializer().getExpr() = literal)
}
predicate literalInArrayInitializer(Literal literal) {
exists(AggregateLiteral arrayInit |
arrayInitializerChild(arrayInit, literal)
)
}
predicate arrayInitializerChild(AggregateLiteral parent, Expr e) {
e = parent
or
exists (Expr mid | arrayInitializerChild(parent, mid) and e.getParent() = mid)
}
// i.e. not a constant folded expression
predicate literallyLiteral(Literal lit) {
lit.getValueText().regexpMatch(".*\".*|\\s*+[-+]?+\\s*+(0[xob][0-9a-fA-F]|[0-9])[0-9a-fA-F,._]*+([eE][-+]?+[0-9,._]*+)?+\\s*+[a-zA-Z]*+\\s*+")
}
predicate nonTrivialValue(string value, Literal literal) {
value = literal.getValue() and
not trivial(literal) and
not literalIsConstantInitializer(literal, _) and
not literalIsEnumInitializer(literal) and
not literalInArrayInitializer(literal) and
not literal.isAffectedByMacro() and
literallyLiteral(literal)
}
predicate valueOccurrenceCount(string value, int n) {
n = strictcount(Location loc |
exists(Literal lit |
lit.getLocation() = loc |
nonTrivialValue(value, lit)
) and
// Exclude generated files (they do not have the same maintainability
// concerns as ordinary source files)
not loc.getFile() instanceof AutogeneratedFile
) and
n > 20
}
predicate occurenceCount(Literal lit, string value, int n) {
valueOccurrenceCount(value, n) and
value = lit.getValue() and
nonTrivialValue(_, lit)
}
/*
*
* Literals repeated frequently
*
*/
predicate check(Literal lit, string value, int n, File f) {
// Check that the literal is nontrivial
not trivial(lit) and
// Check that it is repeated a number of times
occurenceCount(lit, value, n) and n > 20 and
f = lit.getFile() and
// Exclude generated files
not f instanceof AutogeneratedFile
}
predicate checkWithFileCount(string value, int overallCount, int fileCount, File f) {
fileCount = strictcount(Location loc |
exists(Literal lit |
lit.getLocation() = loc |
check(lit, value, overallCount, f)))
}
predicate start(Literal lit, int startLine) {
exists(Location l | l = lit.getLocation() and startLine = l.getStartLine())
}
predicate firstOccurrence(Literal lit, string value, int n) {
exists(File f, int fileCount |
checkWithFileCount(value, n, fileCount, f) and
fileCount < 100 and
check(lit, value, n, f) and
not exists(Literal lit2, int start1, int start2 |
check(lit2, value, n, f) and
start(lit, start1) and
start(lit2, start2) and
start2 < start1)
)
}
predicate magicConstant(Literal e, string msg) {
exists(string value, int n | firstOccurrence(e, value, n)
and msg = "Magic constant: literal '" + value + "' is repeated " + n.toString() + " times and should be encapsulated in a constant.")
}
/*
*
* Literals where there is a defined constant with the same value
*
*/
predicate relevantVariable(Variable f, string value) {
exists(Literal lit | not trivial(lit) and value = lit.getValue() and literalIsConstantInitializer(lit, f))
}
predicate relevantCallable(Function f, string value) {
exists(Literal lit | not trivial(lit) and value = lit.getValue() and lit.getEnclosingFunction() = f)
}
predicate isVisible(Variable field, Function fromCallable) {
exists(string value |
//public fields
(
relevantVariable(field, value) and
field.(MemberVariable).isPublic() and
relevantCallable(fromCallable, value)
)
or
//in same class
(
relevantVariable(field, value) and
exists(Type t |
t = field.getDeclaringType() and
t = fromCallable.getDeclaringType())
and relevantCallable(fromCallable, value)
)
or
//in subclass and not private
(
relevantVariable(field, value) and
not field.(MemberVariable).isPrivate() and
exists(Class sup, Class sub |
sup = field.getDeclaringType() and
sub.getABaseClass+() = sup and
sub = fromCallable.getDeclaringType()
) and
relevantCallable(fromCallable, value)
)
)
}
predicate canUseFieldInsteadOfLiteral(Variable constField, Literal magicLiteral) {
exists(Literal initLiteral |
literalIsConstantInitializer(initLiteral, constField) and
not trivial(initLiteral) and
not constField.getType().hasName("boolean") and
exists(string value |
value = initLiteral.getValue() and
magicLiteral.getValue() = value
)
and constField.getType() = magicLiteral.getType()
and not literalIsConstantInitializer(magicLiteral, _)
and exists(Function c |
c = magicLiteral.getEnclosingFunction() and
(
(
constField.isTopLevel() and (not constField.isStatic() or constField.getFile() = c.getFile())
)
or
isVisible(constField,c)
)
)
)
}
predicate literalInsteadOfConstant(Literal magicLiteral, string message, Variable constField, string linkText) {
canUseFieldInsteadOfLiteral(constField, magicLiteral) and
message =
"Literal value '" + magicLiteral.getValue() + "' used instead of constant $@." and
linkText = constField.getName()
}

View File

@@ -0,0 +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]);
}
}
#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]);
}
}

View File

@@ -0,0 +1,50 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A <i>magic number</i> is a numeric literal (for example, <code>8080</code>,
<code>2048</code>) that is used in the middle of a block of code without
explanation. It is considered good practice to avoid magic numbers by assigning
the numbers to named constants and using the named constants instead. The
reasons for this are twofold:</p>
<ol>
<li>A number in isolation can be inexplicable to later programmers, whereas a
named constant (such as <code>MAX_GUESTS</code>) is more readily understood.
</li>
<li>Using the same named constant in many places, makes the code much easier to
update if the requirements change (for example, one more guest is permitted).
</li>
</ol>
<p>This rule finds magic numbers for which there is no pre-existing named
constant (for example, the line marked (4) below).</p>
</overview>
<recommendation>
<p>Consider creating a <code>const</code> or a macro to encapsulate the literal,
then replace all the relevant occurrences in the code.</p>
</recommendation>
<example><sample src="MagicConstantsNumbers.cpp" />
</example>
<references>
<li>
<a href="http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Unnamed_numerical_constants">Magic number (Wikipedia)</a>
</li>
<li>
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
Chapter 5: Object Life Cycle, Rec 5.4 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
</li>
<li>
<a href="https://www.securecoding.cert.org/confluence/display/c/DCL06-C.+Use+meaningful+symbolic+constants+to+represent+literal+values">DCL06-C. Use meaningful symbolic constants to represent literal values</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,22 @@
/**
* @name Magic numbers
* @description 'Magic constants' should be avoided: if a nontrivial constant is used repeatedly, it should be encapsulated into a const variable or macro definition.
* @kind problem
* @id cpp/magic-number
* @problem.severity recommendation
* @precision medium
* @tags maintainability
* statistical
* non-attributable
*/
import cpp
import MagicConstants
pragma[noopt]
predicate selection(Element e, string msg) {
magicConstant(e, msg) and exists(Literal l, Type t | l=e and t = l.getType() and numberType(t) and l instanceof Literal)
}
from Literal e, string msg
where selection(e, msg)
select e, msg

View File

@@ -0,0 +1,49 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A <i>magic string</i> is a string literal (for example, <code>"SELECT"</code>,
<code>"127.0.0.1"</code>) that is used in the middle of a block of code without
explanation. It is considered good practice to avoid magic strings by assigning
the strings to named constants and using the named constants instead. The
reasons for this are twofold:</p>
<ol>
<li>A string in isolation can be inexplicable to later programmers, whereas a
named constant (such as <code>SMTP_HELO</code>) is more readily understood.
</li>
<li>Using the same named constant in many places, makes the code much easier to
update if the requirements change (for example, a protocol is updated).
</li>
</ol>
<p>This rule finds magic strings for which there is no pre-existing named
constant.</p>
</overview>
<recommendation>
<p>Consider replacing the magic string with a new named constant.</p>
</recommendation>
<references>
<li>
<a href="http://en.wikipedia.org/wiki/Magic_string">Magic string (Wikipedia)</a>
</li>
<li>
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
Chapter 5: Object Life Cycle, Rec 5.4 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
</li>
<li>
<a href="https://www.securecoding.cert.org/confluence/display/c/DCL06-C.+Use+meaningful+symbolic+constants+to+represent+literal+values">DCL06-C. Use meaningful symbolic constants to represent literal values</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,22 @@
/**
* @name Magic strings
* @description 'Magic constants' should be avoided: if a nontrivial constant is used repeatedly, it should be encapsulated into a const variable or macro definition.
* @kind problem
* @id cpp/magic-string
* @problem.severity recommendation
* @precision medium
* @tags maintainability
* statistical
* non-attributable
*/
import cpp
import MagicConstants
pragma[noopt]
predicate selection(Element e, string msg) {
magicConstant(e, msg) and stringLiteral(e)
}
from Literal e, string msg
where selection(e, msg)
select e, msg

View File

@@ -0,0 +1,14 @@
/**
* @name Magic numbers: use defined constant
* @description A numeric literal that matches the initializer of a constant variable was found. Consider using the constant variable instead of the numeric literal.
* @kind problem
* @id cpp/use-number-constant
* @problem.severity recommendation
*/
import cpp
import MagicConstants
from Literal magicLiteral, string message, Variable constant, string linkText
where numberType(magicLiteral.getType())
and literalInsteadOfConstant(magicLiteral, message, constant, linkText)
select magicLiteral, message, constant, linkText

View File

@@ -0,0 +1,14 @@
/**
* @name Magic strings: use defined constant
* @description A string literal that matches the initializer of a constant variable was found. Consider using the constant variable instead of the string literal.
* @kind problem
* @id cpp/use-string-constant
* @problem.severity recommendation
*/
import cpp
import MagicConstants
from Literal magicLiteral, string message, Variable constant, string linkText
where stringLiteral(magicLiteral)
and literalInsteadOfConstant(magicLiteral, message, constant, linkText)
select magicLiteral, message, constant, linkText

View File

@@ -0,0 +1,18 @@
/**
* @name Public virtual method
* @description When public methods can be overridden, base classes are unable
* to enforce invariants that should hold for the whole hierarchy.
* @kind problem
* @id cpp/nvi
* @problem.severity warning
*/
import cpp
//see http://www.gotw.ca/publications/mill18.htm
from MemberFunction f
where f.hasSpecifier("public") and
f.hasSpecifier("virtual") and
f.getFile().fromSource() and
not (f instanceof Destructor)
select f, "Avoid having public virtual methods (NVI idiom)"

View File

@@ -0,0 +1,22 @@
/**
* @name Public virtual method in Hub Class
* @description When public methods can be overridden, base classes are unable
* to enforce invariants that should hold for the whole hierarchy.
* This is especially problematic in classes with many
* dependencies or dependents.
* @kind table
* @id cpp/nvi-hub
*/
import cpp
//see http://www.gotw.ca/publications/mill18.htm
from MemberFunction f, int hubIndex, Class fclass
where f.hasSpecifier("public") and
f.hasSpecifier("virtual") and
f.getFile().fromSource() and
not (f instanceof Destructor) 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)"

View File

@@ -0,0 +1,24 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<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">
<p>Explicitly define the missing functions.</p>
</section>
<references>
<li><a href="http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)">Wikipedia article</a>
</li></references></qhelp>

View File

@@ -0,0 +1,26 @@
/**
* @name Rule of three
* @description Classes that have an explicit destructor, copy constructor, or
* copy assignment operator may behave inconsistently if they do
* not have all three.
* @kind problem
* @id cpp/rule-of-three
* @problem.severity warning
* @tags reliability
*/
import cpp
class BigThree extends MemberFunction {
BigThree() {
this instanceof Destructor
or this instanceof CopyConstructor
or this instanceof CopyAssignmentOperator
}
}
from Class c, BigThree b
where b.getDeclaringType() = c and
not (c.hasDestructor() and
c.getAMemberFunction() instanceof CopyConstructor and
c.getAMemberFunction() instanceof CopyAssignmentOperator)
select c, "Class defines a destructor, copy constructor, or copy assignment operator, but not all three."

View File

@@ -0,0 +1,26 @@
class C {
private:
Other* other = NULL;
public:
C(const C& copyFrom) {
Other* newOther = new Other();
*newOther = copyFrom.other;
this->other = newOther;
}
//No operator=, by default will just copy the pointer other, will not create a new object
};
class D {
Other* other = NULL;
public:
D& operator=(D& rhs) {
Other* newOther = new Other();
*newOther = rhs.other;
this->other = newOther;
return *this;
}
//No copy constructor, will just copy the pointer other and not create a new object
};

View File

@@ -0,0 +1,53 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds classes that define a copy constructor or a copy assignment operator, but not both 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 is that of the other.</p>
<p>When a class defines a copy constructor or a copy assignment operator, but not both, this can cause
unexpected behavior. The object initialization (that is, <code>Class c1 = c2</code>) may behave differently
from object assignment (that is, <code>c1 = c2</code>).</p>
</overview>
<recommendation>
<p>First, consider whether the user-defined member needs to be explicitly
defined at all. If no user-defined copy constructor is provided for a class,
the compiler will always attempt to generate a public copy constructor that
recursively invokes the copy constructor of each field. If the existing
user-defined copy constructor does exactly the same, it is most likely
beneficial to delete it. The compiler-generated version may be more efficient,
and it does not need to be manually maintained as fields are added and deleted.
</p>
<p>If the user-defined member <em>does</em> need to exist, the other
corresponding member should be defined too. It can be defined as defaulted
(using <code>= default</code>) if the compiler-generated implementation is
acceptable, or it can be defined as deleted (using <code>= delete</code>) if it
should never be called.
</p>
</recommendation>
<example><sample src="RuleOfTwo.cpp" />
</example>
<references>
<li>
<a href="http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)">Rule of Three [Wikipedia]</a>
</li>
<li>
<a href="http://www.artima.com/cppsource/bigtwo.html">The Law of The Big Two</a>
</li>
<li>
cppreference.com: <a href="http://en.cppreference.com/w/cpp/language/copy_constructor">copy constructor</a>
and <a href="http://en.cppreference.com/w/cpp/language/copy_assignment">copy assignment</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,65 @@
/**
* @name Inconsistent definition of copy constructor and assignment ('Rule of Two')
* @description Classes that have an explicit copy constructor or copy
* assignment operator may behave inconsistently if they do
* not have both.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/rule-of-two
* @tags reliability
* readability
* language-features
*/
import cpp
// This query enforces the Rule of Two, which is a conservative variation of
// the more well-known Rule of Three.
//
// The Rule of Two is usually phrased informally, ignoring the distinction
// between whether a member is missing because it's auto-generated (missing
// from the source) or missing because it can't be called (missing from the
// generated code).
//
// This query checks if one member is explicitly defined while the other is
// auto-generated. This can lead to memory safety issues. It's a separate issue
// whether one is callable while the other is not callable; that is an API
// design question and carries has no safety risk.
predicate generatedCopyAssignment(CopyConstructor cc, string msg) {
cc.getDeclaringType().hasImplicitCopyAssignmentOperator() and
msg = "No matching copy assignment operator in class " +
cc.getDeclaringType().getName() +
". It is good practice to match a copy constructor with a " +
"copy assignment operator."
}
predicate generatedCopyConstructor(CopyAssignmentOperator ca, string msg) {
ca.getDeclaringType().hasImplicitCopyConstructor() and
msg = "No matching copy constructor in class " +
ca.getDeclaringType().getName() +
". It is good practice to match a copy assignment operator with a " +
"copy constructor."
}
from MemberFunction f, string msg
where (generatedCopyAssignment(f, msg) or
generatedCopyConstructor(f, msg))
// Ignore template instantiations to prevent an explosion of alerts
and not f.getDeclaringType().isConstructedFrom(_)
// Ignore private members since a private constructor or assignment operator
// is a common idiom that simulates suppressing the default-generated
// members. It would be better to use C++11's "delete" facility or use
// appropriate Boost helper classes, but it is too common to report as a
// violation.
and not f.isPrivate()
// If it is truly user-defined then it must have a body. This leaves out
// C++11 members that use `= delete` or `= default`.
and exists(f.getBlock())
// In rare cases, the extractor pretends that an auto-generated copy
// constructor has a block that is one character long and is located on top
// of the first character of the class name. Checking for
// `isCompilerGenerated` will remove those results.
and not f.isCompilerGenerated()
and not f.isDeleted()
select f, msg

View File

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This rule finds global variables which have a name of length three characters or less. It is particularly important to use descriptive names for global variables. Use of a clear naming convention for global variables helps document their use, avoids pollution of the namespace and reduces the risk of shadowing with local variables.
</p>
</overview>
<recommendation>
<p>
Review the purpose of the each global variable flagged by this rule and update each name to reflect the purpose of the variable.
</p>
</recommendation>
<references>
<li>
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
Chapter 1: Naming, Rec 1.1 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
</li>
<li>
<a href="http://www.learncpp.com/cpp-tutorial/42-global-variables/">Global variables</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,15 @@
/**
* @name Short global name
* @description Global variables should have descriptive names, to help document their use, avoid namespace pollution and reduce the risk of shadowing with local variables.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/short-global-name
* @tags maintainability
*/
import cpp
from GlobalVariable gv
where gv.getName().length() <= 3
and not gv.isStatic()
select gv, "Poor global variable name '" + gv.getName() + "'. Prefer longer, descriptive names for globals (eg. kMyGlobalConstant, not foo)."

View File

@@ -0,0 +1,32 @@
//This switch statement has long case statements, and can become difficult to
//read as the processing for each message type becomes more complex
switch (message_type) {
case CONNECT:
_state = CONNECTING;
int message_id = message_get_id(message);
int source = connect_get_source(message);
//More code here...
send(connect_response);
break;
case DISCONNECT:
_state = DISCONNECTING;
int message_id = message_get_id(message);
int source = disconnect_get_source(message);
//More code here...
send(disconnect_response);
break;
default:
log("Invalid message, id : %d", message_get_id(message));
}
//This is better, as each case is split out to a separate function
switch (packet_type) {
case STREAM:
process_stream_packet(packet);
break;
case DATAGRAM:
process_datagram_packet(packet);
break;
default:
log("Invalid packet type: %d", packet_type);
}

View File

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This rule finds switch statements that have too much code in their cases.
Long case statements often lead to large amounts of nesting, adding to the difficulty of understanding what the code actually does.
Consider wrapping the code for each case in a function and just using the switch statement to invoke the appropriate function in each case.
</p>
<p>The indicated switch statement has a case that is more than 30 lines long.</p>
</overview>
<recommendation>
<p>Consider creating a separate function for the code in the long case statement.</p>
</recommendation>
<example><sample src="SwitchLongCase.cpp" />
</example>
<references>
<li>
<a href="http://www.learncpp.com/cpp-tutorial/53-switch-statements/">Switch statements</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,45 @@
/**
* @name Long switch case
* @description A switch statement with too much code in its cases can make the control flow hard to follow. Consider wrapping the code for each case in a function and just using the switch statement to invoke the appropriate function in each case.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/long-switch
* @tags maintainability
* readability
*/
import cpp
predicate switchCaseStartLine(SwitchCase sc, int start) {
sc.getLocation().getStartLine() = start
}
predicate switchStmtEndLine(SwitchStmt s, int start) {
s.getLocation().getEndLine() = start
}
predicate switchCaseLength(SwitchCase sc, int length) {
exists(SwitchCase next, int l1, int l2 |
next = sc.getNextSwitchCase() and
switchCaseStartLine(next, l1) and
switchCaseStartLine(sc, l2) and
length = l1 - l2 - 1
)
or
(
not exists(sc.getNextSwitchCase()) and
exists(int l1, int l2 |
switchStmtEndLine(sc.getSwitchStmt(), l1) and
switchCaseStartLine(sc, l2) and
length = l1 - l2 - 1
)
)
}
predicate tooLong(SwitchCase sc) {
exists(int n | switchCaseLength(sc, n) and n > 30)
}
from SwitchStmt switch, SwitchCase sc, int lines
where sc = switch.getASwitchCase() and tooLong(sc)
and switchCaseLength(sc, lines)
select switch, "Switch has at least one case that is too long: $@", sc, sc.getExpr().toString() + " (" + lines.toString() + " lines)"

View File

@@ -0,0 +1,24 @@
/**
* @name Unused include
* @description Finds #include directives that are not needed because none of
* the included elements are used.
* @kind problem
* @id cpp/unused-includes
* @problem.severity warning
*/
import cpp
File sourceFile() {
result instanceof CFile or
result instanceof CppFile
}
from Include include, File source, File unneeded
where include.getFile() = source
and source = sourceFile()
and unneeded = include.getIncludedFile()
and not unneeded.getAnIncludedFile*() = source.getMetrics().getAFileDependency()
and unneeded.fromSource()
and not unneeded.getBaseName().matches("%Debug.h")
select include, "Redundant include, this file does not require $@.", unneeded, unneeded.getAbsolutePath()

View File

@@ -0,0 +1,5 @@
{
int x = 0; //x is unused
int y = 0;
cout << y;
}

View File

@@ -0,0 +1,34 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds local variables that are never accessed after declaration.
Unused variables should be removed to increase readability and avoid misuse.</p>
</overview>
<recommendation>
<p>Removing these unused local variables will make code more readable.</p>
</recommendation>
<example>
<sample src="UnusedLocals.cpp" />
</example>
<references>
<li>
<a href="http://www.tutorialspoint.com/cplusplus/cpp_variable_scope.htm">Variable scope</a>
</li>
<li>
<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</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,57 @@
/**
* @name Unused local variable
* @description A local variable that is never called or accessed may be an
* indication that the code is incomplete or has a typo.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/unused-local-variable
* @tags maintainability
* useless-code
* external/cwe/cwe-563
*/
import cpp
/**
* A type that contains a template parameter type
* (doesn't count pointers or references).
*
* These types may have a constructor / destructor when they are
* instantiated, that is not visible in their template form.
*
* Such types include template parameters, classes with a member variable
* of template parameter type, and classes that derive from other such
* classes.
*/
class TemplateDependentType extends Type {
TemplateDependentType() {
this instanceof TemplateParameter or
exists(TemplateDependentType t |
this.refersToDirectly(t) and
not this instanceof PointerType and
not this instanceof ReferenceType
)
}
}
/**
* A variable whose declaration has, or may have, side effects.
*/
predicate declarationHasSideEffects(Variable v) {
exists (Class c | c = v.getType().getUnderlyingType().getUnspecifiedType() |
c.hasConstructor() or
c.hasDestructor()
) or
v.getType() instanceof TemplateDependentType // may have a constructor/destructor
}
from LocalVariable v, Function f
where f = v.getFunction()
and not exists(v.getAnAccess())
and not v.isConst() // workaround for folded constants
and not exists(DeclStmt ds | ds.getADeclaration() = v and ds.isInMacroExpansion()) // variable declared in a macro expansion
and not declarationHasSideEffects(v)
and not exists(AsmStmt s | f = s.getEnclosingFunction())
and not v.getAnAttribute().getName() = "unused"
and not any(ErrorExpr e).getEnclosingFunction() = f // unextracted expr likely used `v`
select v, "Variable " + v.getName() + " is not used"

View File

@@ -0,0 +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()
//...
}
//end of file

View File

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This rule finds static functions with definitions that are never called or accessed. These unused functions should be
removed to increase code readability, reduce object size and avoid misuse.
</p>
</overview>
<recommendation>
<p>Removing these unused static functions will make code more readable. A common pitfall is that code using a static function is guarded by conditional compilation but the static function is not. Notice that this detects directly unused functions and removing a static function may expose more unused functions.</p>
</recommendation>
<example>
<sample src="UnusedStaticFunctions.cpp" />
</example>
<references>
<li>
<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</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,92 @@
/**
* @name Unused static function
* @description A static function that is never called or accessed may be an
* indication that the code is incomplete or has a typo.
* @description Static functions that are never called or accessed.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/unused-static-function
* @tags efficiency
* useless-code
* external/cwe/cwe-561
*/
import cpp
predicate immediatelyReachableFunction(Function f) {
not f.isStatic()
or exists(BlockExpr be | be.getFunction() = f)
or f instanceof MemberFunction
or f instanceof TemplateFunction
or f.getFile() instanceof HeaderFile
or f.getAnAttribute().hasName("constructor")
or f.getAnAttribute().hasName("destructor")
or f.getAnAttribute().hasName("used")
or f.getAnAttribute().hasName("unused")
}
predicate immediatelyReachableVariable(Variable v) {
(v.isTopLevel() and not v.isStatic())
or exists(v.getDeclaringType())
or v.getFile() instanceof HeaderFile
or v.getAnAttribute().hasName("used")
or v.getAnAttribute().hasName("unused")
}
class ImmediatelyReachableThing extends Thing {
ImmediatelyReachableThing() {
immediatelyReachableFunction(this) or
immediatelyReachableVariable(this)
}
}
predicate reachableThing(Thing t) {
t instanceof ImmediatelyReachableThing or
exists(Thing mid | reachableThing(mid) and mid.callsOrAccesses() = t)
}
class Thing extends Locatable {
Thing() {
this instanceof Function or
this instanceof Variable
}
string getName() {
result = this.(Function).getName() or
result = this.(Variable).getName()
}
Thing callsOrAccesses() {
this.(Function).calls((Function)result) or
this.(Function).accesses((Function)result) or
this.(Function).accesses((Variable)result) or
(exists(Access a | this.(Variable).getInitializer().getExpr().getAChild*() = a
| result = a.getTarget()))
}
}
class FunctionToRemove extends Function {
FunctionToRemove() {
this.hasDefinition()
and not reachableThing(this)
}
Thing getOther() {
result.callsOrAccesses+() = this
and this != result
// We will already be reporting the enclosing function of a
// local variable, so don't also report the variable
and not result instanceof LocalVariable
}
}
from FunctionToRemove f, string clarification, Thing other
where if exists(f.getOther())
then (clarification = " ($@ must be removed at the same time)"
and other = f.getOther())
else (clarification = "" and other = f)
select f,
"Static function " + f.getName() + " is unreachable" + clarification,
other,
other.getName()

View File

@@ -0,0 +1,5 @@
void f() {
static int i = 0; //i is unused
...
return;
}

View File

@@ -0,0 +1,38 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds static variables that are never accessed. These static variables should be removed, to increase code comprehensibility,
reduce memory usage and avoid misuse. Unused static <code>const</code> variables could also be an indication of a defect
caused by an unhandled case.</p>
</overview>
<recommendation>
<p>Check that the unused static variable does not indicate a defect, for example, an unhandled case. If the static variable is genuinuely not needed,
then removing it will make code more readable. If the static variable is needed then you should update the code to fix the defect.</p>
</recommendation>
<example><sample src="UnusedStaticVariables.cpp" />
</example>
<references>
<li>
<a href="http://www.tutorialspoint.com/cplusplus/cpp_variable_scope.htm">Variable scope</a>
</li>
<li>
<a href="https://www.securecoding.cert.org/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">Detect and remove code that has no effect</a>
</li>
<li>
<a href="https://www.securecoding.cert.org/confluence/display/cplusplus/DCL07-CPP.+Minimize+the+scope+of+variables+and+methods">Minimize the scope of variables and methods</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,28 @@
/**
* @name Unused static variable
* @description A static variable that is never accessed may be an indication
* that the code is incomplete or has a typo.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/unused-static-variable
* @tags efficiency
* useless-code
* external/cwe/cwe-563
*/
import cpp
predicate declarationHasSideEffects(Variable v) {
exists (Class c | c = v.getType().getUnderlyingType().getUnspecifiedType() |
c.hasConstructor() or c.hasDestructor()
)
}
from Variable v
where v.isStatic()
and v.hasDefinition()
and not exists(VariableAccess a | a.getTarget() = v)
and not v instanceof MemberVariable
and not declarationHasSideEffects(v)
and not v.getAnAttribute().hasName("used")
select v, "Static variable " + v.getName() + " is never read"

View File

@@ -0,0 +1,56 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Use of <code>goto</code> statements makes code more difficult to understand and maintain.
Consequently, the use of <code>goto</code> statements is deprecated except as a mechanism
for breaking out of multiple nested loops, or jumping to error-handling code at
the end of a function. This rule identifies complex (and therefore hard to
understand) uses of <code>goto</code> statements. Function bodies that include
multiple <code>goto</code> statements that jump forward and multiple
<code>goto</code> statements that jump backwards are highlighted.</p>
</overview>
<recommendation>
<p>
In most cases the code can be rewritten and/or rearranged by:
</p>
<ul>
<li>using structured control flow constructs, such as <code>if</code>, <code>while</code>,
and <code>for</code>;</li>
<li>using <code>break</code> or <code>continue</code> to stop a loop iteration early; or</li>
<li>using <code>return</code> to exit a function early</li>
</ul>
<p>
The <code>goto</code> statement may be the best solution for breaking out of
deeply nested loops, or for jumping to error handling code, without adversely
affecting the readability of the function. Such uses will not be flagged by
this rule.
</p>
</recommendation>
<references>
<li>
<em>Joint Strike Fighter Air Vehicle C++ Coding Standards</em>, AV Rule 189. Lockheed Martin Corporation, 2005.
</li>
<li>
E. W. Dijkstra Archive: <a href="http://www.cs.utexas.edu/users/EWD/transcriptions/EWD02xx/EWD215.html">A Case against the GO TO Statement (EWD-215)</a>.
</li>
<li>
MSDN Library: <a href="http://msdn.microsoft.com/en-gb/library/b34dt9cd%28v=vs.80%29.aspx">The goto Statement</a>.
</li>
<li>
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, Rule 4.6. Prentice Hall PTR, 1997.
(<a href="http://mongers.org/industrial-c++/">PDF</a>).
</li>
<li>
cplusplus.com: <a href="http://www.cplusplus.com/doc/tutorial/control/">Control Structures</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,40 @@
/**
* @name Use of goto
* @description The goto statement can make the control flow of a function hard
* to understand, when used for purposes other than error handling.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/use-of-goto
* @tags maintainability
* readability
* language-features
*/
import cpp
class JumpTarget extends Stmt {
JumpTarget() {
exists(GotoStmt g | g.getTarget() = this)
}
FunctionDeclarationEntry getFDE() {
result.getBlock() = this.getParentStmt+()
}
predicate isForward() {
exists(GotoStmt g | g.getTarget() = this | g.getLocation().getEndLine() < this.getLocation().getStartLine())
}
predicate isBackward() {
exists(GotoStmt g | g.getTarget() = this | this.getLocation().getEndLine() < g.getLocation().getStartLine())
}
}
from FunctionDeclarationEntry fde, int nforward, int nbackward
where nforward = strictcount(JumpTarget t | t.getFDE() = fde and t.isForward())
and nbackward = strictcount(JumpTarget t | t.getFDE() = fde and t.isBackward())
and nforward != 1
and nbackward != 1
select fde, "Multiple forward and backward goto statements may make function "
+ fde.getName() + " hard to understand."

View File

@@ -0,0 +1,16 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>about best practices</p>
<!--TOC-->
</overview>
</qhelp>