Skip to content

Commit ceeb9ab

Browse files
authored
Merge pull request #2622 from MathiasVP/implicit-function-declaration
C++: Add 'implicit function declaration' query
2 parents 9d70358 + c9cc459 commit ceeb9ab

File tree

16 files changed

+297
-148
lines changed

16 files changed

+297
-148
lines changed

change-notes/1.24/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
88

99
| **Query** | **Tags** | **Purpose** |
1010
|-----------------------------|-----------|--------------------------------------------------------------------|
11+
| Implicit function declarations (`cpp/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql`) | correctness, maintainability | This query finds calls to undeclared functions that are compiled by a C compiler. Results are shown on LGTM by default. |
1112

1213
## Changes to existing queries
1314

cpp/config/suites/c/correctness

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ExprHasNoEffect.ql: /Correctness/Common Errors
1919
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooFewArguments.ql: /Correctness/Common Errors
2020
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooManyArguments.ql: /Correctness/Common Errors
21+
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql: /Correctness/Common Errors
2122
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql: /Correctness/Common Errors
2223
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql: /Correctness/Common Errors
2324
+ semmlecode-cpp-queries/Likely Bugs/Arithmetic/FloatComparison.ql: /Correctness/Common Errors

cpp/config/suites/cpp/correctness

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ExprHasNoEffect.ql: /Correctness/Common Errors
2020
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooFewArguments.ql: /Correctness/Common Errors
2121
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooManyArguments.ql: /Correctness/Common Errors
22+
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql: /Correctness/Common Errors
2223
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql: /Correctness/Common Errors
2324
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql: /Correctness/Common Errors
2425
+ semmlecode-cpp-queries/Likely Bugs/Arithmetic/FloatComparison.ql: /Correctness/Common Errors
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* '#include <stdlib.h>' was forgotton */
2+
3+
int main(void) {
4+
/* 'int malloc()' assumed */
5+
unsigned char *p = malloc(100);
6+
*p = 'a';
7+
return 0;
8+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
7+
<overview>
8+
<p>A function is called without a prior function declaration or definition.
9+
When this happens, the compiler generates an implicit declaration of the function,
10+
specifying an integer return type and no parameters.
11+
If the implicit declaration does not match the true signature of the function, the
12+
function may behave unpredictably.</p>
13+
14+
<p>This may indicate a misspelled function name, or that the required header containing
15+
the function declaration has not been included.</p>
16+
17+
</overview>
18+
<recommendation>
19+
<p>Provide an explicit declaration of the function before invoking it.</p>
20+
21+
</recommendation>
22+
<example><sample src="ImplicitFunctionDeclaration.c" />
23+
24+
</example>
25+
26+
<references>
27+
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL31-C.+Declare+identifiers+before+using+them">DCL31-C. Declare identifiers before using them</a></li>
28+
</references>
29+
</qhelp>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @name Implicit function declaration
3+
* @description An implicitly declared function is assumed to take no
4+
* arguments and return an integer. If this assumption does not hold, it
5+
* may lead to unpredictable behavior.
6+
* @kind problem
7+
* @problem.severity warning
8+
* @precision high
9+
* @id cpp/implicit-function-declaration
10+
* @tags correctness
11+
* maintainability
12+
*/
13+
14+
import cpp
15+
import MistypedFunctionArguments
16+
import TooFewArguments
17+
import TooManyArguments
18+
import semmle.code.cpp.commons.Exclusions
19+
20+
predicate locInfo(Locatable e, File file, int line, int col) {
21+
e.getFile() = file and
22+
e.getLocation().getStartLine() = line and
23+
e.getLocation().getStartColumn() = col
24+
}
25+
26+
predicate sameLocation(FunctionDeclarationEntry fde, FunctionCall fc) {
27+
exists(File file, int line, int col |
28+
locInfo(fde, file, line, col) and
29+
locInfo(fc, file, line, col)
30+
)
31+
}
32+
33+
predicate isCompiledAsC(File f) {
34+
f.compiledAsC()
35+
or
36+
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
37+
}
38+
39+
from FunctionDeclarationEntry fdeIm, FunctionCall fc
40+
where
41+
isCompiledAsC(fdeIm.getFile()) and
42+
not isFromMacroDefinition(fc) and
43+
fdeIm.isImplicit() and
44+
sameLocation(fdeIm, fc) and
45+
not mistypedFunctionArguments(fc, _, _) and
46+
not tooFewArguments(fc, _) and
47+
not tooManyArguments(fc, _)
48+
select fc, "Function call implicitly declares '" + fdeIm.getName() + "'."

cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -12,95 +12,10 @@
1212
*/
1313

1414
import cpp
15-
16-
predicate arithTypesMatch(Type arg, Type parm) {
17-
arg = parm
18-
or
19-
arg.getSize() = parm.getSize() and
20-
(
21-
arg instanceof IntegralOrEnumType and
22-
parm instanceof IntegralOrEnumType
23-
or
24-
arg instanceof FloatingPointType and
25-
parm instanceof FloatingPointType
26-
)
27-
}
28-
29-
pragma[inline]
30-
predicate nestedPointerArgTypeMayBeUsed(Type arg, Type parm) {
31-
// arithmetic types
32-
arithTypesMatch(arg, parm)
33-
or
34-
// conversion to/from pointers to void is allowed
35-
arg instanceof VoidType
36-
or
37-
parm instanceof VoidType
38-
}
39-
40-
pragma[inline]
41-
predicate pointerArgTypeMayBeUsed(Type arg, Type parm) {
42-
nestedPointerArgTypeMayBeUsed(arg, parm)
43-
or
44-
// nested pointers
45-
nestedPointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
46-
parm.(PointerType).getBaseType().getUnspecifiedType())
47-
or
48-
nestedPointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
49-
parm.(PointerType).getBaseType().getUnspecifiedType())
50-
}
51-
52-
pragma[inline]
53-
predicate argTypeMayBeUsed(Type arg, Type parm) {
54-
// arithmetic types
55-
arithTypesMatch(arg, parm)
56-
or
57-
// pointers to compatible types
58-
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
59-
parm.(PointerType).getBaseType().getUnspecifiedType())
60-
or
61-
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
62-
parm.(PointerType).getBaseType().getUnspecifiedType())
63-
or
64-
// C11 arrays
65-
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
66-
parm.(ArrayType).getBaseType().getUnspecifiedType())
67-
or
68-
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
69-
parm.(ArrayType).getBaseType().getUnspecifiedType())
70-
}
71-
72-
// This predicate holds whenever expression `arg` may be used to initialize
73-
// function parameter `parm` without need for run-time conversion.
74-
pragma[inline]
75-
predicate argMayBeUsed(Expr arg, Parameter parm) {
76-
argTypeMayBeUsed(arg.getFullyConverted().getUnspecifiedType(), parm.getUnspecifiedType())
77-
}
78-
79-
// True if function was ()-declared, but not (void)-declared or K&R-defined
80-
predicate hasZeroParamDecl(Function f) {
81-
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
82-
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
83-
)
84-
}
85-
86-
// True if this file (or header) was compiled as a C file
87-
predicate isCompiledAsC(File f) {
88-
f.compiledAsC()
89-
or
90-
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
91-
}
15+
import MistypedFunctionArguments
9216

9317
from FunctionCall fc, Function f, Parameter p
94-
where
95-
f = fc.getTarget() and
96-
p = f.getAParameter() and
97-
hasZeroParamDecl(f) and
98-
isCompiledAsC(f.getFile()) and
99-
not f.isVarargs() and
100-
not f instanceof BuiltInFunction and
101-
p.getIndex() < fc.getNumberOfArguments() and
102-
// Parameter p and its corresponding call argument must have mismatched types
103-
not argMayBeUsed(fc.getArgument(p.getIndex()), p)
18+
where mistypedFunctionArguments(fc, f, p)
10419
select fc, "Calling $@: argument $@ of type $@ is incompatible with parameter $@.", f, f.toString(),
10520
fc.getArgument(p.getIndex()) as arg, arg.toString(),
10621
arg.getExplicitlyConverted().getUnspecifiedType() as atype, atype.toString(), p, p.getTypedName()
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Provides the implementation of the MistypedFunctionArguments query. The
3+
* query is implemented as a library, so that we can avoid producing
4+
* duplicate results in other similar queries.
5+
*/
6+
7+
import cpp
8+
9+
private predicate arithTypesMatch(Type arg, Type parm) {
10+
arg = parm
11+
or
12+
arg.getSize() = parm.getSize() and
13+
(
14+
arg instanceof IntegralOrEnumType and
15+
parm instanceof IntegralOrEnumType
16+
or
17+
arg instanceof FloatingPointType and
18+
parm instanceof FloatingPointType
19+
)
20+
}
21+
22+
pragma[inline]
23+
private predicate nestedPointerArgTypeMayBeUsed(Type arg, Type parm) {
24+
// arithmetic types
25+
arithTypesMatch(arg, parm)
26+
or
27+
// conversion to/from pointers to void is allowed
28+
arg instanceof VoidType
29+
or
30+
parm instanceof VoidType
31+
}
32+
33+
pragma[inline]
34+
private predicate pointerArgTypeMayBeUsed(Type arg, Type parm) {
35+
nestedPointerArgTypeMayBeUsed(arg, parm)
36+
or
37+
// nested pointers
38+
nestedPointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
39+
parm.(PointerType).getBaseType().getUnspecifiedType())
40+
or
41+
nestedPointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
42+
parm.(PointerType).getBaseType().getUnspecifiedType())
43+
}
44+
45+
pragma[inline]
46+
private predicate argTypeMayBeUsed(Type arg, Type parm) {
47+
// arithmetic types
48+
arithTypesMatch(arg, parm)
49+
or
50+
// pointers to compatible types
51+
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
52+
parm.(PointerType).getBaseType().getUnspecifiedType())
53+
or
54+
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
55+
parm.(PointerType).getBaseType().getUnspecifiedType())
56+
or
57+
// C11 arrays
58+
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
59+
parm.(ArrayType).getBaseType().getUnspecifiedType())
60+
or
61+
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
62+
parm.(ArrayType).getBaseType().getUnspecifiedType())
63+
}
64+
65+
// This predicate holds whenever expression `arg` may be used to initialize
66+
// function parameter `parm` without need for run-time conversion.
67+
pragma[inline]
68+
private predicate argMayBeUsed(Expr arg, Parameter parm) {
69+
argTypeMayBeUsed(arg.getFullyConverted().getUnspecifiedType(), parm.getUnspecifiedType())
70+
}
71+
72+
// True if function was ()-declared, but not (void)-declared or K&R-defined
73+
private predicate hasZeroParamDecl(Function f) {
74+
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
75+
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
76+
)
77+
}
78+
79+
// True if this file (or header) was compiled as a C file
80+
private predicate isCompiledAsC(File f) {
81+
f.compiledAsC()
82+
or
83+
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
84+
}
85+
86+
predicate mistypedFunctionArguments(FunctionCall fc, Function f, Parameter p) {
87+
f = fc.getTarget() and
88+
p = f.getAParameter() and
89+
hasZeroParamDecl(f) and
90+
isCompiledAsC(f.getFile()) and
91+
not f.isVarargs() and
92+
not f instanceof BuiltInFunction and
93+
p.getIndex() < fc.getNumberOfArguments() and
94+
// Parameter p and its corresponding call argument must have mismatched types
95+
not argMayBeUsed(fc.getArgument(p.getIndex()), p)
96+
}

cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,8 @@
1515
*/
1616

1717
import cpp
18-
19-
// True if function was ()-declared, but not (void)-declared or K&R-defined
20-
predicate hasZeroParamDecl(Function f) {
21-
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
22-
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
23-
)
24-
}
25-
26-
// True if this file (or header) was compiled as a C file
27-
predicate isCompiledAsC(File f) {
28-
f.compiledAsC()
29-
or
30-
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
31-
}
18+
import TooFewArguments
3219

3320
from FunctionCall fc, Function f
34-
where
35-
f = fc.getTarget() and
36-
not f.isVarargs() and
37-
not f instanceof BuiltInFunction and
38-
hasZeroParamDecl(f) and
39-
isCompiledAsC(f.getFile()) and
40-
// There is an explicit declaration of the function whose parameter count is larger
41-
// than the number of call arguments
42-
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
43-
fde.getNumberOfParameters() > fc.getNumberOfArguments()
44-
)
21+
where tooFewArguments(fc, f)
4522
select fc, "This call has fewer arguments than required by $@.", f, f.toString()
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Provides the implementation of the TooFewArguments query. The
3+
* query is implemented as a library, so that we can avoid producing
4+
* duplicate results in other similar queries.
5+
*/
6+
7+
import cpp
8+
9+
// True if function was ()-declared, but not (void)-declared or K&R-defined
10+
private predicate hasZeroParamDecl(Function f) {
11+
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
12+
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
13+
)
14+
}
15+
16+
// True if this file (or header) was compiled as a C file
17+
private predicate isCompiledAsC(File f) {
18+
f.compiledAsC()
19+
or
20+
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
21+
}
22+
23+
predicate tooFewArguments(FunctionCall fc, Function f) {
24+
f = fc.getTarget() and
25+
not f.isVarargs() and
26+
not f instanceof BuiltInFunction and
27+
hasZeroParamDecl(f) and
28+
isCompiledAsC(f.getFile()) and
29+
// There is an explicit declaration of the function whose parameter count is larger
30+
// than the number of call arguments
31+
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
32+
fde.getNumberOfParameters() > fc.getNumberOfArguments()
33+
)
34+
}

0 commit comments

Comments
 (0)