Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `A3-3-2` - `StaticOrThreadLocalObjectsNonConstantInit`:
- The checks for non-constant initialization have been moved to be usable in other queries, such as MISRA C++23 Rule 6.7.2.
- No visible changes in query results expected.
Original file line number Diff line number Diff line change
Expand Up @@ -15,100 +15,7 @@

import cpp
import codingstandards.cpp.autosar

/**
* Holds if `e` is a full expression or `AggregateLiteral` in the initializer of a
* `StaticStorageDurationVariable`.
*
* Although `AggregateLiteral`s are expressions according to our model, they are not considered
* expressions from the perspective of the standard. Therefore, we should consider components of
* aggregate literals within static initializers to also be full expressions.
*/
private predicate isFullExprOrAggregateInStaticInitializer(Expr e) {
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
or
isFullExprOrAggregateInStaticInitializer(e.getParent().(AggregateLiteral))
}

/**
* Holds if `e` is a non-constant full expression in a static initializer, for the given `reason`
* and `reasonElement`.
*/
private predicate nonConstantFullExprInStaticInitializer(
Expr e, Element reasonElement, string reason
) {
isFullExprOrAggregateInStaticInitializer(e) and
if e instanceof AggregateLiteral
then
// If this is an aggregate literal, we apply this recursively
nonConstantFullExprInStaticInitializer(e.getAChild(), reasonElement, reason)
else (
// Otherwise we check this component to determine if it is constant
not (
e.getFullyConverted().isConstant() or
e.(Call).getTarget().isConstexpr() or
e.(VariableAccess).getTarget().isConstexpr()
) and
reason = "uses a non-constant element in the initialization" and
reasonElement = e
)
}

/**
* A `ConstructorCall` that does not represent a constant initializer for an object according to
* `[basic.start.init]`.
*
* In addition to identifying `ConstructorCall`s which are not constant initializers, this also
* provides an explanatory "reason" for why this constructor is not considered to be a constant
* initializer.
*/
predicate isNotConstantInitializer(ConstructorCall cc, Element reasonElement, string reason) {
// Must call a constexpr constructor
not cc.getTarget().isConstexpr() and
reason =
"calls the " + cc.getTarget().getName() + "(..) constructor which is not marked as constexpr" and
reasonElement = cc
or
// And all arguments must either be constant, or themselves call constexpr constructors
cc.getTarget().isConstexpr() and
exists(Expr arg | arg = cc.getAnArgument() |
isNotConstantInitializer(arg, reasonElement, reason)
or
not arg instanceof ConstructorCall and
not arg.getFullyConverted().isConstant() and
not arg.(Call).getTarget().isConstexpr() and
not arg.(VariableAccess).getTarget().isConstexpr() and
reason = "includes a non constant " + arg.getType() + " argument to a constexpr constructor" and
reasonElement = arg
)
}

/**
* Identifies if a `StaticStorageDurationVariable` is not constant initialized according to
* `[basic.start.init]`.
*/
predicate isNotConstantInitialized(
StaticStorageDurationVariable v, string reason, Element reasonElement
) {
if v.getInitializer().getExpr() instanceof ConstructorCall
then
// (2.2) if initialized by a constructor call, then that constructor call must be a constant
// initializer for the variable to be constant initialized
isNotConstantInitializer(v.getInitializer().getExpr(), reasonElement, reason)
else
// (2.3) If it is not initialized by a constructor call, then it must be the case that every full
// expr in the initializer is a constant expression or that the object was "value initialized"
// but without a constructor call. For value initialization, there are two non-constructor call
// cases to consider:
//
// 1. The object was zero initialized - in which case, the extractor does not include a
// constructor call - instead, it has a blank aggregate literal, or no initializer.
// 2. The object is an array, which will be initialized by an aggregate literal.
//
// In both cases it is sufficient for us to find a non-constant full expression in the static
// initializer
nonConstantFullExprInStaticInitializer(v.getInitializer().getExpr(), reasonElement, reason)
}
import codingstandards.cpp.orderofevaluation.Initialization

from StaticStorageDurationVariable staticOrThreadLocalVar, string reason, Element reasonElement
where
Expand Down
26 changes: 26 additions & 0 deletions cpp/common/src/codingstandards/cpp/exclusions/cpp/Banned1.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
import cpp
import RuleMetadata
import codingstandards.cpp.exclusions.RuleMetadata

newtype Banned1Query = TGlobalVariableUsedQuery()

predicate isBanned1QueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
// `Query` instance for the `globalVariableUsed` query
Banned1Package::globalVariableUsedQuery() and
queryId =
// `@id` for the `globalVariableUsed` query
"cpp/misra/global-variable-used" and
ruleId = "RULE-6-7-2" and
category = "required"
}

module Banned1Package {
Query globalVariableUsedQuery() {
//autogenerate `Query` type
result =
// `Query` type for `globalVariableUsed` query
TQueryCPP(TBanned1PackageQuery(TGlobalVariableUsedQuery()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import cpp
import codingstandards.cpp.exclusions.RuleMetadata
//** Import packages for this language **/
import Allocations
import Banned1
import BannedAPIs
import BannedFunctions
import BannedLibraries
Expand Down Expand Up @@ -76,6 +77,7 @@ import VirtualFunctions
/** The TQuery type representing this language * */
newtype TCPPQuery =
TAllocationsPackageQuery(AllocationsQuery q) or
TBanned1PackageQuery(Banned1Query q) or
TBannedAPIsPackageQuery(BannedAPIsQuery q) or
TBannedFunctionsPackageQuery(BannedFunctionsQuery q) or
TBannedLibrariesPackageQuery(BannedLibrariesQuery q) or
Expand Down Expand Up @@ -149,6 +151,7 @@ newtype TCPPQuery =
/** The metadata predicate * */
predicate isQueryMetadata(Query query, string queryId, string ruleId, string category) {
isAllocationsQueryMetadata(query, queryId, ruleId, category) or
isBanned1QueryMetadata(query, queryId, ruleId, category) or
isBannedAPIsQueryMetadata(query, queryId, ruleId, category) or
isBannedFunctionsQueryMetadata(query, queryId, ruleId, category) or
isBannedLibrariesQueryMetadata(query, queryId, ruleId, category) or
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import cpp

/**
* Holds if `e` is a full expression or `AggregateLiteral` in the initializer of a
* `StaticStorageDurationVariable`.
*
* Although `AggregateLiteral`s are expressions according to our model, they are not considered
* expressions from the perspective of the standard. Therefore, we should consider components of
* aggregate literals within static initializers to also be full expressions.
*/
private predicate isFullExprOrAggregateInStaticInitializer(Expr e) {
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
or
isFullExprOrAggregateInStaticInitializer(e.getParent().(AggregateLiteral))
}

/**
* Holds if `e` is a non-constant full expression in a static initializer, for the given `reason`
* and `reasonElement`.
*/
private predicate nonConstantFullExprInStaticInitializer(
Expr e, Element reasonElement, string reason
) {
isFullExprOrAggregateInStaticInitializer(e) and
if e instanceof AggregateLiteral
then
// If this is an aggregate literal, we apply this recursively
nonConstantFullExprInStaticInitializer(e.getAChild(), reasonElement, reason)
else (
// Otherwise we check this component to determine if it is constant
not (
e.getFullyConverted().isConstant() or
e.(Call).getTarget().isConstexpr() or
e.(VariableAccess).getTarget().isConstexpr()
) and
reason = "uses a non-constant element in the initialization" and
reasonElement = e
)
}

/**
* A `ConstructorCall` that does not represent a constant initializer for an object according to
* `[basic.start.init]`.
*
* In addition to identifying `ConstructorCall`s which are not constant initializers, this also
* provides an explanatory "reason" for why this constructor is not considered to be a constant
* initializer.
*/
predicate isNotConstantInitializer(ConstructorCall cc, Element reasonElement, string reason) {
// Must call a constexpr constructor
not cc.getTarget().isConstexpr() and
reason =
"calls the " + cc.getTarget().getName() + "(..) constructor which is not marked as constexpr" and
reasonElement = cc
or
// And all arguments must either be constant, or themselves call constexpr constructors
cc.getTarget().isConstexpr() and
exists(Expr arg | arg = cc.getAnArgument() |
isNotConstantInitializer(arg, reasonElement, reason)
or
not arg instanceof ConstructorCall and
not arg.getFullyConverted().isConstant() and
not arg.(Call).getTarget().isConstexpr() and
not arg.(VariableAccess).getTarget().isConstexpr() and
reason = "includes a non constant " + arg.getType() + " argument to a constexpr constructor" and
reasonElement = arg
)
}

/**
* Identifies if a `StaticStorageDurationVariable` is not constant initialized according to
* `[basic.start.init]`.
*/
predicate isNotConstantInitialized(
StaticStorageDurationVariable v, string reason, Element reasonElement
) {
if v.getInitializer().getExpr() instanceof ConstructorCall
then
// (2.2) if initialized by a constructor call, then that constructor call must be a constant
// initializer for the variable to be constant initialized
isNotConstantInitializer(v.getInitializer().getExpr(), reasonElement, reason)
else
// (2.3) If it is not initialized by a constructor call, then it must be the case that every full
// expr in the initializer is a constant expression or that the object was "value initialized"
// but without a constructor call. For value initialization, there are two non-constructor call
// cases to consider:
//
// 1. The object was zero initialized - in which case, the extractor does not include a
// constructor call - instead, it has a blank aggregate literal, or no initializer.
// 2. The object is an array, which will be initialized by an aggregate literal.
//
// In both cases it is sufficient for us to find a non-constant full expression in the static
// initializer
nonConstantFullExprInStaticInitializer(v.getInitializer().getExpr(), reasonElement, reason)
}
43 changes: 43 additions & 0 deletions cpp/misra/src/rules/RULE-6-7-2/GlobalVariableUsed.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @id cpp/misra/global-variable-used
* @name RULE-6-7-2: Global variables shall not be used
* @description Global variables can be accessed and modified from distant and unclear points in the
* code, creating a risk of data races and unexpected behavior.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-6-7-2
* scope/single-translation-unit
* maintainability
* external/misra/enforcement/decidable
* external/misra/obligation/required
*/

import cpp
import codingstandards.cpp.misra
import codingstandards.cpp.Linkage
import codingstandards.cpp.orderofevaluation.Initialization

class GlobalVariable extends Variable {
GlobalVariable() {
hasNamespaceScope(this) or
this.(MemberVariable).isStatic()
}
}

from GlobalVariable var, string suffix, Element reasonElement, string reasonString
where
not isExcluded(var, Banned1Package::globalVariableUsedQuery()) and
not var.isConstexpr() and
(
not var.isConst() and
suffix = "as non-const" and
// Placeholder is not used, but they must be bound.
reasonString = "" and
reasonElement = var
or
var.isConst() and
isNotConstantInitialized(var, reasonString, reasonElement) and
suffix = "as const, but is not constant initialized because it $@"
)
select var, "Global variable " + var.getName() + " declared " + suffix, reasonElement, reasonString
8 changes: 8 additions & 0 deletions cpp/misra/test/rules/RULE-6-7-2/GlobalVariableUsed.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
| test.cpp:7:14:7:15 | g1 | Global variable g1 declared as non-const | test.cpp:7:14:7:15 | g1 | |
| test.cpp:8:20:8:21 | g2 | Global variable g2 declared as const, but is not constant initialized because it $@ | test.cpp:9:5:9:6 | g1 | uses a non-constant element in the initialization |
| test.cpp:14:14:14:15 | g5 | Global variable g5 declared as non-const | test.cpp:14:14:14:15 | g5 | |
| test.cpp:26:19:26:20 | g9 | Global variable g9 declared as const, but is not constant initialized because it $@ | test.cpp:26:22:26:22 | call to ComplexInit | calls the ComplexInit(..) constructor which is not marked as constexpr |
| test.cpp:27:14:27:16 | g10 | Global variable g10 declared as non-const | test.cpp:27:14:27:16 | g10 | |
| test.cpp:28:20:28:22 | g11 | Global variable g11 declared as const, but is not constant initialized because it $@ | test.cpp:28:24:28:25 | call to f1 | uses a non-constant element in the initialization |
| test.cpp:32:23:32:24 | m2 | Global variable m2 declared as non-const | test.cpp:32:23:32:24 | m2 | |
| test.cpp:38:14:38:29 | m3 | Global variable m3 declared as non-const | test.cpp:38:14:38:29 | m3 | |
1 change: 1 addition & 0 deletions cpp/misra/test/rules/RULE-6-7-2/GlobalVariableUsed.qlref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-6-7-2/GlobalVariableUsed.ql
44 changes: 44 additions & 0 deletions cpp/misra/test/rules/RULE-6-7-2/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <cstdint>

// Non-compliant global variables (namespace scope, dynamically initialized or
// mutable)
std::int32_t f1();

std::int32_t g1{f1()}; // NON_COMPLIANT - dynamic initialization
const std::int32_t g2{
g1}; // NON_COMPLIANT - dynamic initialization (depends on g1)
const std::int32_t g3{42}; // COMPLIANT - const with static initialization
constexpr std::int32_t g4{0}; // COMPLIANT - constexpr

namespace {
std::int32_t g5{0}; // NON_COMPLIANT - mutable namespace scope variable
constexpr std::int32_t g6{100}; // COMPLIANT - constexpr
const std::int32_t g7{100}; // COMPLIANT - const with static initialization

constexpr std::int32_t f2() { return 42; }
constexpr std::int32_t g8{f2()}; // COMPLIANT - constexpr
} // namespace

struct ComplexInit {
ComplexInit() {}
};

const ComplexInit g9{}; // NON_COMPLIANT - dynamic initialization
std::int32_t g10{0}; // NON_COMPLIANT - mutable namespace scope variable
const std::int32_t g11{f1()}; // NON_COMPLIANT - dynamic initialization

class StaticMember {
std::int32_t m1;
static std::int32_t m2; // NON_COMPLIANT - class static data member
static std::int32_t m3; // marked non_compliant at definition below
static constexpr std::int32_t m4{0}; // COMPLIANT - constexpr static member
static const std::int32_t m5;
};

std::int32_t StaticMember::m3 =
0; // NON_COMPLIANT - class static data member definition
const std::int32_t StaticMember::m5 =
42; // COMPLIANT - const with static initialization

constexpr auto g12 = // COMPLIANT - constexpr lambda
[](auto x, auto y) { return x + y; };
25 changes: 25 additions & 0 deletions rule_packages/cpp/Banned1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"MISRA-C++-2023": {
"RULE-6-7-2": {
"properties": {
"enforcement": "decidable",
"obligation": "required"
},
"queries": [
{
"description": "Global variables can be accessed and modified from distant and unclear points in the code, creating a risk of data races and unexpected behavior.",
"kind": "problem",
"name": "Global variables shall not be used",
"precision": "very-high",
"severity": "error",
"short_name": "GlobalVariableUsed",
"tags": [
"scope/single-translation-unit",
"maintainability"
]
}
],
"title": "Global variables shall not be used"
}
}
}
2 changes: 1 addition & 1 deletion rules.csv
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ cpp,MISRA-C++-2023,RULE-6-4-3,Yes,Required,Decidable,Single Translation Unit,A n
cpp,MISRA-C++-2023,RULE-6-5-1,Yes,Advisory,Decidable,Single Translation Unit,A function or object with external linkage should be introduced in a header file,A3-3-1,Linkage1,Import,
cpp,MISRA-C++-2023,RULE-6-5-2,Yes,Advisory,Decidable,Single Translation Unit,Internal linkage should be specified appropriately,,Linkage2,Medium,
cpp,MISRA-C++-2023,RULE-6-7-1,Yes,Required,Decidable,Single Translation Unit,Local variables shall not have static storage duration,,Declarations2,Easy,
cpp,MISRA-C++-2023,RULE-6-7-2,Yes,Required,Decidable,Single Translation Unit,Global variables shall not be used,,Banned,Easy,
cpp,MISRA-C++-2023,RULE-6-7-2,Yes,Required,Decidable,Single Translation Unit,Global variables shall not be used,,Banned1,Easy,
cpp,MISRA-C++-2023,RULE-6-8-1,Yes,Required,Undecidable,System,An object shall not be accessed outside of its lifetime,A3-8-1,ImportMisra23,Import,
cpp,MISRA-C++-2023,RULE-6-8-2,Yes,Mandatory,Decidable,Single Translation Unit,A function must not return a reference or a pointer to a local variable with automatic storage duration,M7-5-1,ImportMisra23,Import,
cpp,MISRA-C++-2023,RULE-6-8-3,Yes,Required,Decidable,Single Translation Unit,An assignment operator shall not assign the address of an object with automatic storage duration to an object with a greater lifetime,,Lifetime,Medium,
Expand Down
Loading