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