ci(github): patch CodeQL lifetime rule for Qt (#1866)

This commit is contained in:
Oleg Shparber
2026-05-11 14:41:56 +03:00
committed by GitHub
parent d69ae4bd55
commit 5783f7eade
4 changed files with 741 additions and 1 deletions
+8
View File
@@ -5,3 +5,11 @@ name: Zeal CodeQL Configuration
packs:
- codeql/cpp-queries:codeql-suites/cpp-security-experimental.qls
# The upstream cpp/use-after-expired-lifetime rule misclassifies most Qt value
# types (QModelIndex, QString, etc.) as LifetimePointerType because they contain
# private d-pointer fields. We disable it and ship a patched copy in
# .github/codeql/queries/ that tightens the type classification.
query-filters:
- exclude:
id: cpp/use-after-expired-lifetime
@@ -0,0 +1,727 @@
/**
* @id zeal/cpp/use-after-expired-lifetime
* @name Use of object after its lifetime has ended (Qt-aware patch)
* @description Accessing an object after its lifetime has ended can result in security vulnerabilities and undefined behavior. This is a local patched copy of the upstream `cpp/use-after-expired-lifetime` query that drops the over-broad "has a field which is a lifetime pointer/owner type" classification clauses, which misclassify any class with a private d-pointer (most Qt value types) as pointer-like.
* @kind problem
* @precision medium
* @problem.severity error
* @tags correctness
* security
* experimental
* external/cwe/cwe-416
*/
import cpp
import semmle.code.cpp.controlflow.Nullness
class StarOperator extends Operator {
StarOperator() {
this.hasName("operator*") and
this.getNumberOfParameters() = 0
}
}
class IncrementOperator extends Operator {
IncrementOperator() {
this.hasName("operator++") and
this.getNumberOfParameters() = 0
}
}
class StructureDerefOperator extends Operator {
StructureDerefOperator() {
this.hasName("operator->") and
this.getNumberOfParameters() = 0
}
}
class SubscriptOperator extends Operator {
SubscriptOperator() {
this.hasName("operator[]") and
this.getNumberOfParameters() = 1
}
}
/**
* A type which is an `Indirection` type according to the Lifetime profile.
*
* An indirection type is either a `LifetimePointerType` or `LifetimeOwnerType`.
*/
abstract class LifetimeIndirectionType extends Type {
/**
* Gets the `DerefType` of this indirection type.
*
* This corresponds to the owned or pointed to type.
*/
Type getDerefType() {
result = this.(PointerType).getBaseType()
or
result = this.(ReferenceType).getBaseType()
or
exists(MemberFunction mf | mf.getDeclaringType() = this |
result = mf.(StarOperator).getType().getUnspecifiedType().(ReferenceType).getBaseType()
or
result = mf.(SubscriptOperator).getType().getUnspecifiedType().(ReferenceType).getBaseType()
or
result =
mf.(StructureDerefOperator).getType().getUnspecifiedType().(PointerType).getBaseType()
or
mf.getName() = "begin" and
result = mf.getType().(LifetimePointerType).getDerefType()
)
}
}
/**
* A lifetime owner type.
*
* A type which owns another object. For example, `std::unique_ptr`. Includes
* `LifetimeSharedOwnerType`.
*
* NOTE (zeal patch): the upstream `Has a field which is a lifetime owner type`
* clause is intentionally omitted — it caused widespread false positives on Qt
* value classes that hold private d-pointers.
*/
class LifetimeOwnerType extends LifetimeIndirectionType {
LifetimeOwnerType() {
// Any shared owner types are also owner types
this instanceof LifetimeSharedOwnerType
or
// This is a container type, or a type with a star operator and..
(
this instanceof ContainerType
or
exists(StarOperator mf | mf.getDeclaringType() = this)
) and
// .. has a "user" provided destructor
exists(Destructor d |
d.getDeclaringType() = this and
not d.isCompilerGenerated()
)
or
// Any specified version of an owner type is also an owner type
this.getUnspecifiedType() instanceof LifetimeOwnerType
or
// Derived from a public base class which is a owner type
exists(ClassDerivation cd |
cd = this.(Class).getADerivation() and
cd.getBaseClass() instanceof LifetimeOwnerType and
cd.getASpecifier().hasName("public")
)
or
// Lifetime profile treats the following types as owner types, even though they don't fully
// adhere to the requirements above
this.(Class)
.hasQualifiedName("std",
["stack", "queue", "priority_queue", "optional", "variant", "any", "regex"])
or
// Explicit annotation on the type
this.getAnAttribute().getName().matches("gsl::Owner%")
}
}
/**
* A `ContainerType`, based on `[container.requirements]` with some adaptions to capture more real
* world containers.
*/
class ContainerType extends Class {
ContainerType() {
// We use a simpler set of heuristics than the `[container.requirements]`, requiring only
// `begin()`/`end()`/`size()` as the minimum API for something to be considered a "container"
// type
this.getAMemberFunction().getName() = "begin" and
this.getAMemberFunction().getName() = "end" and
this.getAMemberFunction().getName() = "size"
or
// This class is a `ContainerType` if it is constructed from a `ContainerType`. This is
// important, because templates may not have instantiated all the required member functions
exists(TemplateClass tc |
this.isConstructedFrom(tc) and
tc instanceof ContainerType
)
}
}
/**
* A lifetime "shared owner" type.
*
* A shared owner is type that "owns" another object, and shares that ownership with other owners.
* Examples include `std::shared_ptr` along with other reference counting types.
*
* NOTE (zeal patch): the upstream `Has a field which is a lifetime shared owner
* type` clause is intentionally omitted. Qt's `QSharedDataPointer` /
* `QExplicitlySharedDataPointer` themselves satisfy the shared-owner criteria
* (operator*, user destructor, copy semantics); leaving the field clause in
* would re-route the same Qt-value-class false positives through
* `LifetimeSharedOwnerType` -> `LifetimeOwnerType`.
*/
class LifetimeSharedOwnerType extends Type {
LifetimeSharedOwnerType() {
/*
* Find all types which can be dereferenced (i.e. have unary * operator), and are therefore
* likely to be "owner"s or "pointer"s to other objects. We then consider these classes to be
* shared owners if:
* - They can be copied (a unique "owner" type would not be copyable)
* - They can destroyed
*/
// unary * (i.e. can be dereferenced)
exists(StarOperator mf | mf.getDeclaringType() = this) and
// "User" provided destructor
exists(Destructor d |
d.getDeclaringType() = this and
not d.isCompilerGenerated()
) and
// A copy constructor and copy assignment operator
exists(CopyConstructor cc | cc.getDeclaringType() = this and not cc.isDeleted()) and
exists(CopyAssignmentOperator cc | cc.getDeclaringType() = this and not cc.isDeleted())
or
// This class is a `SharedOwnerType` if it is constructed from a `SharedOwnerType`. This is
// important, because templates may not have instantiated all the required member functions
exists(TemplateClass tc |
this.(Class).isConstructedFrom(tc) and
tc instanceof LifetimeSharedOwnerType
)
or
// Any specified version of a shared owner type is also a shared owner type
this.getUnspecifiedType() instanceof LifetimeSharedOwnerType
or
// Derived from a public base class which is a shared owner type
exists(ClassDerivation cd |
cd = this.(Class).getADerivation() and
cd.getBaseClass() instanceof LifetimeSharedOwnerType and
cd.getASpecifier().hasName("public")
)
or
// Lifetime profile treats the following types as shared owner types, even though they don't
// fully adhere to the requirements above
this.(Class).hasQualifiedName("std", "shared_future")
or
// Explicit annotation on the type
this.getAnAttribute().getName().matches("gsl::SharedOwner%")
}
}
/**
* An `IteratorType`, based on `[iterator.requirements]` with some adaptions to capture more real
* world iterators.
*/
class IteratorType extends Type {
IteratorType() {
// We consider anything with an increment and * operator to be sufficient to be an iterator type
exists(StarOperator mf |
mf.getDeclaringType() = this and mf.getType().getUnspecifiedType() instanceof ReferenceType
) and
exists(IncrementOperator op |
op.getDeclaringType() = this and op.getType().(ReferenceType).getBaseType() = this
)
or
// Along with unspecified versions of the types above
this.getUnspecifiedType() instanceof IteratorType
}
}
/**
* A lifetime pointer type.
*
* A type which points to another object. For example, `std::unique_ptr`. Includes
* `LifetimeSharedOwnerType`.
*
* NOTE (zeal patch): the upstream `Has a field which is a lifetime pointer type`
* clause is intentionally omitted — it caused widespread false positives on Qt
* value classes that hold private d-pointers.
*/
class LifetimePointerType extends LifetimeIndirectionType {
LifetimePointerType() {
this instanceof IteratorType
or
this instanceof PointerType
or
this instanceof ReferenceType
or
// A shared owner type is a pointer type, but an owner type is not.
this instanceof LifetimeSharedOwnerType and
not this instanceof LifetimeOwnerType
or
this.(Class).hasQualifiedName("std", "reference_wrapper")
or
exists(Class vectorBool, UserType reference |
vectorBool.hasQualifiedName("std", "vector") and
vectorBool.getATemplateArgument() instanceof BoolType and
reference.hasName("reference") and
reference.getDeclaringType() = vectorBool and
this = reference.getUnderlyingType()
)
or
// Any specified version of a pointer type is also an owner type
this.getUnspecifiedType() instanceof LifetimePointerType
or
// Derived from a public base class which is a pointer type
exists(ClassDerivation cd |
cd = this.(Class).getADerivation() and
cd.getBaseClass() instanceof LifetimePointerType and
cd.getASpecifier().hasName("public")
)
or
// Explicit annotation on the type
this.getAnAttribute().getName().matches("gsl::Pointer%")
}
}
/** A full expression as defined in [intro.execution] of N3797. */
class FullExpr extends Expr {
FullExpr() {
// A full-expression is not a subexpression
not this.getParent() instanceof Expr
or
// A sub-expression that is an unevaluated operand
this.isUnevaluated()
}
}
/** Gets the `FullExpression` scope of the `TemporaryObjectExpr`. */
FullExpr getTemporaryObjectExprScope(TemporaryObjectExpr toe) {
result = toe.getUnconverted().getParent*()
}
/**
* See `LifetimeLocalVariable` and subclasses.
*/
private newtype TLifetimeLocalVariable =
TLocalScopeVariable(LocalScopeVariable lsv) { not lsv.isStatic() } or
TTemporaryObject(TemporaryObjectExpr toe)
/**
* A "LocalVariable" as defined by the lifetime profile.
*
* This includes newly introduced objects with a local scope.
*/
class LifetimeLocalVariable extends TLifetimeLocalVariable {
string toString() { none() } // specified in sub-classes
Type getType() { none() }
}
/**
* A parameter or `LocalVariable`, used as a `LifetimeLocalVariable`
*/
class LifetimeLocalScopeVariable extends TLocalScopeVariable, LifetimeLocalVariable {
LocalScopeVariable getVariable() { this = TLocalScopeVariable(result) }
override Type getType() { result = this.getVariable().getType() }
override string toString() { result = this.getVariable().toString() }
}
/**
* A temporary object used as a `LifetimeLocalVariable`.
*/
class LifetimeTemporaryObject extends TTemporaryObject, LifetimeLocalVariable {
TemporaryObjectExpr getTemporaryObjectExpr() { this = TTemporaryObject(result) }
override Type getType() { result = this.getTemporaryObjectExpr().getType() }
override string toString() { result = this.getTemporaryObjectExpr().toString() }
}
newtype TInvalidReason =
/** LifetimeLocalVariable is invalid because it hasn't been initialized. */
TUninitialized(DeclStmt ds, Variable v) { ds.getADeclaration() = v } or
/** LifetimeLocalVariable is invalid because it points to a variable which has gone out of scope. */
TVariableOutOfScope(LocalScopeVariable v, ControlFlowNode cfn) { goesOutOfScopeAt(v, cfn) } or
/** LifetimeLocalVariable is invalid because it points to a temporary object expression which has gone out of scope. */
TTemporaryOutOfScope(TemporaryObjectExpr toe) or
/** LifetimeLocalVariable is invalid because it points to data held by an owner which has since been invalidated. */
TOwnerModified(FunctionCall fc)
/**
* A reason why a pointer may be invalid.
*/
class InvalidReason extends TInvalidReason {
/** Holds if this reason indicates the pointer is accessed before the lifetime of an object began. */
predicate isBeforeLifetime() { this instanceof TUninitialized }
/** Holds if this reason indicates the pointer is accessed after the lifetime of an object has finished. */
predicate isAfterLifetime() { not this.isBeforeLifetime() }
/** Gets a description of the reason why this pointer may be invalid. */
string getDescription() {
exists(DeclStmt ds, Variable v |
this = TUninitialized(ds, v) and
result = "variable " + v.getName() + " was never initialized"
)
or
exists(LocalScopeVariable v, ControlFlowNode cfn |
this = TVariableOutOfScope(v, cfn) and
result = "variable " + v.getName() + " went out of scope"
)
or
exists(TemporaryObjectExpr toe |
this = TTemporaryOutOfScope(toe) and
result = "temporary object went out of scope"
)
or
exists(FunctionCall fc |
this = TOwnerModified(fc) and
result = "owner type was modified"
)
}
string toString() { result = this.getDescription() }
/** Get an element that explains the reason for the invalid determination. */
private Element getExplanatoryElement() {
exists(DeclStmt ds |
this = TUninitialized(ds, _) and
result = ds
)
or
exists(ControlFlowNode cfn |
this = TVariableOutOfScope(_, cfn) and
result = cfn
)
or
exists(TemporaryObjectExpr toe |
this = TTemporaryOutOfScope(toe) and
result = getTemporaryObjectExprScope(toe)
)
or
exists(FunctionCall fc |
this = TOwnerModified(fc) and
result = fc
)
}
/**
* Provides a `message` for use in alert messages.
*
* The message will contain a `$@` placeholder, for which `explanation` and `explanationDesc` are
* the placeholder components which should be added as extra columns.
*/
predicate hasMessage(string message, Element explanation, string explanationDesc) {
message = "because the " + this.getDescription() + " $@." and
explanation = this.getExplanatoryElement() and
explanationDesc = "here"
}
}
/**
* A reason why a pointer may be null.
*/
newtype TNullReason =
// Null because the `NullValue` was assigned
TNullAssignment(NullValue e)
class NullReason extends TNullReason {
/** Gets a description of the reason why this pointer may be null. */
string getDescription() {
exists(NullValue nv |
this = TNullAssignment(nv) and
result = "null value was assigned"
)
}
string toString() { result = this.getDescription() }
}
/** See `PSetEntry` and subclasses. */
newtype TPSetEntry =
/** Points to a lifetime local variable. */
PSetVar(LifetimeLocalVariable lv) or
/** Points to a lifetime local variable that represents an owner type. */
PSetOwner(LifetimeLocalVariable lv, int level) {
level = [0 .. 2] and lv.getType() instanceof LifetimeOwnerType
} or
/** Points to a global variable. */
PSetGlobal() or
/** A null pointer. */
PSetNull(NullReason nr) or
/** An invalid pointer, for the given reason. */
PSetInvalid(InvalidReason ir) or
/** An unknown pointer. */
PSetUnknown()
/**
* An entry in the points-to set for a particular "LifetimeLocalVariable" at a particular
* point in the program.
*/
class PSetEntry extends TPSetEntry {
string toString() {
exists(LifetimeLocalVariable lv |
this = PSetVar(lv) and
result = "Var(" + lv.toString() + ")"
)
or
this = PSetGlobal() and result = "global"
or
exists(LifetimeLocalVariable lv, int level |
this = PSetOwner(lv, level) and
result = "Owner(" + lv.toString() + "," + level + ")"
)
or
exists(NullReason nr | this = PSetNull(nr) and result = "null because" + nr)
or
exists(InvalidReason ir | this = PSetInvalid(ir) and result = "invalid because " + ir)
or
this = PSetUnknown() and result = "unknown"
}
}
/**
* The "pmap" or "points-to map" for a "lifetime" local variable.
*/
predicate pointsToMap(ControlFlowNode cfn, LifetimeLocalVariable lv, PSetEntry ps) {
if isPSetReassigned(cfn, lv)
then ps = getAnAssignedPSetEntry(cfn, lv)
else
// Exclude unknown for now
exists(ControlFlowNode pred, PSetEntry prevPSet |
pred = cfn.getAPredecessor() and
pointsToMap(pred, lv, prevPSet) and
// Not PSetNull() and a non-null successor of a null check
not exists(AnalysedExpr ae |
ps = PSetNull(_) and
cfn = ae.getNonNullSuccessor(lv.(LifetimeLocalScopeVariable).getVariable())
) and
// lv is not out of scope at this node
not goesOutOfScopeAt(lv.(LifetimeLocalScopeVariable).getVariable(), cfn)
|
// Propagate a PSetEntry from the predecessor node, so long as the
// PSetEntry is not invalidated at this node
ps = prevPSet and
not exists(getAnInvalidation(prevPSet, cfn))
or
// Replace prevPSet with an invalidation reason at this node
ps = getAnInvalidation(prevPSet, cfn)
)
}
private predicate isPSetReassigned(ControlFlowNode cfn, LifetimeLocalVariable lv) {
exists(DeclStmt ds |
cfn = ds and
ds.getADeclaration() = lv.(LifetimeLocalScopeVariable).getVariable() and
lv.getType() instanceof PointerType
)
or
exists(TemporaryObjectExpr toe |
toe = lv.(LifetimeTemporaryObject).getTemporaryObjectExpr() and
cfn = toe
)
or
// Assigned a value
cfn = lv.(LifetimeLocalScopeVariable).getVariable().getAnAssignedValue()
or
// If the address of a local var is passed to a function, then assume it initializes it
exists(Call fc, AddressOfExpr aoe |
cfn = aoe and
fc.getAnArgument() = aoe and
lv.(LifetimeLocalScopeVariable).getVariable() = aoe.getOperand().(VariableAccess).getTarget()
)
}
/** Is the `lv` assigned or reassigned at this ControlFlowNode `cfn`. */
private PSetEntry getAnAssignedPSetEntry(ControlFlowNode cfn, LifetimeLocalVariable lv) {
exists(DeclStmt ds |
cfn = ds and
ds.getADeclaration() = lv.(LifetimeLocalScopeVariable).getVariable()
|
lv.getType() instanceof PointerType and
result = PSetInvalid(TUninitialized(ds, lv.(LifetimeLocalScopeVariable).getVariable()))
)
or
exists(TemporaryObjectExpr toe |
toe = lv.(LifetimeTemporaryObject).getTemporaryObjectExpr() and
cfn = toe and
result = PSetVar(lv)
)
or
// Assigned a value
exists(Expr assign |
assign = lv.(LifetimeLocalScopeVariable).getVariable().getAnAssignedValue() and
cfn = assign
|
if isKnownAssignmentType(assign)
then knownAssignmentType(assign, result)
else result = PSetUnknown()
)
or
// If the address of a local var is passed to a function, then assume it initializes it
exists(Call fc, AddressOfExpr aoe |
cfn = aoe and
fc.getAnArgument() = aoe and
lv.(LifetimeLocalScopeVariable).getVariable() = aoe.getOperand().(VariableAccess).getTarget() and
result = PSetUnknown()
)
}
predicate isKnownAssignmentType(Expr assign) {
assign = any(LocalScopeVariable lv).getAnAssignedValue() and
(
exists(Variable v | v = assign.(AddressOfExpr).getOperand().(VariableAccess).getTarget() |
v instanceof LocalScopeVariable
or
v instanceof GlobalVariable
)
or
// Assignment of a previous variable
exists(VariableAccess va |
va = assign and
va.getTarget().(LocalScopeVariable).getType() instanceof LifetimePointerType
)
or
assign instanceof NullValue
or
exists(FunctionCall fc |
assign = fc and
fc.getNumberOfArguments() = 0 and
fc.getType() instanceof LifetimePointerType
|
// A function call is a product of its inputs (just handle qualifiers at the moment)
exists(LifetimeLocalVariable lv |
lv = TTemporaryObject(fc.getQualifier().getConversion())
or
lv = TLocalScopeVariable(fc.getQualifier().(VariableAccess).getTarget())
|
lv.getType() instanceof LifetimePointerType
or
lv.getType() instanceof LifetimeOwnerType
)
)
)
}
/**
* An expression which is assigned to a `LocalScopeVariable`, which has a known PSet value i.e. not
* an "Unknown" PSet value.
*/
predicate knownAssignmentType(Expr assign, PSetEntry ps) {
assign = any(LocalScopeVariable lv).getAnAssignedValue() and
(
// The assigned value is `&v`
exists(Variable v | v = assign.(AddressOfExpr).getOperand().(VariableAccess).getTarget() |
v instanceof LocalScopeVariable and
(
// If the variable we are taking the address of is a reference type, then we are really
// taking the address of whatever the reference type "points-to". Use the `pointsToMap`
// to determine viable `LifetimeLocalScopeVariable`s this could point to.
if v.getType() instanceof ReferenceType
then
pointsToMap(assign.getAPredecessor(),
any(LifetimeLocalScopeVariable lv | lv.getVariable() = v), ps)
else
// This assignment points-to `v` itself.
ps = PSetVar(TLocalScopeVariable(v))
)
or
// If the variable we are taking the address of is a reference variable, then this points-to
// a global. If the variable we taking the address of is a reference type, we need to consider
// that it might point-to a global, even if it is a LocalScopeVariable (this case is required
// so that we still produce a result even if the pointsToMap is empty for `lv`).
(v instanceof GlobalVariable or v.getType() instanceof ReferenceType) and
ps = PSetGlobal()
)
or
// Assignment of a previous variable
exists(VariableAccess va |
va = assign and
va.getTarget().(LocalScopeVariable).getType() instanceof LifetimePointerType and
// PSet of that become PSet of this
pointsToMap(assign.getAPredecessor(),
any(LifetimeLocalScopeVariable lv | lv.getVariable() = va.getTarget()), ps)
)
or
// The `NullValue` class covers all types of null equivalent expressions. This case also handles
// default and value initialization, where an "implicit" null value expression is added by the
// extractor
assign instanceof NullValue and ps = PSetNull(TNullAssignment(assign))
or
exists(FunctionCall fc |
assign = fc and
// If the assignment is being converted via a ReferenceDereferenceExpr, then
// we are essentially copying the original object
not assign.getFullyConverted() instanceof ReferenceDereferenceExpr and
fc.getNumberOfArguments() = 0 and
fc.getType() instanceof LifetimePointerType
|
// A function call is a product of its inputs (just handle qualifiers at the moment)
exists(LifetimeLocalVariable lv |
lv = TTemporaryObject(fc.getQualifier().getConversion())
or
lv = TLocalScopeVariable(fc.getQualifier().(VariableAccess).getTarget())
|
ps = PSetVar(lv) and lv.getType() instanceof LifetimePointerType
or
ps = PSetOwner(lv, 0) and lv.getType() instanceof LifetimeOwnerType
)
)
)
}
/**
* Holds if `cfn` is a node that occur directly after the local scope variable `lv` has gone out of scope.
*/
predicate goesOutOfScopeAt(LocalScopeVariable lv, ControlFlowNode cfn) {
exists(BlockStmt scope |
scope = lv.getParentScope() and
scope.getAChild+() = cfn.getAPredecessor().getEnclosingStmt() and
not scope.getAChild+() = cfn.getEnclosingStmt()
)
}
PSetInvalid getAnInvalidation(PSetEntry ps, ControlFlowNode cfn) {
exists(LifetimeLocalScopeVariable lv | ps = PSetVar(lv) |
result = PSetInvalid(TVariableOutOfScope(lv.getVariable(), cfn))
)
or
exists(LifetimeLocalScopeVariable lv | ps = PSetOwner(lv, _) |
result = PSetInvalid(TVariableOutOfScope(lv.getVariable(), cfn))
or
exists(FunctionCall fc |
fc = cfn and
fc.getQualifier() = lv.getVariable().getAnAccess() and
not fc.getTarget() instanceof ConstMemberFunction and
// non-const versions of begin and end should nevertheless be considered const
not fc.getTarget().hasName(["begin", "end"]) and
result = PSetInvalid(TOwnerModified(fc))
)
)
or
// temporary objects end after the full expression
exists(LifetimeTemporaryObject lto |
ps = PSetVar(lto)
or
ps = PSetOwner(lto, _)
|
cfn = lto.getTemporaryObjectExpr().getUnconverted().getParent*().(FullExpr).getASuccessor() and
result = PSetInvalid(TTemporaryOutOfScope(lto.getTemporaryObjectExpr()))
)
}
/**
* An expression which is dereferenced and may be an "invalid" value.
*/
class InvalidDereference extends VariableAccess {
InvalidReason ir;
InvalidDereference() {
// The local points to map suggests this points to an invalid set
exists(LocalScopeVariable lv |
lv = this.getTarget() and
pointsToMap(this, TLocalScopeVariable(lv), PSetInvalid(ir))
)
}
/** Gets a reason why this dereference could point to an invalid value. */
InvalidReason getAnInvalidReason() { result = ir }
}
from
InvalidDereference e, Element explanation, string explanationDesc, InvalidReason ir,
string invalidMessage
where
ir = e.getAnInvalidReason() and
ir.isAfterLifetime() and
ir.hasMessage(invalidMessage, explanation, explanationDesc)
select e,
e.(VariableAccess).getTarget().getName() + " is dereferenced here but accesses invalid memory " +
invalidMessage, explanation, explanationDesc
+5
View File
@@ -0,0 +1,5 @@
name: zealdocs/cpp-zeal-custom
version: 0.0.1
library: false
dependencies:
codeql/cpp-all: "*"
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
with:
languages: c-cpp
build-mode: manual
queries: security-and-quality
queries: security-and-quality,./.github/codeql/queries
config-file: .github/codeql/config.yml
trap-caching: true
dependency-caching: true