From a559f7ac5037f52b7f4b9457d585940ae93b694b Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Thu, 16 Apr 2026 16:57:11 -0400 Subject: [PATCH 1/3] Add RULE-11-6-1 --- .../cpp/exclusions/cpp/Declarations7.qll | 26 +++++++++++++ .../cpp/exclusions/cpp/RuleMetadata.qll | 3 ++ .../RULE-11-6-1/UninitializedVariable.ql | 21 ++++++++++ .../UninitializedVariable.expected | 5 +++ .../RULE-11-6-1/UninitializedVariable.qlref | 1 + cpp/misra/test/rules/RULE-11-6-1/test.cpp | 38 +++++++++++++++++++ rule_packages/cpp/Declarations7.json | 25 ++++++++++++ rules.csv | 2 +- 8 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations7.qll create mode 100644 cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql create mode 100644 cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.expected create mode 100644 cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.qlref create mode 100644 cpp/misra/test/rules/RULE-11-6-1/test.cpp create mode 100644 rule_packages/cpp/Declarations7.json diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations7.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations7.qll new file mode 100644 index 000000000..ca9b740bc --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations7.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations7Query = TUninitializedVariableQuery() + +predicate isDeclarations7QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `uninitializedVariable` query + Declarations7Package::uninitializedVariableQuery() and + queryId = + // `@id` for the `uninitializedVariable` query + "cpp/misra/uninitialized-variable" and + ruleId = "RULE-11-6-1" and + category = "advisory" +} + +module Declarations7Package { + Query uninitializedVariableQuery() { + //autogenerate `Query` type + result = + // `Query` type for `uninitializedVariable` query + TQueryCPP(TDeclarations7PackageQuery(TUninitializedVariableQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 8771bbc79..64ba33a68 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -39,6 +39,7 @@ import Declarations1 import Declarations2 import Declarations3 import Declarations4 +import Declarations7 import ExceptionSafety import Exceptions1 import Exceptions2 @@ -141,6 +142,7 @@ newtype TCPPQuery = TDeclarations2PackageQuery(Declarations2Query q) or TDeclarations3PackageQuery(Declarations3Query q) or TDeclarations4PackageQuery(Declarations4Query q) or + TDeclarations7PackageQuery(Declarations7Query q) or TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or TExceptions1PackageQuery(Exceptions1Query q) or TExceptions2PackageQuery(Exceptions2Query q) or @@ -243,6 +245,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isDeclarations2QueryMetadata(query, queryId, ruleId, category) or isDeclarations3QueryMetadata(query, queryId, ruleId, category) or isDeclarations4QueryMetadata(query, queryId, ruleId, category) or + isDeclarations7QueryMetadata(query, queryId, ruleId, category) or isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or isExceptions1QueryMetadata(query, queryId, ruleId, category) or isExceptions2QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql b/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql new file mode 100644 index 000000000..bf169d233 --- /dev/null +++ b/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql @@ -0,0 +1,21 @@ +/** + * @id cpp/misra/uninitialized-variable + * @name RULE-11-6-1: All variables should be initialized, otherwise it may lead to a read of uninitialized memory which is undefined behavior. + * @description All variables should be initialized + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-11-6-1 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.readofuninitializedmemory.ReadOfUninitializedMemory + +from UninitializedVariable v +where not isExcluded(v, Declarations7Package::uninitializedVariableQuery()) +select v, "Uninitialized variable found." diff --git a/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.expected b/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.expected new file mode 100644 index 000000000..2c5880978 --- /dev/null +++ b/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.expected @@ -0,0 +1,5 @@ +problems +#select +| test.cpp:11:7:11:7 | i | Uninitialized variable found. | +| test.cpp:18:8:18:9 | i4 | Uninitialized variable found. | +| test.cpp:20:8:20:9 | p4 | Uninitialized variable found. | diff --git a/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.qlref b/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.qlref new file mode 100644 index 000000000..cb5542281 --- /dev/null +++ b/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.qlref @@ -0,0 +1 @@ +rules/RULE-11-6-1/UninitializedVariable.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-11-6-1/test.cpp b/cpp/misra/test/rules/RULE-11-6-1/test.cpp new file mode 100644 index 000000000..34b5fd7dd --- /dev/null +++ b/cpp/misra/test/rules/RULE-11-6-1/test.cpp @@ -0,0 +1,38 @@ +#include + +int g; // COMPLIANT + +class A { +public: + int m1; +}; + +void f(int p) { // COMPLIANT - not applicable to parameters + int i; // NON_COMPLIANT + int i1 = 0; // COMPLIANT + int i2[10] = {1, 0}; // COMPLIANT + int i3[10]; // COMPLIANT + A a; // COMPLIANT - default initialized + A a1; + a1.m1 = 1; // COMPLIANT + int *i4 = new int; // NON_COMPLIANT + int *i5 = new int(1); // COMPLIANT + int *p4; // NON_COMPLIANT + p4 = new int; + std::string s; // COMPLIANT +} + +void test_non_default_init() { + static int sl; // COMPLIANT - static variables are zero initialized + thread_local int + tl; // COMPLIANT - thread local variables are zero initialized + static int *slp; // COMPLIANT - static variables are zero initialized + thread_local int + *tlp; // COMPLIANT - thread local variables are zero initialized + _Atomic int + ai; // COMPLIANT - atomics are special and not covered by this rule +} + +namespace { +int i; // COMPLIANT +} \ No newline at end of file diff --git a/rule_packages/cpp/Declarations7.json b/rule_packages/cpp/Declarations7.json new file mode 100644 index 000000000..a0588cd4c --- /dev/null +++ b/rule_packages/cpp/Declarations7.json @@ -0,0 +1,25 @@ +{ + "MISRA-C++-2023": { + "RULE-11-6-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "All variables should be initialized", + "kind": "problem", + "name": "All variables should be initialized, otherwise it may lead to a read of uninitialized memory which is undefined behavior.", + "precision": "very-high", + "severity": "error", + "short_name": "UninitializedVariable", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "All variables should be initialized" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 7abe0599a..09d4de3db 100644 --- a/rules.csv +++ b/rules.csv @@ -855,7 +855,7 @@ cpp,MISRA-C++-2023,RULE-6-0-3,Yes,Advisory,Decidable,Single Translation Unit,"Th cpp,MISRA-C++-2023,RULE-6-0-4,Yes,Required,Decidable,Single Translation Unit,The identifier main shall not be used for a function other than the global function main,M7-3-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-6-2-1,Yes,Required,Decidable,System,The one-definition rule shall not be violated,M3-2-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-6-2-2,Yes,Required,Decidable,System,All declarations of a variable or function shall have the same type,"M3-9-1,DCL40-C",Declarations2,Easy, -cpp,MISRA-C++-2023,RULE-6-2-3,Yes,Required,Decidable,System,The source code used to implement an entity shall appear only once,,Declarations7,Medium, +cpp,MISRA-C++-2023,RULE-6-2-3,Yes,Required,Decidable,System,The source code used to implement an entity shall appear only once,,Declarations8,Medium, cpp,MISRA-C++-2023,RULE-6-2-4,Yes,Required,Decidable,Single Translation Unit,A header file shall not contain definitions of functions or objects that are non-inline and have external linkage,A3-1-1,Linkage2,Import, cpp,MISRA-C++-2023,RULE-6-4-1,Yes,Required,Decidable,Single Translation Unit,A variable declared in an inner scope shall not hide a variable declared in an outer scope,A2-10-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-6-4-2,Yes,Required,Decidable,Single Translation Unit,Derived classes shall not conceal functions that are inherited from their bases,A7-3-1,ImportMisra23,Import, From 8b5733a50a3db1183460da41b9f127475a26b9b3 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Fri, 17 Apr 2026 11:35:43 -0400 Subject: [PATCH 2/3] Refactor ReadOfUninitializedMemory to separate library into usable pieces previously importing the shared query lib to other queries does not work bc it introduces a select stmt and a problems predicate into the same scope this is now more modular and usable --- .../cpp/InitializationContext.qll | 262 ++++++++++++++++++ .../InitializationFunctions.qll | 0 .../ReadOfUninitializedMemory.qll | 258 +---------------- .../RULE-11-6-1/UninitializedVariable.ql | 2 +- .../UninitializedVariable.expected | 2 - 5 files changed, 264 insertions(+), 260 deletions(-) create mode 100644 cpp/common/src/codingstandards/cpp/InitializationContext.qll rename cpp/common/src/codingstandards/cpp/{rules/readofuninitializedmemory => }/InitializationFunctions.qll (100%) diff --git a/cpp/common/src/codingstandards/cpp/InitializationContext.qll b/cpp/common/src/codingstandards/cpp/InitializationContext.qll new file mode 100644 index 000000000..46fa581e5 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/InitializationContext.qll @@ -0,0 +1,262 @@ +/** + * Provides classes and predicates for identifying uninitialized variables. + */ + +import cpp +import semmle.code.cpp.controlflow.Guards +import semmle.code.cpp.controlflow.SubBasicBlocks +import codingstandards.cpp.InitializationFunctions + +private newtype TInitializationContext = + /** No specific context - for functions where conditional initialization doesn't play a role */ + NoContext(UninitializedVariable uv) { count(uv.getACorrelatedConditionVariable()) = 0 } or + /** + * A context where the given `LocalScopeVariable` is identified as a correlated variable with the + * given state value. + */ + CorrelatedVariable(UninitializedVariable uv, LocalScopeVariable correlatedVariable, boolean state) { + uv.getACorrelatedConditionVariable() = correlatedVariable and state = [true, false] + } + +/** + * A context to apply when determining whether a given variable is uninitialized at a particular + * `SubBasicBlock` in the control flow graph. + * + * If no suitable context is found for an `UninitializedVariable`, `NoContext` is used. + * + * If one or more correlated variables are found, a `CorrelatedVariable` context is provided, with + * `true` and `false` states. + */ +class InitializationContext extends TInitializationContext { + /** + * Gets the `UninitializedVariable` for this context. + */ + UninitializedVariable getUninitializedVariable() { + this = NoContext(result) or + this = CorrelatedVariable(result, _, _) + } + + /** + * Gets the correlated variable, if any. + */ + LocalScopeVariable getCorrelatedVariable() { this = CorrelatedVariable(_, result, _) } + + string toString() { + if this instanceof CorrelatedVariable + then + result = + "Uninitialized variable " + getUninitializedVariable().getName() + " where location " + + getCorrelatedVariable().getName() + " is " + + any(boolean b | this = CorrelatedVariable(_, _, b)) + else result = "Uninitialized variable " + getUninitializedVariable().getName() + } +} + +/** + * A `SubBasicBlockCutNode` that ensures that any uninitialized variable definitions appear at the + * start of a `SubBasicBlock`. + */ +class InitializationSubBasicBlock extends SubBasicBlockCutNode { + InitializationSubBasicBlock() { this = any(UninitializedVariable uv).getADefinitionAccess() } +} + +/** + * Holds if the `gc` dictates the state of variable `lv`, i.e. the state is heuristically + * identified to be different under different branches. + */ +private predicate guardDictatesLocalVariableState( + GuardCondition gc, LocalScopeVariable lv, boolean lvStateOnTrueBranch +) { + // Condition is a boolean check on the variable + gc = lv.getAnAccess() and lvStateOnTrueBranch = true + or + // Condition is a negated boolean check on the variable + gc.(NotExpr).getOperand() = lv.getAnAccess() and lvStateOnTrueBranch = false + or + // Condition controls a block which assigns to `lv` + gc.controls(lv.getAnAssignedValue().getBasicBlock(), lvStateOnTrueBranch) +} + +/** + * Catches `new int;` as an expression that doesn't initialize its value. Note that the pointer returned has been initialized (ie it is a valid pointer), + * but the pointee/value has not. In our analysis, we simply count `x` as uninitialized in `x = new int` for now, though a more thorough analysis might track the initialization of `x` and `*x` separately. + */ +class NewNotInit extends NewExpr { + NewNotInit() { + this.getAllocatedType() instanceof BuiltInType and + not exists(this.getAChild()) + } +} + +class NonInitAssignment extends Assignment { + NonInitAssignment() { this.getRValue() instanceof NewNotInit } +} + +/** + * A local variable without an initializer which is amenable to initialization analysis. + */ +class UninitializedVariable extends LocalVariable { + UninitializedVariable() { + // Not initialized at declaration + ( + not exists(getInitializer().getExpr()) + or + getInitializer().getExpr() instanceof NewNotInit + ) and + // Not static or thread local, because they are not initialized with indeterminate values + not isStatic() and + not isThreadLocal() and + // Not atomic, which have special initialization rules + not getType().hasSpecifier("atomic") and + // Not a class type, because default initialization of a class calls the default constructor + // The default constructor may leave certain fields uninitialized, but that would be a separate + // field-wise analysis + not this.getType().getUnspecifiedType() instanceof Class and + // An analysis of an array entry also requires a field wise analysis + not this.getType().getUnspecifiedType() instanceof ArrayType and + // Ignore variables in uninstantiated templates, because we often do not have enough semantic + // information to accurately determine initialization state. + not isFromUninstantiatedTemplate(_) and + // Ignore `va_list`, that is part of the mechanism for + not getType().hasName("va_list") and + // Ignore variable defined in a block with an `asm` statement, as that may initialized the variables + not exists(AsmStmt asm | asm.getEnclosingBlock() = getParentScope()) and + // Ignore variables generated for `RangeBasedForStmt` e.g. `for(auto x : y)` + not this = any(RangeBasedForStmt f).getAChild().(DeclStmt).getADeclaration() + } + + /** Gets a variable correlated with at least one use of `this` uninitialized variable. */ + private LocalScopeVariable getAUseCorrelatedConditionVariable() { + /* Extracted to improve join order of getACorrelatedConditionVariable(). */ + // The use is guarded by the access of a variable + exists(GuardCondition gc | + gc.controls(getAUse().getBasicBlock(), _) and + gc = result.getAnAccess() + ) + } + + /** Find another variable which looks like it may be correlated with the initialization of this variable. */ + pragma[noinline] + LocalScopeVariable getACorrelatedConditionVariable() { + result = getAUseCorrelatedConditionVariable() and + ( + // Definition is guarded by an access of the same variable + exists(GuardCondition gc | + gc.controls(getADefinitionAccess().getBasicBlock(), _) and + gc = result.getAnAccess() + ) + or + // The variable is assigned in the same basic block as one of our definitions + result.getAnAssignedValue().getBasicBlock() = getADefinitionAccess().getBasicBlock() + ) + } + + /** + * Get a access of the variable that is assumed to initialize the variable. + * This approximates that any access in the lvalue category may be a definition. + */ + VariableAccess getADefinitionAccess() { + result = getAnAccess() and + result.isLValueCategory() and + // Not a pointless read + not result = any(ExprStmt es).getExpr() and + // not involved in a new expr assignment since that does not define + not result = any(NonInitAssignment a).getLValue() + } + + /** + * Gets an access of the this variable which is not used as an lvalue, and not used as an argument + * to an initialization function. + */ + VariableAccess getAUse() { + result = this.getAnAccess() and + ( + //count rvalue x (or *x) as a use if not new int + result.isRValue() and + not this.getInitializer().getExpr() instanceof NewNotInit + or + //count lvalue x as a use if used in *x and not new int + result.isLValue() and + exists(PointerDereferenceExpr e | result = e.getAChild()) and + exists(this.getInitializer()) and + not this.getInitializer().getExpr() instanceof NewNotInit + or + //count rvalue *x as a use if has new int + result.isRValue() and + this.getInitializer().getExpr() instanceof NewNotInit and + exists(PointerDereferenceExpr e | result = e.getAChild()) + ) and + // Not passed to another initialization function + not exists(Call c, int j | j = c.getTarget().(InitializationFunction).initializedParameter() | + result = c.getArgument(j).(AddressOfExpr).getOperand() + ) and + // Not a pointless read + not result = any(ExprStmt es).getExpr() and + // sizeof operators are not real uses + not result.getParent+() instanceof SizeofOperator + } + + /** Get a read of the variable that may occur while the variable is uninitialized. */ + VariableAccess getAnUnitializedUse() { + exists(SubBasicBlock useSbb | + result = getAUse() and + useSbb.getANode() = result and + // This sbb is considered uninitialized in all the contexts we identified + forex(InitializationContext ct | ct.getUninitializedVariable() = this | + useSbb = getAnUninitializedSubBasicBlock(ct) + ) + ) + } + + /** + * Gets a `SubBasicBlock` where this variable is uninitialized under the conditions specified by + * `InitializationContext`. + */ + private SubBasicBlock getAnUninitializedSubBasicBlock(InitializationContext ic) { + ic.getUninitializedVariable() = this and + ( + // Base case - this SBB is the one that declares the variable + exists(DeclStmt ds | + ds.getADeclaration() = this and + result.getANode() = ds + ) + or + // Recursive case - SBB is a successor of an SBB where this variable is uninitialized + exists(SubBasicBlock mid | + // result is the successor of an SBB where this is considered to be uninitialized under the + // context ic + mid = getAnUninitializedSubBasicBlock(ic) and + result = mid.getASuccessor() and + // Result is not an SBB where this variable is initialized + not getADefinitionAccess() = result and + // Determine if this is a branch at __builtin_expect where the initialization occurs inside + // the checked argument, and exclude it if so, because the CFG is known to be broken here. + not exists(FunctionCall fc | + mid.getEnd() = fc and + fc.getTarget().hasName("__builtin_expect") and + fc.getArgument(0).getAChild*() = getADefinitionAccess() + ) + | + // If this is an analysis with no context + ic = NoContext(this) + or + exists(LocalScopeVariable lv | lv = ic.getCorrelatedVariable() | + // If the final node in `mid` SBB is a guard condition that affects our tracked correlated + // variable + guardDictatesLocalVariableState(mid.getEnd(), lv, _) + implies + // Then our context must match the inferred state of the correlated variable after the branch + exists(boolean lvStateOnTrueBranch | + guardDictatesLocalVariableState(mid.getEnd(), lv, lvStateOnTrueBranch) + | + result = mid.getATrueSuccessor() and + ic = CorrelatedVariable(this, lv, lvStateOnTrueBranch) + or + result = mid.getAFalseSuccessor() and + ic = CorrelatedVariable(this, lv, lvStateOnTrueBranch.booleanNot()) + ) + ) + ) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/InitializationFunctions.qll b/cpp/common/src/codingstandards/cpp/InitializationFunctions.qll similarity index 100% rename from cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/InitializationFunctions.qll rename to cpp/common/src/codingstandards/cpp/InitializationFunctions.qll diff --git a/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/ReadOfUninitializedMemory.qll b/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/ReadOfUninitializedMemory.qll index ad1453b92..5344d87e0 100644 --- a/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/ReadOfUninitializedMemory.qll +++ b/cpp/common/src/codingstandards/cpp/rules/readofuninitializedmemory/ReadOfUninitializedMemory.qll @@ -5,9 +5,7 @@ import cpp import codingstandards.cpp.Customizations import codingstandards.cpp.Exclusions -import semmle.code.cpp.controlflow.Guards -import semmle.code.cpp.controlflow.SubBasicBlocks -import InitializationFunctions +import codingstandards.cpp.InitializationContext abstract class ReadOfUninitializedMemorySharedQuery extends Query { } @@ -52,260 +50,6 @@ Query getQuery() { result instanceof ReadOfUninitializedMemorySharedQuery } * contexts we attempted for. */ -/** - * A `SubBasicBlockCutNode` that ensures that any uninitialized variable definitions appear at the - * start of a `SubBasicBlock`. - */ -class InitializationSubBasicBlock extends SubBasicBlockCutNode { - InitializationSubBasicBlock() { this = any(UninitializedVariable uv).getADefinitionAccess() } -} - -/** - * Holds if the `gc` dictates the state of variable `lv`, i.e. the state is heuristically - * identified to be different under different branches. - */ -private predicate guardDictatesLocalVariableState( - GuardCondition gc, LocalScopeVariable lv, boolean lvStateOnTrueBranch -) { - // Condition is a boolean check on the variable - gc = lv.getAnAccess() and lvStateOnTrueBranch = true - or - // Condition is a negated boolean check on the variable - gc.(NotExpr).getOperand() = lv.getAnAccess() and lvStateOnTrueBranch = false - or - // Condition controls a block which assigns to `lv` - gc.controls(lv.getAnAssignedValue().getBasicBlock(), lvStateOnTrueBranch) -} - -private newtype TInitializationContext = - /** No specific context - for functions where conditional initialization doesn't play a role */ - NoContext(UninitializedVariable uv) { count(uv.getACorrelatedConditionVariable()) = 0 } or - /** - * A context where the given `LocalScopeVariable` is identified as a correlated variable with the - * given state value. - */ - CorrelatedVariable(UninitializedVariable uv, LocalScopeVariable correlatedVariable, boolean state) { - uv.getACorrelatedConditionVariable() = correlatedVariable and state = [true, false] - } - -/** - * A context to apply when determining whether a given variable is uninitialized at a particular - * `SubBasicBlock` in the control flow graph. - * - * If no suitable context is found for an `UninitializedVariable`, `NoContext` is used. - * - * If one or more correlated variables are found, a `CorrelatedVariable` context is provided, with - * `true` and `false` states. - */ -class InitializationContext extends TInitializationContext { - /** - * Gets the `UninitializedVariable` for this context. - */ - UninitializedVariable getUninitializedVariable() { - this = NoContext(result) or - this = CorrelatedVariable(result, _, _) - } - - /** - * Gets the correlated variable, if any. - */ - LocalScopeVariable getCorrelatedVariable() { this = CorrelatedVariable(_, result, _) } - - string toString() { - if this instanceof CorrelatedVariable - then - result = - "Uninitialized variable " + getUninitializedVariable().getName() + " where location " + - getCorrelatedVariable().getName() + " is " + - any(boolean b | this = CorrelatedVariable(_, _, b)) - else result = "Uninitialized variable " + getUninitializedVariable().getName() - } -} - -/** - * Catches `new int;` as an expression that doesn't initialize its value. Note that the pointer returned has been initialized (ie it is a valid pointer), - * but the pointee/value has not. In our analysis, we simply count `x` as uninitialized in `x = new int` for now, though a more thorough analysis might track the initialization of `x` and `*x` separately. - */ -class NewNotInit extends NewExpr { - NewNotInit() { - this.getAllocatedType() instanceof BuiltInType and - not exists(this.getAChild()) - } -} - -class NonInitAssignment extends Assignment { - NonInitAssignment() { this.getRValue() instanceof NewNotInit } -} - -/** - * A local variable without an initializer which is amenable to initialization analysis. - */ -class UninitializedVariable extends LocalVariable { - UninitializedVariable() { - // Not initialized at declaration - ( - not exists(getInitializer().getExpr()) - or - getInitializer().getExpr() instanceof NewNotInit - ) and - // Not static or thread local, because they are not initialized with indeterminate values - not isStatic() and - not isThreadLocal() and - // Not atomic, which have special initialization rules - not getType().hasSpecifier("atomic") and - // Not a class type, because default initialization of a class calls the default constructor - // The default constructor may leave certain fields uninitialized, but that would be a separate - // field-wise analysis - not this.getType().getUnspecifiedType() instanceof Class and - // An analysis of an array entry also requires a field wise analysis - not this.getType().getUnspecifiedType() instanceof ArrayType and - // Ignore variables in uninstantiated templates, because we often do not have enough semantic - // information to accurately determine initialization state. - not isFromUninstantiatedTemplate(_) and - // Ignore `va_list`, that is part of the mechanism for - not getType().hasName("va_list") and - // Ignore variable defined in a block with an `asm` statement, as that may initialized the variables - not exists(AsmStmt asm | asm.getEnclosingBlock() = getParentScope()) and - // Ignore variables generated for `RangeBasedForStmt` e.g. `for(auto x : y)` - not this = any(RangeBasedForStmt f).getAChild().(DeclStmt).getADeclaration() - } - - /** Gets a variable correlated with at least one use of `this` uninitialized variable. */ - private LocalScopeVariable getAUseCorrelatedConditionVariable() { - /* Extracted to improve join order of getACorrelatedConditionVariable(). */ - // The use is guarded by the access of a variable - exists(GuardCondition gc | - gc.controls(getAUse().getBasicBlock(), _) and - gc = result.getAnAccess() - ) - } - - /** Find another variable which looks like it may be correlated with the initialization of this variable. */ - pragma[noinline] - LocalScopeVariable getACorrelatedConditionVariable() { - result = getAUseCorrelatedConditionVariable() and - ( - // Definition is guarded by an access of the same variable - exists(GuardCondition gc | - gc.controls(getADefinitionAccess().getBasicBlock(), _) and - gc = result.getAnAccess() - ) - or - // The variable is assigned in the same basic block as one of our definitions - result.getAnAssignedValue().getBasicBlock() = getADefinitionAccess().getBasicBlock() - ) - } - - /** - * Get a access of the variable that is assumed to initialize the variable. - * This approximates that any access in the lvalue category may be a definition. - */ - VariableAccess getADefinitionAccess() { - result = getAnAccess() and - result.isLValueCategory() and - // Not a pointless read - not result = any(ExprStmt es).getExpr() and - // not involved in a new expr assignment since that does not define - not result = any(NonInitAssignment a).getLValue() - } - - /** - * Gets an access of the this variable which is not used as an lvalue, and not used as an argument - * to an initialization function. - */ - VariableAccess getAUse() { - result = this.getAnAccess() and - ( - //count rvalue x (or *x) as a use if not new int - result.isRValue() and - not this.getInitializer().getExpr() instanceof NewNotInit - or - //count lvalue x as a use if used in *x and not new int - result.isLValue() and - exists(PointerDereferenceExpr e | result = e.getAChild()) and - exists(this.getInitializer()) and - not this.getInitializer().getExpr() instanceof NewNotInit - or - //count rvalue *x as a use if has new int - result.isRValue() and - this.getInitializer().getExpr() instanceof NewNotInit and - exists(PointerDereferenceExpr e | result = e.getAChild()) - ) and - // Not passed to another initialization function - not exists(Call c, int j | j = c.getTarget().(InitializationFunction).initializedParameter() | - result = c.getArgument(j).(AddressOfExpr).getOperand() - ) and - // Not a pointless read - not result = any(ExprStmt es).getExpr() and - // sizeof operators are not real uses - not result.getParent+() instanceof SizeofOperator - } - - /** Get a read of the variable that may occur while the variable is uninitialized. */ - VariableAccess getAnUnitializedUse() { - exists(SubBasicBlock useSbb | - result = getAUse() and - useSbb.getANode() = result and - // This sbb is considered uninitialized in all the contexts we identified - forex(InitializationContext ct | ct.getUninitializedVariable() = this | - useSbb = getAnUninitializedSubBasicBlock(ct) - ) - ) - } - - /** - * Gets a `SubBasicBlock` where this variable is uninitialized under the conditions specified by - * `InitializationContext`. - */ - private SubBasicBlock getAnUninitializedSubBasicBlock(InitializationContext ic) { - ic.getUninitializedVariable() = this and - ( - // Base case - this SBB is the one that declares the variable - exists(DeclStmt ds | - ds.getADeclaration() = this and - result.getANode() = ds - ) - or - // Recursive case - SBB is a successor of an SBB where this variable is uninitialized - exists(SubBasicBlock mid | - // result is the successor of an SBB where this is considered to be uninitialized under the - // context ic - mid = getAnUninitializedSubBasicBlock(ic) and - result = mid.getASuccessor() and - // Result is not an SBB where this variable is initialized - not getADefinitionAccess() = result and - // Determine if this is a branch at __builtin_expect where the initialization occurs inside - // the checked argument, and exclude it if so, because the CFG is known to be broken here. - not exists(FunctionCall fc | - mid.getEnd() = fc and - fc.getTarget().hasName("__builtin_expect") and - fc.getArgument(0).getAChild*() = getADefinitionAccess() - ) - | - // If this is an analysis with no context - ic = NoContext(this) - or - exists(LocalScopeVariable lv | lv = ic.getCorrelatedVariable() | - // If the final node in `mid` SBB is a guard condition that affects our tracked correlated - // variable - guardDictatesLocalVariableState(mid.getEnd(), lv, _) - implies - // Then our context must match the inferred state of the correlated variable after the branch - exists(boolean lvStateOnTrueBranch | - guardDictatesLocalVariableState(mid.getEnd(), lv, lvStateOnTrueBranch) - | - result = mid.getATrueSuccessor() and - ic = CorrelatedVariable(this, lv, lvStateOnTrueBranch) - or - result = mid.getAFalseSuccessor() and - ic = CorrelatedVariable(this, lv, lvStateOnTrueBranch.booleanNot()) - ) - ) - ) - ) - } -} - query predicate problems(VariableAccess va, string message, UninitializedVariable uv, string name) { not isExcluded(va, getQuery()) and name = uv.getName() and diff --git a/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql b/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql index bf169d233..0a9c9b458 100644 --- a/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql +++ b/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql @@ -14,7 +14,7 @@ import cpp import codingstandards.cpp.misra -import codingstandards.cpp.rules.readofuninitializedmemory.ReadOfUninitializedMemory +import codingstandards.cpp.InitializationContext from UninitializedVariable v where not isExcluded(v, Declarations7Package::uninitializedVariableQuery()) diff --git a/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.expected b/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.expected index 2c5880978..b2b688669 100644 --- a/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.expected +++ b/cpp/misra/test/rules/RULE-11-6-1/UninitializedVariable.expected @@ -1,5 +1,3 @@ -problems -#select | test.cpp:11:7:11:7 | i | Uninitialized variable found. | | test.cpp:18:8:18:9 | i4 | Uninitialized variable found. | | test.cpp:20:8:20:9 | p4 | Uninitialized variable found. | From 645a1d71b9380bccb501cf0153bce0627b35433d Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Fri, 17 Apr 2026 11:39:36 -0400 Subject: [PATCH 3/3] Fix swapped name and description --- cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql | 5 +++-- rule_packages/cpp/Declarations7.json | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql b/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql index 0a9c9b458..ae264be06 100644 --- a/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql +++ b/cpp/misra/src/rules/RULE-11-6-1/UninitializedVariable.ql @@ -1,7 +1,8 @@ /** * @id cpp/misra/uninitialized-variable - * @name RULE-11-6-1: All variables should be initialized, otherwise it may lead to a read of uninitialized memory which is undefined behavior. - * @description All variables should be initialized + * @name RULE-11-6-1: All variables should be initialized + * @description All variables should be initialized, otherwise it may lead to a read of + * uninitialized memory which is undefined behavior. * @kind problem * @precision very-high * @problem.severity error diff --git a/rule_packages/cpp/Declarations7.json b/rule_packages/cpp/Declarations7.json index a0588cd4c..1e3026c97 100644 --- a/rule_packages/cpp/Declarations7.json +++ b/rule_packages/cpp/Declarations7.json @@ -7,9 +7,9 @@ }, "queries": [ { - "description": "All variables should be initialized", + "description": "All variables should be initialized, otherwise it may lead to a read of uninitialized memory which is undefined behavior.", "kind": "problem", - "name": "All variables should be initialized, otherwise it may lead to a read of uninitialized memory which is undefined behavior.", + "name": "All variables should be initialized", "precision": "very-high", "severity": "error", "short_name": "UninitializedVariable",