From 46d09db02ea2ce17a6a6996039dddb9a969592dd Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Tue, 24 Sep 2024 10:32:11 -0700 Subject: [PATCH 1/6] First pass on 17-12, 17-13, both facing extractor issues. --- .../FunctionAddressesShouldAddressOperator.ql | 83 ++++++++++ .../DisallowedFunctionTypeQualifier.ql | 147 ++++++++++++++++++ ...ionAddressesShouldAddressOperator.expected | 18 +++ ...nctionAddressesShouldAddressOperator.qlref | 1 + c/misra/test/rules/RULE-17-12/test.c | 106 +++++++++++++ .../DisallowedFunctionTypeQualifier.expected | 1 + .../DisallowedFunctionTypeQualifier.qlref | 1 + c/misra/test/rules/RULE-17-13/test.c | 56 +++++++ .../cpp/exclusions/c/FunctionTypes.qll | 44 ++++++ .../cpp/exclusions/c/RuleMetadata.qll | 3 + rule_packages/c/FunctionTypes.json | 42 +++++ 11 files changed, 502 insertions(+) create mode 100644 c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql create mode 100644 c/misra/src/rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql create mode 100644 c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected create mode 100644 c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.qlref create mode 100644 c/misra/test/rules/RULE-17-12/test.c create mode 100644 c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.expected create mode 100644 c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.qlref create mode 100644 c/misra/test/rules/RULE-17-13/test.c create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll create mode 100644 rule_packages/c/FunctionTypes.json diff --git a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql new file mode 100644 index 0000000000..4c3386c68b --- /dev/null +++ b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql @@ -0,0 +1,83 @@ +/** + * @id c/misra/function-addresses-should-address-operator + * @name RULE-17-12: A function identifier should only be called with a parenthesized parameter list or used with a & + * @description A function identifier should only be called with a parenthesized parameter list or + * used with a & (address-of). + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-12 + * readability + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +abstract class AddressOfFunction extends Expr { + abstract predicate isImplicitlyAddressed(); + + abstract string getFuncName(); +} + +class FunctionTypeAccess extends FunctionAccess, AddressOfFunction { + + predicate isImmediatelyParenthesized() { + exists(ParenthesisExpr parens | parens.getExpr() = this) + } + + predicate isExplicitlyAddressed() { + getParent() instanceof AddressOfExpr and + not isImmediatelyParenthesized() + } + + override predicate isImplicitlyAddressed() { + not isExplicitlyAddressed() + } + + override string getFuncName() { + result = getTarget().getName() + } +} + +/* +class IndirectFunctionCall extends FunctionCall, AddressOfFunction { + override predicate isImplicitlyAddressed() { + getConversion+() instanceof ParenthesisExpr + } + + override string getFuncName() { + result = getTarget().getName() + } +} + */ + +class MacroArgTakesFunction extends AddressOfFunction { + MacroInvocation m; + MacroArgTakesFunction() { + m.getExpr() = this + } + + override predicate isImplicitlyAddressed() { + any() + } + + string getProp() { + result = m.getExpandedArgument(_) + and this.get + } + + override string getFuncName() { + result = "a macro argument" + } + +} + +from AddressOfFunction funcAddr +where + not isExcluded(funcAddr, FunctionTypesPackage::functionAddressesShouldAddressOperatorQuery()) and + //not funcAccess.isImmediatelyCalled() and + //not funcAccess.isExplicitlyAddressed() + funcAddr.isImplicitlyAddressed() +select + funcAddr, "The address of function " + funcAddr.getFuncName() + " is taken without the & operator." \ No newline at end of file diff --git a/c/misra/src/rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql b/c/misra/src/rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql new file mode 100644 index 0000000000..a40dadef6c --- /dev/null +++ b/c/misra/src/rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql @@ -0,0 +1,147 @@ +/** + * @id c/misra/disallowed-function-type-qualifier + * @name RULE-17-13: A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic) + * @description The behavior of type qualifiers on a function type is undefined. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-13 + * correctness + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +//from DeclarationEntry decl, Type type //, Specifier specifier +//where +// not isExcluded(decl, FunctionTypesPackage::disallowedFunctionTypeQualifierQuery()) and +// //decl.getType() instanceof FunctionPointerType and +// //decl.getType().(FunctionPointerType).hasSpecifier(specifier) +// (type = decl.getType().getUnderlyingType*() +// or type = decl.getType()) +// and +// //specifier = type.getASpecifier() +// any() +//select decl, type //, specifier // "The behavior of type qualifier " + specifier + " on a function type is undefined." + +newtype TDeclaredFunction = + TFunctionDeclaration(Declaration declaration) + +abstract class DeclaredFunction extends TDeclaredFunction { + abstract string toString(); +} + +predicate isConstFunction(Type type) { + (type.getASpecifier().getName() = "const" + or type.isConst()) + and isFunctionType(type) + or isConstFunction(type.getUnderlyingType()) +} + +predicate isFunctionType(Type type) { + type instanceof FunctionPointerType + or isFunctionType(type.getUnderlyingType()) +} + +predicate declaresConstFunction(DeclarationEntry entry) { + (entry.getDeclaration().getASpecifier().getName() = "const" + and isFunctionType(entry.getType())) + or isConstFunction(entry.getType()) +} + +class QualifiableRoutineType extends RoutineType, QualifiableType { + override string explainQualifiers() { + result = "func{" + + specifiersOf(this) + this.getReturnType().(QualifiableType).explainQualifiers() + + " (" + + paramString(0) + + ")}" + } + + string paramString(int i) { + i = 0 and result = "" and not exists(this.getAParameterType()) + or + ( + if i < max(int j | exists(this.getParameterType(j))) + then + // Not the last one + result = this.getParameterType(i).(QualifiableType).explainQualifiers() + "," + this.paramString(i + 1) + else + // Last parameter + result = this.getParameterType(i).(QualifiableType).explainQualifiers() + ) + } +} + +class QualifiableIntType extends IntType, QualifiableType { + override string explainQualifiers() { + result = specifiersOf(this) + " " + this.toString() + } +} + +class QualifiablePointerType extends PointerType, QualifiableType { + override string explainQualifiers() { + result = "{" + + specifiersOf(this) + + " pointer to " + + this.getBaseType().(QualifiableType).explainQualifiers() + + "}" + } +} + +class QualifiableType extends Type { + string explainQualifiers() { + result = "Unimplemented explainQualifiers for type(s): " + concat(string s | s = getAQlClass() | s, ",") + } +} + +class QualifiableTypedefType extends TypedefType, QualifiableType { + override string explainQualifiers() { + result = "{ typedef " + + specifiersOf(this) + + " " + + this.getBaseType().(QualifiableType).explainQualifiers() + + "}" + } +} + +class QualifiableSpecifiedType extends SpecifiedType, QualifiableType { + override string explainQualifiers() { + result = "{" + + specifiersOf(this) + + " " + + this.getBaseType().(QualifiableType).explainQualifiers() + + "}" + } +} + +string typeString(Type t) { + //if + // t instanceof CTypedefType + // then result = t.(CTypedefType).explain() + "specs:" + specifiersOf(t.(CTypedefType).getBaseType()) + "/" + typeString(t.(CTypedefType).getBaseType()) + // else + //result = concat(string s | s = t.getAQlClass() | s, ",") + result = t.(QualifiableType).explainQualifiers() +} + +string specifiersOf(Type t) { + result = concat(Specifier s | s = t.getASpecifier()| s.getName(), ", ") +} + +string declSpecifiersOf(Declaration d) { + result = concat(Specifier s | s = d.getASpecifier()| s.getName(), ", ") +} + +string underlying(Type t) { + exists(Type u | u = t.getUnderlyingType() | result = u.toString()) + or result = "[no underlying]" + +} + +from DeclarationEntry entry +select entry, entry.getType(), typeString(entry.getType()), declSpecifiersOf(entry.getDeclaration()), specifiersOf(entry.getType()) + +//from Type t +//where any()//isFunctionType(t) +//select t, specifiersOf(t), underlying(t) \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected new file mode 100644 index 0000000000..1cc92e95e1 --- /dev/null +++ b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected @@ -0,0 +1,18 @@ +| test.c:15:25:15:29 | func2 | The address of function func2 is taken without the & operator. | +| test.c:16:27:16:31 | func3 | The address of function func3 is taken without the & operator. | +| test.c:22:16:22:20 | func1 | The address of function func1 is taken without the & operator. | +| test.c:38:5:38:9 | func1 | The address of function func1 is taken without the & operator. | +| test.c:39:5:39:9 | func2 | The address of function func2 is taken without the & operator. | +| test.c:47:7:47:11 | func1 | The address of function func1 is taken without the & operator. | +| test.c:48:7:48:11 | func2 | The address of function func2 is taken without the & operator. | +| test.c:57:15:57:19 | func1 | The address of function func1 is taken without the & operator. | +| test.c:58:23:58:27 | func2 | The address of function func2 is taken without the & operator. | +| test.c:59:15:59:19 | func1 | The address of function func1 is taken without the & operator. | +| test.c:59:22:59:26 | func2 | The address of function func2 is taken without the & operator. | +| test.c:67:13:67:17 | func1 | The address of function func1 is taken without the & operator. | +| test.c:68:14:68:18 | func1 | The address of function func1 is taken without the & operator. | +| test.c:69:14:69:18 | func1 | The address of function func1 is taken without the & operator. | +| test.c:71:20:71:24 | func1 | The address of function func1 is taken without the & operator. | +| test.c:72:20:72:24 | func1 | The address of function func1 is taken without the & operator. | +| test.c:76:20:76:24 | func1 | The address of function func1 is taken without the & operator. | +| test.c:77:20:77:24 | func1 | The address of function func1 is taken without the & operator. | diff --git a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.qlref b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.qlref new file mode 100644 index 0000000000..f0a4753620 --- /dev/null +++ b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.qlref @@ -0,0 +1 @@ +rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-12/test.c b/c/misra/test/rules/RULE-17-12/test.c new file mode 100644 index 0000000000..1c580f917e --- /dev/null +++ b/c/misra/test/rules/RULE-17-12/test.c @@ -0,0 +1,106 @@ +void func1() {} +void func2(int x, char* y) {} + +typedef struct {} s; + +int func3() { + return 0; +} + +typedef void (*func_ptr_t1)(); +typedef void (*func_ptr_t2)(int x, char* y); +typedef s (*func_ptr_t3)(); + +func_ptr_t1 func_ptr1 = &func1; // COMPLIANT +func_ptr_t2 func_ptr2 = func2; // NON-COMPLIANT +func_ptr_t3 func_ptr3 = &(func3); // NON-COMPLIANT + +void take_func(func_ptr_t1 f1, func_ptr_t2 f2); + +func_ptr_t1 returns_func(int x) { + if (x == 0) { + return func1; // NON-COMPLIANT + } else if (x == 1) { + return &func1; // COMPLIANT + } + + return returns_func(0); // COMPLIANT +} + +#define MACRO_IDENTITY(f) (f) +#define MACRO_INVOKE_RISKY(f) (f()) +#define MACRO_INVOKE_IMPROVED(f) ((f)()) + +void test() { + func1(); // COMPLIANT + func2(1, "hello"); // COMPLIANT + + func1; // NON-COMPLIANT + func2; // NON-COMPLIANT + + &func1; // COMPLIANT + &func2; // COMPLIANT + + (func1)(); // COMPLIANT + (func2)(1, "hello"); // COMPLIANT + + &(func1); // NON-COMPLIANT + &(func2); // NON-COMPLIANT + + (&func1)(); // COMPLIANT + (&func2)(1, "hello"); // COMPLIANT + + (func1()); // COMPLIANT + (func2(1, "hello")); // COMPLIANT + + take_func(&func1, &func2); // COMPLIANT + take_func(func1, &func2); // NON-COMPLIANT + take_func(&func1, func2); // NON-COMPLIANT + take_func(func1, func2); // NON-COMPLIANT + + returns_func(0); // COMPLIANT + returns_func(0)(); // COMPLIANT + (returns_func(0))(); // COMPLIANT + + (void*) &func1; // COMPLIANT + (void*) (&func1); // COMPLIANT + (void*) func1; // NON-COMPLIANT + (void*) (func1); // NON-COMPLIANT + ((void*) func1); // NON-COMPLIANT + + MACRO_IDENTITY(func1); // NON-COMPLIANT + MACRO_IDENTITY(func1)(); // NON-COMPLIANT + MACRO_IDENTITY(&func1); // COMPLIANT + MACRO_IDENTITY(&func1)(); // COMPLIANT + + MACRO_INVOKE_RISKY(func3); // NON-COMPLIANT + MACRO_INVOKE_IMPROVED(func3); // NON-COMPLIANT + MACRO_INVOKE_IMPROVED(&func3); // COMPLIANT + + // Function pointers are exempt from this rule. + func_ptr1(); // COMPLIANT + func_ptr2(1, "hello"); // COMPLIANT + func_ptr1; // COMPLIANT + func_ptr2; // COMPLIANT + &func_ptr1; // COMPLIANT + &func_ptr2; // COMPLIANT + (func_ptr1)(); // COMPLIANT + (func_ptr2)(1, "hello"); // COMPLIANT + (*func_ptr1)(); // COMPLIANT + (*func_ptr2)(1, "hello"); // COMPLIANT + take_func(func_ptr1, func_ptr2); // COMPLIANT + (void*) func_ptr1; // COMPLIANT + (void*) &func_ptr1; // COMPLIANT + (void*) (&func_ptr1); // COMPLIANT + (void*) func_ptr1; // COMPLIANT + (void*) (func_ptr1); // COMPLIANT + ((void*) func_ptr1); // COMPLIANT + MACRO_IDENTITY(func_ptr1); // COMPLIANT + MACRO_IDENTITY(func_ptr1)(); // COMPLIANT + MACRO_IDENTITY(&func_ptr1); // COMPLIANT + (*MACRO_IDENTITY(&func_ptr1))(); // COMPLIANT + MACRO_INVOKE_RISKY(func_ptr3); // COMPLIANT + MACRO_INVOKE_IMPROVED(func_ptr3); // COMPLIANT + MACRO_INVOKE_IMPROVED(*&func_ptr3); // COMPLIANT + +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.expected b/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.qlref b/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.qlref new file mode 100644 index 0000000000..cbf7c583ec --- /dev/null +++ b/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.qlref @@ -0,0 +1 @@ +rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-13/test.c b/c/misra/test/rules/RULE-17-13/test.c new file mode 100644 index 0000000000..8326b30a28 --- /dev/null +++ b/c/misra/test/rules/RULE-17-13/test.c @@ -0,0 +1,56 @@ +// semmle-extractor-options: --language=c -std=c99 +const int x; // COMPLIANT +const int f_ret_const_int(void); // COMPLIANT +const int* f_ret_const_int_ptr(void); // COMPLIANT + +// Basic function typedefs +typedef int ftype_ret_int(void); +typedef const int ftype_ret_const_int(void); // COMPLIANT +typedef const int* ftype_ret_const_int_ptr(void); // COMPLIANT +typedef int const* ftype_ret_int_const_ptr(void); // COMPLIANT + +// Typedefs that use function typedefs +typedef ftype_ret_int ftype_ret_int2; // COMPLIANT +typedef const ftype_ret_int *ptr_const_ftype_ret_int; // NON-COMPLIANT +typedef ftype_ret_int *const const_ptr_ftype_ret_int; // COMPLIANT +typedef ftype_ret_int const* const_ptr_ftype_ret_int_; // NON-COMPLIANT + +// Test all qualifiers +typedef const ftype_ret_int const_ftype_ret_int; // NON-COMPLIANT +typedef volatile ftype_ret_int volatile_ftype_ret_int; // NON-COMPLIANT +typedef _Atomic ftype_ret_int atomic_ftype_ret_int; // NON-COMPLIANT +//extern restrict ftype_ret_int restrict_ftype_ret_int; // NON-COMPLIANT + +// Test parameters of declaration specifiers +typedef void (*take_ftype_ret_int)(ftype_ret_int); // COMPLIANT +typedef void (*take_const_ftype_ret_int)(const ftype_ret_int); // NON-COMPLIANT +typedef void (*take_ptr_ftype_ret_int)(ftype_ret_int*); // COMPLIANT +typedef void (*take_ptr_const_ftype_ret_int)(const ftype_ret_int *); // NON-COMPLIANT +typedef void (*take_const_ptr_ftype_ret_int)(ftype_ret_int const *); // COMPLIANT + +// Test return types of declaration specifiers +typedef ftype_ret_int* (return_ftype_ret_int)(void); // COMPLIANT +typedef const ftype_ret_int* (return_ftype_ret_int)(void); // NON-COMPLIANT +typedef ftype_ret_int const* (return_ftype_ret_int)(void); // COMPLIANT + +// Other storage class specifiers +extern const ftype_ret_int extern_ftype; // NON-COMPLIANT +extern const ftype_ret_int *extern_const_ftype_type; // NON-COMPLIANT +extern ftype_ret_int const * extern_ftype_const_ptr; // COMPLIANT + +// Other declarations +void param_list( + const ftype_ret_int *param_ftype, // NON-COMPLIANT + const ftype_ret_int *param_const_ftype_type, // NON-COMPLIANT + ftype_ret_int const *param_ftype_const_ptr // COMPLIANT +) { + const ftype_ret_int *var_ftype; // NON-COMPLIANT + const ftype_ret_int *var_const_ftype_type; // NON-COMPLIANT + ftype_ret_int const *var_ftype_const_ptr; // COMPLIANT + + struct TestStruct { + const ftype_ret_int *struct_ftype; // NON-COMPLIANT + const ftype_ret_int *struct_const_ftype_type; // NON-COMPLIANT + ftype_ret_int const *struct_ftype_const_ptr; // COMPLIANT + }; +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll new file mode 100644 index 0000000000..176b9871d3 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype FunctionTypesQuery = + TFunctionAddressesShouldAddressOperatorQuery() or + TDisallowedFunctionTypeQualifierQuery() + +predicate isFunctionTypesQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `functionAddressesShouldAddressOperator` query + FunctionTypesPackage::functionAddressesShouldAddressOperatorQuery() and + queryId = + // `@id` for the `functionAddressesShouldAddressOperator` query + "c/misra/function-addresses-should-address-operator" and + ruleId = "RULE-17-12" and + category = "advisory" + or + query = + // `Query` instance for the `disallowedFunctionTypeQualifier` query + FunctionTypesPackage::disallowedFunctionTypeQualifierQuery() and + queryId = + // `@id` for the `disallowedFunctionTypeQualifier` query + "c/misra/disallowed-function-type-qualifier" and + ruleId = "RULE-17-13" and + category = "required" +} + +module FunctionTypesPackage { + Query functionAddressesShouldAddressOperatorQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionAddressesShouldAddressOperator` query + TQueryC(TFunctionTypesPackageQuery(TFunctionAddressesShouldAddressOperatorQuery())) + } + + Query disallowedFunctionTypeQualifierQuery() { + //autogenerate `Query` type + result = + // `Query` type for `disallowedFunctionTypeQualifier` query + TQueryC(TFunctionTypesPackageQuery(TDisallowedFunctionTypeQualifierQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index b10fbf0a2f..581585da5c 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -29,6 +29,7 @@ import Declarations8 import EssentialTypes import Expressions import FloatingTypes +import FunctionTypes import IO1 import IO2 import IO3 @@ -102,6 +103,7 @@ newtype TCQuery = TEssentialTypesPackageQuery(EssentialTypesQuery q) or TExpressionsPackageQuery(ExpressionsQuery q) or TFloatingTypesPackageQuery(FloatingTypesQuery q) or + TFunctionTypesPackageQuery(FunctionTypesQuery q) or TIO1PackageQuery(IO1Query q) or TIO2PackageQuery(IO2Query q) or TIO3PackageQuery(IO3Query q) or @@ -175,6 +177,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isEssentialTypesQueryMetadata(query, queryId, ruleId, category) or isExpressionsQueryMetadata(query, queryId, ruleId, category) or isFloatingTypesQueryMetadata(query, queryId, ruleId, category) or + isFunctionTypesQueryMetadata(query, queryId, ruleId, category) or isIO1QueryMetadata(query, queryId, ruleId, category) or isIO2QueryMetadata(query, queryId, ruleId, category) or isIO3QueryMetadata(query, queryId, ruleId, category) or diff --git a/rule_packages/c/FunctionTypes.json b/rule_packages/c/FunctionTypes.json new file mode 100644 index 0000000000..bfa96d09eb --- /dev/null +++ b/rule_packages/c/FunctionTypes.json @@ -0,0 +1,42 @@ +{ + "MISRA-C-2012": { + "RULE-17-12": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A function identifier should only be called with a parenthesized parameter list or used with a & (address-of).", + "kind": "problem", + "name": "A function identifier should only be called with a parenthesized parameter list or used with a &", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionAddressesShouldAddressOperator", + "tags": [ + "readability" + ] + } + ], + "title": "A function identifier should only be called with a parenthesized parameter list or used with a & (address-of)" + }, + "RULE-17-13": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The behavior of type qualifiers on a function type is undefined.", + "kind": "problem", + "name": "A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic)", + "precision": "very-high", + "severity": "error", + "short_name": "DisallowedFunctionTypeQualifier", + "tags": [ + "correctness" + ] + } + ], + "title": "A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic)" + } + } +} \ No newline at end of file From d798fc87149908f801fceb4d495888d9d909ff08 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 27 Sep 2024 15:34:14 -0700 Subject: [PATCH 2/6] Remove 17-13, simpler implementation for 17-12. --- .../FunctionAddressesShouldAddressOperator.ql | 73 ++------- .../DisallowedFunctionTypeQualifier.ql | 147 ------------------ ...ionAddressesShouldAddressOperator.expected | 23 ++- c/misra/test/rules/RULE-17-12/test.c | 9 +- .../DisallowedFunctionTypeQualifier.expected | 1 - .../DisallowedFunctionTypeQualifier.qlref | 1 - c/misra/test/rules/RULE-17-13/test.c | 56 ------- .../cpp/exclusions/c/FunctionTypes.qll | 20 +-- rule_packages/c/FunctionTypes.json | 14 +- 9 files changed, 27 insertions(+), 317 deletions(-) delete mode 100644 c/misra/src/rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql delete mode 100644 c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.expected delete mode 100644 c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.qlref delete mode 100644 c/misra/test/rules/RULE-17-13/test.c diff --git a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql index 4c3386c68b..96c466150b 100644 --- a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql +++ b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql @@ -14,70 +14,15 @@ import cpp import codingstandards.c.misra -abstract class AddressOfFunction extends Expr { - abstract predicate isImplicitlyAddressed(); - - abstract string getFuncName(); -} - -class FunctionTypeAccess extends FunctionAccess, AddressOfFunction { - - predicate isImmediatelyParenthesized() { - exists(ParenthesisExpr parens | parens.getExpr() = this) - } - - predicate isExplicitlyAddressed() { - getParent() instanceof AddressOfExpr and - not isImmediatelyParenthesized() - } - - override predicate isImplicitlyAddressed() { - not isExplicitlyAddressed() - } - - override string getFuncName() { - result = getTarget().getName() - } -} - -/* -class IndirectFunctionCall extends FunctionCall, AddressOfFunction { - override predicate isImplicitlyAddressed() { - getConversion+() instanceof ParenthesisExpr - } - - override string getFuncName() { - result = getTarget().getName() - } -} - */ - -class MacroArgTakesFunction extends AddressOfFunction { - MacroInvocation m; - MacroArgTakesFunction() { - m.getExpr() = this - } - - override predicate isImplicitlyAddressed() { - any() - } - - string getProp() { - result = m.getExpandedArgument(_) - and this.get - } - - override string getFuncName() { - result = "a macro argument" - } - +predicate isImplicitlyAddressed(FunctionAccess access) { + not access.getParent() instanceof AddressOfExpr or + exists(ParenthesisExpr parens | parens.getExpr() = access) } -from AddressOfFunction funcAddr +from FunctionAccess funcAccess where - not isExcluded(funcAddr, FunctionTypesPackage::functionAddressesShouldAddressOperatorQuery()) and - //not funcAccess.isImmediatelyCalled() and - //not funcAccess.isExplicitlyAddressed() - funcAddr.isImplicitlyAddressed() -select - funcAddr, "The address of function " + funcAddr.getFuncName() + " is taken without the & operator." \ No newline at end of file + not isExcluded(funcAccess, FunctionTypesPackage::functionAddressesShouldAddressOperatorQuery()) and + isImplicitlyAddressed(funcAccess) +select funcAccess, + "The address of function " + funcAccess.getTarget().getName() + + " is taken without the & operator." diff --git a/c/misra/src/rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql b/c/misra/src/rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql deleted file mode 100644 index a40dadef6c..0000000000 --- a/c/misra/src/rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql +++ /dev/null @@ -1,147 +0,0 @@ -/** - * @id c/misra/disallowed-function-type-qualifier - * @name RULE-17-13: A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic) - * @description The behavior of type qualifiers on a function type is undefined. - * @kind problem - * @precision very-high - * @problem.severity error - * @tags external/misra/id/rule-17-13 - * correctness - * external/misra/obligation/required - */ - -import cpp -import codingstandards.c.misra - -//from DeclarationEntry decl, Type type //, Specifier specifier -//where -// not isExcluded(decl, FunctionTypesPackage::disallowedFunctionTypeQualifierQuery()) and -// //decl.getType() instanceof FunctionPointerType and -// //decl.getType().(FunctionPointerType).hasSpecifier(specifier) -// (type = decl.getType().getUnderlyingType*() -// or type = decl.getType()) -// and -// //specifier = type.getASpecifier() -// any() -//select decl, type //, specifier // "The behavior of type qualifier " + specifier + " on a function type is undefined." - -newtype TDeclaredFunction = - TFunctionDeclaration(Declaration declaration) - -abstract class DeclaredFunction extends TDeclaredFunction { - abstract string toString(); -} - -predicate isConstFunction(Type type) { - (type.getASpecifier().getName() = "const" - or type.isConst()) - and isFunctionType(type) - or isConstFunction(type.getUnderlyingType()) -} - -predicate isFunctionType(Type type) { - type instanceof FunctionPointerType - or isFunctionType(type.getUnderlyingType()) -} - -predicate declaresConstFunction(DeclarationEntry entry) { - (entry.getDeclaration().getASpecifier().getName() = "const" - and isFunctionType(entry.getType())) - or isConstFunction(entry.getType()) -} - -class QualifiableRoutineType extends RoutineType, QualifiableType { - override string explainQualifiers() { - result = "func{" - + specifiersOf(this) + this.getReturnType().(QualifiableType).explainQualifiers() - + " (" - + paramString(0) - + ")}" - } - - string paramString(int i) { - i = 0 and result = "" and not exists(this.getAParameterType()) - or - ( - if i < max(int j | exists(this.getParameterType(j))) - then - // Not the last one - result = this.getParameterType(i).(QualifiableType).explainQualifiers() + "," + this.paramString(i + 1) - else - // Last parameter - result = this.getParameterType(i).(QualifiableType).explainQualifiers() - ) - } -} - -class QualifiableIntType extends IntType, QualifiableType { - override string explainQualifiers() { - result = specifiersOf(this) + " " + this.toString() - } -} - -class QualifiablePointerType extends PointerType, QualifiableType { - override string explainQualifiers() { - result = "{" - + specifiersOf(this) - + " pointer to " - + this.getBaseType().(QualifiableType).explainQualifiers() - + "}" - } -} - -class QualifiableType extends Type { - string explainQualifiers() { - result = "Unimplemented explainQualifiers for type(s): " + concat(string s | s = getAQlClass() | s, ",") - } -} - -class QualifiableTypedefType extends TypedefType, QualifiableType { - override string explainQualifiers() { - result = "{ typedef " - + specifiersOf(this) - + " " - + this.getBaseType().(QualifiableType).explainQualifiers() - + "}" - } -} - -class QualifiableSpecifiedType extends SpecifiedType, QualifiableType { - override string explainQualifiers() { - result = "{" - + specifiersOf(this) - + " " - + this.getBaseType().(QualifiableType).explainQualifiers() - + "}" - } -} - -string typeString(Type t) { - //if - // t instanceof CTypedefType - // then result = t.(CTypedefType).explain() + "specs:" + specifiersOf(t.(CTypedefType).getBaseType()) + "/" + typeString(t.(CTypedefType).getBaseType()) - // else - //result = concat(string s | s = t.getAQlClass() | s, ",") - result = t.(QualifiableType).explainQualifiers() -} - -string specifiersOf(Type t) { - result = concat(Specifier s | s = t.getASpecifier()| s.getName(), ", ") -} - -string declSpecifiersOf(Declaration d) { - result = concat(Specifier s | s = d.getASpecifier()| s.getName(), ", ") -} - -string underlying(Type t) { - exists(Type u | u = t.getUnderlyingType() | result = u.toString()) - or result = "[no underlying]" - -} - -from DeclarationEntry entry -select entry, entry.getType(), typeString(entry.getType()), declSpecifiersOf(entry.getDeclaration()), specifiersOf(entry.getType()) - -//from Type t -//where any()//isFunctionType(t) -//select t, specifiersOf(t), underlying(t) \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected index 1cc92e95e1..d4862c5978 100644 --- a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected +++ b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected @@ -1,18 +1,15 @@ | test.c:15:25:15:29 | func2 | The address of function func2 is taken without the & operator. | | test.c:16:27:16:31 | func3 | The address of function func3 is taken without the & operator. | | test.c:22:16:22:20 | func1 | The address of function func1 is taken without the & operator. | -| test.c:38:5:38:9 | func1 | The address of function func1 is taken without the & operator. | -| test.c:39:5:39:9 | func2 | The address of function func2 is taken without the & operator. | -| test.c:47:7:47:11 | func1 | The address of function func1 is taken without the & operator. | -| test.c:48:7:48:11 | func2 | The address of function func2 is taken without the & operator. | -| test.c:57:15:57:19 | func1 | The address of function func1 is taken without the & operator. | -| test.c:58:23:58:27 | func2 | The address of function func2 is taken without the & operator. | -| test.c:59:15:59:19 | func1 | The address of function func1 is taken without the & operator. | -| test.c:59:22:59:26 | func2 | The address of function func2 is taken without the & operator. | -| test.c:67:13:67:17 | func1 | The address of function func1 is taken without the & operator. | -| test.c:68:14:68:18 | func1 | The address of function func1 is taken without the & operator. | +| test.c:39:5:39:9 | func1 | The address of function func1 is taken without the & operator. | +| test.c:40:5:40:9 | func2 | The address of function func2 is taken without the & operator. | +| test.c:48:7:48:11 | func1 | The address of function func1 is taken without the & operator. | +| test.c:49:7:49:11 | func2 | The address of function func2 is taken without the & operator. | +| test.c:58:15:58:19 | func1 | The address of function func1 is taken without the & operator. | +| test.c:59:23:59:27 | func2 | The address of function func2 is taken without the & operator. | +| test.c:60:15:60:19 | func1 | The address of function func1 is taken without the & operator. | +| test.c:60:22:60:26 | func2 | The address of function func2 is taken without the & operator. | +| test.c:68:13:68:17 | func1 | The address of function func1 is taken without the & operator. | | test.c:69:14:69:18 | func1 | The address of function func1 is taken without the & operator. | -| test.c:71:20:71:24 | func1 | The address of function func1 is taken without the & operator. | +| test.c:70:14:70:18 | func1 | The address of function func1 is taken without the & operator. | | test.c:72:20:72:24 | func1 | The address of function func1 is taken without the & operator. | -| test.c:76:20:76:24 | func1 | The address of function func1 is taken without the & operator. | -| test.c:77:20:77:24 | func1 | The address of function func1 is taken without the & operator. | diff --git a/c/misra/test/rules/RULE-17-12/test.c b/c/misra/test/rules/RULE-17-12/test.c index 1c580f917e..4cfe1f6de6 100644 --- a/c/misra/test/rules/RULE-17-12/test.c +++ b/c/misra/test/rules/RULE-17-12/test.c @@ -30,6 +30,7 @@ func_ptr_t1 returns_func(int x) { #define MACRO_IDENTITY(f) (f) #define MACRO_INVOKE_RISKY(f) (f()) #define MACRO_INVOKE_IMPROVED(f) ((f)()) +#define MACRO_INVOKE_AND_USE_AS_TOKEN(f) f(0, #f) void test() { func1(); // COMPLIANT @@ -69,14 +70,16 @@ void test() { ((void*) func1); // NON-COMPLIANT MACRO_IDENTITY(func1); // NON-COMPLIANT - MACRO_IDENTITY(func1)(); // NON-COMPLIANT + MACRO_IDENTITY(func1)(); // NON-COMPLIANT[FALSE NEGATIVE] MACRO_IDENTITY(&func1); // COMPLIANT MACRO_IDENTITY(&func1)(); // COMPLIANT - MACRO_INVOKE_RISKY(func3); // NON-COMPLIANT - MACRO_INVOKE_IMPROVED(func3); // NON-COMPLIANT + MACRO_INVOKE_RISKY(func3); // NON-COMPLIANT[FALSE NEGATIVE] + MACRO_INVOKE_IMPROVED(func3); // NON-COMPLIANT[FALSE NEGATIVE] MACRO_INVOKE_IMPROVED(&func3); // COMPLIANT + MACRO_INVOKE_AND_USE_AS_TOKEN(func1); // COMPLIANT + // Function pointers are exempt from this rule. func_ptr1(); // COMPLIANT func_ptr2(1, "hello"); // COMPLIANT diff --git a/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.expected b/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.expected deleted file mode 100644 index 2ec1a0ac6c..0000000000 --- a/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.expected +++ /dev/null @@ -1 +0,0 @@ -No expected results have yet been specified \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.qlref b/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.qlref deleted file mode 100644 index cbf7c583ec..0000000000 --- a/c/misra/test/rules/RULE-17-13/DisallowedFunctionTypeQualifier.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/RULE-17-13/DisallowedFunctionTypeQualifier.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-13/test.c b/c/misra/test/rules/RULE-17-13/test.c deleted file mode 100644 index 8326b30a28..0000000000 --- a/c/misra/test/rules/RULE-17-13/test.c +++ /dev/null @@ -1,56 +0,0 @@ -// semmle-extractor-options: --language=c -std=c99 -const int x; // COMPLIANT -const int f_ret_const_int(void); // COMPLIANT -const int* f_ret_const_int_ptr(void); // COMPLIANT - -// Basic function typedefs -typedef int ftype_ret_int(void); -typedef const int ftype_ret_const_int(void); // COMPLIANT -typedef const int* ftype_ret_const_int_ptr(void); // COMPLIANT -typedef int const* ftype_ret_int_const_ptr(void); // COMPLIANT - -// Typedefs that use function typedefs -typedef ftype_ret_int ftype_ret_int2; // COMPLIANT -typedef const ftype_ret_int *ptr_const_ftype_ret_int; // NON-COMPLIANT -typedef ftype_ret_int *const const_ptr_ftype_ret_int; // COMPLIANT -typedef ftype_ret_int const* const_ptr_ftype_ret_int_; // NON-COMPLIANT - -// Test all qualifiers -typedef const ftype_ret_int const_ftype_ret_int; // NON-COMPLIANT -typedef volatile ftype_ret_int volatile_ftype_ret_int; // NON-COMPLIANT -typedef _Atomic ftype_ret_int atomic_ftype_ret_int; // NON-COMPLIANT -//extern restrict ftype_ret_int restrict_ftype_ret_int; // NON-COMPLIANT - -// Test parameters of declaration specifiers -typedef void (*take_ftype_ret_int)(ftype_ret_int); // COMPLIANT -typedef void (*take_const_ftype_ret_int)(const ftype_ret_int); // NON-COMPLIANT -typedef void (*take_ptr_ftype_ret_int)(ftype_ret_int*); // COMPLIANT -typedef void (*take_ptr_const_ftype_ret_int)(const ftype_ret_int *); // NON-COMPLIANT -typedef void (*take_const_ptr_ftype_ret_int)(ftype_ret_int const *); // COMPLIANT - -// Test return types of declaration specifiers -typedef ftype_ret_int* (return_ftype_ret_int)(void); // COMPLIANT -typedef const ftype_ret_int* (return_ftype_ret_int)(void); // NON-COMPLIANT -typedef ftype_ret_int const* (return_ftype_ret_int)(void); // COMPLIANT - -// Other storage class specifiers -extern const ftype_ret_int extern_ftype; // NON-COMPLIANT -extern const ftype_ret_int *extern_const_ftype_type; // NON-COMPLIANT -extern ftype_ret_int const * extern_ftype_const_ptr; // COMPLIANT - -// Other declarations -void param_list( - const ftype_ret_int *param_ftype, // NON-COMPLIANT - const ftype_ret_int *param_const_ftype_type, // NON-COMPLIANT - ftype_ret_int const *param_ftype_const_ptr // COMPLIANT -) { - const ftype_ret_int *var_ftype; // NON-COMPLIANT - const ftype_ret_int *var_const_ftype_type; // NON-COMPLIANT - ftype_ret_int const *var_ftype_const_ptr; // COMPLIANT - - struct TestStruct { - const ftype_ret_int *struct_ftype; // NON-COMPLIANT - const ftype_ret_int *struct_const_ftype_type; // NON-COMPLIANT - ftype_ret_int const *struct_ftype_const_ptr; // COMPLIANT - }; -} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll index 176b9871d3..3d6faadb42 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/FunctionTypes.qll @@ -3,9 +3,7 @@ import cpp import RuleMetadata import codingstandards.cpp.exclusions.RuleMetadata -newtype FunctionTypesQuery = - TFunctionAddressesShouldAddressOperatorQuery() or - TDisallowedFunctionTypeQualifierQuery() +newtype FunctionTypesQuery = TFunctionAddressesShouldAddressOperatorQuery() predicate isFunctionTypesQueryMetadata(Query query, string queryId, string ruleId, string category) { query = @@ -16,15 +14,6 @@ predicate isFunctionTypesQueryMetadata(Query query, string queryId, string ruleI "c/misra/function-addresses-should-address-operator" and ruleId = "RULE-17-12" and category = "advisory" - or - query = - // `Query` instance for the `disallowedFunctionTypeQualifier` query - FunctionTypesPackage::disallowedFunctionTypeQualifierQuery() and - queryId = - // `@id` for the `disallowedFunctionTypeQualifier` query - "c/misra/disallowed-function-type-qualifier" and - ruleId = "RULE-17-13" and - category = "required" } module FunctionTypesPackage { @@ -34,11 +23,4 @@ module FunctionTypesPackage { // `Query` type for `functionAddressesShouldAddressOperator` query TQueryC(TFunctionTypesPackageQuery(TFunctionAddressesShouldAddressOperatorQuery())) } - - Query disallowedFunctionTypeQualifierQuery() { - //autogenerate `Query` type - result = - // `Query` type for `disallowedFunctionTypeQualifier` query - TQueryC(TFunctionTypesPackageQuery(TDisallowedFunctionTypeQualifierQuery())) - } } diff --git a/rule_packages/c/FunctionTypes.json b/rule_packages/c/FunctionTypes.json index bfa96d09eb..aae4ba8a0f 100644 --- a/rule_packages/c/FunctionTypes.json +++ b/rule_packages/c/FunctionTypes.json @@ -23,19 +23,7 @@ "properties": { "obligation": "required" }, - "queries": [ - { - "description": "The behavior of type qualifiers on a function type is undefined.", - "kind": "problem", - "name": "A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic)", - "precision": "very-high", - "severity": "error", - "short_name": "DisallowedFunctionTypeQualifier", - "tags": [ - "correctness" - ] - } - ], + "queries": [], "title": "A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic)" } } From d49984d0216a62f25e983e70198e6f7b11a50f0e Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 27 Sep 2024 16:13:42 -0700 Subject: [PATCH 3/6] Format test, commit user README changes. --- ...ionAddressesShouldAddressOperator.expected | 30 +-- c/misra/test/rules/RULE-17-12/test.c | 172 +++++++++--------- docs/user_manual.md | 2 +- 3 files changed, 101 insertions(+), 103 deletions(-) diff --git a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected index d4862c5978..1a3165a32f 100644 --- a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected +++ b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected @@ -1,15 +1,15 @@ -| test.c:15:25:15:29 | func2 | The address of function func2 is taken without the & operator. | -| test.c:16:27:16:31 | func3 | The address of function func3 is taken without the & operator. | -| test.c:22:16:22:20 | func1 | The address of function func1 is taken without the & operator. | -| test.c:39:5:39:9 | func1 | The address of function func1 is taken without the & operator. | -| test.c:40:5:40:9 | func2 | The address of function func2 is taken without the & operator. | -| test.c:48:7:48:11 | func1 | The address of function func1 is taken without the & operator. | -| test.c:49:7:49:11 | func2 | The address of function func2 is taken without the & operator. | -| test.c:58:15:58:19 | func1 | The address of function func1 is taken without the & operator. | -| test.c:59:23:59:27 | func2 | The address of function func2 is taken without the & operator. | -| test.c:60:15:60:19 | func1 | The address of function func1 is taken without the & operator. | -| test.c:60:22:60:26 | func2 | The address of function func2 is taken without the & operator. | -| test.c:68:13:68:17 | func1 | The address of function func1 is taken without the & operator. | -| test.c:69:14:69:18 | func1 | The address of function func1 is taken without the & operator. | -| test.c:70:14:70:18 | func1 | The address of function func1 is taken without the & operator. | -| test.c:72:20:72:24 | func1 | The address of function func1 is taken without the & operator. | +| test.c:14:25:14:29 | func2 | The address of function func2 is taken without the & operator. | +| test.c:15:27:15:31 | func3 | The address of function func3 is taken without the & operator. | +| test.c:21:12:21:16 | func1 | The address of function func1 is taken without the & operator. | +| test.c:38:3:38:7 | func1 | The address of function func1 is taken without the & operator. | +| test.c:39:3:39:7 | func2 | The address of function func2 is taken without the & operator. | +| test.c:47:5:47:9 | func1 | The address of function func1 is taken without the & operator. | +| test.c:48:5:48:9 | func2 | The address of function func2 is taken without the & operator. | +| test.c:57:13:57:17 | func1 | The address of function func1 is taken without the & operator. | +| test.c:58:21:58:25 | func2 | The address of function func2 is taken without the & operator. | +| test.c:59:13:59:17 | func1 | The address of function func1 is taken without the & operator. | +| test.c:59:20:59:24 | func2 | The address of function func2 is taken without the & operator. | +| test.c:67:11:67:15 | func1 | The address of function func1 is taken without the & operator. | +| test.c:68:12:68:16 | func1 | The address of function func1 is taken without the & operator. | +| test.c:69:12:69:16 | func1 | The address of function func1 is taken without the & operator. | +| test.c:71:18:71:22 | func1 | The address of function func1 is taken without the & operator. | diff --git a/c/misra/test/rules/RULE-17-12/test.c b/c/misra/test/rules/RULE-17-12/test.c index 4cfe1f6de6..04aaa96af6 100644 --- a/c/misra/test/rules/RULE-17-12/test.c +++ b/c/misra/test/rules/RULE-17-12/test.c @@ -1,30 +1,29 @@ void func1() {} -void func2(int x, char* y) {} +void func2(int x, char *y) {} -typedef struct {} s; +typedef struct { +} s; -int func3() { - return 0; -} +int func3() { return 0; } typedef void (*func_ptr_t1)(); -typedef void (*func_ptr_t2)(int x, char* y); +typedef void (*func_ptr_t2)(int x, char *y); typedef s (*func_ptr_t3)(); -func_ptr_t1 func_ptr1 = &func1; // COMPLIANT -func_ptr_t2 func_ptr2 = func2; // NON-COMPLIANT +func_ptr_t1 func_ptr1 = &func1; // COMPLIANT +func_ptr_t2 func_ptr2 = func2; // NON-COMPLIANT func_ptr_t3 func_ptr3 = &(func3); // NON-COMPLIANT void take_func(func_ptr_t1 f1, func_ptr_t2 f2); func_ptr_t1 returns_func(int x) { - if (x == 0) { - return func1; // NON-COMPLIANT - } else if (x == 1) { - return &func1; // COMPLIANT - } + if (x == 0) { + return func1; // NON-COMPLIANT + } else if (x == 1) { + return &func1; // COMPLIANT + } - return returns_func(0); // COMPLIANT + return returns_func(0); // COMPLIANT } #define MACRO_IDENTITY(f) (f) @@ -33,77 +32,76 @@ func_ptr_t1 returns_func(int x) { #define MACRO_INVOKE_AND_USE_AS_TOKEN(f) f(0, #f) void test() { - func1(); // COMPLIANT - func2(1, "hello"); // COMPLIANT - - func1; // NON-COMPLIANT - func2; // NON-COMPLIANT - - &func1; // COMPLIANT - &func2; // COMPLIANT - - (func1)(); // COMPLIANT - (func2)(1, "hello"); // COMPLIANT - - &(func1); // NON-COMPLIANT - &(func2); // NON-COMPLIANT - - (&func1)(); // COMPLIANT - (&func2)(1, "hello"); // COMPLIANT - - (func1()); // COMPLIANT - (func2(1, "hello")); // COMPLIANT - - take_func(&func1, &func2); // COMPLIANT - take_func(func1, &func2); // NON-COMPLIANT - take_func(&func1, func2); // NON-COMPLIANT - take_func(func1, func2); // NON-COMPLIANT - - returns_func(0); // COMPLIANT - returns_func(0)(); // COMPLIANT - (returns_func(0))(); // COMPLIANT - - (void*) &func1; // COMPLIANT - (void*) (&func1); // COMPLIANT - (void*) func1; // NON-COMPLIANT - (void*) (func1); // NON-COMPLIANT - ((void*) func1); // NON-COMPLIANT - - MACRO_IDENTITY(func1); // NON-COMPLIANT - MACRO_IDENTITY(func1)(); // NON-COMPLIANT[FALSE NEGATIVE] - MACRO_IDENTITY(&func1); // COMPLIANT - MACRO_IDENTITY(&func1)(); // COMPLIANT - - MACRO_INVOKE_RISKY(func3); // NON-COMPLIANT[FALSE NEGATIVE] - MACRO_INVOKE_IMPROVED(func3); // NON-COMPLIANT[FALSE NEGATIVE] - MACRO_INVOKE_IMPROVED(&func3); // COMPLIANT - - MACRO_INVOKE_AND_USE_AS_TOKEN(func1); // COMPLIANT - - // Function pointers are exempt from this rule. - func_ptr1(); // COMPLIANT - func_ptr2(1, "hello"); // COMPLIANT - func_ptr1; // COMPLIANT - func_ptr2; // COMPLIANT - &func_ptr1; // COMPLIANT - &func_ptr2; // COMPLIANT - (func_ptr1)(); // COMPLIANT - (func_ptr2)(1, "hello"); // COMPLIANT - (*func_ptr1)(); // COMPLIANT - (*func_ptr2)(1, "hello"); // COMPLIANT - take_func(func_ptr1, func_ptr2); // COMPLIANT - (void*) func_ptr1; // COMPLIANT - (void*) &func_ptr1; // COMPLIANT - (void*) (&func_ptr1); // COMPLIANT - (void*) func_ptr1; // COMPLIANT - (void*) (func_ptr1); // COMPLIANT - ((void*) func_ptr1); // COMPLIANT - MACRO_IDENTITY(func_ptr1); // COMPLIANT - MACRO_IDENTITY(func_ptr1)(); // COMPLIANT - MACRO_IDENTITY(&func_ptr1); // COMPLIANT - (*MACRO_IDENTITY(&func_ptr1))(); // COMPLIANT - MACRO_INVOKE_RISKY(func_ptr3); // COMPLIANT - MACRO_INVOKE_IMPROVED(func_ptr3); // COMPLIANT - MACRO_INVOKE_IMPROVED(*&func_ptr3); // COMPLIANT - + func1(); // COMPLIANT + func2(1, "hello"); // COMPLIANT + + func1; // NON-COMPLIANT + func2; // NON-COMPLIANT + + &func1; // COMPLIANT + &func2; // COMPLIANT + + (func1)(); // COMPLIANT + (func2)(1, "hello"); // COMPLIANT + + &(func1); // NON-COMPLIANT + &(func2); // NON-COMPLIANT + + (&func1)(); // COMPLIANT + (&func2)(1, "hello"); // COMPLIANT + + (func1()); // COMPLIANT + (func2(1, "hello")); // COMPLIANT + + take_func(&func1, &func2); // COMPLIANT + take_func(func1, &func2); // NON-COMPLIANT + take_func(&func1, func2); // NON-COMPLIANT + take_func(func1, func2); // NON-COMPLIANT + + returns_func(0); // COMPLIANT + returns_func(0)(); // COMPLIANT + (returns_func(0))(); // COMPLIANT + + (void *)&func1; // COMPLIANT + (void *)(&func1); // COMPLIANT + (void *)func1; // NON-COMPLIANT + (void *)(func1); // NON-COMPLIANT + ((void *)func1); // NON-COMPLIANT + + MACRO_IDENTITY(func1); // NON-COMPLIANT + MACRO_IDENTITY(func1)(); // NON-COMPLIANT[FALSE NEGATIVE] + MACRO_IDENTITY(&func1); // COMPLIANT + MACRO_IDENTITY (&func1)(); // COMPLIANT + + MACRO_INVOKE_RISKY(func3); // NON-COMPLIANT[FALSE NEGATIVE] + MACRO_INVOKE_IMPROVED(func3); // NON-COMPLIANT[FALSE NEGATIVE] + MACRO_INVOKE_IMPROVED(&func3); // COMPLIANT + + MACRO_INVOKE_AND_USE_AS_TOKEN(func1); // COMPLIANT + + // Function pointers are exempt from this rule. + func_ptr1(); // COMPLIANT + func_ptr2(1, "hello"); // COMPLIANT + func_ptr1; // COMPLIANT + func_ptr2; // COMPLIANT + &func_ptr1; // COMPLIANT + &func_ptr2; // COMPLIANT + (func_ptr1)(); // COMPLIANT + (func_ptr2)(1, "hello"); // COMPLIANT + (*func_ptr1)(); // COMPLIANT + (*func_ptr2)(1, "hello"); // COMPLIANT + take_func(func_ptr1, func_ptr2); // COMPLIANT + (void *)func_ptr1; // COMPLIANT + (void *)&func_ptr1; // COMPLIANT + (void *)(&func_ptr1); // COMPLIANT + (void *)func_ptr1; // COMPLIANT + (void *)(func_ptr1); // COMPLIANT + ((void *)func_ptr1); // COMPLIANT + MACRO_IDENTITY(func_ptr1); // COMPLIANT + MACRO_IDENTITY(func_ptr1)(); // COMPLIANT + MACRO_IDENTITY(&func_ptr1); // COMPLIANT + (*MACRO_IDENTITY(&func_ptr1))(); // COMPLIANT + MACRO_INVOKE_RISKY(func_ptr3); // COMPLIANT + MACRO_INVOKE_IMPROVED(func_ptr3); // COMPLIANT + MACRO_INVOKE_IMPROVED(*&func_ptr3); // COMPLIANT } \ No newline at end of file diff --git a/docs/user_manual.md b/docs/user_manual.md index 5799d73e6f..db0f836339 100644 --- a/docs/user_manual.md +++ b/docs/user_manual.md @@ -78,7 +78,7 @@ The datasheet _"CodeQL Coding Standards: supported rules"_, provided with each r [^1]: AUTOSAR C++ versions R22-11, R21-11, R20-11, R19-11 and R19-03 are all identical as indicated in the document change history. [^2]: The unimplemented supportable AUTOSAR rules are `A7-1-8` and `A8-2-1`. These rules require additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of these rules. -[^3]: The unimplemented supportable MISRA C 2012 rules are `Rule 9.5` and `Dir 4.14`. `Rule 9.5` requires additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of these rules. `Dir 4.14` is covered by the default CodeQL queries, which identify potential security vulnerabilities caused by not validating external input. +[^3]: The unimplemented supportable MISRA C 2012 rules are `Rule 9.5`, `Rule 17.13`, and `Dir 4.14`. `Rule 9.5` and `Rule 17.13` require additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of these rules. `Dir 4.14` is covered by the default CodeQL queries, which identify potential security vulnerabilities caused by not validating external input. [^4]: The rules 5.13.7, 19.0.1 and 19.1.2 are not planned to be implemented by CodeQL as they are compiler checked in all supported compilers. ## Supported environment From a8d832b0606b46a5dd05bc22558b95ebbc7c0df7 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sat, 28 Sep 2024 11:24:01 -0700 Subject: [PATCH 4/6] Add defense against cpp code based on MRVA results. Add comments. --- .../FunctionAddressesShouldAddressOperator.ql | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql index 96c466150b..824e873e28 100644 --- a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql +++ b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql @@ -15,8 +15,18 @@ import cpp import codingstandards.c.misra predicate isImplicitlyAddressed(FunctionAccess access) { - not access.getParent() instanceof AddressOfExpr or - exists(ParenthesisExpr parens | parens.getExpr() = access) + ( + not access.getParent() instanceof AddressOfExpr + or + // This catches "&(foo)", which could be considered to be somewhat less + // readable than "(&foo)". + exists(ParenthesisExpr parens | parens.getExpr() = access) + ) and + // Note: the following *seems* to only exist in c++ codebases, for instance, + // when calling a member. In c, this syntax should always extract as a + // [FunctionCall] rather than a [ExprCall] of a [FunctionAccess]. Still, this + // is a good pattern to be defensive against. + not exists(ExprCall call | call.getExpr() = access) } from FunctionAccess funcAccess From 1007a5324883e41523ff7e4bd6e20024c4f151a4 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 4 Oct 2024 09:38:41 -0700 Subject: [PATCH 5/6] Allow `&(f)` under rule 17-12 --- .../FunctionAddressesShouldAddressOperator.ql | 8 +------- .../FunctionAddressesShouldAddressOperator.expected | 4 +--- c/misra/test/rules/RULE-17-12/test.c | 10 +++++----- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql index 824e873e28..30d86b0447 100644 --- a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql +++ b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql @@ -15,13 +15,7 @@ import cpp import codingstandards.c.misra predicate isImplicitlyAddressed(FunctionAccess access) { - ( - not access.getParent() instanceof AddressOfExpr - or - // This catches "&(foo)", which could be considered to be somewhat less - // readable than "(&foo)". - exists(ParenthesisExpr parens | parens.getExpr() = access) - ) and + not access.getParent() instanceof AddressOfExpr and // Note: the following *seems* to only exist in c++ codebases, for instance, // when calling a member. In c, this syntax should always extract as a // [FunctionCall] rather than a [ExprCall] of a [FunctionAccess]. Still, this diff --git a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected index 1a3165a32f..5a37cbd97e 100644 --- a/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected +++ b/c/misra/test/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.expected @@ -1,10 +1,8 @@ | test.c:14:25:14:29 | func2 | The address of function func2 is taken without the & operator. | -| test.c:15:27:15:31 | func3 | The address of function func3 is taken without the & operator. | +| test.c:15:25:15:29 | func3 | The address of function func3 is taken without the & operator. | | test.c:21:12:21:16 | func1 | The address of function func1 is taken without the & operator. | | test.c:38:3:38:7 | func1 | The address of function func1 is taken without the & operator. | | test.c:39:3:39:7 | func2 | The address of function func2 is taken without the & operator. | -| test.c:47:5:47:9 | func1 | The address of function func1 is taken without the & operator. | -| test.c:48:5:48:9 | func2 | The address of function func2 is taken without the & operator. | | test.c:57:13:57:17 | func1 | The address of function func1 is taken without the & operator. | | test.c:58:21:58:25 | func2 | The address of function func2 is taken without the & operator. | | test.c:59:13:59:17 | func1 | The address of function func1 is taken without the & operator. | diff --git a/c/misra/test/rules/RULE-17-12/test.c b/c/misra/test/rules/RULE-17-12/test.c index 04aaa96af6..5ab5a4984d 100644 --- a/c/misra/test/rules/RULE-17-12/test.c +++ b/c/misra/test/rules/RULE-17-12/test.c @@ -10,9 +10,9 @@ typedef void (*func_ptr_t1)(); typedef void (*func_ptr_t2)(int x, char *y); typedef s (*func_ptr_t3)(); -func_ptr_t1 func_ptr1 = &func1; // COMPLIANT -func_ptr_t2 func_ptr2 = func2; // NON-COMPLIANT -func_ptr_t3 func_ptr3 = &(func3); // NON-COMPLIANT +func_ptr_t1 func_ptr1 = &func1; // COMPLIANT +func_ptr_t2 func_ptr2 = func2; // NON-COMPLIANT +func_ptr_t3 func_ptr3 = func3 + 0; // NON-COMPLIANT void take_func(func_ptr_t1 f1, func_ptr_t2 f2); @@ -44,8 +44,8 @@ void test() { (func1)(); // COMPLIANT (func2)(1, "hello"); // COMPLIANT - &(func1); // NON-COMPLIANT - &(func2); // NON-COMPLIANT + &(func1); // COMPLIANT + &(func2); // COMPLIANT (&func1)(); // COMPLIANT (&func2)(1, "hello"); // COMPLIANT From c972403d1a2f39874d66a355cc69cdbb6d5cd523 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 4 Oct 2024 11:35:49 -0700 Subject: [PATCH 6/6] Tag as amendment3, mark 17-13 not supportable --- .../FunctionAddressesShouldAddressOperator.ql | 1 + rule_packages/c/FunctionTypes.json | 10 ++-------- rules.csv | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql index 30d86b0447..c95612b7ba 100644 --- a/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql +++ b/c/misra/src/rules/RULE-17-12/FunctionAddressesShouldAddressOperator.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-17-12 * readability + * external/misra/c/2012/amendment3 * external/misra/obligation/advisory */ diff --git a/rule_packages/c/FunctionTypes.json b/rule_packages/c/FunctionTypes.json index aae4ba8a0f..d9d8b6496d 100644 --- a/rule_packages/c/FunctionTypes.json +++ b/rule_packages/c/FunctionTypes.json @@ -13,18 +13,12 @@ "severity": "error", "short_name": "FunctionAddressesShouldAddressOperator", "tags": [ - "readability" + "readability", + "external/misra/c/2012/amendment3" ] } ], "title": "A function identifier should only be called with a parenthesized parameter list or used with a & (address-of)" - }, - "RULE-17-13": { - "properties": { - "obligation": "required" - }, - "queries": [], - "title": "A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic)" } } } \ No newline at end of file diff --git a/rules.csv b/rules.csv index b5e15ba6f6..475ea1d66c 100644 --- a/rules.csv +++ b/rules.csv @@ -739,7 +739,7 @@ c,MISRA-C-2012,RULE-17-9,Yes,Mandatory,,,Verify that a function declared with _N c,MISRA-C-2012,RULE-17-10,Yes,Required,,,A function declared with _noreturn shall have a return type of void,,NoReturn,Easy, c,MISRA-C-2012,RULE-17-11,Yes,Advisory,,,A function without a branch that returns shall be declared with _Noreturn,,NoReturn,Easy, c,MISRA-C-2012,RULE-17-12,Yes,Advisory,,,A function identifier should only be called with a parenthesized parameter list or used with a & (address-of),,FunctionTypes,Easy, -c,MISRA-C-2012,RULE-17-13,Yes,Required,,,"A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic)",,FunctionTypes,Easy, +c,MISRA-C-2012,RULE-17-13,No,Required,,,"A function type shall not include any type qualifiers (const, volatile, restrict, or _Atomic)",,,Easy, c,MISRA-C-2012,RULE-18-1,Yes,Required,,,A pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operand,M5-0-16,Pointers1,Import, c,MISRA-C-2012,RULE-18-2,Yes,Required,,,Subtraction between pointers shall only be applied to pointers that address elements of the same array,M5-0-17,Pointers1,Import, c,MISRA-C-2012,RULE-18-3,Yes,Required,,,"The relational operators >, >=, < and <= shall not be applied to objects of pointer type except where they point into the same object",M5-0-18,Pointers1,Import,