[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

@@ -6,6 +6,7 @@
+ semmlecode-cpp-queries/Likely Bugs/Arithmetic/IntMultToLong.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
# Consistent Use
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
@@ -15,7 +16,8 @@
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/CompareWhereAssignMeant.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ExprHasNoEffect.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/FutileParams.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooFewArguments.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooManyArguments.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Arithmetic/FloatComparison.ql: /Correctness/Common Errors

View File

@@ -7,6 +7,7 @@
+ semmlecode-cpp-queries/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
# Consistent Use
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
@@ -16,7 +17,8 @@
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/CompareWhereAssignMeant.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ExprHasNoEffect.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/FutileParams.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooFewArguments.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooManyArguments.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql: /Correctness/Common Errors
+ semmlecode-cpp-queries/Likely Bugs/Arithmetic/FloatComparison.ql: /Correctness/Common Errors

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()

View File

@@ -0,0 +1,11 @@
| test.c:25:3:25:19 | call to not_yet_declared2 | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:24:3:24:3 | not_yet_declared2 | not_yet_declared2 | test.c:25:21:25:22 | ca | ca | file://:0:0:0:0 | int * | int * | test.c:45:24:45:26 | p#0 | int p#0 |
| test.c:25:3:25:19 | call to not_yet_declared2 | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:45:6:45:22 | not_yet_declared2 | not_yet_declared2 | test.c:25:21:25:22 | ca | ca | file://:0:0:0:0 | int * | int * | test.c:45:24:45:26 | p#0 | int p#0 |
| test.c:32:3:32:29 | call to declared_empty_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:46:6:46:32 | declared_empty_defined_with | declared_empty_defined_with | test.c:32:31:32:32 | & ... | & ... | file://:0:0:0:0 | int * | int * | test.c:46:38:46:38 | x | int x |
| test.c:36:3:36:27 | call to not_declared_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:49:6:49:30 | not_declared_defined_with | not_declared_defined_with | test.c:36:29:36:31 | 4 | 4 | file://:0:0:0:0 | long long | long long | test.c:49:36:49:36 | x | int x |
| test.c:36:3:36:27 | call to not_declared_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:49:6:49:30 | not_declared_defined_with | not_declared_defined_with | test.c:36:37:36:42 | 2500000000.0 | 2500000000.0 | file://:0:0:0:0 | double | double | test.c:49:50:49:50 | z | int z |
| test.c:39:3:39:24 | call to declared_with_pointers | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:5:6:5:27 | declared_with_pointers | declared_with_pointers | test.c:39:26:39:31 | 3500000000000000.0 | 3500000000000000.0 | file://:0:0:0:0 | double | double | test.c:61:34:61:34 | x | int * x |
| test.c:39:3:39:24 | call to declared_with_pointers | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:5:6:5:27 | declared_with_pointers | declared_with_pointers | test.c:39:34:39:34 | 0 | 0 | file://:0:0:0:0 | int | int | test.c:61:43:61:43 | y | void * y |
| test.c:39:3:39:24 | call to declared_with_pointers | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:61:6:61:27 | declared_with_pointers | declared_with_pointers | test.c:39:26:39:31 | 3500000000000000.0 | 3500000000000000.0 | file://:0:0:0:0 | double | double | test.c:61:34:61:34 | x | int * x |
| test.c:39:3:39:24 | call to declared_with_pointers | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:61:6:61:27 | declared_with_pointers | declared_with_pointers | test.c:39:34:39:34 | 0 | 0 | file://:0:0:0:0 | int | int | test.c:61:43:61:43 | y | void * y |
| test.c:41:3:41:21 | call to declared_with_array | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:6:6:6:24 | declared_with_array | declared_with_array | test.c:41:23:41:24 | & ... | & ... | file://:0:0:0:0 | int * | int * | test.c:62:31:62:31 | a | char[6] a |
| test.c:41:3:41:21 | call to declared_with_array | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:62:6:62:24 | declared_with_array | declared_with_array | test.c:41:23:41:24 | & ... | & ... | file://:0:0:0:0 | int * | int * | test.c:62:31:62:31 | a | char[6] a |

View File

@@ -0,0 +1 @@
Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql

View File

@@ -0,0 +1,4 @@
| test.c:26:3:26:19 | call to not_yet_declared2 | This call has fewer arguments than required by $@. | test.c:24:3:24:3 | not_yet_declared2 | not_yet_declared2 |
| test.c:26:3:26:19 | call to not_yet_declared2 | This call has fewer arguments than required by $@. | test.c:45:6:45:22 | not_yet_declared2 | not_yet_declared2 |
| test.c:28:3:28:29 | call to declared_empty_defined_with | This call has fewer arguments than required by $@. | test.c:46:6:46:32 | declared_empty_defined_with | declared_empty_defined_with |
| test.c:56:10:56:20 | call to dereference | This call has fewer arguments than required by $@. | test.c:59:5:59:15 | dereference | dereference |

View File

@@ -0,0 +1 @@
Likely Bugs/Underspecified Functions/TooFewArguments.ql

View File

@@ -0,0 +1,5 @@
| test.c:16:3:16:16 | call to declared_empty | This call has more arguments than required by $@. | test.c:1:6:1:19 | declared_empty | declared_empty |
| test.c:21:3:21:12 | call to undeclared | This call has more arguments than required by $@. | test.c:20:3:20:3 | undeclared | undeclared |
| test.c:23:3:23:19 | call to not_yet_declared1 | This call has more arguments than required by $@. | test.c:23:3:23:3 | not_yet_declared1 | not_yet_declared1 |
| test.c:23:3:23:19 | call to not_yet_declared1 | This call has more arguments than required by $@. | test.c:44:6:44:22 | not_yet_declared1 | not_yet_declared1 |
| test.c:33:3:33:29 | call to declared_empty_defined_with | This call has more arguments than required by $@. | test.c:46:6:46:32 | declared_empty_defined_with | declared_empty_defined_with |

View File

@@ -0,0 +1 @@
Likely Bugs/Underspecified Functions/TooManyArguments.ql

View File

@@ -0,0 +1,63 @@
void declared_empty();
void declared_void(void);
void declared_with(int);
void declared_empty_defined_with();
void declared_with_pointers();
void declared_with_array();
struct _s { int a, b; } s;
int ca[4] = { 1, 2, 3, 4 };
void *pv;
void test() {
declared_empty(); // GOOD
declared_empty(1); // BAD
declared_void(); // GOOD
declared_with(1); // GOOD
undeclared(); // GOOD
undeclared(1); // BAD
not_yet_declared1(1); // BAD
not_yet_declared2(1); // GOOD
not_yet_declared2(ca); // BAD
not_yet_declared2(); // BAD
declared_empty_defined_with(); // BAD
declared_empty_defined_with(1); // GOOD
int x;
declared_empty_defined_with(&x); // BAD
declared_empty_defined_with(3, &x); // BAD
not_declared_defined_with(-1, 0, 2U); // GOOD
not_declared_defined_with(4LL, 0, 2.5e9f); // BAD
declared_with_pointers(pv, ca); // GOOD
declared_with_pointers(3.5e15, 0); // BAD
declared_with_array("Hello"); // GOOD
declared_with_array(&x); // BAD
}
void not_yet_declared1();
void not_yet_declared2(int);
void declared_empty_defined_with(int x) {
// do nothing
}
void not_declared_defined_with(int x, int y, int z) {
return;
}
int dereference();
int caller(void) {
return dereference(); // BAD
}
int dereference(int *x) { return *x; }
void declared_with_pointers(int *x, void *y);
void declared_with_array(char a [6]);

View File

@@ -0,0 +1,8 @@
void cpp_varargs(...);
void bar();
void test() {
cpp_varargs(); // GOOD
cpp_varargs(1); // GOOD
__builtin_constant_p("something"); // GOOD: builtin
}