[CPP-340] Refine the test query for mismatching args/params by applying

C promotion rules.  The following issues are now flagged:
             (1) passing a larger type than the receiver can accept
                 (e.g., long long -> int)
             (2) passing a type of different signedness than the
                 parameter specified.
This commit is contained in:
Ziemowit Laski
2019-03-24 19:42:05 -07:00
parent 5a092d0fed
commit 29af56d21b
9 changed files with 103 additions and 14 deletions

View File

@@ -14,6 +14,91 @@
import cpp
predicate argTypeMayBePromoted(Type arg, Type parm) {
arg = parm
or
// floating-point and integral promotions
arg instanceof FloatType and parm instanceof DoubleType
or
arg instanceof FloatType and parm instanceof LongDoubleType
or
arg instanceof DoubleType and parm instanceof LongDoubleType
or
arg instanceof CharType and parm instanceof IntType
or
arg instanceof CharType and parm instanceof IntType
or
arg instanceof CharType and parm instanceof LongType
or
arg instanceof CharType and parm instanceof LongLongType
or
arg instanceof ShortType and parm instanceof IntType
or
arg instanceof ShortType and parm instanceof LongType
or
arg instanceof ShortType and parm instanceof LongLongType
or
arg instanceof IntType and parm instanceof LongType
or
arg instanceof IntType and parm instanceof LongLongType
or
arg instanceof LongType and parm instanceof LongLongType
or
arg instanceof Enum and parm instanceof IntType
or
arg instanceof Enum and parm instanceof LongType
or
arg instanceof Enum and parm instanceof LongLongType
or
// enums are usually sized as ints
arg instanceof IntType and parm instanceof Enum
or
// pointer types
arg.(PointerType).getBaseType().getUnspecifiedType() = parm
.getUnspecifiedType()
.(PointerType)
.getBaseType()
.getUnspecifiedType()
or
// void * parameters accept arbitrary pointer arguments
arg instanceof PointerType and
parm.(PointerType).getBaseType().getUnspecifiedType() instanceof VoidType
or
// handle reference types
argTypeMayBePromoted(arg.(ReferenceType).getBaseType().getUnspecifiedType(), parm)
or
argTypeMayBePromoted(arg, parm.(ReferenceType).getBaseType().getUnspecifiedType())
or
// array-to-pointer decay
argTypeMayBePromoted(arg.(ArrayType).getBaseType().getUnspecifiedType(),
parm.(PointerType).getBaseType().getUnspecifiedType())
or
// pointer-to-array promotion (C99)
argTypeMayBePromoted(arg.(PointerType).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 argMayBePromoted(Expr arg, Parameter parm) {
argTypeMayBePromoted(arg.getExplicitlyConverted().getType().getUnspecifiedType(),
parm.getType().getUnspecifiedType())
or
// The value 0 is often passed in to indicate NULL, or to initialize an arbitrary integer type.
// we will allow all literal values for simplicity. Pointer parameters are sometime passed
// special-case literals such as -1, -2, etc.
arg.(Literal).getType() instanceof IntegralType and
(
parm.getType().getUnspecifiedType() instanceof PointerType
or
parm.getType().getUnspecifiedType() instanceof IntegralType and
arg.(Literal).getType().getSize() <= parm.getType().getUnspecifiedType().getSize()
)
}
from FunctionCall fc, Function f, Parameter p
where
f = fc.getTarget() and
@@ -25,6 +110,8 @@ where
fde.getNumberOfParameters() = 0
) and
// Parameter p and its corresponding call argument must have mismatched types
p.getType() != fc.getArgument(p.getIndex()).getType()
select fc, "Calling $@: $@ is incompatible with $@.", f, f.toString(), fc.getArgument(p.getIndex()),
fc.getArgument(p.getIndex()).toString(), p, p.getTypedName()
not argMayBePromoted(fc.getArgument(p.getIndex()), p)
select fc, "Calling $@: argument $@ of type $@ is incompatible with parameter $@.", f, f.toString(),
fc.getArgument(p.getIndex()), fc.getArgument(p.getIndex()).toString(),
fc.getArgument(p.getIndex()).getType(), fc.getArgument(p.getIndex()).getType().toString(), p,
p.getTypedName()

View File

@@ -10,6 +10,7 @@
* @id cpp/too-few-arguments
* @tags correctness
* maintainability
* security
*/
import cpp

View File

@@ -1,4 +1,4 @@
| test.c:23:3:23:29 | call to declared_empty_defined_with | Calling $@: $@ is incompatible with $@. | test.c:31:6:31:32 | declared_empty_defined_with | declared_empty_defined_with | test.c:23:31:23:32 | & ... | & ... | test.c:31:38:31:38 | x | int x |
| test.c:24:3:24:29 | call to declared_empty_defined_with | Calling $@: $@ is incompatible with $@. | test.c:31:6:31:32 | declared_empty_defined_with | declared_empty_defined_with | test.c:24:31:24:34 | 3.0 | 3.0 | test.c:31:38:31:38 | x | int x |
| test.c:26:3:26:27 | call to not_declared_defined_with | Calling $@: $@ is incompatible with $@. | test.c:34:6:34:30 | not_declared_defined_with | not_declared_defined_with | test.c:26:29:26:31 | 1.0 | 1.0 | test.c:34:36:34:36 | x | int x |
| test.c:26:3:26:27 | call to not_declared_defined_with | Calling $@: $@ is incompatible with $@. | test.c:34:6:34:30 | not_declared_defined_with | not_declared_defined_with | test.c:26:37:26:38 | 2 | 2 | test.c:34:50:34:50 | z | int z |
| test.c:23:3:23:29 | call to declared_empty_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:32:6:32:32 | declared_empty_defined_with | declared_empty_defined_with | test.c:23:31:23:32 | & ... | & ... | file://:0:0:0:0 | int * | int * | test.c:32:38:32:38 | x | int x |
| test.c:24:3:24:29 | call to declared_empty_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:32:6:32:32 | declared_empty_defined_with | declared_empty_defined_with | test.c:24:31:24:34 | 3.0 | 3.0 | file://:0:0:0:0 | float | float | test.c:32:38:32:38 | x | int x |
| test.c:26:3:26:27 | call to not_declared_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:35:6:35:30 | not_declared_defined_with | not_declared_defined_with | test.c:26:29:26:31 | 1.0 | 1.0 | file://:0:0:0:0 | double | double | test.c:35:36:35:36 | x | int x |
| test.c:27:3:27:27 | call to not_declared_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:35:6:35:30 | not_declared_defined_with | not_declared_defined_with | test.c:27:29:27:31 | 4 | 4 | file://:0:0:0:0 | long long | long long | test.c:35:36:35:36 | x | int x |

View File

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

View File

@@ -1,3 +1,3 @@
| test.c:17:3:17:19 | call to not_yet_declared2 | This call has fewer arguments than required by $@. | test.c:16:3:16:3 | not_yet_declared2 | not_yet_declared2 |
| test.c:17:3:17:19 | call to not_yet_declared2 | This call has fewer arguments than required by $@. | test.c:30:6:30:22 | not_yet_declared2 | not_yet_declared2 |
| test.c:19:3:19:29 | call to declared_empty_defined_with | This call has fewer arguments than required by $@. | test.c:31:6:31:32 | declared_empty_defined_with | declared_empty_defined_with |
| test.c:17:3:17:19 | call to not_yet_declared2 | This call has fewer arguments than required by $@. | test.c:31:6:31:22 | not_yet_declared2 | not_yet_declared2 |
| test.c:19:3:19:29 | call to declared_empty_defined_with | This call has fewer arguments than required by $@. | test.c:32:6:32:32 | declared_empty_defined_with | declared_empty_defined_with |

View File

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

View File

@@ -1,5 +1,5 @@
| test.c:8:3:8: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:13:3:13:12 | call to undeclared | This call has more arguments than required by $@. | test.c:12:3:12:3 | undeclared | undeclared |
| test.c:15:3:15:19 | call to not_yet_declared1 | This call has more arguments than required by $@. | test.c:15:3:15:3 | not_yet_declared1 | not_yet_declared1 |
| test.c:15:3:15:19 | call to not_yet_declared1 | This call has more arguments than required by $@. | test.c:29:6:29:22 | not_yet_declared1 | not_yet_declared1 |
| test.c:24:3:24:29 | call to declared_empty_defined_with | This call has more arguments than required by $@. | test.c:31:6:31:32 | declared_empty_defined_with | declared_empty_defined_with |
| test.c:15:3:15:19 | call to not_yet_declared1 | This call has more arguments than required by $@. | test.c:30:6:30:22 | not_yet_declared1 | not_yet_declared1 |
| test.c:24:3:24:29 | call to declared_empty_defined_with | This call has more arguments than required by $@. | test.c:32:6:32:32 | declared_empty_defined_with | declared_empty_defined_with |

View File

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

View File

@@ -24,6 +24,7 @@ void test() {
declared_empty_defined_with(3.0f, &x); // BAD (type mismatch)
not_declared_defined_with(1.0, 0, 2U); // GOOD (type mismatch)
not_declared_defined_with(4LL, 0, 2); // GOOD (type mismatch)
}
void not_yet_declared1();