From d2e638eedab86f076b1cbd04cd81383e09cb5611 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 30 Mar 2025 18:27:49 -0700 Subject: [PATCH 01/10] Implement expressions2 package --- ...CompareFunctionPointersToConstantValues.md | 129 ++++++++++++++++++ ...CompareFunctionPointersToConstantValues.ql | 101 ++++++++++++++ ...eFunctionPointersToConstantValues.expected | 16 +++ ...pareFunctionPointersToConstantValues.qlref | 1 + c/cert/test/rules/EXP16-C/test.c | 113 +++++++++++++++ .../cpp/exclusions/c/Expressions2.qll | 26 ++++ .../cpp/exclusions/c/RuleMetadata.qll | 3 + .../codingstandards/cpp/types/Compatible.qll | 19 +-- .../cpp/types/FunctionType.qll | 19 +++ rule_packages/c/Expressions2.json | 23 ++++ 10 files changed, 432 insertions(+), 18 deletions(-) create mode 100644 c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.md create mode 100644 c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql create mode 100644 c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected create mode 100644 c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.qlref create mode 100644 c/cert/test/rules/EXP16-C/test.c create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll create mode 100644 cpp/common/src/codingstandards/cpp/types/FunctionType.qll create mode 100644 rule_packages/c/Expressions2.json diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.md b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.md new file mode 100644 index 000000000..b5a7c98bd --- /dev/null +++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.md @@ -0,0 +1,129 @@ +# EXP16-C: Do not compare function pointers to constant values + +This query implements the CERT-C rule EXP16-C: + +> Do not compare function pointers to constant values + + +## Description + +Comparing a function pointer to a value that is not a null function pointer of the same type will be diagnosed because it typically indicates programmer error and can result in [unexpected behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior). Implicit comparisons will be diagnosed, as well. + +## Noncompliant Code Example + +In this noncompliant code example, the addresses of the POSIX functions `getuid` and `geteuid` are compared for equality to 0. Because no function address shall be null, the first subexpression will always evaluate to false (0), and the second subexpression always to true (nonzero). Consequently, the entire expression will always evaluate to true, leading to a potential security vulnerability. + +```cpp +/* First the options that are allowed only for root */ +if (getuid == 0 || geteuid != 0) { + /* ... */ +} + +``` + +## Noncompliant Code Example + +In this noncompliant code example, the function pointers `getuid` and `geteuid` are compared to 0. This example is from an actual [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) ([VU\#837857](http://www.kb.cert.org/vuls/id/837857)) discovered in some versions of the X Window System server. The vulnerability exists because the programmer neglected to provide the open and close parentheses following the `geteuid()` function identifier. As a result, the `geteuid` token returns the address of the function, which is never equal to 0. Consequently, the `or` condition of this `if` statement is always true, and access is provided to the protected block for all users. Many compilers issue a warning noting such pointless expressions. Therefore, this coding error is normally detected by adherence to [MSC00-C. Compile cleanly at high warning levels](https://wiki.sei.cmu.edu/confluence/display/c/MSC00-C.+Compile+cleanly+at+high+warning+levels). + +```cpp +/* First the options that are allowed only for root */ +if (getuid() == 0 || geteuid != 0) { + /* ... */ +} + +``` + +## Compliant Solution + +The solution is to provide the open and close parentheses following the `geteuid` token so that the function is properly invoked: + +```cpp +/* First the options that are allowed only for root */ +if (getuid() == 0 || geteuid() != 0) { + /* ... */ +} + +``` + +## Compliant Solution + +A function pointer can be compared to a null function pointer of the same type: + +```cpp +/* First the options that are allowed only for root */ +if (getuid == (uid_t(*)(void))0 || geteuid != (uid_t(*)(void))0) { + /* ... */ +} + +``` +This code should not be diagnosed by an analyzer. + +## Noncompliant Code Example + +In this noncompliant code example, the function pointer `do_xyz` is implicitly compared unequal to 0: + +```cpp +int do_xyz(void); + +int f(void) { +/* ... */ + if (do_xyz) { + return -1; /* Indicate failure */ + } +/* ... */ + return 0; +} + +``` + +## Compliant Solution + +In this compliant solution, the function `do_xyz()` is invoked and the return value is compared to 0: + +```cpp +int do_xyz(void); + +int f(void) { +/* ... */ + if (do_xyz()) { + return -1; /* Indicate failure */ + } +/* ... */ + return 0; +} + +``` + +## Risk Assessment + +Errors of omission can result in unintended program flow. + +
Recommendation Severity Likelihood Remediation Cost Priority Level
EXP16-C Low Likely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 24.04 function-name-constant-comparison Partially checked
Coverity 2017.07 BAD_COMPARE Can detect the specific instance where the address of a function is compared against 0, such as in the case of geteuid versus getuid() in the implementation-specific details
GCC 4.3.5 Can detect violations of this recommendation when the -Wall flag is used
Helix QAC 2024.4 C0428, C3004, C3344
Klocwork 2024.4 CWARN.NULLCHECK.FUNCNAMECWARN.FUNCADDR
LDRA tool suite 9.7.1 99 S Partially implemented
Parasoft C/C++test 2024.2 CERT_C-EXP16-a Function address should not be compared to zero
PC-lint Plus 1.4 2440, 2441 Partially supported: reports address of function, array, or variable directly or indirectly compared to null
PVS-Studio 7.35 V516, V1058
RuleChecker 24.04 function-name-constant-comparison Partially checked
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP16-C). + +## Related Guidelines + +
SEI CERT C++ Coding Standard VOID EXP16-CPP. Avoid conversions using void pointers
ISO/IEC TR 24772:2013 Likely incorrect expressions \[KOA\]
ISO/IEC TS 17961 Comparing function addresses to zero \[funcaddr\]
MITRE CWE CWE-480 , Use of incorrect operator CWE-482 , Comparing instead of assigning
+ + +## Bibliography + +
\[ Hatton 1995 \] Section 2.7.2, "Errors of Omission and Addition"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [EXP16-C: Do not compare function pointers to constant values](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql new file mode 100644 index 000000000..561374d66 --- /dev/null +++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql @@ -0,0 +1,101 @@ +/** + * @id c/cert/do-not-compare-function-pointers-to-constant-values + * @name EXP16-C: Do not compare function pointers to constant values + * @description Comparing function pointers to a constant value is not reliable and likely indicates + * a programmer error. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/exp16-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import codingstandards.cpp.types.FunctionType +import semmle.code.cpp.controlflow.IRGuards + +class FunctionExpr extends Expr { + Element function; + string funcName; + + FunctionExpr() { + function = this.(FunctionAccess).getTarget() and + funcName = "Function " + function.(Function).getName() + or + this.(VariableAccess).getUnderlyingType() instanceof FunctionType and + function = this and + funcName = "Function pointer variable " + this.(VariableAccess).getTarget().getName() + or + this.getUnderlyingType() instanceof FunctionType and + not this instanceof FunctionAccess and + not this instanceof VariableAccess and + function = this and + funcName = "Expression with function pointer type" + } + + Element getFunction() { result = function } + + string getFuncName() { result = funcName } +} + +abstract class EffectivelyComparison extends Element { + abstract string getExplanation(); + + abstract FunctionExpr getFunctionExpr(); +} + +class ExplicitComparison extends EffectivelyComparison, ComparisonOperation { + Expr constantExpr; + FunctionExpr funcExpr; + + ExplicitComparison() { + funcExpr = getAnOperand() and + constantExpr = getAnOperand() and + exists(constantExpr.getValue()) and + not funcExpr = constantExpr and + not constantExpr.getExplicitlyConverted().getUnderlyingType() = + funcExpr.getExplicitlyConverted().getUnderlyingType() + } + + override string getExplanation() { result = "$@ compared to constant value." } + + override FunctionExpr getFunctionExpr() { result = funcExpr } +} + +class ImplicitComparison extends EffectivelyComparison, GuardCondition { + ImplicitComparison() { + this instanceof FunctionExpr and + not getParent() instanceof ComparisonOperation + } + + override string getExplanation() { result = "$@ undergoes implicit constant comparison." } + + override FunctionExpr getFunctionExpr() { result = this } +} + +from EffectivelyComparison comparison, FunctionExpr funcExpr, Element function, string funcName +where + not isExcluded(comparison, + Expressions2Package::doNotCompareFunctionPointersToConstantValuesQuery()) and + funcExpr = comparison.getFunctionExpr() and + function = funcExpr.getFunction() and + funcName = funcExpr.getFuncName() +select comparison, comparison.getExplanation(), function, funcName +//from +// EqualityOperation equality, FunctionExpr funcExpr, Element function, string funcName, +// Expr constantExpr +//where +// not isExcluded(equality, Expressions2Package::doNotCompareFunctionPointersToConstantValuesQuery()) and +// funcExpr = equality.getAnOperand() and +// constantExpr = equality.getAnOperand() and +// exists(constantExpr.getValue()) and +// function = funcExpr.getFunction() and +// funcName = funcExpr.getFuncName() and +// constantExpr.getFullyConverted().getUnderlyingType() = +// funcExpr.getFullyConverted().getUnderlyingType() +//select equality, +// "Pointer to function $@ compared to constant value." + +// constantExpr.getFullyConverted().getUnderlyingType().explain() + " / " + +// funcExpr.getFullyConverted().getUnderlyingType().explain(), function, funcName diff --git a/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected new file mode 100644 index 000000000..a18f0d32f --- /dev/null +++ b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected @@ -0,0 +1,16 @@ +| test.c:17:7:17:13 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | +| test.c:20:7:20:12 | ... > ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | +| test.c:29:7:29:13 | ... == ... | $@ compared to constant value. | test.c:29:7:29:8 | g1 | Function pointer variable g1 | +| test.c:32:7:32:16 | ... == ... | $@ compared to constant value. | test.c:32:7:32:8 | g2 | Function pointer variable g2 | +| test.c:35:7:35:15 | ... != ... | $@ compared to constant value. | test.c:35:7:35:8 | g1 | Function pointer variable g1 | +| test.c:38:7:38:8 | f1 | $@ undergoes implicit constant comparison. | test.c:3:5:3:6 | f1 | Function f1 | +| test.c:41:7:41:8 | g1 | $@ undergoes implicit constant comparison. | test.c:41:7:41:8 | g1 | Function pointer variable g1 | +| test.c:68:7:68:27 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | +| test.c:71:7:71:18 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | +| test.c:74:7:76:14 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | +| test.c:83:3:83:9 | ... == ... | $@ compared to constant value. | test.c:83:3:83:4 | l1 | Function pointer variable l1 | +| test.c:84:3:84:12 | ... == ... | $@ compared to constant value. | test.c:84:3:84:4 | l1 | Function pointer variable l1 | +| test.c:91:3:91:4 | g1 | $@ undergoes implicit constant comparison. | test.c:91:3:91:4 | g1 | Function pointer variable g1 | +| test.c:96:7:96:18 | ... == ... | $@ compared to constant value. | test.c:96:9:96:10 | fp | Function pointer variable fp | +| test.c:102:7:102:22 | ... == ... | $@ compared to constant value. | test.c:14:11:14:21 | get_handler | Function get_handler | +| test.c:105:7:105:24 | ... == ... | $@ compared to constant value. | test.c:105:7:105:17 | call to get_handler | Expression with function pointer type | diff --git a/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.qlref b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.qlref new file mode 100644 index 000000000..7d99fa987 --- /dev/null +++ b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.qlref @@ -0,0 +1 @@ +rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP16-C/test.c b/c/cert/test/rules/EXP16-C/test.c new file mode 100644 index 000000000..afc1b1b53 --- /dev/null +++ b/c/cert/test/rules/EXP16-C/test.c @@ -0,0 +1,113 @@ +#include + +int f1(); +void (*g1)(void); +int (*g2)(int); +void *g3 = NULL; + +struct S { + int (*fp)(void); + int x; +}; + +typedef int (*handler_t)(void); +handler_t get_handler(void); + +void f2(void) { + if (f1 == 0) // NON-COMPLIANT + return; + + if (f1 > 0) // NON-COMPLIANT + return; + + if (f1() == 0) // COMPLIANT + return; + + if (f1() > 0) // COMPLIANT + return; + + if (g1 == 0) // NON-COMPLIANT + return; + + if (g2 == NULL) // NON-COMPLIANT + return; + + if (g1 != 0x0) // NON-COMPLIANT + return; + + if (f1) // NON-COMPLIANT - implicit comparison + return; + + if (g1) // NON-COMPLIANT - implicit comparison + return; +} + +void f3(void *p1) { + if (g1 == p1) // COMPLIANT - comparing to variable + return; + + if (g2 == g3) // COMPLIANT - comparing to variable + return; +} + +void f4(void) { + int (*l1)(void) = 0; + + if (f1 == f1) // COMPLIANT - comparing to constant value of same type + return; + + if (f1 == l1) // COMPLIANT - comparing to constant value of same type + return; + + if (f1 == (int (*)(void))0) // COMPLIANT - explicit cast + return; + + if (f1 == (int (*)(void))0) // COMPLIANT - explicit cast + return; + + if (f1 == (int (*)(int))0) // NON-COMPLIANT - explicit cast to wrong type + return; + + if (f1 == (int)0) // NON-COMPLIANT - cast to non-function pointer type + return; + + if (f1 == + (int)(int (*)(void)) + NULL) // NON-COMPLIANT - compliant cast subsumed by non-compliant cast + return; +} + +typedef void (*func_t)(void); +void f5(void) { + func_t l1 = g1; + l1 == 0; // NON-COMPLIANT + l1 == NULL; // NON-COMPLIANT + l1 == (func_t)0; // COMPLIANT - cast to function pointer type +} + +void f6(void) { + g1 + 0; // COMPLIANT - not a comparison + g1 == g2; // COMPLIANT - not comparing to constant + g1 ? 1 : 0; // NON-COMPLIANT - implicit comparison +} + +void f7(void) { + struct S s; + if (s.fp == NULL) // NON-COMPLIANT + return; + + if (s.fp() == NULL) // COMPLIANT + return; + + if (get_handler == 0) // NON-COMPLIANT - missing parentheses + return; + + if (get_handler() == 0) // NON-COMPLIANT + return; + + if (get_handler() == (handler_t)0) // COMPLIANT + return; + + if (get_handler()() == 0) // COMPLIANT + return; +} \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll new file mode 100644 index 000000000..cc22a5ce0 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Expressions2Query = TDoNotCompareFunctionPointersToConstantValuesQuery() + +predicate isExpressions2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `doNotCompareFunctionPointersToConstantValues` query + Expressions2Package::doNotCompareFunctionPointersToConstantValuesQuery() and + queryId = + // `@id` for the `doNotCompareFunctionPointersToConstantValues` query + "c/cert/do-not-compare-function-pointers-to-constant-values" and + ruleId = "EXP16-C" and + category = "rule" +} + +module Expressions2Package { + Query doNotCompareFunctionPointersToConstantValuesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotCompareFunctionPointersToConstantValues` query + TQueryC(TExpressions2PackageQuery(TDoNotCompareFunctionPointersToConstantValuesQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index ae035b9c5..b574f7551 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -38,6 +38,7 @@ import Declarations9 import EssentialTypes import EssentialTypes2 import Expressions +import Expressions2 import FloatingTypes import FloatingTypes2 import FunctionTypes @@ -127,6 +128,7 @@ newtype TCQuery = TEssentialTypesPackageQuery(EssentialTypesQuery q) or TEssentialTypes2PackageQuery(EssentialTypes2Query q) or TExpressionsPackageQuery(ExpressionsQuery q) or + TExpressions2PackageQuery(Expressions2Query q) or TFloatingTypesPackageQuery(FloatingTypesQuery q) or TFloatingTypes2PackageQuery(FloatingTypes2Query q) or TFunctionTypesPackageQuery(FunctionTypesQuery q) or @@ -216,6 +218,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isEssentialTypesQueryMetadata(query, queryId, ruleId, category) or isEssentialTypes2QueryMetadata(query, queryId, ruleId, category) or isExpressionsQueryMetadata(query, queryId, ruleId, category) or + isExpressions2QueryMetadata(query, queryId, ruleId, category) or isFloatingTypesQueryMetadata(query, queryId, ruleId, category) or isFloatingTypes2QueryMetadata(query, queryId, ruleId, category) or isFunctionTypesQueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll index d6f65126e..56966b577 100644 --- a/cpp/common/src/codingstandards/cpp/types/Compatible.qll +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -1,6 +1,7 @@ import cpp import codeql.util.Boolean import codingstandards.cpp.types.Graph +import codingstandards.cpp.types.FunctionType module TypeNamesMatchConfig implements TypeEquivalenceSig { predicate resolveTypedefs() { @@ -352,21 +353,3 @@ module FunctionDeclarationTypeEquivalence { ) } } - -/** - * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` - * don't have a common ancestor. - */ -private class FunctionType extends Type { - FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } - - Type getReturnType() { - result = this.(RoutineType).getReturnType() or - result = this.(FunctionPointerIshType).getReturnType() - } - - Type getParameterType(int i) { - result = this.(RoutineType).getParameterType(i) or - result = this.(FunctionPointerIshType).getParameterType(i) - } -} diff --git a/cpp/common/src/codingstandards/cpp/types/FunctionType.qll b/cpp/common/src/codingstandards/cpp/types/FunctionType.qll new file mode 100644 index 000000000..e43d1067f --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/FunctionType.qll @@ -0,0 +1,19 @@ +import cpp + +/** + * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` + * don't have a common ancestor. + */ +class FunctionType extends Type { + FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } + + Type getReturnType() { + result = this.(RoutineType).getReturnType() or + result = this.(FunctionPointerIshType).getReturnType() + } + + Type getParameterType(int i) { + result = this.(RoutineType).getParameterType(i) or + result = this.(FunctionPointerIshType).getParameterType(i) + } +} \ No newline at end of file diff --git a/rule_packages/c/Expressions2.json b/rule_packages/c/Expressions2.json new file mode 100644 index 000000000..88b5dff98 --- /dev/null +++ b/rule_packages/c/Expressions2.json @@ -0,0 +1,23 @@ +{ + "CERT-C": { + "EXP16-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Comparing function pointers to a constant value is not reliable and likely indicates a programmer error.", + "kind": "problem", + "name": "Do not compare function pointers to constant values", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotCompareFunctionPointersToConstantValues", + "tags": [ + "correctness" + ] + } + ], + "title": "Do not compare function pointers to constant values" + } + } +} \ No newline at end of file From f6d62ecf55ec81dec636c3ce3d9fa2ead440aa3e Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Mon, 31 Mar 2025 13:09:44 -0700 Subject: [PATCH 02/10] Format --- cpp/common/src/codingstandards/cpp/types/FunctionType.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/common/src/codingstandards/cpp/types/FunctionType.qll b/cpp/common/src/codingstandards/cpp/types/FunctionType.qll index e43d1067f..20166322f 100644 --- a/cpp/common/src/codingstandards/cpp/types/FunctionType.qll +++ b/cpp/common/src/codingstandards/cpp/types/FunctionType.qll @@ -16,4 +16,4 @@ class FunctionType extends Type { result = this.(RoutineType).getParameterType(i) or result = this.(FunctionPointerIshType).getParameterType(i) } -} \ No newline at end of file +} From 067e1724a04df9eaaad348c4886a72e3276c9bd1 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 9 Apr 2025 20:39:32 -0700 Subject: [PATCH 03/10] Change obligation --- .../EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql | 2 +- .../src/codingstandards/cpp/exclusions/c/Expressions2.qll | 2 +- rule_packages/c/Expressions2.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql index 561374d66..88beae6bc 100644 --- a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql +++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql @@ -8,7 +8,7 @@ * @problem.severity error * @tags external/cert/id/exp16-c * correctness - * external/cert/obligation/rule + * external/cert/obligation/recommendation */ import cpp diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll index cc22a5ce0..e7dffc30b 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions2.qll @@ -13,7 +13,7 @@ predicate isExpressions2QueryMetadata(Query query, string queryId, string ruleId // `@id` for the `doNotCompareFunctionPointersToConstantValues` query "c/cert/do-not-compare-function-pointers-to-constant-values" and ruleId = "EXP16-C" and - category = "rule" + category = "recommendation" } module Expressions2Package { diff --git a/rule_packages/c/Expressions2.json b/rule_packages/c/Expressions2.json index 88b5dff98..7eaa2ff2e 100644 --- a/rule_packages/c/Expressions2.json +++ b/rule_packages/c/Expressions2.json @@ -2,7 +2,7 @@ "CERT-C": { "EXP16-C": { "properties": { - "obligation": "rule" + "obligation": "recommendation" }, "queries": [ { From 010b7c7d2b626dad88511d6292afc837eecd534d Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 9 Apr 2025 20:40:20 -0700 Subject: [PATCH 04/10] Remove commented out code --- ...NotCompareFunctionPointersToConstantValues.ql | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql index 88beae6bc..d9622b03b 100644 --- a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql +++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql @@ -83,19 +83,3 @@ where function = funcExpr.getFunction() and funcName = funcExpr.getFuncName() select comparison, comparison.getExplanation(), function, funcName -//from -// EqualityOperation equality, FunctionExpr funcExpr, Element function, string funcName, -// Expr constantExpr -//where -// not isExcluded(equality, Expressions2Package::doNotCompareFunctionPointersToConstantValuesQuery()) and -// funcExpr = equality.getAnOperand() and -// constantExpr = equality.getAnOperand() and -// exists(constantExpr.getValue()) and -// function = funcExpr.getFunction() and -// funcName = funcExpr.getFuncName() and -// constantExpr.getFullyConverted().getUnderlyingType() = -// funcExpr.getFullyConverted().getUnderlyingType() -//select equality, -// "Pointer to function $@ compared to constant value." + -// constantExpr.getFullyConverted().getUnderlyingType().explain() + " / " + -// funcExpr.getFullyConverted().getUnderlyingType().explain(), function, funcName From 6a5aa1f09699c6f1259adf4cc7c2b57bc087e007 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 13 Jun 2025 08:30:34 -0700 Subject: [PATCH 05/10] Fix compilation error --- .../codingstandards/cpp/types/Compatible.qll | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll index 3b77d5caf..eb0e13101 100644 --- a/cpp/common/src/codingstandards/cpp/types/Compatible.qll +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -522,29 +522,3 @@ module FunctionDeclarationTypeEquivalence< .getType(), f2.getParameterDeclarationEntry(pragma[only_bind_into](i)).getType()) } } - -/** - * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` - * don't have a common ancestor. - */ -private class FunctionType extends Type { - FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } - - Type getReturnType() { - result = this.(RoutineType).getReturnType() or - result = this.(FunctionPointerIshType).getReturnType() - } - - Type getParameterType(int i) { - result = this.(RoutineType).getParameterType(i) or - result = this.(FunctionPointerIshType).getParameterType(i) - } -} - -private class LeafType extends Type { - LeafType() { - not this instanceof DerivedType and - not this instanceof FunctionType and - not this instanceof FunctionType - } -} From df1bd3a084718b0b554d613dd9f4a7614a95e4d9 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 15 Jun 2025 21:16:11 -0700 Subject: [PATCH 06/10] Add missing CERT-C query tags --- .../DoNotCompareFunctionPointersToConstantValues.ql | 6 ++++++ rule_packages/c/Expressions2.json | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql index d9622b03b..fecd5450d 100644 --- a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql +++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql @@ -9,6 +9,12 @@ * @tags external/cert/id/exp16-c * correctness * external/cert/obligation/recommendation + * external/cert/severity/low + * external/cert/likelihood/likely + * external/cert/remediation-cost/ + * external/cert/priority/p6 + * external/cert/level/l2 + * external/cert/obligation/recommendation */ import cpp diff --git a/rule_packages/c/Expressions2.json b/rule_packages/c/Expressions2.json index 7eaa2ff2e..c9e2434f8 100644 --- a/rule_packages/c/Expressions2.json +++ b/rule_packages/c/Expressions2.json @@ -13,7 +13,13 @@ "severity": "error", "short_name": "DoNotCompareFunctionPointersToConstantValues", "tags": [ - "correctness" + "correctness", + "external/cert/obligation/recommendation", + "external/cert/severity/low", + "external/cert/likelihood/likely", + "external/cert/remediation-cost/", + "external/cert/priority/p6", + "external/cert/level/l2" ] } ], From bb7c913eea8391f110f6289db379fea0d96cbd4d Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 15 Jun 2025 21:36:45 -0700 Subject: [PATCH 07/10] Fix remediation cost --- .../EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql | 2 +- rule_packages/c/Expressions2.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql index fecd5450d..bd2167c92 100644 --- a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql +++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql @@ -11,7 +11,7 @@ * external/cert/obligation/recommendation * external/cert/severity/low * external/cert/likelihood/likely - * external/cert/remediation-cost/ + * external/cert/remediation-cost/medium * external/cert/priority/p6 * external/cert/level/l2 * external/cert/obligation/recommendation diff --git a/rule_packages/c/Expressions2.json b/rule_packages/c/Expressions2.json index c9e2434f8..7c78e02fd 100644 --- a/rule_packages/c/Expressions2.json +++ b/rule_packages/c/Expressions2.json @@ -17,7 +17,7 @@ "external/cert/obligation/recommendation", "external/cert/severity/low", "external/cert/likelihood/likely", - "external/cert/remediation-cost/", + "external/cert/remediation-cost/medium", "external/cert/priority/p6", "external/cert/level/l2" ] From fbf21bd3ae4b630f39b15e2c86866ac0db0772e2 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 15 Jun 2025 21:37:32 -0700 Subject: [PATCH 08/10] Undo accidental deletion of LeafType --- cpp/common/src/codingstandards/cpp/types/Compatible.qll | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll index eb0e13101..83983e1df 100644 --- a/cpp/common/src/codingstandards/cpp/types/Compatible.qll +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -522,3 +522,11 @@ module FunctionDeclarationTypeEquivalence< .getType(), f2.getParameterDeclarationEntry(pragma[only_bind_into](i)).getType()) } } + +private class LeafType extends Type { + LeafType() { + not this instanceof DerivedType and + not this instanceof FunctionType and + not this instanceof FunctionType + } +} From e4121eeb044129a2205162b2dc20326b72491ffe Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 15 Jun 2025 21:41:35 -0700 Subject: [PATCH 09/10] Remove cert c obligation tag --- .../EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql | 1 - rule_packages/c/Expressions2.json | 1 - 2 files changed, 2 deletions(-) diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql index bd2167c92..bdd4f1b37 100644 --- a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql +++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql @@ -8,7 +8,6 @@ * @problem.severity error * @tags external/cert/id/exp16-c * correctness - * external/cert/obligation/recommendation * external/cert/severity/low * external/cert/likelihood/likely * external/cert/remediation-cost/medium diff --git a/rule_packages/c/Expressions2.json b/rule_packages/c/Expressions2.json index 7c78e02fd..d639ae2c3 100644 --- a/rule_packages/c/Expressions2.json +++ b/rule_packages/c/Expressions2.json @@ -14,7 +14,6 @@ "short_name": "DoNotCompareFunctionPointersToConstantValues", "tags": [ "correctness", - "external/cert/obligation/recommendation", "external/cert/severity/low", "external/cert/likelihood/likely", "external/cert/remediation-cost/medium", From e655ed85787c8e301370c9a941f4d7d4f1279cad Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Thu, 19 Jun 2025 14:56:50 -0700 Subject: [PATCH 10/10] Exclude if (f) f() from EXP-16-C --- ...CompareFunctionPointersToConstantValues.ql | 31 ++-------- ...eFunctionPointersToConstantValues.expected | 34 ++++++----- c/cert/test/rules/EXP16-C/test.c | 42 ++++++++++++- .../cpp/exprs/FunctionExprs.qll | 59 +++++++++++++++++++ .../src/codingstandards/cpp/exprs/Guards.qll | 34 +++++++++++ 5 files changed, 158 insertions(+), 42 deletions(-) create mode 100644 cpp/common/src/codingstandards/cpp/exprs/FunctionExprs.qll create mode 100644 cpp/common/src/codingstandards/cpp/exprs/Guards.qll diff --git a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql index bdd4f1b37..e65d58a65 100644 --- a/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql +++ b/c/cert/src/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.ql @@ -17,33 +17,11 @@ */ import cpp +import semmle.code.cpp.controlflow.IRGuards import codingstandards.c.cert import codingstandards.cpp.types.FunctionType -import semmle.code.cpp.controlflow.IRGuards - -class FunctionExpr extends Expr { - Element function; - string funcName; - - FunctionExpr() { - function = this.(FunctionAccess).getTarget() and - funcName = "Function " + function.(Function).getName() - or - this.(VariableAccess).getUnderlyingType() instanceof FunctionType and - function = this and - funcName = "Function pointer variable " + this.(VariableAccess).getTarget().getName() - or - this.getUnderlyingType() instanceof FunctionType and - not this instanceof FunctionAccess and - not this instanceof VariableAccess and - function = this and - funcName = "Expression with function pointer type" - } - - Element getFunction() { result = function } - - string getFuncName() { result = funcName } -} +import codingstandards.cpp.exprs.FunctionExprs +import codingstandards.cpp.exprs.Guards abstract class EffectivelyComparison extends Element { abstract string getExplanation(); @@ -85,6 +63,7 @@ where not isExcluded(comparison, Expressions2Package::doNotCompareFunctionPointersToConstantValuesQuery()) and funcExpr = comparison.getFunctionExpr() and + not exists(NullFunctionCallGuard nullGuard | nullGuard.getFunctionExpr() = funcExpr) and function = funcExpr.getFunction() and - funcName = funcExpr.getFuncName() + funcName = funcExpr.describe() select comparison, comparison.getExplanation(), function, funcName diff --git a/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected index a18f0d32f..403d21165 100644 --- a/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected +++ b/c/cert/test/rules/EXP16-C/DoNotCompareFunctionPointersToConstantValues.expected @@ -1,16 +1,20 @@ -| test.c:17:7:17:13 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | -| test.c:20:7:20:12 | ... > ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | -| test.c:29:7:29:13 | ... == ... | $@ compared to constant value. | test.c:29:7:29:8 | g1 | Function pointer variable g1 | -| test.c:32:7:32:16 | ... == ... | $@ compared to constant value. | test.c:32:7:32:8 | g2 | Function pointer variable g2 | -| test.c:35:7:35:15 | ... != ... | $@ compared to constant value. | test.c:35:7:35:8 | g1 | Function pointer variable g1 | -| test.c:38:7:38:8 | f1 | $@ undergoes implicit constant comparison. | test.c:3:5:3:6 | f1 | Function f1 | -| test.c:41:7:41:8 | g1 | $@ undergoes implicit constant comparison. | test.c:41:7:41:8 | g1 | Function pointer variable g1 | -| test.c:68:7:68:27 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | -| test.c:71:7:71:18 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | -| test.c:74:7:76:14 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Function f1 | -| test.c:83:3:83:9 | ... == ... | $@ compared to constant value. | test.c:83:3:83:4 | l1 | Function pointer variable l1 | -| test.c:84:3:84:12 | ... == ... | $@ compared to constant value. | test.c:84:3:84:4 | l1 | Function pointer variable l1 | -| test.c:91:3:91:4 | g1 | $@ undergoes implicit constant comparison. | test.c:91:3:91:4 | g1 | Function pointer variable g1 | -| test.c:96:7:96:18 | ... == ... | $@ compared to constant value. | test.c:96:9:96:10 | fp | Function pointer variable fp | -| test.c:102:7:102:22 | ... == ... | $@ compared to constant value. | test.c:14:11:14:21 | get_handler | Function get_handler | +| test.c:17:7:17:13 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:20:7:20:12 | ... > ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:29:7:29:13 | ... == ... | $@ compared to constant value. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:32:7:32:16 | ... == ... | $@ compared to constant value. | test.c:5:7:5:8 | g2 | Function pointer variable g2 | +| test.c:35:7:35:15 | ... != ... | $@ compared to constant value. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:38:7:38:8 | f1 | $@ undergoes implicit constant comparison. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:41:7:41:8 | g1 | $@ undergoes implicit constant comparison. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:68:7:68:27 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:71:7:71:18 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:74:7:76:14 | ... == ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:83:3:83:9 | ... == ... | $@ compared to constant value. | test.c:82:10:82:11 | l1 | Function pointer variable l1 | +| test.c:84:3:84:12 | ... == ... | $@ compared to constant value. | test.c:82:10:82:11 | l1 | Function pointer variable l1 | +| test.c:91:3:91:4 | g1 | $@ undergoes implicit constant comparison. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:96:7:96:18 | ... == ... | $@ compared to constant value. | test.c:9:9:9:10 | fp | Function pointer variable fp | +| test.c:102:7:102:22 | ... == ... | $@ compared to constant value. | test.c:14:11:14:21 | get_handler | Address of function get_handler | | test.c:105:7:105:24 | ... == ... | $@ compared to constant value. | test.c:105:7:105:17 | call to get_handler | Expression with function pointer type | +| test.c:121:7:121:13 | ... != ... | $@ compared to constant value. | test.c:3:5:3:6 | f1 | Address of function f1 | +| test.c:133:7:133:13 | ... != ... | $@ compared to constant value. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:139:7:139:13 | ... == ... | $@ compared to constant value. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | +| test.c:149:8:149:9 | g1 | $@ undergoes implicit constant comparison. | test.c:4:8:4:9 | g1 | Function pointer variable g1 | diff --git a/c/cert/test/rules/EXP16-C/test.c b/c/cert/test/rules/EXP16-C/test.c index afc1b1b53..16dfea9bc 100644 --- a/c/cert/test/rules/EXP16-C/test.c +++ b/c/cert/test/rules/EXP16-C/test.c @@ -94,7 +94,7 @@ void f6(void) { void f7(void) { struct S s; if (s.fp == NULL) // NON-COMPLIANT - return; + f1(); if (s.fp() == NULL) // COMPLIANT return; @@ -110,4 +110,44 @@ void f7(void) { if (get_handler()() == 0) // COMPLIANT return; +} + +void f8(void) { + // Test instances of where the function pointer check is used to guard calls + // to that function. + + // Technically, this function may perhaps be set to NULL by the linker. But + // it is not a variable that should need to be null-checked at runtime. + if (f1 != 0) // NON-COMPLIANT + { + f1(); + } + + // Check guards a call, so it is compliant. + if (g1 != 0) // COMPLIANT + { + g1(); + } + + // Incorrect check, not compliant. + if (g1 != 0) // NON-COMPLIANT + { + f1(); + } + + // Incorrect check, not compliant. + if (g1 == 0) // NON-COMPLIANT + { + g1(); + } + + if (g1) // COMPLIANT + { + g1(); + } + + if (!g1) // NON-COMPLIANT + { + g1(); + } } \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/exprs/FunctionExprs.qll b/cpp/common/src/codingstandards/cpp/exprs/FunctionExprs.qll new file mode 100644 index 000000000..5c46fce07 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exprs/FunctionExprs.qll @@ -0,0 +1,59 @@ +import cpp +import codingstandards.cpp.types.FunctionType + +/** + * A class representing an expression that has a function pointer type. This can be a function + * access, a variable access, or any expression that has a function pointer type. + */ +abstract class FunctionExpr extends Expr { + /** Any element that could represent the function, for example, a variable or an expression. */ + abstract Element getFunction(); + + /** A name or string that describes the function. */ + abstract string describe(); + + /** Get calls of this function */ + abstract Call getACall(); +} + +/** + * A function access is an an expression of function type where we know the function. + */ +class SimpleFunctionAccess extends FunctionExpr, FunctionAccess { + override Element getFunction() { result = this.getTarget() } + + override string describe() { result = "Address of function " + this.getTarget().getName() } + + override FunctionCall getACall() { result.getTarget() = this.getTarget() } +} + +/** + * An access of a variable that has a function pointer type is also a function expression, for which + * we can track certain properties of the function. + */ +class FunctionVariableAccess extends FunctionExpr, VariableAccess { + FunctionVariableAccess() { this.getUnderlyingType() instanceof FunctionType } + + override Element getFunction() { result = this.getTarget() } + + override string describe() { result = "Function pointer variable " + this.getTarget().getName() } + + override ExprCall getACall() { result.getExpr().(VariableAccess).getTarget() = this.getTarget() } +} + +/** + * A function typed expression that is not a function access or a variable access. + */ +class FunctionTypedExpr extends FunctionExpr { + FunctionTypedExpr() { + this.getUnderlyingType() instanceof FunctionType and + not this instanceof FunctionAccess and + not this instanceof VariableAccess + } + + override Element getFunction() { result = this } + + override string describe() { result = "Expression with function pointer type" } + + override ExprCall getACall() { result.getExpr() = this } +} diff --git a/cpp/common/src/codingstandards/cpp/exprs/Guards.qll b/cpp/common/src/codingstandards/cpp/exprs/Guards.qll new file mode 100644 index 000000000..73a35ccc6 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exprs/Guards.qll @@ -0,0 +1,34 @@ +import cpp +import codeql.util.Boolean +import semmle.code.cpp.controlflow.Guards +import codingstandards.cpp.exprs.FunctionExprs + +/** + * A guard of the form: `if (funcPtr) funcPtr();`, e.g., a null check on a function before calling + * that function. + * + * Note this does not consider the above to be a null function call guard if `funcPtr` is a + * function name, as that could only be null via unusual linkage steps, and is not expected to be + * an intentional null check. + */ +class NullFunctionCallGuard extends GuardCondition { + FunctionExpr expr; + + NullFunctionCallGuard() { + exists(BasicBlock block, Call guardedCall | + ( + // Handles 'if (funcPtr != NULL)`: + this.ensuresEq(expr, 0, block, false) + or + // Handles `if (funcPtr)` in C where no implicit conversion to bool exists: + expr = this and + expr.getFunction() instanceof Variable and + this.controls(block, true) + ) and + guardedCall = expr.getACall() and + block.contains(guardedCall) + ) + } + + FunctionExpr getFunctionExpr() { result = expr } +}