mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
3
cpp/config/suites/security/cwe-428
Normal file
3
cpp/config/suites/security/cwe-428
Normal file
@@ -0,0 +1,3 @@
|
||||
# CWE-428: Unquoted Search Path or Element
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql: /CWE/CWE-428
|
||||
@name NULL application name with an unquoted path in call to CreateProcess (CWE-428)
|
||||
@@ -19,6 +19,7 @@
|
||||
@import "cwe-327"
|
||||
@import "cwe-367"
|
||||
@import "cwe-416"
|
||||
@import "cwe-428"
|
||||
@import "cwe-457"
|
||||
@import "cwe-468"
|
||||
@import "cwe-676"
|
||||
|
||||
7
cpp/ql/src/Critical/DeadCodeGoto.cpp
Normal file
7
cpp/ql/src/Critical/DeadCodeGoto.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
goto err1;
|
||||
free(pointer); // BAD: this line is unreachable
|
||||
err1: return -1;
|
||||
|
||||
free(pointer); // GOOD: this line is reachable
|
||||
goto err2;
|
||||
err2: return -1;
|
||||
28
cpp/ql/src/Critical/DeadCodeGoto.qhelp
Normal file
28
cpp/ql/src/Critical/DeadCodeGoto.qhelp
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Code immediately following a <code>goto</code> or <code>break</code> statement will not be executed,
|
||||
unless there is a label or switch case. When the code is necessary, this leads to logical errors or
|
||||
resource leaks. If the code is unnecessary, it may confuse readers.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
If the unreachable code is necessary, move the <code>goto</code> or <code>break</code> statement to
|
||||
after the code. Otherwise, delete the unreachable code.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="DeadCodeGoto.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
The CERT C Secure Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect or is never executed</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
33
cpp/ql/src/Critical/DeadCodeGoto.ql
Normal file
33
cpp/ql/src/Critical/DeadCodeGoto.ql
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Dead code due to goto or break statement
|
||||
* @description A goto or break statement is followed by unreachable code.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/dead-code-goto
|
||||
* @tags maintainability
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
Stmt getNextRealStmt(Block b, int i) {
|
||||
result = b.getStmt(i + 1) and
|
||||
not result instanceof EmptyStmt
|
||||
or
|
||||
b.getStmt(i + 1) instanceof EmptyStmt and
|
||||
result = getNextRealStmt(b, i + 1)
|
||||
}
|
||||
|
||||
from JumpStmt js, Block b, int i, Stmt s
|
||||
where b.getStmt(i) = js
|
||||
and s = getNextRealStmt(b, i)
|
||||
// the next statement isn't jumped to
|
||||
and not s instanceof LabelStmt
|
||||
and not s instanceof SwitchCase
|
||||
// the next statement isn't breaking out of a switch
|
||||
and not s.(BreakStmt).getBreakable() instanceof SwitchStmt
|
||||
// the next statement isn't a loop that can be jumped into
|
||||
and not exists (LabelStmt ls | s.(Loop).getStmt().getAChild*() = ls)
|
||||
and not exists (SwitchCase sc | s.(Loop).getStmt().getAChild*() = sc)
|
||||
select js, "This statement makes $@ unreachable.", s, s.toString()
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
char param[SIZE];
|
||||
char param[20];
|
||||
char *arg1;
|
||||
|
||||
char arg1[10];
|
||||
char arg2[20];
|
||||
arg1 = argv[1];
|
||||
|
||||
//wrong: only uses the size of the source (argv[1]) when using strncpy
|
||||
strncpy(param, argv[1], strlen(arg1));
|
||||
strncpy(param, arg1, strlen(arg1));
|
||||
|
||||
//correct: uses the size of the destination array as well
|
||||
strncpy(param, argv[1], min(strlen(arg1, sizeof(param) -1)));
|
||||
strncpy(param, arg1, min(strlen(arg1), sizeof(param) -1));
|
||||
}
|
||||
|
||||
@@ -5,26 +5,38 @@
|
||||
* @kind problem
|
||||
* @id cpp/overflow-destination
|
||||
* @problem.severity warning
|
||||
* @precision low
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-119
|
||||
* external/cwe/cwe-131
|
||||
*/
|
||||
import cpp
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
predicate sourceSized(FunctionCall fc)
|
||||
/**
|
||||
* Holds if `fc` is a call to a copy operation where the size argument contains
|
||||
* a reference to the source argument. For example:
|
||||
* ```
|
||||
* memcpy(dest, src, sizeof(src));
|
||||
* ```
|
||||
*/
|
||||
predicate sourceSized(FunctionCall fc, Expr src)
|
||||
{
|
||||
exists(string name |
|
||||
(name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and
|
||||
fc.getTarget().hasQualifiedName(name))
|
||||
and
|
||||
exists(Expr dest, Expr src, Expr size, Variable v |
|
||||
exists(Expr dest, Expr size, Variable v |
|
||||
fc.getArgument(0) = dest and fc.getArgument(1) = src and fc.getArgument(2) = size and
|
||||
src = v.getAnAccess() and size.getAChild+() = v.getAnAccess() and
|
||||
|
||||
// exception: `dest` is also referenced in the size argument
|
||||
not exists(Variable other |
|
||||
dest = other.getAnAccess() and size.getAChild+() = other.getAnAccess())
|
||||
and
|
||||
|
||||
// exception: `src` and `dest` are both arrays of the same type and size
|
||||
not exists(ArrayType srctype, ArrayType desttype |
|
||||
dest.getType().getUnderlyingType() = desttype and
|
||||
src.getType().getUnderlyingType() = srctype and
|
||||
@@ -32,48 +44,7 @@ predicate sourceSized(FunctionCall fc)
|
||||
desttype.getArraySize() = srctype.getArraySize()))
|
||||
}
|
||||
|
||||
class VulnerableArgument extends PointsToExpr
|
||||
{
|
||||
VulnerableArgument() { sourceSized(this.getParent()) }
|
||||
override predicate interesting() { sourceSized(this.getParent()) }
|
||||
}
|
||||
|
||||
predicate taintingFunction(Function f, int buf)
|
||||
{
|
||||
(f.hasQualifiedName("read") and buf = 1) or
|
||||
(f.hasQualifiedName("fgets") and buf = 0) or
|
||||
(f.hasQualifiedName("fread") and buf = 0)
|
||||
}
|
||||
|
||||
// Taint `argv[i]`, for all i, but also `*argv`, etc.
|
||||
predicate commandLineArg(Expr e)
|
||||
{
|
||||
exists(Function f, Parameter argv, VariableAccess access |
|
||||
f.hasQualifiedName("main") and f.getParameter(1) = argv and
|
||||
argv.getAnAccess() = access and access.isRValue() and
|
||||
pointer(access, e))
|
||||
}
|
||||
|
||||
predicate tainted(Expr e)
|
||||
{
|
||||
exists(FunctionCall fc, int arg |
|
||||
taintingFunction(fc.getTarget(), arg) and
|
||||
e = fc.getArgument(arg))
|
||||
or
|
||||
e.(FunctionCall).getTarget().hasQualifiedName("getenv")
|
||||
or
|
||||
commandLineArg(e)
|
||||
}
|
||||
|
||||
class TaintedArgument extends PointsToExpr
|
||||
{
|
||||
TaintedArgument() { tainted(this) }
|
||||
override predicate interesting() { tainted(this) }
|
||||
}
|
||||
|
||||
from FunctionCall fc, VulnerableArgument vuln, TaintedArgument tainted
|
||||
where sourceSized(fc)
|
||||
and fc.getArgument(1) = vuln
|
||||
and vuln.pointsTo() = tainted.pointsTo()
|
||||
and vuln.confidence() > 0.01
|
||||
from FunctionCall fc, Expr vuln, Expr taintSource
|
||||
where sourceSized(fc, vuln)
|
||||
and tainted(taintSource, vuln)
|
||||
select fc, "To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size."
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
void f()
|
||||
{
|
||||
for (signed char i = 0; i < 100; i--)
|
||||
{
|
||||
// code ...
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>A <code>for-loop</code> iteration expression goes backwards with respect of the initialization statement and condition expression.</p>
|
||||
<p>This warning indicates that a <code>for-loop</code> might not function as intended.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>To fix this issue, check that the loop condition is correct and change the iteration expression to match.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, the initialization statement (<code>i = 0</code>) and the condition expression (<code>i < 100</code>) indicate that the intended iteration expression should have been incrementing, but instead a postfix decrement operator is used (<code>i--</code>).</p>
|
||||
<sample src="inconsistentLoopDirection.c" />
|
||||
|
||||
<p>To fix this issue, change the iteration expression to match the direction of the initialization statement and the condition expression: <code>i++</code>.</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li><a href="https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/58teb7hd(v=vs.110)">warning C6293: Ill-defined for-loop: counts down from minimum</a>
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
116
cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
Normal file
116
cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @name Inconsistent direction of for loop
|
||||
* @description A for-loop iteration expression goes backward with respect of the initialization statement and condition expression.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id cpp/inconsistent-loop-direction
|
||||
* @tags correctness
|
||||
* external/cwe/cwe-835
|
||||
* external/microsoft/6293
|
||||
* @msrc.severity important
|
||||
*/
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
predicate illDefinedDecrForStmt( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition ) {
|
||||
v.getAnAssignedValue() = initialCondition
|
||||
and
|
||||
exists(
|
||||
RelationalOperation rel |
|
||||
rel = forstmt.getCondition() |
|
||||
terminalCondition = rel.getGreaterOperand()
|
||||
and v.getAnAccess() = rel.getLesserOperand()
|
||||
and
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(rel.getLesserOperand()))
|
||||
)
|
||||
and
|
||||
exists(
|
||||
DecrementOperation dec |
|
||||
dec = forstmt.getUpdate().(DecrementOperation)
|
||||
and dec.getAnOperand() = v.getAnAccess()
|
||||
)
|
||||
and
|
||||
(
|
||||
( upperBound(initialCondition) < lowerBound(terminalCondition) )
|
||||
or
|
||||
( forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue() )
|
||||
)
|
||||
}
|
||||
|
||||
predicate illDefinedIncrForStmt( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition ) {
|
||||
v.getAnAssignedValue() = initialCondition
|
||||
and
|
||||
exists(
|
||||
RelationalOperation rel |
|
||||
rel = forstmt.getCondition() |
|
||||
terminalCondition = rel.getLesserOperand()
|
||||
and v.getAnAccess() = rel.getGreaterOperand()
|
||||
and
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(rel.getGreaterOperand()))
|
||||
)
|
||||
and
|
||||
exists( IncrementOperation incr |
|
||||
incr = forstmt.getUpdate().(IncrementOperation)
|
||||
and
|
||||
incr.getAnOperand() = v.getAnAccess()
|
||||
)
|
||||
and
|
||||
(
|
||||
( upperBound(terminalCondition) < lowerBound(initialCondition))
|
||||
or
|
||||
( forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue())
|
||||
)
|
||||
}
|
||||
|
||||
predicate illDefinedForStmtWrongDirection( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition
|
||||
, boolean isIncr ) {
|
||||
( illDefinedDecrForStmt( forstmt, v, initialCondition, terminalCondition) and isIncr = false )
|
||||
or
|
||||
( illDefinedIncrForStmt( forstmt, v, initialCondition, terminalCondition) and isIncr = true)
|
||||
}
|
||||
|
||||
bindingset[b]
|
||||
private string forLoopdirection(boolean b){
|
||||
if( b = true ) then result = "upward"
|
||||
else result = "downward"
|
||||
}
|
||||
|
||||
bindingset[b]
|
||||
private string forLoopTerminalConditionRelationship(boolean b){
|
||||
if( b = true ) then result = "lower"
|
||||
else result = "higher"
|
||||
}
|
||||
|
||||
predicate illDefinedForStmt( ForStmt for, string message ) {
|
||||
exists(
|
||||
boolean isIncr,
|
||||
Variable v,
|
||||
Expr initialCondition,
|
||||
Expr terminalCondition |
|
||||
illDefinedForStmtWrongDirection(for, v, initialCondition, terminalCondition, isIncr)
|
||||
and
|
||||
if( for.conditionAlwaysFalse() ) then
|
||||
(
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts "
|
||||
+ forLoopdirection(isIncr) + " from a value ("+ initialCondition +"), but the terminal condition is always false."
|
||||
)
|
||||
else if
|
||||
(
|
||||
for.conditionAlwaysTrue() ) then (
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts "
|
||||
+ forLoopdirection(isIncr) + " from a value ("+ initialCondition +"), but the terminal condition is always true."
|
||||
)
|
||||
else
|
||||
(
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts "
|
||||
+ forLoopdirection(isIncr) + " from a value ("+ initialCondition +"), but the terminal condition is "
|
||||
+ forLoopTerminalConditionRelationship(isIncr) + " (" + terminalCondition + ")."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from ForStmt forstmt, string message
|
||||
where illDefinedForStmt(forstmt, message)
|
||||
select forstmt, message
|
||||
11
cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.cpp
Normal file
11
cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
// ...
|
||||
|
||||
CreateProcessW( // BUG
|
||||
NULL, // lpApplicationName
|
||||
(LPWSTR)L"C:\\Program Files\\MyApp", // lpCommandLine
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
||||
|
||||
// ...
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>This query indicates that there is a call to a function of the <code>CreateProcess*</code> family of functions, which introduces a security vulnerability.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Do not use <code>NULL</code> for the <code>lpApplicationName</code> argument to the <code>CreateProcess*</code> function.</p>
|
||||
<p>If you pass <code>NULL</code> for <code>lpApplicationName</code>, use quotation marks around the executable path in <code>lpCommandLine</code>.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, <code>CreateProcessW</code> is called with a <code>NULL</code> value for <code>lpApplicationName</code>,
|
||||
and the value for <code>lpCommandLine</code> that represent the application path is not quoted and has spaces in it.</p>
|
||||
<p>If an attacker has access to the file system, they can elevate privileges by creating a file such as <code>C:\Program.exe</code> that will be executed instead of the intended application.</p>
|
||||
<sample src="UnsafeCreateProcessCall.cpp" />
|
||||
|
||||
<p>To fix this issue, specify a valid string for <code>lpApplicationName</code>, or quote the path for <code>lpCommandLine</code>. For example:</p>
|
||||
<p><code>(LPWSTR)L"\"C:\\Program Files\\MyApp\"", // lpCommandLine</code></p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa">CreateProcessA function (Microsoft documentation).</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessw">CreateProcessW function (Microsoft documentation).</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasusera">CreateProcessAsUserA function (Microsoft documentation).</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw">CreateProcessAsUserW function (Microsoft documentation).</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithlogonw">CreateProcessWithLogonW function (Microsoft documentation).</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithtokenw">CreateProcessWithTokenW function (Microsoft documentation).</a>
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
130
cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
Normal file
130
cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @name NULL application name with an unquoted path in call to CreateProcess
|
||||
* @description Calling a function of the CreateProcess* family of functions, where the path contains spaces, introduces a security vulnerability.
|
||||
* @id cpp/unsafe-create-process-call
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
* @msrc.severity important
|
||||
* @tags security
|
||||
* external/cwe/cwe-428
|
||||
* external/microsoft/C6277
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
|
||||
predicate isCreateProcessFunction(FunctionCall call, int applicationNameIndex, int commandLineIndex) {
|
||||
(
|
||||
call.getTarget().hasGlobalName("CreateProcessA")
|
||||
and applicationNameIndex = 0
|
||||
and commandLineIndex = 1
|
||||
) or (
|
||||
call.getTarget().hasGlobalName("CreateProcessW")
|
||||
and applicationNameIndex = 0
|
||||
and commandLineIndex = 1
|
||||
) or (
|
||||
call.getTarget().hasGlobalName("CreateProcessWithTokenW")
|
||||
and applicationNameIndex = 2
|
||||
and commandLineIndex = 3
|
||||
) or (
|
||||
call.getTarget().hasGlobalName("CreateProcessWithLogonW")
|
||||
and applicationNameIndex = 4
|
||||
and commandLineIndex = 5
|
||||
) or (
|
||||
call.getTarget().hasGlobalName("CreateProcessAsUserA")
|
||||
and applicationNameIndex = 1
|
||||
and commandLineIndex = 2
|
||||
) or (
|
||||
call.getTarget().hasGlobalName("CreateProcessAsUserW")
|
||||
and applicationNameIndex = 1
|
||||
and commandLineIndex = 2
|
||||
)
|
||||
}
|
||||
/**
|
||||
* A function call to CreateProcess (either wide-char or single byte string versions)
|
||||
*/
|
||||
class CreateProcessFunctionCall extends FunctionCall {
|
||||
CreateProcessFunctionCall() {
|
||||
isCreateProcessFunction( this, _, _)
|
||||
}
|
||||
|
||||
int getApplicationNameArgumentId() {
|
||||
isCreateProcessFunction( this, result, _)
|
||||
}
|
||||
|
||||
int getCommandLineArgumentId() {
|
||||
isCreateProcessFunction( this, _, result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataflow that detects a call to CreateProcess with a NULL value for lpApplicationName argument
|
||||
*/
|
||||
class NullAppNameCreateProcessFunctionConfiguration extends DataFlow::Configuration {
|
||||
NullAppNameCreateProcessFunctionConfiguration() {
|
||||
this = "NullAppNameCreateProcessFunctionConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
nullValue(source.asExpr())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(
|
||||
CreateProcessFunctionCall call, Expr val |
|
||||
val = sink.asExpr() |
|
||||
val = call.getArgument(call.getApplicationNameArgumentId())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataflow that detects a call to CreateProcess with an unquoted commandLine argument
|
||||
*/
|
||||
class QuotedCommandInCreateProcessFunctionConfiguration extends DataFlow2::Configuration {
|
||||
QuotedCommandInCreateProcessFunctionConfiguration() {
|
||||
this = "QuotedCommandInCreateProcessFunctionConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow2::Node source) {
|
||||
exists( string s |
|
||||
s = source.asExpr().getValue().toString()
|
||||
and
|
||||
not isQuotedOrNoSpaceApplicationNameOnCmd(s)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow2::Node sink) {
|
||||
exists(
|
||||
CreateProcessFunctionCall call, Expr val |
|
||||
val = sink.asExpr() |
|
||||
val = call.getArgument(call.getCommandLineArgumentId())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bindingset[s]
|
||||
predicate isQuotedOrNoSpaceApplicationNameOnCmd(string s){
|
||||
s.regexpMatch("\"([^\"])*\"(\\s|.)*") // The first element (path) is quoted
|
||||
or
|
||||
s.regexpMatch("[^\\s]+") // There are no spaces in the string
|
||||
}
|
||||
|
||||
from CreateProcessFunctionCall call, string msg1, string msg2
|
||||
where
|
||||
exists( Expr source, Expr appName,
|
||||
NullAppNameCreateProcessFunctionConfiguration nullAppConfig |
|
||||
appName = call.getArgument(call.getApplicationNameArgumentId())
|
||||
and nullAppConfig.hasFlow(DataFlow2::exprNode(source), DataFlow2::exprNode(appName))
|
||||
and msg1 = call.toString() + " with lpApplicationName == NULL (" + appName + ")"
|
||||
)
|
||||
and
|
||||
exists( Expr source, Expr cmd,
|
||||
QuotedCommandInCreateProcessFunctionConfiguration quotedConfig |
|
||||
cmd = call.getArgument(call.getCommandLineArgumentId())
|
||||
and quotedConfig.hasFlow(DataFlow2::exprNode(source), DataFlow2::exprNode(cmd))
|
||||
and msg2 = " and with an unquoted lpCommandLine (" + cmd + ") introduces a security vulnerability if the path contains spaces."
|
||||
)
|
||||
select call, msg1 + " " + msg2
|
||||
@@ -107,15 +107,17 @@ predicate defUndef(File f, string macroName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Header file `hf` looks like it contains an x-macro called
|
||||
* `macroName`. That is, a macro that is used to interpret the
|
||||
* Header file `hf` looks like it contains an x-macro.
|
||||
* That is, a macro that is used to interpret the
|
||||
* data in `hf`, usually defined just before including that file
|
||||
* and undefined immediately afterwards.
|
||||
*/
|
||||
predicate hasXMacro(HeaderFile hf, string macroName) {
|
||||
usesMacro(hf, macroName) and
|
||||
forex(Include i | i.getIncludedFile() = hf |
|
||||
defUndef(i.getFile(), macroName)
|
||||
predicate hasXMacro(HeaderFile hf) {
|
||||
exists(string macroName |
|
||||
usesMacro(hf, macroName) and
|
||||
forex(File f | f.getAnIncludedFile() = hf |
|
||||
defUndef(f, macroName)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,7 +137,7 @@ where not hf instanceof IncludeGuardedHeader
|
||||
exists(UsingEntry ue | ue.getFile() = hf)
|
||||
)
|
||||
// Exclude files which look like they contain 'x-macros'
|
||||
and not hasXMacro(hf, _)
|
||||
and not hasXMacro(hf)
|
||||
// Exclude files which are always #imported.
|
||||
and not forex(Include i | i.getIncludedFile() = hf | i instanceof Import)
|
||||
// Exclude files which are only included once.
|
||||
|
||||
@@ -159,6 +159,17 @@ predicate unreleasedResource(Resource r, Expr acquire, File f, int acquireLine)
|
||||
)
|
||||
and f = acquire.getFile()
|
||||
and acquireLine = acquire.getLocation().getStartLine()
|
||||
|
||||
// check that any destructor for this class has a block; if it doesn't,
|
||||
// we must be missing information.
|
||||
and forall(Class c, Destructor d |
|
||||
r.getDeclaringType().isConstructedFrom*(c) and
|
||||
d = c.getAMember() and
|
||||
not d.isCompilerGenerated() and
|
||||
not d.isDefaulted() and
|
||||
not d.isDeleted() |
|
||||
exists(d.getBlock())
|
||||
)
|
||||
}
|
||||
|
||||
predicate freedInSameMethod(Resource r, Expr acquire) {
|
||||
|
||||
@@ -19,7 +19,11 @@ import cpp
|
||||
|
||||
predicate functionsMissingReturnStmt(Function f, ControlFlowNode blame) {
|
||||
f.fromSource() and
|
||||
not f.getType().getUnderlyingType().getUnspecifiedType() instanceof VoidType and
|
||||
exists(Type returnType |
|
||||
returnType = f.getType().getUnderlyingType().getUnspecifiedType() and
|
||||
not returnType instanceof VoidType and
|
||||
not returnType instanceof TemplateParameter
|
||||
) and
|
||||
exists(ReturnStmt s | f.getAPredecessor() = s | blame = s.getAPredecessor())}
|
||||
|
||||
/* If a function has a value-carrying return statement, but the extractor hit a snag
|
||||
@@ -32,13 +36,11 @@ predicate functionImperfectlyExtracted(Function f) {
|
||||
exists(ErrorExpr ee | ee.getEnclosingFunction() = f)
|
||||
}
|
||||
|
||||
from Stmt stmt, string msg
|
||||
from Stmt stmt, string msg, Function f, ControlFlowNode blame
|
||||
where
|
||||
exists(Function f, ControlFlowNode blame |
|
||||
functionsMissingReturnStmt(f, blame) and
|
||||
reachable(blame) and
|
||||
not functionImperfectlyExtracted(f) and
|
||||
(blame = stmt or blame.(Expr).getEnclosingStmt() = stmt) and
|
||||
msg = "Function " + f.getName() + " should return a value of type " + f.getType().getName() + " but does not return a value here"
|
||||
)
|
||||
functionsMissingReturnStmt(f, blame) and
|
||||
reachable(blame) and
|
||||
not functionImperfectlyExtracted(f) and
|
||||
(blame = stmt or blame.(Expr).getEnclosingStmt() = stmt) and
|
||||
msg = "Function " + f.getName() + " should return a value of type " + f.getType().getName() + " but does not return a value here"
|
||||
select stmt, msg
|
||||
|
||||
@@ -724,76 +724,27 @@ private predicate localFlowBigStep(
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` may contain an object of the same type, `t`, as the one
|
||||
* that contains `f`, and if this fact should be used to compress
|
||||
* access paths.
|
||||
*
|
||||
* Examples include the tail pointer in a linked list or the left and right
|
||||
* pointers in a binary tree.
|
||||
*/
|
||||
private predicate selfRef(Content f, RefType t) {
|
||||
t = f.getDeclaringType() and
|
||||
f.isSelfRef()
|
||||
}
|
||||
|
||||
private newtype TFlowContainer =
|
||||
TRegularContent(Content f) { not selfRef(f, _) } or
|
||||
TSelfRefContent(RefType t) { selfRef(_, t) }
|
||||
|
||||
/**
|
||||
* A `Content` or a `Content` abstracted as its declaring type.
|
||||
*
|
||||
* Sequences of one or more `Content`s in the same declaring type for which
|
||||
* `isSelfRef()` holds are represented as a single `FlowContainer` in an
|
||||
* `AccessPath`.
|
||||
*/
|
||||
private class FlowContainer extends TFlowContainer {
|
||||
string toString() {
|
||||
exists(Content f | this = TRegularContent(f) and result = f.toString())
|
||||
or
|
||||
exists(RefType t | this = TSelfRefContent(t) and result = t.toString())
|
||||
}
|
||||
|
||||
predicate usesContent(Content f) {
|
||||
this = TRegularContent(f)
|
||||
or
|
||||
exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t))
|
||||
}
|
||||
|
||||
RefType getContainerType() {
|
||||
exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType())
|
||||
or
|
||||
this = TSelfRefContent(result)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontHead(FlowContainer f)
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
* The front of an `AccessPath`. This is either a head or a nil.
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = t.toString())
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(FlowContainer f | this = TFrontHead(f) | result = f.toString())
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
predicate headUsesContent(Content f) {
|
||||
exists(FlowContainer fc |
|
||||
fc.usesContent(f) and
|
||||
this = TFrontHead(fc)
|
||||
)
|
||||
}
|
||||
predicate headUsesContent(Content f) { this = TFrontHead(f) }
|
||||
}
|
||||
|
||||
private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
@@ -1004,7 +955,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TCons(FlowContainer f, int len) { len in [1 .. 5] }
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
@@ -1016,7 +967,7 @@ private newtype TAccessPath =
|
||||
private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
FlowContainer getHead() { this = TCons(result, _) }
|
||||
Content getHead() { this = TCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TNil(_) and result = 0
|
||||
@@ -1027,27 +978,27 @@ private class AccessPath extends TAccessPath {
|
||||
Type getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType())
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) }
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
override string toString() {
|
||||
exists(FlowContainer f, int len | this = TCons(f, len) |
|
||||
exists(Content f, int len | this = TCons(f, len) |
|
||||
result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f))
|
||||
exists(Content f | this = TCons(f, _) | result = TFrontHead(f))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -724,76 +724,27 @@ private predicate localFlowBigStep(
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` may contain an object of the same type, `t`, as the one
|
||||
* that contains `f`, and if this fact should be used to compress
|
||||
* access paths.
|
||||
*
|
||||
* Examples include the tail pointer in a linked list or the left and right
|
||||
* pointers in a binary tree.
|
||||
*/
|
||||
private predicate selfRef(Content f, RefType t) {
|
||||
t = f.getDeclaringType() and
|
||||
f.isSelfRef()
|
||||
}
|
||||
|
||||
private newtype TFlowContainer =
|
||||
TRegularContent(Content f) { not selfRef(f, _) } or
|
||||
TSelfRefContent(RefType t) { selfRef(_, t) }
|
||||
|
||||
/**
|
||||
* A `Content` or a `Content` abstracted as its declaring type.
|
||||
*
|
||||
* Sequences of one or more `Content`s in the same declaring type for which
|
||||
* `isSelfRef()` holds are represented as a single `FlowContainer` in an
|
||||
* `AccessPath`.
|
||||
*/
|
||||
private class FlowContainer extends TFlowContainer {
|
||||
string toString() {
|
||||
exists(Content f | this = TRegularContent(f) and result = f.toString())
|
||||
or
|
||||
exists(RefType t | this = TSelfRefContent(t) and result = t.toString())
|
||||
}
|
||||
|
||||
predicate usesContent(Content f) {
|
||||
this = TRegularContent(f)
|
||||
or
|
||||
exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t))
|
||||
}
|
||||
|
||||
RefType getContainerType() {
|
||||
exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType())
|
||||
or
|
||||
this = TSelfRefContent(result)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontHead(FlowContainer f)
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
* The front of an `AccessPath`. This is either a head or a nil.
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = t.toString())
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(FlowContainer f | this = TFrontHead(f) | result = f.toString())
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
predicate headUsesContent(Content f) {
|
||||
exists(FlowContainer fc |
|
||||
fc.usesContent(f) and
|
||||
this = TFrontHead(fc)
|
||||
)
|
||||
}
|
||||
predicate headUsesContent(Content f) { this = TFrontHead(f) }
|
||||
}
|
||||
|
||||
private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
@@ -1004,7 +955,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TCons(FlowContainer f, int len) { len in [1 .. 5] }
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
@@ -1016,7 +967,7 @@ private newtype TAccessPath =
|
||||
private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
FlowContainer getHead() { this = TCons(result, _) }
|
||||
Content getHead() { this = TCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TNil(_) and result = 0
|
||||
@@ -1027,27 +978,27 @@ private class AccessPath extends TAccessPath {
|
||||
Type getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType())
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) }
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
override string toString() {
|
||||
exists(FlowContainer f, int len | this = TCons(f, len) |
|
||||
exists(Content f, int len | this = TCons(f, len) |
|
||||
result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f))
|
||||
exists(Content f | this = TCons(f, _) | result = TFrontHead(f))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -724,76 +724,27 @@ private predicate localFlowBigStep(
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` may contain an object of the same type, `t`, as the one
|
||||
* that contains `f`, and if this fact should be used to compress
|
||||
* access paths.
|
||||
*
|
||||
* Examples include the tail pointer in a linked list or the left and right
|
||||
* pointers in a binary tree.
|
||||
*/
|
||||
private predicate selfRef(Content f, RefType t) {
|
||||
t = f.getDeclaringType() and
|
||||
f.isSelfRef()
|
||||
}
|
||||
|
||||
private newtype TFlowContainer =
|
||||
TRegularContent(Content f) { not selfRef(f, _) } or
|
||||
TSelfRefContent(RefType t) { selfRef(_, t) }
|
||||
|
||||
/**
|
||||
* A `Content` or a `Content` abstracted as its declaring type.
|
||||
*
|
||||
* Sequences of one or more `Content`s in the same declaring type for which
|
||||
* `isSelfRef()` holds are represented as a single `FlowContainer` in an
|
||||
* `AccessPath`.
|
||||
*/
|
||||
private class FlowContainer extends TFlowContainer {
|
||||
string toString() {
|
||||
exists(Content f | this = TRegularContent(f) and result = f.toString())
|
||||
or
|
||||
exists(RefType t | this = TSelfRefContent(t) and result = t.toString())
|
||||
}
|
||||
|
||||
predicate usesContent(Content f) {
|
||||
this = TRegularContent(f)
|
||||
or
|
||||
exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t))
|
||||
}
|
||||
|
||||
RefType getContainerType() {
|
||||
exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType())
|
||||
or
|
||||
this = TSelfRefContent(result)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontHead(FlowContainer f)
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
* The front of an `AccessPath`. This is either a head or a nil.
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = t.toString())
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(FlowContainer f | this = TFrontHead(f) | result = f.toString())
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
predicate headUsesContent(Content f) {
|
||||
exists(FlowContainer fc |
|
||||
fc.usesContent(f) and
|
||||
this = TFrontHead(fc)
|
||||
)
|
||||
}
|
||||
predicate headUsesContent(Content f) { this = TFrontHead(f) }
|
||||
}
|
||||
|
||||
private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
@@ -1004,7 +955,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TCons(FlowContainer f, int len) { len in [1 .. 5] }
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
@@ -1016,7 +967,7 @@ private newtype TAccessPath =
|
||||
private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
FlowContainer getHead() { this = TCons(result, _) }
|
||||
Content getHead() { this = TCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TNil(_) and result = 0
|
||||
@@ -1027,27 +978,27 @@ private class AccessPath extends TAccessPath {
|
||||
Type getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType())
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) }
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
override string toString() {
|
||||
exists(FlowContainer f, int len | this = TCons(f, len) |
|
||||
exists(Content f, int len | this = TCons(f, len) |
|
||||
result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f))
|
||||
exists(Content f | this = TCons(f, _) | result = TFrontHead(f))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -724,76 +724,27 @@ private predicate localFlowBigStep(
|
||||
localFlowExit(node2, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` may contain an object of the same type, `t`, as the one
|
||||
* that contains `f`, and if this fact should be used to compress
|
||||
* access paths.
|
||||
*
|
||||
* Examples include the tail pointer in a linked list or the left and right
|
||||
* pointers in a binary tree.
|
||||
*/
|
||||
private predicate selfRef(Content f, RefType t) {
|
||||
t = f.getDeclaringType() and
|
||||
f.isSelfRef()
|
||||
}
|
||||
|
||||
private newtype TFlowContainer =
|
||||
TRegularContent(Content f) { not selfRef(f, _) } or
|
||||
TSelfRefContent(RefType t) { selfRef(_, t) }
|
||||
|
||||
/**
|
||||
* A `Content` or a `Content` abstracted as its declaring type.
|
||||
*
|
||||
* Sequences of one or more `Content`s in the same declaring type for which
|
||||
* `isSelfRef()` holds are represented as a single `FlowContainer` in an
|
||||
* `AccessPath`.
|
||||
*/
|
||||
private class FlowContainer extends TFlowContainer {
|
||||
string toString() {
|
||||
exists(Content f | this = TRegularContent(f) and result = f.toString())
|
||||
or
|
||||
exists(RefType t | this = TSelfRefContent(t) and result = t.toString())
|
||||
}
|
||||
|
||||
predicate usesContent(Content f) {
|
||||
this = TRegularContent(f)
|
||||
or
|
||||
exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t))
|
||||
}
|
||||
|
||||
RefType getContainerType() {
|
||||
exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType())
|
||||
or
|
||||
this = TSelfRefContent(result)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontHead(FlowContainer f)
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
* The front of an `AccessPath`. This is either a head or a nil.
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = t.toString())
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(FlowContainer f | this = TFrontHead(f) | result = f.toString())
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
predicate headUsesContent(Content f) {
|
||||
exists(FlowContainer fc |
|
||||
fc.usesContent(f) and
|
||||
this = TFrontHead(fc)
|
||||
)
|
||||
}
|
||||
predicate headUsesContent(Content f) { this = TFrontHead(f) }
|
||||
}
|
||||
|
||||
private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
@@ -1004,7 +955,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TCons(FlowContainer f, int len) { len in [1 .. 5] }
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
@@ -1016,7 +967,7 @@ private newtype TAccessPath =
|
||||
private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
FlowContainer getHead() { this = TCons(result, _) }
|
||||
Content getHead() { this = TCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TNil(_) and result = 0
|
||||
@@ -1027,27 +978,27 @@ private class AccessPath extends TAccessPath {
|
||||
Type getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType())
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) }
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
override string toString() {
|
||||
exists(FlowContainer f, int len | this = TCons(f, len) |
|
||||
exists(Content f, int len | this = TCons(f, len) |
|
||||
result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f))
|
||||
exists(Content f | this = TCons(f, _) | result = TFrontHead(f))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ private module ImplCommon {
|
||||
node1.(ArgumentNode).argumentOf(call, i1) and
|
||||
node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getDeclaringType())
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ private module ImplCommon {
|
||||
setterReturn(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getDeclaringType())
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ private module ImplCommon {
|
||||
viableParamArg(p, arg) and
|
||||
getter(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getDeclaringType()) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getType())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -69,18 +69,9 @@ class Content extends TContent {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract RefType getDeclaringType();
|
||||
abstract RefType getContainerType();
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
/**
|
||||
* Holds if this content may contain an object of the same type as the one
|
||||
* that contains this content, and if this fact should be used to compress
|
||||
* access paths.
|
||||
*
|
||||
* Examples include the tail pointer in a linked list or the left and right
|
||||
* pointers in a binary tree.
|
||||
*/
|
||||
predicate isSelfRef() { none() }
|
||||
}
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
Field f;
|
||||
@@ -90,17 +81,17 @@ private class FieldContent extends Content, TFieldContent {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
override RefType getDeclaringType() { result = f.getDeclaringType() }
|
||||
override RefType getContainerType() { result = f.getDeclaringType() }
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
override RefType getDeclaringType() { none() }
|
||||
override RefType getContainerType() { none() }
|
||||
override Type getType() { none() }
|
||||
}
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
override RefType getDeclaringType() { none() }
|
||||
override RefType getContainerType() { none() }
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
@@ -132,6 +123,11 @@ RefType getErasedRepr(Type t) {
|
||||
result instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getErasedRepr`. */
|
||||
string ppReprType(Type t) {
|
||||
result = t.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
|
||||
@@ -114,8 +114,11 @@ pragma[noopt] predicate correctIncludeGuard(HeaderFile hf, PreprocessorDirective
|
||||
*/
|
||||
predicate startsWithIfndef(HeaderFile hf, PreprocessorDirective ifndef, string macroName) {
|
||||
ifndefDirective(ifndef, macroName) and
|
||||
ifndef.getFile() = hf and
|
||||
ifndef.getLocation().getStartLine() = min(int l | includeGuardRelevantLine(hf, l))
|
||||
exists(Location loc |
|
||||
loc = ifndef.getLocation() and
|
||||
loc.getFile() = hf and
|
||||
loc.getStartLine() = min(int l | includeGuardRelevantLine(hf, l))
|
||||
)
|
||||
}
|
||||
|
||||
private predicate endifLocation(PreprocessorEndif endif, File f, int line) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.cpp:2:2:2:12 | goto ... | This statement makes $@ unreachable. | test.cpp:3:2:3:5 | ExprStmt | ExprStmt |
|
||||
| test.cpp:9:3:9:8 | break; | This statement makes $@ unreachable. | test.cpp:10:3:10:6 | ExprStmt | ExprStmt |
|
||||
| test.cpp:37:3:37:8 | break; | This statement makes $@ unreachable. | test.cpp:38:3:38:11 | return ... | return ... |
|
||||
@@ -0,0 +1 @@
|
||||
Critical/DeadCodeGoto.ql
|
||||
83
cpp/ql/test/query-tests/Critical/DeadCodeGoto/test.cpp
Normal file
83
cpp/ql/test/query-tests/Critical/DeadCodeGoto/test.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
int test1(int x) {
|
||||
goto label; // BAD
|
||||
x++;
|
||||
label: return x;
|
||||
}
|
||||
|
||||
int test2(int x) {
|
||||
do {
|
||||
break; // BAD
|
||||
x++;
|
||||
} while(false);
|
||||
return x;
|
||||
}
|
||||
|
||||
int test3(int x) {
|
||||
goto label; // GOOD
|
||||
label: x++;
|
||||
return x;
|
||||
}
|
||||
|
||||
int test4(int x) {
|
||||
goto label; // GOOD
|
||||
do {
|
||||
label: x++;
|
||||
} while(false);
|
||||
return x;
|
||||
}
|
||||
|
||||
int test5(int x, int y) {
|
||||
switch(y) {
|
||||
case 0:
|
||||
break; // GOOD
|
||||
case 1:
|
||||
goto label; // GOOD
|
||||
break;
|
||||
case 2:
|
||||
break; // BAD
|
||||
return x;
|
||||
case 3:
|
||||
return x;
|
||||
break; // GOOD
|
||||
case 4:
|
||||
goto label; // GOOD
|
||||
case 5:
|
||||
goto label;; // GOOD
|
||||
default:
|
||||
x++;
|
||||
}
|
||||
label:
|
||||
return x;
|
||||
}
|
||||
|
||||
void test6(int x, int cond) {
|
||||
if (cond) {
|
||||
x++;
|
||||
} else goto end; // GOOD
|
||||
x++;
|
||||
end:
|
||||
}
|
||||
|
||||
void test7(int x, int cond) {
|
||||
if (cond)
|
||||
{
|
||||
goto target;
|
||||
}
|
||||
goto somewhere_else; // GOOD
|
||||
while (x < 10) // not dead code
|
||||
{
|
||||
target:
|
||||
x++;
|
||||
}
|
||||
somewhere_else:
|
||||
switch (1)
|
||||
{
|
||||
goto end;
|
||||
while (x < 10) // not dead code
|
||||
{
|
||||
case 1:
|
||||
x++;
|
||||
} break;
|
||||
}
|
||||
end:
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
void Signed()
|
||||
{
|
||||
signed char i;
|
||||
|
||||
for (i = 0; i < 100; i--) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 100; i >= 0; i++) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 100; i >= 0; i--)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Unsigned()
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < 100; i--) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 100; i >= 0; i++) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 100; i >= 0; i--)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void InitializationOutsideLoop()
|
||||
{
|
||||
signed char i = 0;
|
||||
|
||||
for (; i < 100; i--) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (; i < 100; i++)
|
||||
{
|
||||
}
|
||||
|
||||
i = 100;
|
||||
for (; i >= 0; i++) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
i = 100;
|
||||
for (; i >= 0; i--)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void NegativeTestCase()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; (200 - i) < 100; i--)
|
||||
{
|
||||
// code ...
|
||||
}
|
||||
}
|
||||
|
||||
void NegativeTestCaseNested()
|
||||
{
|
||||
int k;
|
||||
int i;
|
||||
|
||||
for (k = 200; k < 300; k++)
|
||||
{
|
||||
for (i = 0; (k - i) < 100; i--)
|
||||
{
|
||||
// code ...
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
void Signed()
|
||||
{
|
||||
signed char i;
|
||||
|
||||
for (i = 0; i < 100; i--) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 100; i >= 0; i++) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 100; i >= 0; i--)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Unsigned()
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < 100; i--) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 100; i >= 0; i++) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = 100; i >= 0; i--)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarationInLoop()
|
||||
{
|
||||
for (signed char i = 0; i < 100; --i) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (signed char i = 0; i < 100; ++i)
|
||||
{
|
||||
}
|
||||
|
||||
for (unsigned char i = 100; i >= 0; ++i) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (unsigned char i = 100; i >= 0; --i)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void SignedWithVariables()
|
||||
{
|
||||
signed char i;
|
||||
signed char min = 0;
|
||||
signed char max = 100;
|
||||
|
||||
for (i = min; i < max; i--) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = min; i < max; i++)
|
||||
{
|
||||
}
|
||||
|
||||
for (i = max; i >= min; i++) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = max; i >= min; i--)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializationOutsideLoop()
|
||||
{
|
||||
signed char i = 0;
|
||||
|
||||
for (; i < 100; --i) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (; i < 100; ++i)
|
||||
{
|
||||
}
|
||||
|
||||
i = 100;
|
||||
for (; i >= 0; ++i) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
i = 100;
|
||||
for (; i >= 0; --i)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InvalidCondition()
|
||||
{
|
||||
signed char i;
|
||||
signed char min = 0;
|
||||
signed char max = 100;
|
||||
|
||||
for (i = max; i < min; i--) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
for (i = min; i > max; i++) //BUG
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void InvalidConditionUnsignedCornerCase()
|
||||
{
|
||||
unsigned char i;
|
||||
unsigned char min = 0;
|
||||
unsigned char max = 100;
|
||||
|
||||
for (i = 100; i < 0; i--) //BUG
|
||||
{
|
||||
}
|
||||
|
||||
// Limitation.
|
||||
// Currently odasa will not detect this for-loop condition as always true
|
||||
// The rule will still detect the mismatch iterator, but the error message may change in the future.
|
||||
for (i = 200; i >= 0; i++) //BUG
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void NegativeTestCase()
|
||||
{
|
||||
for (int i = 0; (100 - i) < 200; i--)
|
||||
{
|
||||
// code ...
|
||||
}
|
||||
}
|
||||
|
||||
void NegativeTestCaseNested()
|
||||
{
|
||||
for (int k = 200; k < 300; k++)
|
||||
{
|
||||
for (int i = 0; (k - i) < 100; i--)
|
||||
{
|
||||
// code ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// Query limitation:
|
||||
//
|
||||
// The following test cases are bugs,
|
||||
// but will not be found due to the itearion expression
|
||||
// not being a prefix or postfix increment/decrement
|
||||
//
|
||||
void FalseNegativeTestCases()
|
||||
{
|
||||
for (int i = 0; i < 10; i = i - 1) {}
|
||||
// For comparison
|
||||
for (int i = 0; i < 10; i-- ) {} // BUG
|
||||
|
||||
for (int i = 100; i > 0; i += 2) {}
|
||||
// For comparison
|
||||
for (int i = 100; i > 0; i ++ ) {} // BUG
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
| inconsistentLoopDirection.c:5:5:7:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (100). |
|
||||
| inconsistentLoopDirection.c:13:5:15:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.c:27:5:29:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (100). |
|
||||
| inconsistentLoopDirection.c:35:5:37:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.c:48:5:50:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (100). |
|
||||
| inconsistentLoopDirection.c:58:5:60:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.cpp:5:5:7:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (100). |
|
||||
| inconsistentLoopDirection.cpp:13:5:15:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.cpp:27:5:29:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (100). |
|
||||
| inconsistentLoopDirection.cpp:35:5:37:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.cpp:46:5:48:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (100). |
|
||||
| inconsistentLoopDirection.cpp:54:5:56:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.cpp:69:5:71:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (min), but the terminal condition is higher (max). |
|
||||
| inconsistentLoopDirection.cpp:77:5:79:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (max), but the terminal condition is lower (min). |
|
||||
| inconsistentLoopDirection.cpp:91:5:93:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (100). |
|
||||
| inconsistentLoopDirection.cpp:101:5:103:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.cpp:118:5:120:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (max), but the terminal condition is always false. |
|
||||
| inconsistentLoopDirection.cpp:122:5:124:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (min), but the terminal condition is always false. |
|
||||
| inconsistentLoopDirection.cpp:133:5:135:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (100), but the terminal condition is always false. |
|
||||
| inconsistentLoopDirection.cpp:140:5:142:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (200), but the terminal condition is lower (0). |
|
||||
| inconsistentLoopDirection.cpp:175:5:175:36 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (10). |
|
||||
| inconsistentLoopDirection.cpp:179:5:179:38 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
|
||||
@@ -0,0 +1 @@
|
||||
Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
|
||||
@@ -0,0 +1,430 @@
|
||||
// semmle-extractor-options: --microsoft
|
||||
#define NULL 0
|
||||
#define FALSE 0
|
||||
#define LOGON_WITH_PROFILE 0x00000001
|
||||
|
||||
int
|
||||
CreateProcessA(
|
||||
const char* lpApplicationName,
|
||||
char* lpCommandLine,
|
||||
void* lpProcessAttributes,
|
||||
void* lpThreadAttributes,
|
||||
int bInheritHandles,
|
||||
unsigned long dwCreationFlags,
|
||||
void* lpEnvironment,
|
||||
const char* lpCurrentDirectory,
|
||||
void* lpStartupInfo,
|
||||
void* lpProcessInformation
|
||||
);
|
||||
|
||||
int
|
||||
CreateProcessW(
|
||||
const wchar_t* lpApplicationName,
|
||||
wchar_t* lpCommandLine,
|
||||
void* lpProcessAttributes,
|
||||
void* lpThreadAttributes,
|
||||
int bInheritHandles,
|
||||
unsigned long dwCreationFlags,
|
||||
void* lpEnvironment,
|
||||
const wchar_t* lpCurrentDirectory,
|
||||
void* lpStartupInfo,
|
||||
void* lpProcessInformation
|
||||
);
|
||||
|
||||
#define CreateProcess CreateProcessW
|
||||
|
||||
int
|
||||
CreateProcessWithTokenW(
|
||||
void* hToken,
|
||||
unsigned long dwLogonFlags,
|
||||
const wchar_t* lpApplicationName,
|
||||
wchar_t* lpCommandLine,
|
||||
unsigned long dwCreationFlags,
|
||||
void* lpEnvironment,
|
||||
const wchar_t* lpCurrentDirectory,
|
||||
void* lpStartupInfo,
|
||||
void* lpProcessInformation
|
||||
);
|
||||
|
||||
int
|
||||
CreateProcessWithLogonW(
|
||||
const wchar_t* lpUsername,
|
||||
const wchar_t* lpDomain,
|
||||
const wchar_t* lpPassword,
|
||||
unsigned long dwLogonFlags,
|
||||
const wchar_t* lpApplicationName,
|
||||
wchar_t* lpCommandLine,
|
||||
unsigned long dwCreationFlags,
|
||||
void* lpEnvironment,
|
||||
const wchar_t* lpCurrentDirectory,
|
||||
void* lpStartupInfo,
|
||||
void* lpProcessInformation
|
||||
);
|
||||
|
||||
int
|
||||
CreateProcessAsUserA(
|
||||
void* hToken,
|
||||
const char* lpApplicationName,
|
||||
char* lpCommandLine,
|
||||
void* lpProcessAttributes,
|
||||
void* lpThreadAttributes,
|
||||
int bInheritHandles,
|
||||
unsigned long dwCreationFlags,
|
||||
void* lpEnvironment,
|
||||
const char* lpCurrentDirectory,
|
||||
void* lpStartupInfo,
|
||||
void* lpProcessInformation
|
||||
);
|
||||
|
||||
int
|
||||
CreateProcessAsUserW(
|
||||
void* hToken,
|
||||
const wchar_t* lpApplicationName,
|
||||
wchar_t* lpCommandLine,
|
||||
void* lpProcessAttributes,
|
||||
void* lpThreadAttributes,
|
||||
int bInheritHandles,
|
||||
unsigned long dwCreationFlags,
|
||||
void* lpEnvironment,
|
||||
const wchar_t* lpCurrentDirectory,
|
||||
void* lpStartupInfo,
|
||||
void* lpProcessInformation
|
||||
);
|
||||
|
||||
#define CreateProcessAsUser CreateProcessAsUserW
|
||||
|
||||
void positiveTestCases()
|
||||
{
|
||||
const wchar_t* lpCommandLine = (const wchar_t*)L"C:\\Program Files\\MyApp";
|
||||
void* h = 0;
|
||||
wchar_t* lpApplicationName = NULL;
|
||||
|
||||
// CreatePorcessA
|
||||
CreateProcessA( //BUG
|
||||
NULL,
|
||||
(char*)"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcessW
|
||||
CreateProcessW( //BUG
|
||||
NULL,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcess
|
||||
CreateProcess( //BUG
|
||||
NULL,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// lpCommandLine as hardcoded variable
|
||||
CreateProcess( //BUG
|
||||
NULL,
|
||||
(wchar_t*)lpCommandLine,
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessWithTokenW
|
||||
CreateProcessWithTokenW( //BUG
|
||||
h,
|
||||
LOGON_WITH_PROFILE,
|
||||
NULL,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessWithLogonW
|
||||
CreateProcessWithLogonW( //BUG
|
||||
(const wchar_t*)L"UserName",
|
||||
(const wchar_t*)L"CONTOSO",
|
||||
(const wchar_t*)L"<fake_password!>",
|
||||
LOGON_WITH_PROFILE,
|
||||
NULL,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUserA
|
||||
CreateProcessAsUserA( //BUG
|
||||
h,
|
||||
NULL,
|
||||
(char*)"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUserW
|
||||
CreateProcessAsUserW( //BUG
|
||||
h,
|
||||
NULL,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUser
|
||||
CreateProcessAsUser( //BUG
|
||||
h,
|
||||
NULL,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcess with a hardcoded variable for application Name (NULL)
|
||||
// Variation: tab instead of space
|
||||
CreateProcess( //BUG
|
||||
lpApplicationName,
|
||||
(wchar_t*)L"C:\\Program\tFiles\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void PositiveTestCasesWithCmdLineParameter(wchar_t* lpCommandLine)
|
||||
{
|
||||
// lpCommandLine as variable
|
||||
CreateProcess( //BUG - Depends on the caller
|
||||
NULL,
|
||||
lpCommandLine,
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void PositiveTestCasesWithCmdLineParameter_caller()
|
||||
{
|
||||
PositiveTestCasesWithCmdLineParameter((wchar_t*)L"C:\\Program Files\\MyApp");
|
||||
}
|
||||
|
||||
// NOTE: This function will not be flagged as having a bug by this rule.
|
||||
// but as it is, the function can still be misused
|
||||
void FalseNegativeTestCasesWithCmdLineParameter(wchar_t* lpCommandLine)
|
||||
{
|
||||
// lpCommandLine as variable
|
||||
CreateProcess( //Depends on the caller, this time the caller will quote
|
||||
NULL,
|
||||
lpCommandLine,
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void FalseNegativeTestCasesWithCmdLineParameter_caller()
|
||||
{
|
||||
// No bug - escaped command line
|
||||
// But compare with "PositiveTestCasesWithCmdLineParameter"
|
||||
FalseNegativeTestCasesWithCmdLineParameter((wchar_t*)L"\"C:\\Program Files\\MyApp\"");
|
||||
}
|
||||
|
||||
void PositiveTestCasesWithAppNameParameter(wchar_t* lpApplicationName)
|
||||
{
|
||||
void* h = 0;
|
||||
|
||||
CreateProcessWithTokenW( //BUG - Depends on the caller. In this case the caller sends NULL
|
||||
h,
|
||||
LOGON_WITH_PROFILE,
|
||||
lpApplicationName,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void PositiveTestCasesWithAppNameParameter_caller()
|
||||
{
|
||||
PositiveTestCasesWithAppNameParameter(NULL);
|
||||
}
|
||||
|
||||
// NOTE: This function will not be flagged as having a bug by this rule.
|
||||
// but as it is, the function can still be misused
|
||||
void FalseNegativeTestCasesWithAppNameParameter(wchar_t* lpApplicationName)
|
||||
{
|
||||
void* h = 0;
|
||||
|
||||
CreateProcessWithTokenW( // Depends on the caller. In this case the caller sends an ApplicatioName
|
||||
h,
|
||||
LOGON_WITH_PROFILE,
|
||||
lpApplicationName,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void FalseNegativeTestCasesWithAppNameParameter_caller()
|
||||
{
|
||||
// No bug - escaped command line
|
||||
// But compare with "PositiveTestCasesWithAppNameParameter"
|
||||
FalseNegativeTestCasesWithAppNameParameter((wchar_t*)L"MyApp.exe");
|
||||
}
|
||||
|
||||
int MayReturnFalse()
|
||||
{
|
||||
// return ((rand() % 2) == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestCaseProbablyBug()
|
||||
{
|
||||
const wchar_t* lpApplicationName = NULL;
|
||||
|
||||
if (!MayReturnFalse())
|
||||
{
|
||||
lpApplicationName = (const wchar_t*)L"app.exe";
|
||||
}
|
||||
|
||||
CreateProcessWithLogonW( // BUG (Probably - depends on a condition that may be false)
|
||||
(const wchar_t*)L"UserName",
|
||||
(const wchar_t*)L"CONTOSO",
|
||||
(const wchar_t*)L"<fake_password!>",
|
||||
LOGON_WITH_PROFILE,
|
||||
(wchar_t*)lpApplicationName,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (lpApplicationName)
|
||||
{
|
||||
delete[] lpApplicationName;
|
||||
}
|
||||
}
|
||||
|
||||
void negativeTestCases_quotedCommandLine()
|
||||
{
|
||||
const wchar_t* lpCommandLine = (const wchar_t*)L"\"C:\\Program Files\\MyApp\" with additional params";
|
||||
void* h = 0;
|
||||
wchar_t* lpApplicationName = NULL;
|
||||
|
||||
// CreatePorcessA
|
||||
CreateProcessA(
|
||||
NULL,
|
||||
(char*)"\"C:\\Program Files\\MyApp\"",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcessW
|
||||
CreateProcessW(
|
||||
NULL,
|
||||
(wchar_t*)L"\"C:\\Program Files\\MyApp\"",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcess
|
||||
CreateProcess(
|
||||
NULL,
|
||||
(wchar_t*)L"\"C:\\Program Files\\MyApp\"",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// lpCommandLine as hardcoded variable
|
||||
CreateProcess(
|
||||
NULL,
|
||||
(wchar_t*)lpCommandLine,
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessWithTokenW
|
||||
CreateProcessWithTokenW(
|
||||
h,
|
||||
LOGON_WITH_PROFILE,
|
||||
NULL,
|
||||
(wchar_t*)L"\"C:\\Program Files\\MyApp\"",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessWithLogonW
|
||||
CreateProcessWithLogonW(
|
||||
(const wchar_t*)L"UserName",
|
||||
(const wchar_t*)L"CONTOSO",
|
||||
(const wchar_t*)L"<fake_password!>",
|
||||
LOGON_WITH_PROFILE,
|
||||
NULL,
|
||||
(wchar_t*)L"\"C:\\Program Files\\MyApp\"",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUserA
|
||||
CreateProcessAsUserA(
|
||||
h,
|
||||
NULL,
|
||||
(char*)"\"C:\\Program Files\\MyApp\"",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUserW
|
||||
CreateProcessAsUserW(
|
||||
h,
|
||||
NULL,
|
||||
(wchar_t*)L"\"C:\\Program Files\\MyApp\"",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUser
|
||||
CreateProcessAsUser(
|
||||
h,
|
||||
NULL,
|
||||
(wchar_t*)L"\"C:\\Program Files\\MyApp\"",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcess with a hardcoded variable for application Name (NULL)
|
||||
CreateProcess(
|
||||
lpApplicationName,
|
||||
(wchar_t*)L"\"C:\\Program Files\\MyApp\"",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// Null AppName, but lpComamndLine has no spaces/tabs
|
||||
CreateProcessA(
|
||||
NULL,
|
||||
(char*)"C:\\MyFolder\\MyApp.exe",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
void negativeTestCases_AppNameSet()
|
||||
{
|
||||
const wchar_t* lpCommandLine = (const wchar_t*)L"C:\\Program Files\\MyApp";
|
||||
void* h = 0;
|
||||
const wchar_t* lpApplicationName = (const wchar_t*)L"MyApp.exe";
|
||||
|
||||
// CreatePorcessA
|
||||
CreateProcessA(
|
||||
(char*)"MyApp.exe",
|
||||
(char*)"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcessW
|
||||
CreateProcessW(
|
||||
(wchar_t*)L"MyApp.exe",
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcess
|
||||
CreateProcess(
|
||||
(wchar_t*)L"MyApp.exe",
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// lpCommandLine as hardcoded variable
|
||||
CreateProcess(
|
||||
(wchar_t*)L"MyApp.exe",
|
||||
(wchar_t*)lpCommandLine,
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessWithTokenW
|
||||
CreateProcessWithTokenW(
|
||||
h,
|
||||
LOGON_WITH_PROFILE,
|
||||
(wchar_t*)L"MyApp.exe",
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessWithLogonW
|
||||
CreateProcessWithLogonW(
|
||||
(const wchar_t*)L"UserName",
|
||||
(const wchar_t*)L"CONTOSO",
|
||||
(const wchar_t*)L"<fake_password!>",
|
||||
LOGON_WITH_PROFILE,
|
||||
(wchar_t*)L"MyApp.exe",
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUserA
|
||||
CreateProcessAsUserA(
|
||||
h,
|
||||
(char*)"MyApp.exe",
|
||||
(char*)"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUserW
|
||||
CreateProcessAsUserW(
|
||||
h,
|
||||
(wchar_t*)L"MyApp.exe",
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreateProcessAsUser
|
||||
CreateProcessAsUser(
|
||||
h,
|
||||
(wchar_t*)L"MyApp.exe",
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
// CreatePorcess with a hardcoded variable for application Name (NULL)
|
||||
CreateProcess(
|
||||
(wchar_t*)lpApplicationName,
|
||||
(wchar_t*)L"C:\\Program Files\\MyApp",
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
| UnsafeCreateProcessCall.cpp:103:5:103:18 | call to CreateProcessA | call to CreateProcessA with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:109:5:109:18 | call to CreateProcessW | call to CreateProcessW with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:115:5:115:17 | call to CreateProcessW | call to CreateProcessW with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:121:5:121:17 | call to CreateProcessW | call to CreateProcessW with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (lpCommandLine) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:127:5:127:27 | call to CreateProcessWithTokenW | call to CreateProcessWithTokenW with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:135:5:135:27 | call to CreateProcessWithLogonW | call to CreateProcessWithLogonW with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:145:5:145:24 | call to CreateProcessAsUserA | call to CreateProcessAsUserA with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:152:5:152:24 | call to CreateProcessAsUserW | call to CreateProcessAsUserW with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:159:5:159:23 | call to CreateProcessAsUserW | call to CreateProcessAsUserW with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:167:5:167:17 | call to CreateProcessW | call to CreateProcessW with lpApplicationName == NULL (lpApplicationName) and with an unquoted lpCommandLine (C:\\Program\tFiles\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:176:5:176:17 | call to CreateProcessW | call to CreateProcessW with lpApplicationName == NULL (0) and with an unquoted lpCommandLine (lpCommandLine) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:209:5:209:27 | call to CreateProcessWithTokenW | call to CreateProcessWithTokenW with lpApplicationName == NULL (lpApplicationName) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
| UnsafeCreateProcessCall.cpp:258:5:258:27 | call to CreateProcessWithLogonW | call to CreateProcessWithLogonW with lpApplicationName == NULL (lpApplicationName) and with an unquoted lpCommandLine (C:\\Program Files\\MyApp) introduces a security vulnerability if the path contains spaces. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
|
||||
@@ -17,3 +17,5 @@
|
||||
| Variants.cpp:65:3:65:17 | ... = ... | Resource a is acquired by class MyClass6 but not released anywhere in this class. |
|
||||
| Variants.cpp:66:3:66:36 | ... = ... | Resource b is acquired by class MyClass6 but not released anywhere in this class. |
|
||||
| Variants.cpp:67:3:67:41 | ... = ... | Resource c is acquired by class MyClass6 but not released anywhere in this class. |
|
||||
| Wrapped.cpp:46:3:46:22 | ... = ... | Resource ptr2 is acquired by class Wrapped2 but not released anywhere in this class. |
|
||||
| Wrapped.cpp:59:3:59:22 | ... = ... | Resource ptr4 is acquired by class Wrapped2 but not released anywhere in this class. |
|
||||
|
||||
@@ -66,3 +66,25 @@ public:
|
||||
n = new MyNumber(200); // GOOD: deleted in base class
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class TemplateWithDestructor
|
||||
{
|
||||
public:
|
||||
TemplateWithDestructor(int len) {
|
||||
ptr = new char[len]; // GOOD
|
||||
}
|
||||
|
||||
~TemplateWithDestructor()
|
||||
{
|
||||
delete [] ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
char *ptr;
|
||||
};
|
||||
|
||||
void test() {
|
||||
TemplateWithDestructor<int *> *t_ptr = new TemplateWithDestructor<int *>(10);
|
||||
//delete t_ptr; --- destructor never used
|
||||
}
|
||||
|
||||
@@ -37,3 +37,34 @@ public:
|
||||
private:
|
||||
char *ptr1, *ptr2, *ptr3;
|
||||
};
|
||||
|
||||
class Wrapped2
|
||||
{
|
||||
public:
|
||||
Wrapped2(int len) {
|
||||
ptr1 = new char[len]; // GOOD
|
||||
ptr2 = new char[len]; // BAD: not released in destructor
|
||||
|
||||
Init(len);
|
||||
}
|
||||
|
||||
~Wrapped2()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Init(int len)
|
||||
{
|
||||
ptr3 = new char[len]; // GOOD
|
||||
ptr4 = new char[len]; // BAD: not released in destructor
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
delete [] ptr1;
|
||||
delete [] ptr3;
|
||||
}
|
||||
|
||||
private:
|
||||
char *ptr1, *ptr2, *ptr3, *ptr4;
|
||||
};
|
||||
|
||||
@@ -3,3 +3,5 @@
|
||||
| test.c:39:9:39:14 | ExprStmt | Function f6 should return a value of type int but does not return a value here |
|
||||
| test.cpp:16:1:18:1 | { ... } | Function g2 should return a value of type MyValue but does not return a value here |
|
||||
| test.cpp:48:2:48:26 | if (...) ... | Function g7 should return a value of type MyValue but does not return a value here |
|
||||
| test.cpp:74:1:76:1 | { ... } | Function g10 should return a value of type second but does not return a value here |
|
||||
| test.cpp:86:1:88:1 | { ... } | Function g12 should return a value of type second but does not return a value here |
|
||||
|
||||
@@ -50,3 +50,45 @@ MyValue g7(bool c)
|
||||
DONOTHING
|
||||
// BAD [the alert here is unfortunately placed]
|
||||
}
|
||||
|
||||
typedef void MYVOID;
|
||||
MYVOID g8()
|
||||
{
|
||||
// GOOD
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
class TypePair
|
||||
{
|
||||
public:
|
||||
typedef T first;
|
||||
typedef U second;
|
||||
};
|
||||
|
||||
TypePair<void, int>::first g9()
|
||||
{
|
||||
// GOOD (the return type amounts to void)
|
||||
}
|
||||
|
||||
TypePair<void, int>::second g10()
|
||||
{
|
||||
// BAD (the return type amounts to int)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename TypePair<void, T>::first g11()
|
||||
{
|
||||
// GOOD (the return type amounts to void)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename TypePair<void, T>::second g12()
|
||||
{
|
||||
// BAD (the return type amounts to T / int)
|
||||
}
|
||||
|
||||
void instantiate()
|
||||
{
|
||||
g11<int>();
|
||||
g12<int>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user