From 22eda4ef0aa46f86967634ba4bf48e9a20cb3f06 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 20 Mar 2026 17:44:14 +0100 Subject: [PATCH] C++: Add call side effects for default field initializations to the IR --- .../raw/internal/SideEffects.qll | 23 ++++++-- .../raw/internal/TranslatedCall.qll | 52 +++++++++++++------ .../raw/internal/TranslatedElement.qll | 11 ++-- .../raw/internal/TranslatedInitialization.qll | 15 +++++- 4 files changed, 77 insertions(+), 24 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll index b7b4be7f787..0ce1f898c0d 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll @@ -133,7 +133,7 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff * An expression that can have call side effects. * * All kinds of expressions invoke a function as part of their evaluation. This class provides a - * way to treat those functions similarly, and to get the invoked `Function`. + * way to treat those expressions similarly, and to get the invoked `Declaration`. */ class ExprWithCallSizeEffects extends Expr { ExprWithCallSizeEffects() { @@ -142,15 +142,19 @@ class ExprWithCallSizeEffects extends Expr { this instanceof NewOrNewArrayExpr or this instanceof DeleteOrDeleteArrayExpr + or + this instanceof ConstructorDefaultFieldInit } - /** Gets the `Function` invoked by this expression, if known. */ - final Function getTarget() { + /** Gets the `Declaration` invoked by this expression, if known. */ + final Declaration getTarget() { result = this.(Call).getTarget() or result = this.(NewOrNewArrayExpr).getAllocator() or result = this.(DeleteOrDeleteArrayExpr).getDeallocator() + or + result = this.(ConstructorDefaultFieldInit).getTarget() } } @@ -175,7 +179,7 @@ Opcode getCallSideEffectOpcode(ExprWithCallSizeEffects expr) { /** * Returns a side effect opcode for parameter index `i` of the specified call. * - * This predicate will return at most two results: one read side effect, and one write side effect. + * This predicate will yield at most two results: one read side effect, and one write side effect. */ Opcode getASideEffectOpcode(Call call, ParameterIndex i) { exists(boolean buffer | @@ -228,3 +232,14 @@ Opcode getASideEffectOpcode(Call call, ParameterIndex i) { ) ) } + +/** + * Returns a side effect opcode for a default field initialization. + * + * This predicate will yield two results: one read side effect, and one write side effect. + */ +Opcode getDefaultFieldInitSideEffectOpcode() { + result instanceof Opcode::IndirectReadSideEffect + or + result instanceof Opcode::IndirectMayWriteSideEffect +} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll index f3d084883a7..572ce5f2858 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll @@ -10,6 +10,7 @@ private import SideEffects private import TranslatedElement private import TranslatedExpr private import TranslatedFunction +private import TranslatedInitialization private import DefaultOptions as DefaultOptions /** @@ -429,6 +430,9 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi or expr instanceof DeleteOrDeleteArrayExpr and result = getTranslatedDeleteOrDeleteArray(expr).getInstruction(CallTag()) + or + expr instanceof ConstructorDefaultFieldInit and + result = getTranslatedConstructorFieldInitialization(expr).getInstruction(CallTag()) } } @@ -504,11 +508,25 @@ abstract class TranslatedSideEffect extends TranslatedElement { abstract predicate sideEffectInstruction(Opcode opcode, CppType type); } +private class CallOrDefaultFieldInit extends Expr { + CallOrDefaultFieldInit() { + this instanceof Call + or + this instanceof ConstructorDefaultFieldInit + } + + Declaration getTarget() { + result = this.(Call).getTarget() + or + result = this.(ConstructorDefaultFieldInit).getTarget() + } +} + /** * The IR translation of a single argument side effect for a call. */ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect { - Call call; + CallOrDefaultFieldInit callOrInit; int index; SideEffectOpcode sideEffectOpcode; @@ -524,7 +542,7 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect { result = "(read side effect for " + this.getArgString() + ")" } - override Call getPrimaryExpr() { result = call } + override Expr getPrimaryExpr() { result = callOrInit } override predicate sortOrder(int group, int indexInGroup) { indexInGroup = index and @@ -586,9 +604,10 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect { tag instanceof OnlyInstructionTag and operandTag instanceof BufferSizeOperandTag and result = - getTranslatedExpr(call.getArgument(call.getTarget() - .(SideEffectFunction) - .getParameterSizeIndex(index)).getFullyConverted()).getResult() + getTranslatedExpr(callOrInit + .(Call) + .getArgument(callOrInit.getTarget().(SideEffectFunction).getParameterSizeIndex(index)) + .getFullyConverted()).getResult() } /** Holds if this side effect is a write side effect, rather than a read side effect. */ @@ -616,7 +635,7 @@ class TranslatedArgumentExprSideEffect extends TranslatedArgumentSideEffect, Expr arg; TranslatedArgumentExprSideEffect() { - this = TTranslatedArgumentExprSideEffect(call, arg, index, sideEffectOpcode) + this = TTranslatedArgumentExprSideEffect(callOrInit, arg, index, sideEffectOpcode) } final override Locatable getAst() { result = arg } @@ -640,28 +659,31 @@ class TranslatedArgumentExprSideEffect extends TranslatedArgumentSideEffect, * The IR translation of an argument side effect for `*this` on a call, where there is no `Expr` * object that represents the `this` argument. * - * The applies only to constructor calls, as the AST has exploit qualifier `Expr`s for all other - * calls to non-static member functions. + * The applies to constructor calls and default field initializations, as the AST has explicit + * qualifier `Expr`s for all other calls to non-static member functions. */ -class TranslatedStructorQualifierSideEffect extends TranslatedArgumentSideEffect, - TTranslatedStructorQualifierSideEffect +class TranslatedImplicitThisQualifierSideEffect extends TranslatedArgumentSideEffect, + TTranslatedImplicitThisQualifierSideEffect { - TranslatedStructorQualifierSideEffect() { - this = TTranslatedStructorQualifierSideEffect(call, sideEffectOpcode) and + TranslatedImplicitThisQualifierSideEffect() { + this = TTranslatedImplicitThisQualifierSideEffect(callOrInit, sideEffectOpcode) and index = -1 } - final override Locatable getAst() { result = call } + final override Locatable getAst() { result = callOrInit } - final override Type getIndirectionType() { result = call.getTarget().getDeclaringType() } + final override Type getIndirectionType() { result = callOrInit.getTarget().getDeclaringType() } final override string getArgString() { result = "this" } final override Instruction getArgInstruction() { exists(TranslatedStructorCall structorCall | - structorCall.getExpr() = call and + structorCall.getExpr() = callOrInit and result = structorCall.getQualifierResult() ) + or + callOrInit instanceof ConstructorDefaultFieldInit and + result = getTranslatedFunction(callOrInit.getEnclosingFunction()).getLoadThisInstruction() } } 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 66ad2ae2679..2f86a3f476b 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,11 +918,16 @@ newtype TTranslatedElement = } or // Constructor calls lack a qualifier (`this`) expression, so we need to handle the side effects // on `*this` without an `Expr`. - TTranslatedStructorQualifierSideEffect(Call call, SideEffectOpcode opcode) { + TTranslatedImplicitThisQualifierSideEffect(ExprWithCallSizeEffects call, SideEffectOpcode opcode) { not ignoreExpr(call) and not ignoreSideEffects(call) and - call instanceof ConstructorCall and - opcode = getASideEffectOpcode(call, -1) + ( + call instanceof ConstructorCall and + opcode = getASideEffectOpcode(call, -1) + or + call instanceof ConstructorFieldInit and + opcode = getDefaultFieldInitSideEffectOpcode() + ) } or // The side effect that initializes newly-allocated memory. TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } or 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 f5d092ca44a..614d9dd5899 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 @@ -634,13 +634,22 @@ class TranslatedDefaultFieldInitialization extends TranslatedFieldInitialization kind instanceof GotoEdge } - override Instruction getALastInstructionInternal() { result = this.getInstruction(CallTag()) } + override Instruction getALastInstructionInternal() { + result = this.getSideEffects().getALastInstruction() + } + + override TranslatedElement getLastChild() { result = this.getSideEffects() } override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { tag = CallTargetTag() and result = this.getInstruction(CallTag()) or tag = CallTag() and + result = this.getSideEffects().getFirstInstruction(kind) + } + + override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { + child = this.getSideEffects() and result = this.getParent().getChildSuccessor(this, kind) } @@ -670,7 +679,9 @@ class TranslatedDefaultFieldInitialization extends TranslatedFieldInitialization result = field } - override TranslatedElement getChild(int id) { none() } + override TranslatedElement getChild(int id) { id = 0 and result = this.getSideEffects() } + + final TranslatedSideEffects getSideEffects() { result.getExpr() = ast } } private string getZeroValue(Type type) {