Skip to content

Commit bb9f5a1

Browse files
committed
RULE-7-11-3 - FunctionPointerConversionContext
Detects inappropriate implicit conversions from function type to pointer-to-function type outside of static_cast or assignment contexts. Prevents ambiguous code where function pointer usage in boolean or arithmetic expressions may contradict developer intent. [a]
1 parent 983f256 commit bb9f5a1

File tree

5 files changed

+180
-2
lines changed

5 files changed

+180
-2
lines changed

cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ class CanonicalIntegerTypes extends NumericType, IntegralType {
121121
CanonicalIntegerTypes() { this = this.getCanonicalArithmeticType() }
122122
}
123123

124-
predicate isAssignment(Expr source, NumericType targetType, string context) {
124+
predicate isAssignment(Expr source, Type targetType, string context) {
125125
exists(Expr preConversionAssignment |
126126
isPreConversionAssignment(preConversionAssignment, targetType, context) and
127127
preConversionAssignment.getExplicitlyConverted() = source
128128
)
129129
}
130130

131-
predicate isPreConversionAssignment(Expr source, NumericType targetType, string context) {
131+
predicate isPreConversionAssignment(Expr source, Type targetType, string context) {
132132
// Assignment expression (which excludes compound assignments)
133133
exists(AssignExpr assign |
134134
assign.getRValue() = source and
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* @id cpp/misra/function-pointer-conversion-context
3+
* @name RULE-7-11-3: A conversion from function type to pointer-to-function type shall only occur in appropriate contexts
4+
* @description Converting a function type to a pointer-to-function type outside of static_cast or
5+
* assignment to a pointer-to-function object creates ambiguous behavior and potential
6+
* unintended effects.
7+
* @kind problem
8+
* @precision very-high
9+
* @problem.severity error
10+
* @tags external/misra/id/rule-7-11-3
11+
* scope/single-translation-unit
12+
* external/misra/enforcement/decidable
13+
* external/misra/obligation/required
14+
*/
15+
16+
import cpp
17+
import codingstandards.cpp.misra
18+
import codingstandards.cpp.misra.BuiltInTypeRules
19+
import codingstandards.cpp.Type
20+
21+
/**
22+
* An `Expr` representing an implicit conversion from a function type to a pointer-to-function type.
23+
*
24+
* Despite the name, these are not `Conversion`s in our model. Instead, they are expressions
25+
* representing functions that have been implicilty converted to function pointers.
26+
*/
27+
abstract class FunctionToFunctionPointerConversion extends Expr { }
28+
29+
/**
30+
* A `FunctionAccess` that has been implicitly converted to a function pointer type.
31+
*/
32+
class FunctionAccessConversionToFunctionPointer extends FunctionAccess,
33+
FunctionToFunctionPointerConversion
34+
{
35+
FunctionAccessConversionToFunctionPointer() {
36+
this.getType().getUnspecifiedType() instanceof FunctionPointerIshType
37+
}
38+
}
39+
40+
/**
41+
* A `Call` to a `ConversionOperator` that converts a lambda to a function pointer type.
42+
*/
43+
class LambdaFunctionPointerConversion extends Call, FunctionToFunctionPointerConversion {
44+
LambdaFunctionPointerConversion() {
45+
this.getTarget().(ConversionOperator).getDestType() instanceof FunctionPointerIshType and
46+
this.getQualifier().getType().getUnspecifiedType() instanceof Closure
47+
}
48+
}
49+
50+
from FunctionToFunctionPointerConversion f
51+
where
52+
not isExcluded(f, ConversionsPackage::functionPointerConversionContextQuery()) and
53+
// Not converted by an explicit static cast
54+
not exists(Conversion c |
55+
c.getExpr() = f and
56+
not c.isImplicit() and
57+
c.getType().getUnspecifiedType() instanceof FunctionPointerIshType
58+
) and
59+
// Not a MISRA compliant assignment to a function pointer type
60+
not exists(FunctionPointerIshType targetType | isAssignment(f, targetType, _))
61+
select f,
62+
"Inappropriate conversion from function type to pointer-to-function type in '" + f.toString() +
63+
"'."
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
| test.cpp:11:7:11:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
2+
| test.cpp:14:7:14:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
3+
| test.cpp:18:16:18:16 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
4+
| test.cpp:21:14:21:14 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
5+
| test.cpp:25:7:25:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. |
6+
| test.cpp:33:14:33:14 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. |
7+
| test.cpp:61:13:61:13 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
8+
| test.cpp:64:13:64:13 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
9+
| test.cpp:67:7:67:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
10+
| test.cpp:71:7:71:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
11+
| test.cpp:74:7:74:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
12+
| test.cpp:77:7:77:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
13+
| test.cpp:83:7:83:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
14+
| test.cpp:87:7:87:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. |
15+
| test.cpp:92:7:92:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. |
16+
| test.cpp:96:7:96:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-7-11-3/FunctionPointerConversionContext.ql
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include <cstdint>
2+
#include <iostream>
3+
4+
// Test functions
5+
extern int *f();
6+
void f1(double);
7+
void f1(std::uint32_t);
8+
void simple_function();
9+
10+
void test_function_to_pointer_conversion() {
11+
if (f) { // NON_COMPLIANT
12+
}
13+
14+
if (f == nullptr) { // NON_COMPLIANT
15+
}
16+
17+
std::cout << std::boolalpha // COMPLIANT - considered assignment
18+
<< f; // NON_COMPLIANT - not assignment
19+
20+
// Non-compliant: Unary plus operator causing pointer decay
21+
auto l1 = +f; // NON_COMPLIANT
22+
23+
auto lam = []() {};
24+
// Lambda used in boolean context
25+
if (lam) { // NON_COMPLIANT
26+
}
27+
28+
// Lambda used in address-of operator
29+
if (&lam) { // COMPLIANT
30+
}
31+
32+
// Unary plus on lambda
33+
auto l2 = +lam; // NON_COMPLIANT
34+
35+
// Using address-of operator
36+
if (&f != nullptr) { // COMPLIANT
37+
}
38+
39+
// Function call
40+
(f)(); // COMPLIANT
41+
lam(); // COMPLIANT
42+
43+
// static_cast conversion
44+
auto selected = static_cast<void (*)(std::uint32_t)>(f1); // COMPLIANT
45+
46+
// Assignment to pointer-to-function type
47+
void (*p)() = lam; // COMPLIANT
48+
49+
// Assignment to pointer-to-function type
50+
int *(*func_ptr)() = f; // COMPLIANT
51+
52+
// Using address-of operator
53+
void (*simple_ptr)() = &simple_function; // COMPLIANT
54+
55+
// Direct assignment without conversion
56+
void (*simple_ptr2)() = simple_function; // COMPLIANT
57+
}
58+
59+
void test_arithmetic_expressions() {
60+
// Function used in arithmetic expression (triggers pointer decay)
61+
auto l3 = f + 0; // NON_COMPLIANT
62+
63+
// Function used in arithmetic subtraction expression
64+
auto l4 = f - 0; // NON_COMPLIANT
65+
66+
// Function used in comparison with non-null pointer
67+
if (f > nullptr) { // NON_COMPLIANT
68+
}
69+
70+
// Function used in relational operators
71+
if (f < nullptr) { // NON_COMPLIANT
72+
}
73+
74+
if (f <= nullptr) { // NON_COMPLIANT
75+
}
76+
77+
if (f >= nullptr) { // NON_COMPLIANT
78+
}
79+
}
80+
81+
void test_logical_expressions() {
82+
// Function used in logical AND expression
83+
if (f && true) { // NON_COMPLIANT
84+
}
85+
86+
// Function used in logical OR expression
87+
if (f || false) { // NON_COMPLIANT
88+
}
89+
90+
// Lambda used in logical AND expression
91+
auto lam = []() {};
92+
if (lam && true) { // NON_COMPLIANT
93+
}
94+
95+
// Lambda used in logical OR expression
96+
if (lam || false) { // NON_COMPLIANT
97+
}
98+
}

0 commit comments

Comments
 (0)