Adds another redundant null check rule

This commit is contained in:
Philippe Antoine
2020-07-06 21:45:54 +02:00
parent fe0c5a9ea6
commit 8f7ff1a537
3 changed files with 99 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
void test(char *arg1, int *arg2) {
if (arg1[0] == 'A') {
if (arg2 != NULL) {
*arg2 = 42;
}
}
if (arg1[1] == 'B')
{
*arg2 = 54;
}
}

View File

@@ -0,0 +1,29 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds comparisons of a function parameter to null that occur when in another path the parameter is dereferenced without a guard check. It's
likely either the check is not required and can be removed, or it should be added before the dereference
so that a null pointer dereference does not occur.</p>
</overview>
<recommendation>
<p>A check should be added to before the dereference, in a way that prevents a null pointer value from
being dereferenced. If it's clear that the pointer cannot be null, consider removing the check instead.</p>
</recommendation>
<example>
<sample src="RedundantNullCheckParam.cpp" />
</example>
<references>
<li>
<a href="https://www.owasp.org/index.php/Null_Dereference">
Null Dereference
</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,59 @@
/**
* @name Redundant null check or missing null check
* @description Checking a parameter for nullness in one path,
* and not in another is likely to be a sign that either
* the check can be removed, or added in the other case.
* @kind problem
* @id cpp/redundant-null-check-param
* @problem.severity recommendation
* @tags reliability
* security
* external/cwe/cwe-476
*/
import cpp
predicate blockDominates(Block check, Block access) {
check.getLocation().getStartLine() <= access.getLocation().getStartLine() and
check.getLocation().getEndLine() >= access.getLocation().getEndLine()
}
predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked) {
checked =
any(VariableAccess va |
va.getTarget() = unchecked.getTarget()
) and
//Simple test if the first access in this code path is dereferenced
not dereferenced(checked) and
blockDominates(checked.getEnclosingBlock(), unchecked.getEnclosingBlock())
}
pragma[noinline]
predicate candidateResultUnchecked(VariableAccess unchecked) {
not isCheckedInstruction(unchecked, _)
}
pragma[noinline]
predicate candidateResultChecked(VariableAccess check, EqualityOperation eqop, Parameter param) {
//not dereferenced to check against pointer, not its pointed value
not dereferenced(check) and
//assert macros are not taken into account
not check.isInMacroExpansion() and
// is part of a comarison against some constant NULL
eqop.getAnOperand() = check and eqop.getAnOperand() instanceof NullValue and
// this function parameter is not overwritten
count(param.getAnAssignment()) = 0
}
from VariableAccess unchecked, VariableAccess check, EqualityOperation eqop, Parameter param
where
// a dereference
dereferenced(unchecked) and
// for a function parameter
unchecked.getTarget() = param and
check.getTarget() = param and
// which is once checked
candidateResultChecked(check, eqop, param) and
// and which has not been checked before in this code path
candidateResultUnchecked(unchecked)
select check, "This null check is redundant because the value is $@ ", unchecked, "dereferenced here"