From 09f930f4e86d64f5b65455682d4d8e8ecac9a828 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 27 Feb 2026 16:42:31 +0100 Subject: [PATCH] C++: Generate initialization function for each NSDMI --- .../raw/internal/IRConstruction.qll | 5 + .../raw/internal/TranslatedCondition.qll | 3 +- .../internal/TranslatedDeclarationEntry.qll | 1 + .../raw/internal/TranslatedElement.qll | 7 +- .../raw/internal/TranslatedExpr.qll | 53 ++++- .../raw/internal/TranslatedInitialization.qll | 8 +- .../TranslatedNonStaticDataMember.qll | 217 ++++++++++++++++++ 7 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedNonStaticDataMember.qll diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 9e9a47a5b4f..7a667fcc017 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -15,6 +15,7 @@ private import TranslatedCall private import TranslatedStmt private import TranslatedFunction private import TranslatedGlobalVar +private import TranslatedNonStaticDataMember private import TranslatedInitialization TranslatedElement getInstructionTranslatedElement(Instruction instruction) { @@ -45,6 +46,8 @@ module Raw { or not var.isFromUninstantiatedTemplate(_) and var instanceof StaticInitializedStaticLocalVariable + or + var instanceof Field ) and var.hasInitializer() and ( @@ -64,6 +67,8 @@ module Raw { getTranslatedFunction(decl).hasUserVariable(var, type) or getTranslatedVarInit(decl).hasUserVariable(var, type) + or + getTranslatedFieldInit(decl).hasUserVariable(var, type) } cached diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll index ff8867db696..be8bff5b05c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll @@ -36,7 +36,8 @@ abstract class TranslatedCondition extends TranslatedElement { final override Declaration getFunction() { result = getEnclosingFunction(expr) or result = getEnclosingVariable(expr).(GlobalOrNamespaceVariable) or - result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable) + result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable) or + result = getEnclosingVariable(expr).(Field) } final Type getResultType() { result = expr.getUnspecifiedType() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll index c0fe9cd2207..b10bba90536 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll @@ -36,6 +36,7 @@ abstract class TranslatedDeclarationEntry extends TranslatedElement, TTranslated or not entry.getDeclaration() instanceof StaticInitializedStaticLocalVariable and not entry.getDeclaration() instanceof GlobalOrNamespaceVariable and + not entry.getDeclaration() instanceof Field and result = stmt.getEnclosingFunction() ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 93d7e9b02cf..c30cc4bbc5c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -918,7 +918,10 @@ newtype TTranslatedElement = } or // The side effect that initializes newly-allocated memory. TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } or - TTranslatedStaticStorageDurationVarInit(Variable var) { Raw::varHasIRFunc(var) } or + TTranslatedStaticStorageDurationVarInit(Variable var) { + Raw::varHasIRFunc(var) and not var instanceof Field + } or + TTranslatedNonStaticDataMemberVarInit(Field var) { Raw::varHasIRFunc(var) } or TTranslatedAssertionOperand(MacroInvocation mi, int index) { hasAssertionOperand(mi, index) } /** @@ -1297,5 +1300,7 @@ abstract class TranslatedRootElement extends TranslatedElement { this instanceof TTranslatedFunction or this instanceof TTranslatedStaticStorageDurationVarInit + or + this instanceof TTranslatedNonStaticDataMemberVarInit } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll index 2f7ffa636da..712f140e5b8 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -14,6 +14,7 @@ private import TranslatedFunction private import TranslatedInitialization private import TranslatedStmt private import TranslatedGlobalVar +private import TranslatedNonStaticDataMember private import IRConstruction import TranslatedCall @@ -138,6 +139,8 @@ abstract class TranslatedExpr extends TranslatedElement { result = getTranslatedFunction(getEnclosingFunction(expr)) or result = getTranslatedVarInit(getEnclosingVariable(expr)) + or + result = getTranslatedFieldInit(getEnclosingVariable(expr)) } } @@ -153,7 +156,10 @@ Declaration getEnclosingDeclaration0(Expr e) { i.getExpr().getFullyConverted() = e and v = i.getDeclaration() | - if v instanceof StaticInitializedStaticLocalVariable or v instanceof GlobalOrNamespaceVariable + if + v instanceof StaticInitializedStaticLocalVariable or + v instanceof GlobalOrNamespaceVariable or + v instanceof Field then result = v else result = e.getEnclosingDeclaration() ) @@ -173,7 +179,10 @@ Variable getEnclosingVariable0(Expr e) { i.getExpr().getFullyConverted() = e and v = i.getDeclaration() | - if v instanceof StaticInitializedStaticLocalVariable or v instanceof GlobalOrNamespaceVariable + if + v instanceof StaticInitializedStaticLocalVariable or + v instanceof GlobalOrNamespaceVariable or + v instanceof Field then result = v else result = e.getEnclosingVariable() ) @@ -826,6 +835,46 @@ class TranslatedPostfixCrementOperation extends TranslatedCrementOperation { override Instruction getResult() { result = this.getLoadedOperand().getResult() } } +class TranslatedParamAccessForType extends TranslatedNonConstantExpr { + override ParamAccessForType expr; + + TranslatedParamAccessForType() { + // Currently only needed for this parameter accesses. + expr.isThisAccess() + } + + final override Instruction getFirstInstruction(EdgeKind kind) { + result = this.getInstruction(OnlyInstructionTag()) and + kind instanceof GotoEdge + } + + override Instruction getALastInstructionInternal() { + result = this.getInstruction(OnlyInstructionTag()) + } + + final override TranslatedElement getChildInternal(int id) { none() } + + override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { + tag = OnlyInstructionTag() and + result = this.getParent().getChildSuccessor(this, kind) + } + + override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::CopyValue and + resultType = getTypeForPRValue(expr.getType()) + } + + override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag instanceof UnaryOperandTag and + result = + this.getEnclosingFunction().(TranslatedNonStaticDataMemberVarInit).getLoadThisInstruction() + } +} + /** * IR translation of an array access expression (e.g. `a[i]`). The array being accessed will either * be a prvalue of pointer type (possibly due to an implicit array-to-pointer conversion), or a diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll index b280dd7bc70..581b0886f7f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -148,7 +148,8 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn final override Declaration getFunction() { result = getEnclosingFunction(expr) or result = getEnclosingVariable(expr).(GlobalOrNamespaceVariable) or - result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable) + result = getEnclosingVariable(expr).(StaticInitializedStaticLocalVariable) or + result = getEnclosingVariable(expr).(Field) } final override Locatable getAst() { result = expr } @@ -528,7 +529,8 @@ abstract class TranslatedFieldInitialization extends TranslatedElement { final override Declaration getFunction() { result = getEnclosingFunction(ast) or result = getEnclosingVariable(ast).(GlobalOrNamespaceVariable) or - result = getEnclosingVariable(ast).(StaticInitializedStaticLocalVariable) + result = getEnclosingVariable(ast).(StaticInitializedStaticLocalVariable) or + result = getEnclosingVariable(ast).(Field) } final override Instruction getFirstInstruction(EdgeKind kind) { @@ -701,6 +703,8 @@ abstract class TranslatedElementInitialization extends TranslatedElement { result = getEnclosingVariable(initList).(GlobalOrNamespaceVariable) or result = getEnclosingVariable(initList).(StaticInitializedStaticLocalVariable) + or + result = getEnclosingVariable(initList).(Field) } final override Instruction getFirstInstruction(EdgeKind kind) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedNonStaticDataMember.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedNonStaticDataMember.qll new file mode 100644 index 00000000000..ff06ff3198e --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedNonStaticDataMember.qll @@ -0,0 +1,217 @@ +import semmle.code.cpp.ir.implementation.raw.internal.TranslatedElement +private import TranslatedExpr +private import cpp +private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.TempVariableTag +private import semmle.code.cpp.ir.internal.CppType +private import TranslatedInitialization +private import InstructionTag +private import semmle.code.cpp.ir.internal.IRUtilities + +class TranslatedNonStaticDataMemberVarInit extends TranslatedRootElement, + TTranslatedNonStaticDataMemberVarInit, InitializationContext +{ + Field field; + Class cls; + + TranslatedNonStaticDataMemberVarInit() { + this = TTranslatedNonStaticDataMemberVarInit(field) and + cls.getAMember() = field + } + + override string toString() { result = cls.toString() + "::" + field.toString() } + + final override Field getAst() { result = field } + + final override Declaration getFunction() { result = field } + + override Instruction getFirstInstruction(EdgeKind kind) { + result = this.getInstruction(EnterFunctionTag()) and + kind instanceof GotoEdge + } + + override Instruction getALastInstructionInternal() { + result = this.getInstruction(ExitFunctionTag()) + } + + override TranslatedElement getChild(int n) { + n = 1 and + result = getTranslatedInitialization(field.getInitializer().getExpr().getFullyConverted()) + } + + override predicate hasInstruction(Opcode op, InstructionTag tag, CppType type) { + op instanceof Opcode::EnterFunction and + tag = EnterFunctionTag() and + type = getVoidType() + or + op instanceof Opcode::AliasedDefinition and + tag = AliasedDefinitionTag() and + type = getUnknownType() + or + op instanceof Opcode::InitializeNonLocal and + tag = InitializeNonLocalTag() and + type = getUnknownType() + or + tag = ThisAddressTag() and + op instanceof Opcode::VariableAddress and + type = getTypeForGLValue(any(UnknownType t)) + or + tag = InitializerStoreTag() and + op instanceof Opcode::InitializeParameter and + type = this.getThisType() + or + tag = ThisLoadTag() and + op instanceof Opcode::Load and + type = this.getThisType() + or + tag = InitializerIndirectStoreTag() and + op instanceof Opcode::InitializeIndirection and + type = getTypeForPRValue(cls) + or + op instanceof Opcode::FieldAddress and + tag = InitializerFieldAddressTag() and + type = getTypeForGLValue(field.getType()) + or + op instanceof Opcode::ReturnVoid and + tag = ReturnTag() and + type = getVoidType() + or + op instanceof Opcode::AliasedUse and + tag = AliasedUseTag() and + type = getVoidType() + or + op instanceof Opcode::ExitFunction and + tag = ExitFunctionTag() and + type = getVoidType() + } + + override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { + kind instanceof GotoEdge and + ( + tag = EnterFunctionTag() and + result = this.getInstruction(AliasedDefinitionTag()) + or + tag = AliasedDefinitionTag() and + result = this.getInstruction(InitializeNonLocalTag()) + or + tag = InitializeNonLocalTag() and + result = this.getInstruction(ThisAddressTag()) + or + tag = ThisAddressTag() and + result = this.getInstruction(InitializerStoreTag()) + or + tag = InitializerStoreTag() and + result = this.getInstruction(ThisLoadTag()) + or + tag = ThisLoadTag() and + result = this.getInstruction(InitializerIndirectStoreTag()) + or + tag = InitializerIndirectStoreTag() and + result = this.getInstruction(InitializerFieldAddressTag()) + ) + or + tag = InitializerFieldAddressTag() and + result = this.getChild(1).getFirstInstruction(kind) + or + kind instanceof GotoEdge and + ( + tag = ReturnTag() and + result = this.getInstruction(AliasedUseTag()) + or + tag = AliasedUseTag() and + result = this.getInstruction(ExitFunctionTag()) + ) + } + + override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { + child = this.getChild(1) and + result = this.getInstruction(ReturnTag()) and + kind instanceof GotoEdge + } + + final override CppType getInstructionMemoryOperandType( + InstructionTag tag, TypedOperandTag operandTag + ) { + tag = AliasedUseTag() and + operandTag instanceof SideEffectOperandTag and + result = getUnknownType() + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + ( + tag = ThisAddressTag() or + tag = InitializerStoreTag() or + tag = InitializerIndirectStoreTag() + ) and + result = getIRTempVariable(field, ThisTempVar()) + } + + override Field getInstructionField(InstructionTag tag) { + tag = InitializerFieldAddressTag() and + result = field + } + + override predicate hasTempVariable(TempVariableTag tag, CppType type) { + tag = ThisTempVar() and + type = this.getThisType() + } + + /** + * Holds if this variable defines or accesses variable `var` with type `type`. This includes all + * parameters and local variables, plus any global variables or static data members that are + * directly accessed by the function. + */ + final predicate hasUserVariable(Variable varUsed, CppType type) { + ( + ( + varUsed instanceof GlobalOrNamespaceVariable + or + varUsed instanceof StaticLocalVariable + or + varUsed instanceof MemberVariable and not varUsed instanceof Field + ) and + exists(VariableAccess access | + access.getTarget() = varUsed and + getEnclosingVariable(access) = field + ) + or + field = varUsed + or + varUsed.(LocalScopeVariable).getEnclosingElement*() = field + or + varUsed.(Parameter).getCatchBlock().getEnclosingElement*() = field + ) and + type = getTypeForPRValue(getVariableType(varUsed)) + } + + override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { + ( + tag = InitializerStoreTag() + or + tag = ThisLoadTag() + ) and + operandTag instanceof AddressOperandTag and + result = this.getInstruction(ThisAddressTag()) + or + ( + tag = InitializerIndirectStoreTag() and + operandTag instanceof AddressOperandTag + or + tag = InitializerFieldAddressTag() and + operandTag instanceof UnaryOperandTag + ) and + result = this.getInstruction(ThisLoadTag()) + } + + override Instruction getTargetAddress() { + result = this.getInstruction(InitializerFieldAddressTag()) + } + + override Type getTargetType() { result = field.getUnspecifiedType() } + + final Instruction getLoadThisInstruction() { result = this.getInstruction(ThisLoadTag()) } + + private CppType getThisType() { result = getTypeForGLValue(cls) } +} + +TranslatedNonStaticDataMemberVarInit getTranslatedFieldInit(Field field) { result.getAst() = field }