[CPP-340] Rename 'UnspecifiedFunctions' to 'Unspecified Functions'

Make MistypedFunctionArguments.ql more restrictive (allowing
          type matching only in the presence of no-op conversions).
This commit is contained in:
Ziemowit Laski
2019-04-01 18:39:46 -07:00
parent 8a653b9adc
commit 3ec988c39b
19 changed files with 389 additions and 2 deletions

View File

@@ -0,0 +1,7 @@
void three_arguments(int x, int y, int z);
void calls() {
int three = 3;
three_arguments(1, 2, three); // GOOD
three_arguments(1, 2, &three); // BAD
}

View File

@@ -0,0 +1,29 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A function is called with at least one argument whose type is incompatible with the type of
the corresponding parameter of the function being called. This may cause the called function
to behave unpredictably.</p>
<p>This may indicate that an incorrect function is being called, or that the
signature (parameter list and parameter types) of the called function
is not known to the author.</p>
</overview>
<recommendation>
<p>Call the function with the proper argument types. In some cases, it may
suffice to provide an explicit cast of an argument to the desired (parameter) type.</p>
</recommendation>
<example><sample src="MistypedFunctionArguments.c" />
</example>
<references>
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL20-C.+Explicitly+specify+void+when+a+function+accepts+no+arguments"> DCL20-C. Explicitly specify void when a function accepts no arguments </a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,109 @@
/**
* @name Call to a function with one or more incompatible arguments
* @description A call to a function with at least one argument whose type does
* not match the type of the corresponding function parameter. This may indicate
* that the author is not familiar with the function being called. Passing mistyped
* arguments on a stack may lead to unpredictable function behavior.
* @kind problem
* @problem.severity warning
* @precision very-high
* @id cpp/mistyped-function-arguments
* @tags correctness
* maintainability
*/
import cpp
pragma[inline]
int sizeofInt() { result = any(IntType pt).getSize() }
pragma[inline]
predicate pointerArgTypeMayBeUsed(Type arg, Type parm) {
arg = parm
or
// arithmetic types
arg instanceof ArithmeticType and
parm instanceof ArithmeticType and
arg.getSize() = parm.getSize() and
(
(
arg instanceof IntegralType and
parm instanceof IntegralType
)
or
(
arg instanceof FloatingPointType and
parm instanceof FloatingPointType
)
)
or
// pointers to void are ok
arg instanceof VoidType
or
parm instanceof VoidType
}
pragma[inline]
predicate argTypeMayBeUsed(Type arg, Type parm) {
arg = parm
or
// float will be promoted to double, and so it should correspond
// to the prototype
arg instanceof FloatType and parm instanceof DoubleType
or
// integral types are promoted "up to" (unsigned) int, but not long long.
arg instanceof IntegralType and
parm instanceof IntegralType and
arg.getSize() <= sizeofInt() and
parm.getSize() <= sizeofInt()
or
/*
* // we allow interoperability between long long and pointer
* arg.getSize() = parm.getSize() and
* (
* (arg instanceof IntegralType and parm instanceof PointerType)
* or
* (arg instanceof PointerType and parm instanceof IntegralType)
* )
* or
*/
// pointers to compatible types
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
parm.(PointerType).getBaseType().getUnspecifiedType())
or
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
parm.(ArrayType).getBaseType().getUnspecifiedType())
or
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
parm.(PointerType).getBaseType().getUnspecifiedType())
or
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
parm.(ArrayType).getBaseType().getUnspecifiedType())
}
// This predicate doesn't necessarily have to exist, but if it does exist
// then it must be inline to make sure we don't enumerate all pairs of
// compatible types.
// Its body could also just be hand-inlined where it's used.
pragma[inline]
predicate argMayBeUsed(Expr arg, Parameter parm) {
argTypeMayBeUsed(arg.getFullyConverted().getType().getUnspecifiedType(),
parm.getType().getUnspecifiedType())
}
from FunctionCall fc, Function f, Parameter p
where
f = fc.getTarget() and
p = f.getAParameter() and
not f.isVarargs() and
p.getIndex() < fc.getNumberOfArguments() and
// There must be a zero-parameter declaration
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
fde.getNumberOfParameters() = 0
) and
// Parameter p and its corresponding call argument must have mismatched types
not argMayBeUsed(fc.getArgument(p.getIndex()), p)
select fc, "Calling $@: argument $@ of type $@ is incompatible with parameter $@.", f, f.toString(),
fc.getArgument(p.getIndex()) as arg, arg.toString(), arg.getFullyConverted().getType() as type,
type.toString(), p, p.getTypedName()

View File

@@ -0,0 +1,9 @@
void one_argument();
void calls() {
one_argument(1); // GOOD: `one_argument` will accept and use the argument
one_argument(); // BAD: `one_argument` will receive an undefined value
}
void one_argument(int x);

View File

@@ -0,0 +1,35 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A function is called with fewer arguments than there are parameters of the function.</p>
<p>This may indicate that an incorrect function is being called, or that the signature
(parameter list) of the called function is not known to the author.</p>
<p>In C, function calls generally need to provide the same number of arguments as there are
arguments to the function. (Variadic functions can accept additional arguments.) Providing
fewer arguments than there are parameters is extremely dangerous, as the called function
will nevertheless try to obtain the missing arguments' values, either from the stack
or from machine registers. As a result, the function may behave unpredictably.</p>
<p>If the called function <i>modifies</i> a parameter corresponding to a missing argument, it
may alter the state of the program upon its return. An attacker could use this to,
for example, alter the control flow of the program to access forbidden resources.</p>
</overview>
<recommendation>
<p>Call the function with the correct number of arguments.</p>
</recommendation>
<example><sample src="TooFewArguments.c" />
</example>
<references>
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL20-C.+Explicitly+specify+void+when+a+function+accepts+no+arguments"> DCL20-C. Explicitly specify void when a function accepts no arguments </a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,31 @@
/**
* @name Call to function with fewer arguments than declared parameters
* @description A function call passed fewer arguments than the number of
* declared parameters of the function. This may indicate
* that the code does not follow the author's intent. It is also a vulnerability,
* since the function is like to operate on undefined data.
* @kind problem
* @problem.severity error
* @precision very-high
* @id cpp/too-few-params
* @tags correctness
* maintainability
* security
*/
import cpp
from FunctionCall fc, Function f
where
f = fc.getTarget() and
not f.isVarargs() and
// There must be a zero-parameter declaration (explicit or implicit)
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
fde.getNumberOfParameters() = 0
) and
// There is an explicit declaration of the function whose parameter count is larger
// than the number of call arguments
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
not fde.isImplicit() and fde.getNumberOfParameters() > fc.getNumberOfArguments()
)
select fc, "This call has fewer arguments than required by $@.", f, f.toString()

View File

@@ -0,0 +1,10 @@
void one_argument();
void calls() {
one_argument(1); // GOOD: `one_argument` will accept and use the argument
one_argument(1, 2); // BAD: `one_argument` will use the first argument but ignore the second
}
void one_argument(int x);

View File

@@ -0,0 +1,30 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A function is called with more arguments than there are parameters of the function.</p>
<p>This may indicate that an incorrect function is being called, or that the signature
(parameter list) of the called function is not known to the author.</p>
<p>In C, function calls generally need to provide the same number of arguments as there are
arguments to the function. (Variadic functions can accept additional arguments.) Providing
more arguments than there are parameters incurs an unneeded computational overhead, both
in terms of time and of additional stack space.</p>
</overview>
<recommendation>
<p>Call the function with the correct number of arguments.</p>
</recommendation>
<example><sample src="TooManyArguments.c" />
</example>
<references>
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL20-C.+Explicitly+specify+void+when+a+function+accepts+no+arguments"> DCL20-C. Explicitly specify void when a function accepts no arguments </a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,29 @@
/**
* @name Call to function with extraneous parameters
* @description A function call to a function passed more arguments than there are
* declared parameters of the function. This may indicate
* that the code does not follow the author's intent.
* @kind problem
* @problem.severity warning
* @precision very-high
* @id cpp/futile-params
* @tags correctness
* maintainability
*/
import cpp
from FunctionCall fc, Function f
where
f = fc.getTarget() and
not f.isVarargs() and
// There must be a zero-parameter declaration (explicit or implicit)
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
fde.getNumberOfParameters() = 0
) and
// There must not exist a declaration with the number of parameters
// at least as large as the number of call arguments
not exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
fde.getNumberOfParameters() >= fc.getNumberOfArguments()
)
select fc, "This call has more arguments than required by $@.", f, f.toString()