mirror of
https://github.com/github/codeql.git
synced 2025-12-22 11:46:32 +01:00
C++: Added query that detects implicit function declarations
This commit is contained in:
@@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* @name Implicit function declaration
|
||||||
|
* @description An implicitly declared function is assumed to take no
|
||||||
|
* arguments and return an integer. If this assumption does not hold, it
|
||||||
|
* may lead to unpredictable behavior.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity warning
|
||||||
|
* @precision medium
|
||||||
|
* @id cpp/implicit-function-declaration
|
||||||
|
* @tags correctness
|
||||||
|
* maintainability
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
|
||||||
|
pragma[inline]
|
||||||
|
predicate arithTypesMatch(Type arg, Type parm) {
|
||||||
|
arg = parm
|
||||||
|
or
|
||||||
|
arg.getSize() = parm.getSize() and
|
||||||
|
(
|
||||||
|
arg instanceof IntegralOrEnumType and
|
||||||
|
parm instanceof IntegralOrEnumType
|
||||||
|
or
|
||||||
|
arg instanceof FloatingPointType and
|
||||||
|
parm instanceof FloatingPointType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[inline]
|
||||||
|
predicate nestedPointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||||
|
// arithmetic types
|
||||||
|
arithTypesMatch(arg, parm)
|
||||||
|
or
|
||||||
|
// conversion to/from pointers to void is allowed
|
||||||
|
arg instanceof VoidType
|
||||||
|
or
|
||||||
|
parm instanceof VoidType
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[inline]
|
||||||
|
predicate pointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||||
|
nestedPointerArgTypeMayBeUsed(arg, parm)
|
||||||
|
or
|
||||||
|
// nested pointers
|
||||||
|
nestedPointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||||
|
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||||
|
or
|
||||||
|
nestedPointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||||
|
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[inline]
|
||||||
|
predicate argTypeMayBeUsed(Type arg, Type parm) {
|
||||||
|
// arithmetic types
|
||||||
|
arithTypesMatch(arg, parm)
|
||||||
|
or
|
||||||
|
// pointers to compatible types
|
||||||
|
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||||
|
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||||
|
or
|
||||||
|
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||||
|
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||||
|
or
|
||||||
|
// C11 arrays
|
||||||
|
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||||
|
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||||
|
or
|
||||||
|
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||||
|
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||||
|
}
|
||||||
|
|
||||||
|
// This predicate holds whenever expression `arg` may be used to initialize
|
||||||
|
// function parameter `parm` without need for run-time conversion.
|
||||||
|
pragma[inline]
|
||||||
|
predicate argMayBeUsed(Expr arg, ParameterDeclarationEntry pde) {
|
||||||
|
argTypeMayBeUsed(arg.getFullyConverted().getUnspecifiedType(), pde.getUnspecifiedType())
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if this file (or header) was compiled as a C file
|
||||||
|
predicate isCompiledAsC(File f) {
|
||||||
|
f.compiledAsC()
|
||||||
|
or
|
||||||
|
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||||
|
// or implicitly declared (i.e., lacking a prototype)
|
||||||
|
predicate hasZeroParamDeclTooMany(Function f) {
|
||||||
|
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||||
|
not fde.isImplicit() and
|
||||||
|
not fde.hasVoidParamList() and
|
||||||
|
fde.getNumberOfParameters() = 0 and
|
||||||
|
not fde.isDefinition()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||||
|
predicate hasZeroParamDecl(Function f) {
|
||||||
|
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||||
|
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingset[name]
|
||||||
|
predicate notAlreadyReported(string name, FunctionCall fc) {
|
||||||
|
forall(FunctionDeclarationEntry fdeEx |
|
||||||
|
fdeEx.getName() = name and
|
||||||
|
not fdeEx.isImplicit()
|
||||||
|
|
|
||||||
|
// only report if not reported by cpp/futile-params
|
||||||
|
(
|
||||||
|
fdeEx.getNumberOfParameters() >= fc.getNumberOfArguments()
|
||||||
|
or
|
||||||
|
exists(Function f |
|
||||||
|
f = fc.getTarget() and
|
||||||
|
not exists(f.getBlock())
|
||||||
|
or
|
||||||
|
not hasZeroParamDeclTooMany(f)
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
// only report if not reported by cpp/too-few-arguments
|
||||||
|
(
|
||||||
|
fdeEx.getNumberOfParameters() <= fc.getNumberOfArguments()
|
||||||
|
or
|
||||||
|
exists(Function f |
|
||||||
|
f.isVarargs() or
|
||||||
|
f instanceof BuiltInFunction or
|
||||||
|
not hasZeroParamDecl(f)
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
// only report if not reported by cpp/mistyped-function-arguments
|
||||||
|
(
|
||||||
|
not hasZeroParamDecl(fc.getTarget())
|
||||||
|
or
|
||||||
|
forall(ParameterDeclarationEntry pde |
|
||||||
|
pde = fdeEx.getAParameterDeclarationEntry() and
|
||||||
|
pde.getIndex() < fc.getNumberOfArguments()
|
||||||
|
|
|
||||||
|
argMayBeUsed(fc.getArgument(pde.getIndex()), pde)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate sameLocation(Location loc1, Location loc2) {
|
||||||
|
loc1.getFile() = loc2.getFile() and
|
||||||
|
loc1.getStartLine() = loc2.getStartLine() and
|
||||||
|
loc1.getStartColumn() = loc2.getStartColumn()
|
||||||
|
}
|
||||||
|
|
||||||
|
from FunctionDeclarationEntry fdeIm, FunctionCall fc
|
||||||
|
where
|
||||||
|
isCompiledAsC(fdeIm.getFile()) and
|
||||||
|
fdeIm.isImplicit() and
|
||||||
|
sameLocation(fdeIm.getLocation(), fc.getLocation()) and
|
||||||
|
notAlreadyReported(fdeIm.getName(), fc)
|
||||||
|
select fc, "Function call implicitly declares $@", fc, fc.toString()
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
| test.c:28:3:28:12 | call to undeclared | Function call implicitly declares $@ | test.c:28:3:28:12 | call to undeclared | call to undeclared |
|
||||||
|
| test.c:31:3:31:19 | call to not_yet_declared1 | Function call implicitly declares $@ | test.c:31:3:31:19 | call to not_yet_declared1 | call to not_yet_declared1 |
|
||||||
|
| test.c:32:3:32:19 | call to not_yet_declared2 | Function call implicitly declares $@ | test.c:32:3:32:19 | call to not_yet_declared2 | call to not_yet_declared2 |
|
||||||
|
| test.c:43:3:43:27 | call to not_declared_defined_with | Function call implicitly declares $@ | test.c:43:3:43:27 | call to not_declared_defined_with | call to not_declared_defined_with |
|
||||||
|
| test.c:54:3:54:21 | call to defined_with_double | Function call implicitly declares $@ | test.c:54:3:54:21 | call to defined_with_double | call to defined_with_double |
|
||||||
|
| test.c:66:3:66:22 | call to defined_with_ptr_ptr | Function call implicitly declares $@ | test.c:66:3:66:22 | call to defined_with_ptr_ptr | call to defined_with_ptr_ptr |
|
||||||
|
| test.c:68:3:68:22 | call to defined_with_ptr_arr | Function call implicitly declares $@ | test.c:68:3:68:22 | call to defined_with_ptr_arr | call to defined_with_ptr_arr |
|
||||||
|
| test.c:132:3:132:22 | call to implicit_declaration | Function call implicitly declares $@ | test.c:132:3:132:22 | call to implicit_declaration | call to implicit_declaration |
|
||||||
|
| test.c:133:3:133:30 | call to implicit_declaration_k_and_r | Function call implicitly declares $@ | test.c:133:3:133:30 | call to implicit_declaration_k_and_r | call to implicit_declaration_k_and_r |
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql
|
||||||
@@ -18,3 +18,4 @@
|
|||||||
| test.c:58:3:58:24 | call to defined_with_long_long | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:103:11:103:32 | defined_with_long_long | defined_with_long_long | test.c:58:26:58:26 | 3 | 3 | file://:0:0:0:0 | int | int | test.c:103:44:103:45 | ll | long long ll |
|
| test.c:58:3:58:24 | call to defined_with_long_long | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:103:11:103:32 | defined_with_long_long | defined_with_long_long | test.c:58:26:58:26 | 3 | 3 | file://:0:0:0:0 | int | int | test.c:103:44:103:45 | ll | long long ll |
|
||||||
| test.c:60:3:60:21 | call to defined_with_double | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:99:8:99:26 | defined_with_double | defined_with_double | test.c:60:23:60:25 | 2 | 2 | file://:0:0:0:0 | long long | long long | test.c:99:35:99:35 | d | double d |
|
| test.c:60:3:60:21 | call to defined_with_double | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:99:8:99:26 | defined_with_double | defined_with_double | test.c:60:23:60:25 | 2 | 2 | file://:0:0:0:0 | long long | long long | test.c:99:35:99:35 | d | double d |
|
||||||
| test.c:61:3:61:24 | call to defined_with_long_long | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:103:11:103:32 | defined_with_long_long | defined_with_long_long | test.c:61:26:61:31 | 3500000000000000.0 | 3500000000000000.0 | file://:0:0:0:0 | double | double | test.c:103:44:103:45 | ll | long long ll |
|
| test.c:61:3:61:24 | call to defined_with_long_long | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:103:11:103:32 | defined_with_long_long | defined_with_long_long | test.c:61:26:61:31 | 3500000000000000.0 | 3500000000000000.0 | file://:0:0:0:0 | double | double | test.c:103:44:103:45 | ll | long long ll |
|
||||||
|
| test.c:137:3:137:27 | call to implicit_declaration_good | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test2.c:8:6:8:30 | implicit_declaration_good | implicit_declaration_good | test.c:137:35:137:38 | 2.0 | 2.0 | file://:0:0:0:0 | float | float | test2.c:8:52:8:52 | f | float f |
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ void test(int *argv[]) {
|
|||||||
declared_void(); // GOOD
|
declared_void(); // GOOD
|
||||||
declared_with(1); // GOOD
|
declared_with(1); // GOOD
|
||||||
|
|
||||||
undeclared(); // GOOD
|
undeclared(); // BAD (GOOD for everything except cpp/implicit-function-declaration)
|
||||||
undeclared(1); // GOOD
|
undeclared(1); // GOOD
|
||||||
|
|
||||||
not_yet_declared1(1); // GOOD
|
not_yet_declared1(1); // BAD (GOOD for everything except for cpp/implicit-function-declaration)
|
||||||
not_yet_declared2(1); // GOOD
|
not_yet_declared2(1); // BAD (GOOD for everything except for cpp/implicit-function-declaration)
|
||||||
not_yet_declared2(ca); // BAD
|
not_yet_declared2(ca); // BAD
|
||||||
not_yet_declared2(); // BAD
|
not_yet_declared2(); // BAD
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ void test(int *argv[]) {
|
|||||||
declared_empty_defined_with(&x); // BAD
|
declared_empty_defined_with(&x); // BAD
|
||||||
declared_empty_defined_with(3, &x); // BAD
|
declared_empty_defined_with(3, &x); // BAD
|
||||||
|
|
||||||
not_declared_defined_with(-1, 0, 2U); // GOOD
|
not_declared_defined_with(-1, 0, 2U); // BAD (GOOD for everything except for cpp/implicit-function-declaration)
|
||||||
not_declared_defined_with(4LL, 0, 2.5e9f); // BAD
|
not_declared_defined_with(4LL, 0, 2.5e9f); // BAD
|
||||||
|
|
||||||
declared_with_pointers(pv, ca); // GOOD
|
declared_with_pointers(pv, ca); // GOOD
|
||||||
@@ -51,7 +51,7 @@ void test(int *argv[]) {
|
|||||||
defined_with_float(2.f); // BAD
|
defined_with_float(2.f); // BAD
|
||||||
defined_with_float(2.0); // BAD
|
defined_with_float(2.0); // BAD
|
||||||
|
|
||||||
defined_with_double(2.f); // GOOD
|
defined_with_double(2.f); // BAD (GOOD for everything except for cpp/implicit-function-declaration)
|
||||||
defined_with_double('c'); // BAD
|
defined_with_double('c'); // BAD
|
||||||
|
|
||||||
defined_with_long_long('c'); // BAD
|
defined_with_long_long('c'); // BAD
|
||||||
@@ -63,9 +63,9 @@ void test(int *argv[]) {
|
|||||||
k_and_r_func(2.5, &s); // GOOD
|
k_and_r_func(2.5, &s); // GOOD
|
||||||
|
|
||||||
int (*parameterName)[2];
|
int (*parameterName)[2];
|
||||||
defined_with_ptr_ptr(parameterName); // GOOD
|
defined_with_ptr_ptr(parameterName); // // BAD (GOOD for everything except for cpp/implicit-function-declaration)
|
||||||
defined_with_ptr_ptr(argv); // GOOD
|
defined_with_ptr_ptr(argv); // GOOD
|
||||||
defined_with_ptr_arr(parameterName); // GOOD
|
defined_with_ptr_arr(parameterName); // // BAD (GOOD for everything except for cpp/implicit-function-declaration)
|
||||||
defined_with_ptr_arr(argv); // GOOD
|
defined_with_ptr_arr(argv); // GOOD
|
||||||
|
|
||||||
declared_and_defined_empty(); // GOOD
|
declared_and_defined_empty(); // GOOD
|
||||||
@@ -124,3 +124,17 @@ int call_k_and_r(int i) {
|
|||||||
int will_be_k_and_r(val)
|
int will_be_k_and_r(val)
|
||||||
int val;
|
int val;
|
||||||
{ return val + 1; }
|
{ return val + 1; }
|
||||||
|
|
||||||
|
extern int extern_definition(double, double*);
|
||||||
|
|
||||||
|
void test_implicit_function_declaration(int x, double d) {
|
||||||
|
int y;
|
||||||
|
implicit_declaration(1, 2); // BAD
|
||||||
|
implicit_declaration_k_and_r(1, 2); // BAD
|
||||||
|
|
||||||
|
implicit_declaration(1, 2); // GOOD (no longer an implicit declaration)
|
||||||
|
|
||||||
|
implicit_declaration_good(1, x, 2.0f); // BAD
|
||||||
|
|
||||||
|
y = extern_definition(3.0f, &d); // GOOD
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
void implicit_declaration(int x) {}
|
||||||
|
|
||||||
|
int implicit_declaration_k_and_r(x) int x;
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void implicit_declaration_good(int x, int y, float f)
|
||||||
|
{
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user