Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0c350dc
[CPP-340] Create QL query for function call argument count mismatches.
zlaski-semmle Mar 18, 2019
5d8b84c
Refine QL query by requiring that a ()-declaration be present.
zlaski-semmle Mar 19, 2019
2def0ee
[CPP-340] Re-work QL query; treat undeclared C functions the same way as
zlaski-semmle Mar 19, 2019
5a092d0
[CPP-340] Create three QL queries: (1) mismatched argument types,
zlaski-semmle Mar 21, 2019
29af56d
[CPP-340] Refine the test query for mismatching args/params by applying
zlaski-semmle Mar 25, 2019
cb5bbd2
[CPP-340] When warning about mismatched parameters, follow what C
zlaski-semmle Mar 30, 2019
2ea9f81
[CPP-340] Refer to C coding standard, not C++.
zlaski-semmle Mar 30, 2019
59a54df
[CPP-340] cpp/too-many-arguments should remain as cpp/futile-params.
zlaski-semmle Mar 30, 2019
8a653b9
[CPP-340] Fix TooFewArguments.c to actually provide a ()-prototype.
zlaski-semmle Mar 30, 2019
3ec988c
[CPP-340] Rename 'UnspecifiedFunctions' to 'Unspecified Functions'
zlaski-semmle Apr 2, 2019
bd13982
[CPP-340] Delete old 'UnspecifiedFunctions' folders
zlaski-semmle Apr 2, 2019
03aa86e
Merge branch 'master' into cpp340a
zlaski-semmle Apr 2, 2019
96b8bdf
[CPP-340] Add new queries to analysis-cpp.md; correct id of
zlaski-semmle Apr 2, 2019
e4ce834
[CPP-340] Simplify MistypedFunctionArguments.ql and reduce its
zlaski-semmle Apr 3, 2019
b060fd1
Merge branch 'master' into cpp340a
zlaski-semmle Apr 4, 2019
970c45e
Merge branch 'master' into cpp340a
zlaski-semmle Apr 4, 2019
921523e
Merge branch 'cpp340a' of github.com:zlaski-semmle/ql into cpp340a
zlaski-semmle Apr 4, 2019
ef54b01
[CPP-340] Fixed .expected file to match new query.
zlaski-semmle Apr 5, 2019
dc74978
[CPP-340] Make the query more strict (again).
zlaski-semmle Apr 10, 2019
d76138f
[CPP-340] Remove use of getUnderlyingType() predicate as it does
zlaski-semmle Apr 10, 2019
b58f414
[CPP-340] Add more test case; exclude K&R definitions of functions wh…
zlaski-semmle Apr 13, 2019
61c91b6
[CPP-340] Refactor MistypedFunctionArguments.ql further.
zlaski-semmle Apr 14, 2019
65130c4
[CPP-340] Add white list (for false positive suppression) to TooManyA…
zlaski-semmle Apr 16, 2019
62b030d
[CPP-340] Add a fourth query, ArgumentsToImplicit.ql, to deal strictl…
zlaski-semmle Apr 19, 2019
36b2c14
[CPP-340] Minor formatting tweaks
zlaski-semmle Apr 19, 2019
ac58bdf
[CPP-340] For MistypedFunctionArguments.ql, add support for pointers …
zlaski-semmle Apr 24, 2019
4a760b1
[CPP-340] Delete ArgumentsToImplicit.ql and associated files.
zlaski-semmle Apr 28, 2019
a0cfe82
[CPP-340] Replace whitelist with f.getBlock() test. Fix doc comment.
zlaski-semmle Apr 29, 2019
d146967
[CPP-340] Fix end-of-line formatting for our change notes.
zlaski-semmle Apr 29, 2019
be77eb7
[CPP-340] Add new test cases to test.c; this required the .expected
zlaski-semmle Apr 29, 2019
17066cf
[CPP-340] Adjust annotations in test.c file.
zlaski-semmle Apr 30, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions change-notes/1.21/analysis-cpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
| `()`-declared function called with too few arguments (`cpp/too-few-arguments`) | Correctness | Find all cases where the number of arguments is less than the number of parameters of the function, provided the function is also properly declared/defined elsewhere. |
| `()`-declared function called with mismatched arguments (`cpp/mismatched-function-arguments`) | Correctness | Find all cases where the types of arguments do not match the types of parameters of the function, provided the function is also properly declared/defined elsewhere. |

## Changes to existing queries

Expand All @@ -20,5 +22,6 @@
| Comparison result is always the same | Fewer false positive results | The range analysis library is now more conservative about floating point values being possibly `NaN` |
| Wrong type of arguments to formatting function (`cpp/wrong-type-format-argument`) | More correct results and fewer false positive results | This query now more accurately identifies wide and non-wide string/character format arguments on different platforms. Platform detection has also been made more accurate for the purposes of this query. |
| Wrong type of arguments to formatting function (`cpp/wrong-type-format-argument`) | Fewer false positive results | Non-standard uses of %L are now understood. |
| `()`-declared function called with too many arguments (`cpp/futile-params`) | Improved coverage | Query has been generalized to find all cases where the number of arguments exceedes the number of parameters of the function, provided the function is also properly declared/defined elsewhere. |

## Changes to QL libraries
4 changes: 3 additions & 1 deletion cpp/config/suites/c/correctness
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 3 additions & 1 deletion cpp/config/suites/cpp/correctness
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
11 changes: 0 additions & 11 deletions cpp/ql/src/Likely Bugs/Likely Typos/FutileParams.c

This file was deleted.

29 changes: 0 additions & 29 deletions cpp/ql/src/Likely Bugs/Likely Typos/FutileParams.qhelp

This file was deleted.

22 changes: 0 additions & 22 deletions cpp/ql/src/Likely Bugs/Likely Typos/FutileParams.ql

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @name Call to a function with one or more incompatible arguments
* @description When the type of a function argument is not compatible
* with the type of the corresponding parameter, it may lead to
* unpredictable behavior.
* @kind problem
* @problem.severity warning
* @precision medium
* @id cpp/mistyped-function-arguments
* @tags correctness
* maintainability
*/

import cpp

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())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's possible for a fully-converted argument to be an ArrayType. It'll undergo an array-to-pointer conversion at some point. That means you can eliminate the last two of these cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I was thinking of C99/C11 fixed-length array parameters (e.g., int arr[ static 10]), but upon further research discovered that the static 10 is merely a codegen hint for the compiler and that a raw pointer is still passed in.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it some more, and now I think you do need the arg.(ArrayType) cases in order to handle the rarely-used "pointer to array" types (declared like int (*parameterName)[2]).

But hang on, where is the recursion that would enable this? If you allow passing a int * to a unsigned int * argument, shouldn't you also allow passing an int ** to a unsigned int ** argument? But recursive predicates can't be inline, so you can't support this without some serious restructuring for the sake of performance.

To start with, please make sure there is test coverage for "pointer to array" and "pointer to pointer" to compatible-but-not-identical base types.

}

// This predicate holds whenever expression `arg` may be used to initialize
// function parameter `parm` without need for run-time conversion.
pragma[inline]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This predicate has changed enough that its comment is no longer relevant. Since it's only called from one place, I suggest inlining it by hand, i.e., removing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the comment is not terribly useful. However, I prefer to separate things into predicates as they improve code readability. Is there a run-time performance hit for doing so?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's almost no run-time difference between using pragma[inline] and hand-inlining a predicate.

In any case, the comment needs to go. It doesn't describe this predicate.

predicate argMayBeUsed(Expr arg, Parameter parm) {
argTypeMayBeUsed(arg.getFullyConverted().getType().getUnspecifiedType(),
parm.getType().getUnspecifiedType())
}

// True if function was ()-declared, but not (void)-declared or K&R-defined
predicate hasZeroParamDecl(Function f) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use pragma[inline] here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I thought it would improve performance. Is this not the case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'll make performance worse in this case. Generally in QL it's best to leave inlining decisions to the compiler.

exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
)
}

// True if this file (or header) was compiled as a C file
predicate isCompiledAsC(Function f) {
exists(File file | file.compiledAsC() |
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
)
}

from FunctionCall fc, Function f, Parameter p
where
f = fc.getTarget() and
p = f.getAParameter() and
hasZeroParamDecl(f) and
isCompiledAsC(f) and
not f.isVarargs() and
not f instanceof BuiltInFunction and
p.getIndex() < fc.getNumberOfArguments() 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.getExplicitlyConverted().getType().getUnspecifiedType() as atype, atype.toString(), p,
p.getTypedName()
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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>
45 changes: 45 additions & 0 deletions cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @name Call to function with fewer arguments than declared parameters
* @description A function call is passing 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 likely to operate on
* undefined data.
* @kind problem
* @problem.severity error
* @precision very-high
* @id cpp/too-few-arguments
* @tags correctness
* maintainability
* security
*/

import cpp

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

// True if this file (or header) was compiled as a C file
predicate isCompiledAsC(Function f) {
exists(File file | file.compiledAsC() |
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
)
}

from FunctionCall fc, Function f
where
f = fc.getTarget() and
not f.isVarargs() and
not f instanceof BuiltInFunction and
hasZeroParamDecl(f) and
isCompiledAsC(f) 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() |
fde.getNumberOfParameters() > fc.getNumberOfArguments()
)
select fc, "This call has fewer arguments than required by $@.", f, f.toString()
10 changes: 10 additions & 0 deletions cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.c
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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>
Loading