mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
QL code and tests for C#/C++/JavaScript.
This commit is contained in:
40
cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.qhelp
Normal file
40
cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.qhelp
Normal 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>
|
||||
25
cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.ql
Normal file
25
cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.ql
Normal 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."
|
||||
51
cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp
Normal file
51
cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp
Normal 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&</code>
|
||||
or <code>const T&</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& 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>
|
||||
17
cpp/ql/src/Best Practices/Exceptions/CatchingByValue.ql
Normal file
17
cpp/ql/src/Best Practices/Exceptions/CatchingByValue.ql
Normal 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."
|
||||
46
cpp/ql/src/Best Practices/Exceptions/LeakyCatch.qhelp
Normal file
46
cpp/ql/src/Best Practices/Exceptions/LeakyCatch.qhelp
Normal 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>
|
||||
48
cpp/ql/src/Best Practices/Exceptions/LeakyCatch.ql
Normal file
48
cpp/ql/src/Best Practices/Exceptions/LeakyCatch.ql
Normal 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."
|
||||
44
cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp
Normal file
44
cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp
Normal 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>
|
||||
20
cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.ql
Normal file
20
cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.ql
Normal 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."
|
||||
Reference in New Issue
Block a user