Merge branch 'main' into signature_model_refactor

This commit is contained in:
Ben Rodes
2025-10-02 09:02:34 -04:00
committed by GitHub
2262 changed files with 122094 additions and 42855 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
description: Link PCH creations and uses
compatibility: full
pch_uses.rel: delete
pch_creations.rel: delete

View File

@@ -7,12 +7,10 @@ ql/cpp/ql/src/Diagnostics/ExtractedFiles.ql
ql/cpp/ql/src/Diagnostics/ExtractionWarnings.ql
ql/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql
ql/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql
ql/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql
ql/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
ql/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql
ql/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql
ql/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql
ql/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
ql/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql
ql/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql
ql/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
@@ -30,7 +28,6 @@ ql/cpp/ql/src/Security/CWE/CWE-120/VeryLikelyOverrunWrite.ql
ql/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql
ql/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql
ql/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql
ql/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
ql/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql
ql/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
ql/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql
@@ -43,7 +40,6 @@ ql/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql
ql/cpp/ql/src/Security/CWE/CWE-416/IteratorToExpiredContainer.ql
ql/cpp/ql/src/Security/CWE/CWE-416/UseOfStringAfterLifetimeEnds.ql
ql/cpp/ql/src/Security/CWE/CWE-416/UseOfUniquePointerAfterLifetimeEnds.ql
ql/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
ql/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
ql/cpp/ql/src/Security/CWE/CWE-611/XXE.ql
ql/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql

View File

@@ -1,3 +1,36 @@
## 5.6.1
No user-facing changes.
## 5.6.0
### Deprecated APIs
* The predicate `getAContructorCall` in the class `SslContextClass` has been deprecated. Use `getAConstructorCall` instead.
### New Features
* Added predicates `getTransitiveNumberOfVlaDimensionStmts`, `getTransitiveVlaDimensionStmt`, and `getParentVlaDecl` to `VlaDeclStmt` for handling `VlaDeclStmt`s whose base type is defined in terms of another `VlaDeclStmt` via a `typedef`.
## 5.5.0
### New Features
* Added a new class `PchFile` representing precompiled header (PCH) files used during project compilation.
### Minor Analysis Improvements
* Added flow summaries for the `Microsoft::WRL::ComPtr` member functions.
* The new dataflow/taint-tracking library (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`) now resolves virtual function calls more precisely. This results in fewer false positives when running dataflow/taint-tracking queries on C++ projects.
## 5.4.1
### Minor Analysis Improvements
* The guards libraries (`semmle.code.cpp.controlflow.Guards` and `semmle.code.cpp.controlflow.IRGuards`) have been improved to recognize more guards.
* Improved dataflow through global variables in the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`). Queries based on these libraries will produce more results on codebases with many global variables.
* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering` and `semmle.code.cpp.ir.ValueNumbering`) has been improved so more expressions are assigned the same value number.
## 5.4.0
### New Features

View File

@@ -0,0 +1,11 @@
/**
* Contains customizations to the standard library.
*
* This module is imported by `cpp.qll`, so any customizations defined here automatically
* apply to all queries.
*
* Typical examples of customizations include adding new subclasses of abstract classes such as
* the `RemoteFlowSource` class to model frameworks that are not covered by the standard library.
*/
import cpp

View File

@@ -35,7 +35,7 @@ class CustomOptions extends Options {
override predicate returnsNull(Call call) { Options.super.returnsNull(call) }
/**
* Holds if a call to this function will never return.
* Holds if a call to the function `f` will never return.
*
* By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`,
* `longjmp`, `error`, `__builtin_unreachable` and any function with a

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering` and `semmle.code.cpp.ir.ValueNumbering`) has been improved so more expressions are assigned the same value number.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Improved dataflow through global variables in the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`). Queries based on these libraries will produce more results on codebases with many global variables.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The guards libraries (`semmle.code.cpp.controlflow.Guards` and `semmle.code.cpp.controlflow.IRGuards`) have been improved to recognize more guards.

View File

@@ -0,0 +1,4 @@
---
category: breaking
---
* The "Guards" libraries (`semmle.code.cpp.controlflow.Guards` and `semmle.code.cpp.controlflow.IRGuards`) have been totally rewritten to recognize many more guards. The API remains unchanged, but the `GuardCondition` class now extends `Element` instead of `Expr`.

View File

@@ -0,0 +1,7 @@
## 5.4.1
### Minor Analysis Improvements
* The guards libraries (`semmle.code.cpp.controlflow.Guards` and `semmle.code.cpp.controlflow.IRGuards`) have been improved to recognize more guards.
* Improved dataflow through global variables in the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`). Queries based on these libraries will produce more results on codebases with many global variables.
* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering` and `semmle.code.cpp.ir.ValueNumbering`) has been improved so more expressions are assigned the same value number.

View File

@@ -0,0 +1,10 @@
## 5.5.0
### New Features
* Added a new class `PchFile` representing precompiled header (PCH) files used during project compilation.
### Minor Analysis Improvements
* Added flow summaries for the `Microsoft::WRL::ComPtr` member functions.
* The new dataflow/taint-tracking library (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`) now resolves virtual function calls more precisely. This results in fewer false positives when running dataflow/taint-tracking queries on C++ projects.

View File

@@ -0,0 +1,9 @@
## 5.6.0
### Deprecated APIs
* The predicate `getAContructorCall` in the class `SslContextClass` has been deprecated. Use `getAConstructorCall` instead.
### New Features
* Added predicates `getTransitiveNumberOfVlaDimensionStmts`, `getTransitiveVlaDimensionStmt`, and `getParentVlaDecl` to `VlaDeclStmt` for handling `VlaDeclStmt`s whose base type is defined in terms of another `VlaDeclStmt` via a `typedef`.

View File

@@ -0,0 +1,3 @@
## 5.6.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 5.4.0
lastReleaseVersion: 5.6.1

View File

@@ -13,7 +13,9 @@
* https://github.com/cplusplus/draft/raw/master/papers/n4140.pdf
*/
import Customizations
import semmle.code.cpp.File
import semmle.code.cpp.PchFile
import semmle.code.cpp.Linkage
import semmle.code.cpp.Location
import semmle.code.cpp.Compilation

View File

@@ -127,7 +127,7 @@ abstract class CryptographicAlgorithm extends CryptographicArtifact {
/**
* Normalizes a raw name into a normalized name as found in `CryptoAlgorithmNames.qll`.
* Subclassess should override for more api-specific normalization.
* By deafult, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
* By default, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
*/
bindingset[s]
string normalizeName(string s) {

View File

@@ -652,14 +652,14 @@ module KeyGeneration {
* Trace from EVP_PKEY_CTX* at algorithm sink to keygen,
* users can then extrapolatae the matching algorithm from the alg sink to the keygen
*/
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize implements DataFlow::ConfigSig {
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSizeConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { isEVP_PKEY_CTX_Source(source, _) }
predicate isSink(DataFlow::Node sink) { isKeyGen_EVP_PKEY_CTX_Sink(sink, _) }
}
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow =
DataFlow::Global<EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize>;
DataFlow::Global<EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSizeConfig>;
/**
* UNKNOWN key sizes to general purpose key generation functions (i.e., that take in no key size and assume

View File

@@ -59,7 +59,7 @@ private string privateNormalizeFunctionName(Function f, string algType) {
*
* The predicate attempts to restrict normalization to what looks like an openssl
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
* This may give false postive functions if a directory erronously appears to be openssl;
* This may give false positive functions if a directory erronously appears to be openssl;
* however, we take the stance that if a function
* exists strongly mapping to a known function name in a directory such as these,
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.

View File

@@ -49,7 +49,7 @@ private string privateNormalizeFunctionName(Function f, string algType) {
*
* The predicate attempts to restrict normalization to what looks like an openssl
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
* This may give false postive functions if a directory erronously appears to be openssl;
* This may give false positive functions if a directory erronously appears to be openssl;
* however, we take the stance that if a function
* exists strongly mapping to a known function name in a directory such as these,
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.

View File

@@ -31,7 +31,7 @@ predicate knownPassthroughFunction(Function f, int inInd, int outInd) {
/**
* `c` is a call to a function that preserves the algorithm but changes its form.
* `onExpr` is the input argument passing through to, `outExpr` is the next expression in a dataflow step associated with `c`
* `inExpr` is the input argument passing through to, `outExpr` is the next expression in a dataflow step associated with `c`
*/
predicate knownPassthoughCall(Call c, Expr inExpr, Expr outExpr) {
exists(int inInd, int outInd |

View File

@@ -298,10 +298,11 @@ private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, bool
else
if strictlyNegative(x)
then upper = true and delta = -1
else
if negative(x)
then upper = true and delta = 0
else none()
else (
negative(x) and
upper = true and
delta = 0
)
)
or
exists(Operand x |
@@ -321,10 +322,11 @@ private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, bool
else
if strictlyNegative(x)
then upper = false and delta = 1
else
if negative(x)
then upper = false and delta = 0
else none()
else (
negative(x) and
upper = false and
delta = 0
)
)
or
i.(RemInstruction).getRightOperand() = op and positive(op) and delta = -1 and upper = true
@@ -410,7 +412,7 @@ private predicate boundFlowStepPhi(
or
exists(IRGuardCondition guard, boolean testIsTrue |
guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and
guard.controlsEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and
guard.controlsBranchEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and
reason = TCondReason(guard)
)
}

View File

@@ -0,0 +1,31 @@
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
- ["Microsoft::WRL", "ComPtr", True, "ComPtr<T>", "(T *)", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "ComPtr", "(const ComPtr &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "ComPtr", "(ComPtr &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "As", "", "", "Argument[-1]", "Argument[*0]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "AsIID", "", "", "Argument[-1]", "Argument[*1]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "AsWeak", "", "", "Argument[-1]", "Argument[*0]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "Attach", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr<T>", True, "CopyTo", "(T **)", "", "Argument[-1].Element[@]", "Argument[**@0]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "CopyTo<T>", "(T **)", "", "Argument[-1].Element[@]", "Argument[**@0]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "CopyTo", "(REFIID,void **)", "", "Argument[-1].Element[@]", "Argument[**@1]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "Detach", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "Get", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "GetAddressOf", "", "", "Argument[-1].Element[@]", "ReturnValue[**@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "ReleaseAndGetAddressOf", "", "", "Argument[-1].Element[@]", "ReturnValue[**@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "Swap", "", "", "Argument[-1]", "Argument[*0]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "Swap", "", "", "Argument[*0]", "Argument[-1]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "operator&", "", "", "Argument[-1]", "ReturnValue.Element", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "operator->", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr<T>", True, "operator=", "(T *)", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr<T>", True, "operator=", "(T *)", "", "Argument[*@0]", "ReturnValue[*].Element[@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "operator=<U>", "(U *)", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "operator=<U>", "(U *)", "", "Argument[*@0]", "ReturnValue[*].Element[@]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "operator=", "(const ComPtr &)", "", "Argument[*0]", "Argument[-1]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "operator=", "(const ComPtr &)", "", "Argument[*0]", "ReturnValue[*]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "operator=", "(ComPtr &&)", "", "Argument[*0]", "Argument[-1]", "value", "manual"]
- ["Microsoft::WRL", "ComPtr", True, "operator=", "(ComPtr &&)", "", "Argument[*0]", "ReturnValue[*]", "value", "manual"]

View File

@@ -0,0 +1,12 @@
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
- ["Microsoft::WRL::Details", "ComPtrRef", True, "ComPtrRef", "", "", "Argument[*0]", "Argument[-1].Element[@]", "value", "manual"]
- ["Microsoft::WRL::Details", "ComPtrRef", True, "GetAddressOf", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
# TODO: We cannot yet model https://learn.microsoft.com/en-us/cpp/cppcx/wrl/comptrref-class?view=msvc-170#operator-interfacetype-star-star
- ["Microsoft::WRL::Details", "ComPtrRef", True, "operator*", "", "", "Argument[-1].Element[@]", "ReturnValue[*@]", "value", "manual"]
# TODO: We cannot yet model https://learn.microsoft.com/en-us/cpp/cppcx/wrl/comptrref-class?view=msvc-170#operator-t-star
- ["Microsoft::WRL::Details", "ComPtrRef", True, "operator void**", "", "", "Argument[-1].Element[@]", "ReturnValue[**@]", "value", "manual"]
- ["Microsoft::WRL::Details", "ComPtrRef", True, "ReleaseAndGetAddressOf", "", "", "Argument[-1].Element[@]", "ReturnValue[**@]", "value", "manual"]

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 5.4.1-dev
version: 5.6.2-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -198,7 +198,7 @@ class ConceptIdExpr extends Expr, @concept_id {
final Locatable getATemplateArgumentKind() { result = this.getTemplateArgumentKind(_) }
/**
* Gets the `i`th template argument passed to the concept.
* Gets template argument at index `index` passed to the concept, if any.
*
* For example, if:
* ```cpp
@@ -219,7 +219,7 @@ class ConceptIdExpr extends Expr, @concept_id {
}
/**
* Gets the kind of the `i`th template argument value passed to the concept.
* Gets the kind of the template argument value at index `index` passed to the concept, if any.
*
* For example, if:
* ```cpp

View File

@@ -223,8 +223,8 @@ class Declaration extends Locatable, @declaration {
final Locatable getATemplateArgumentKind() { result = this.getTemplateArgumentKind(_) }
/**
* Gets the `i`th template argument used to instantiate this declaration from a
* template.
* Gets the template argument at index `index` used to instantiate this declaration from a
* template, if any.
*
* For example:
*
@@ -245,9 +245,9 @@ class Declaration extends Locatable, @declaration {
}
/**
* Gets the `i`th template argument value used to instantiate this declaration
* from a template. When called on a template, this will return the `i`th template
* parameter value if it exists.
* Gets the template argument value at index `index` used to instantiate this declaration
* from a template. When called on a template, this will return the template
* parameter value at index `index` if it exists.
*
* For example:
*

View File

@@ -0,0 +1,26 @@
/**
* Provides the `PchFile` class representing precompiled header (PCH) files created and
* used during the build process.
*/
import semmle.code.cpp.File
/**
* A precompiled header (PCH) file created during the build process.
*/
class PchFile extends @pch {
/**
* Gets a textual representation of this element.
*/
string toString() { result = "PCH for " + this.getHeaderFile() }
/**
* Gets the header file from which the PCH file was created.
*/
File getHeaderFile() { pch_creations(this, _, result) }
/**
* Gets a source file that includes the PCH.
*/
File getAUse() { pch_uses(this, _, result) }
}

View File

@@ -877,7 +877,7 @@ class FormatLiteral extends Literal instanceof StringLiteral {
}
/**
* Gets the char type required by the nth conversion specifier.
* Gets the char type required by the `n`th conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char` for `printf`, `char` or `wchar_t` for `wprintf`).
* - the `%C` format character reverses wideness.
@@ -922,7 +922,7 @@ class FormatLiteral extends Literal instanceof StringLiteral {
}
/**
* Gets the string type required by the nth conversion specifier.
* Gets the string type required by the `n`th conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char *` for `printf`, `char *` or `wchar_t *` for `wprintf`).
* - the `%S` format character reverses wideness on some platforms.

View File

@@ -101,7 +101,7 @@ predicate postDominates(ControlFlowNode postDominator, ControlFlowNode node) {
*/
/**
* Holds if `dominator` is an immediate dominator of `node` in the control-flow
* Holds if `dom` is an immediate dominator of `node` in the control-flow
* graph of basic blocks.
*/
predicate bbIDominates(BasicBlock dom, BasicBlock node) =
@@ -117,7 +117,7 @@ private predicate bb_predecessor(BasicBlock succ, BasicBlock pred) { bb_successo
private predicate bb_exit(ExitBasicBlock exit) { any() }
/**
* Holds if `postDominator` is an immediate post-dominator of `node` in the control-flow
* Holds if `pDom` is an immediate post-dominator of `node` in the control-flow
* graph of basic blocks.
*/
predicate bbIPostDominates(BasicBlock pDom, BasicBlock node) =

File diff suppressed because it is too large Load Diff

View File

@@ -1042,8 +1042,8 @@ private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2)
* - `MicrosoftTryFinallyStmt`: On the edge following the `__finally` block for
* the case where an exception was thrown and needs to be propagated.
*/
DestructorCall getSynthesisedDestructorCallAfterNode(Node n, int i) {
synthetic_destructor_call(n, i, result)
DestructorCall getSynthesisedDestructorCallAfterNode(Node node, int index) {
synthetic_destructor_call(node, index, result)
}
/**

View File

@@ -834,8 +834,10 @@ class ContentSet instanceof Content {
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
super.hasLocationInfo(path, sl, sc, el, ec)
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}

View File

@@ -104,7 +104,9 @@ private module StepsInput implements Impl::Private::StepsInputSig {
result.getStaticCallTarget().getUnderlyingCallable() = sc
}
Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() }
DataFlowCallable getSourceNodeEnclosingCallable(Input::SourceBase source) { none() }
Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponentStack s) { none() }
Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() }
}

View File

@@ -1,218 +1,21 @@
private import cpp
private import semmle.code.cpp.ir.IR
private import DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.DataFlow
private import DataFlowPrivate as DataFlowPrivate
private import DataFlowUtil
private import DataFlowImplCommon as DataFlowImplCommon
private import codeql.typetracking.TypeTracking
private import SsaImpl as SsaImpl
/**
* Gets a function that might be called by `call`.
*
* This predicate does not take additional call targets
* from `AdditionalCallTarget` into account.
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
* an approximation of its signature for the purpose of matching functions that
* might be the same across link targets.
*/
cached
DataFlowCallable defaultViableCallable(DataFlowCall call) {
DataFlowImplCommon::forceCachingInSameStage() and
result = call.getStaticCallTarget()
or
// If the target of the call does not have a body in the snapshot, it might
// be because the target is just a header declaration, and the real target
// will be determined at run time when the caller and callee are linked
// together by the operating system's dynamic linker. In case a _unique_
// function with the right signature is present in the database, we return
// that as a potential callee.
exists(string qualifiedName, int nparams |
callSignatureWithoutBody(qualifiedName, nparams, call.asCallInstruction()) and
functionSignatureWithBody(qualifiedName, nparams, result.getUnderlyingCallable()) and
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
)
or
// Virtual dispatch
result.asSourceCallable() = call.(VirtualDispatch::DataSensitiveCall).resolve()
}
/**
* Gets a function that might be called by `call`.
*/
cached
DataFlowCallable viableCallable(DataFlowCall call) {
result = defaultViableCallable(call)
or
// Additional call targets
result.getUnderlyingCallable() =
any(AdditionalCallTarget additional)
.viableTarget(call.asCallInstruction().getUnconvertedResultExpression())
}
/**
* Provides virtual dispatch support compatible with the original
* implementation of `semmle.code.cpp.security.TaintTracking`.
*/
private module VirtualDispatch {
/** A call that may dispatch differently depending on the qualifier value. */
abstract class DataSensitiveCall extends DataFlowCall {
/**
* Gets the node whose value determines the target of this call. This node
* could be the qualifier of a virtual dispatch or the function-pointer
* expression in a call to a function pointer. What they have in common is
* that we need to find out which data flows there, and then it's up to the
* `resolve` predicate to stitch that information together and resolve the
* call.
*/
abstract Node getDispatchValue();
/** Gets a candidate target for this call. */
abstract Function resolve();
/**
* Whether `src` can flow to this call.
*
* Searches backwards from `getDispatchValue()` to `src`. The `allowFromArg`
* parameter is true when the search is allowed to continue backwards into
* a parameter; non-recursive callers should pass `_` for `allowFromArg`.
*/
predicate flowsFrom(Node src, boolean allowFromArg) {
src = this.getDispatchValue() and allowFromArg = true
or
exists(Node other, boolean allowOtherFromArg | this.flowsFrom(other, allowOtherFromArg) |
// Call argument
exists(DataFlowCall call, Position i |
other.(ParameterNode).isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
src.(ArgumentNode).argumentOf(call, pragma[only_bind_into](pragma[only_bind_out](i)))
) and
allowOtherFromArg = true and
allowFromArg = true
or
// Call return
exists(DataFlowCall call, ReturnKind returnKind |
other = getAnOutNode(call, returnKind) and
returnNodeWithKindAndEnclosingCallable(src, returnKind, call.getStaticCallTarget())
) and
allowFromArg = false
or
// Local flow
localFlowStep(src, other) and
allowFromArg = allowOtherFromArg
or
// Flow from global variable to load.
exists(LoadInstruction load, GlobalOrNamespaceVariable var |
var = src.asVariable() and
other.asInstruction() = load and
addressOfGlobal(load.getSourceAddress(), var) and
// The `allowFromArg` concept doesn't play a role when `src` is a
// global variable, so we just set it to a single arbitrary value for
// performance.
allowFromArg = true
)
or
// Flow from store to global variable.
exists(StoreInstruction store, GlobalOrNamespaceVariable var |
var = other.asVariable() and
store = src.asInstruction() and
storeIntoGlobal(store, var) and
// Setting `allowFromArg` to `true` like in the base case means we
// treat a store to a global variable like the dispatch itself: flow
// may come from anywhere.
allowFromArg = true
)
)
}
}
pragma[noinline]
private predicate storeIntoGlobal(StoreInstruction store, GlobalOrNamespaceVariable var) {
addressOfGlobal(store.getDestinationAddress(), var)
}
/** Holds if `addressInstr` is an instruction that produces the address of `var`. */
private predicate addressOfGlobal(Instruction addressInstr, GlobalOrNamespaceVariable var) {
// Access directly to the global variable
addressInstr.(VariableAddressInstruction).getAstVariable() = var
or
// Access to a field on a global union
exists(FieldAddressInstruction fa |
fa = addressInstr and
fa.getObjectAddress().(VariableAddressInstruction).getAstVariable() = var and
fa.getField().getDeclaringType() instanceof Union
)
}
/**
* A ReturnNode with its ReturnKind and its enclosing callable.
*
* Used to fix a join ordering issue in flowsFrom.
*/
pragma[noinline]
private predicate returnNodeWithKindAndEnclosingCallable(
ReturnNode node, ReturnKind kind, DataFlowCallable callable
) {
node.getKind() = kind and
node.getFunction() = callable.getUnderlyingCallable()
}
/** Call through a function pointer. */
private class DataSensitiveExprCall extends DataSensitiveCall {
DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) }
override Node getDispatchValue() { result.asOperand() = this.getCallTargetOperand() }
override Function resolve() {
exists(FunctionInstruction fi |
this.flowsFrom(instructionNode(fi), _) and
result = fi.getFunctionSymbol()
) and
(
this.getNumberOfArguments() <= result.getEffectiveNumberOfParameters() and
this.getNumberOfArguments() >= result.getEffectiveNumberOfParameters()
or
result.isVarargs()
)
}
}
/** Call to a virtual function. */
private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
DataSensitiveOverriddenFunctionCall() {
exists(
this.getStaticCallTarget()
.getUnderlyingCallable()
.(VirtualFunction)
.getAnOverridingFunction()
)
}
override Node getDispatchValue() { result.asInstruction() = this.getArgument(-1) }
override MemberFunction resolve() {
exists(Class overridingClass |
this.overrideMayAffectCall(overridingClass, result) and
this.hasFlowFromCastFrom(overridingClass)
)
}
/**
* Holds if `this` is a virtual function call whose static target is
* overridden by `overridingFunction` in `overridingClass`.
*/
pragma[noinline]
private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) {
overridingFunction.getAnOverriddenFunction+() =
this.getStaticCallTarget().getUnderlyingCallable().(VirtualFunction) and
overridingFunction.getDeclaringType() = overridingClass
}
/**
* Holds if the qualifier of `this` has flow from an upcast from
* `derivedClass`.
*/
pragma[noinline]
private predicate hasFlowFromCastFrom(Class derivedClass) {
exists(ConvertToBaseInstruction toBase |
this.flowsFrom(instructionNode(toBase), _) and
derivedClass = toBase.getDerivedClass()
)
}
}
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
qualifiedName = f.getQualifiedName() and
nparams = f.getNumberOfParameters() and
not f.isStatic()
}
/**
@@ -238,34 +41,319 @@ private predicate callSignatureWithoutBody(string qualifiedName, int nparams, Ca
}
/**
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
* an approximation of its signature for the purpose of matching functions that
* might be the same across link targets.
* Gets a function that might be called by `call`.
*
* This predicate does not take additional call targets
* from `AdditionalCallTarget` into account.
*/
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
qualifiedName = f.getQualifiedName() and
nparams = f.getNumberOfParameters() and
not f.isStatic()
cached
DataFlowPrivate::DataFlowCallable defaultViableCallable(DataFlowPrivate::DataFlowCall call) {
result = defaultViableCallableWithoutLambda(call)
or
result = DataFlowImplCommon::viableCallableLambda(call, _)
}
private DataFlowPrivate::DataFlowCallable defaultViableCallableWithoutLambda(
DataFlowPrivate::DataFlowCall call
) {
DataFlowImplCommon::forceCachingInSameStage() and
result = call.getStaticCallTarget()
or
// If the target of the call does not have a body in the snapshot, it might
// be because the target is just a header declaration, and the real target
// will be determined at run time when the caller and callee are linked
// together by the operating system's dynamic linker. In case a _unique_
// function with the right signature is present in the database, we return
// that as a potential callee.
exists(string qualifiedName, int nparams |
callSignatureWithoutBody(qualifiedName, nparams, call.asCallInstruction()) and
functionSignatureWithBody(qualifiedName, nparams, result.getUnderlyingCallable()) and
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
)
}
/**
* Gets a function that might be called by `call`.
*/
private DataFlowPrivate::DataFlowCallable nonVirtualDispatch(DataFlowPrivate::DataFlowCall call) {
result = defaultViableCallableWithoutLambda(call)
or
// Additional call targets
result.getUnderlyingCallable() =
any(AdditionalCallTarget additional)
.viableTarget(call.asCallInstruction().getUnconvertedResultExpression())
}
private class RelevantNode extends Node {
RelevantNode() { this.getType().stripType() instanceof Class }
}
private signature DataFlowPrivate::DataFlowCallable methodDispatchSig(
DataFlowPrivate::DataFlowCall c
);
private predicate ignoreConstructor(Expr e) {
e instanceof ConstructorDirectInit or
e instanceof ConstructorVirtualInit or
e instanceof ConstructorDelegationInit or
exists(ConstructorFieldInit init | init.getExpr() = e)
}
/**
* Holds if `n` is either:
* - the post-update node of a qualifier after a call to a constructor which
* constructs an object containing at least one virtual function.
* - a node which represents a derived-to-base instruction that converts from `c`.
*/
private predicate qualifierSourceImpl(RelevantNode n, Class c) {
// Object construction
exists(CallInstruction call, ThisArgumentOperand qualifier, Call e |
qualifier = call.getThisArgumentOperand() and
n.(PostUpdateNode).getPreUpdateNode().asOperand() = qualifier and
call.getStaticCallTarget() instanceof Constructor and
qualifier.getType().stripType() = c and
c.getABaseClass*().getAMemberFunction().isVirtual() and
e = call.getUnconvertedResultExpression() and
not ignoreConstructor(e)
|
exists(c.getABaseClass())
or
exists(c.getADerivedClass())
)
or
// Conversion to a base class
exists(ConvertToBaseInstruction convert |
// Only keep the most specific cast
not convert.getUnary() instanceof ConvertToBaseInstruction and
n.asInstruction() = convert and
convert.getDerivedClass() = c and
c.getABaseClass*().getAMemberFunction().isVirtual()
)
}
private module TrackVirtualDispatch<methodDispatchSig/1 virtualDispatch0> {
/**
* Gets a possible runtime target of `c` using both static call-target
* information, and call-target resolution from `virtualDispatch0`.
*/
private DataFlowPrivate::DataFlowCallable dispatch(DataFlowPrivate::DataFlowCall c) {
result = nonVirtualDispatch(c) or
result = virtualDispatch0(c)
}
private module TtInput implements TypeTrackingInput<Location> {
final class Node = RelevantNode;
class LocalSourceNode extends Node {
LocalSourceNode() {
this instanceof ParameterNode
or
this instanceof DataFlowPrivate::OutNode
or
DataFlowPrivate::readStep(_, _, this)
or
DataFlowPrivate::storeStep(_, _, this)
or
DataFlowPrivate::jumpStep(_, this)
or
qualifierSourceImpl(this, _)
}
}
final private class ContentSetFinal = ContentSet;
class Content extends ContentSetFinal {
Content() {
exists(DataFlow::Content c |
this.isSingleton(c) and
c.getIndirectionIndex() = 1
)
}
}
class ContentFilter extends Content {
Content getAMatchingContent() { result = this }
}
predicate compatibleContents(Content storeContents, Content loadContents) {
storeContents = loadContents
}
predicate simpleLocalSmallStep(Node nodeFrom, Node nodeTo) {
nodeFrom.getFunction() instanceof Function and
simpleLocalFlowStep(nodeFrom, nodeTo, _)
}
predicate levelStepNoCall(Node n1, LocalSourceNode n2) { none() }
predicate levelStepCall(Node n1, LocalSourceNode n2) { none() }
predicate storeStep(Node n1, Node n2, Content f) { DataFlowPrivate::storeStep(n1, f, n2) }
predicate callStep(Node n1, LocalSourceNode n2) {
exists(DataFlowPrivate::DataFlowCall call, DataFlowPrivate::Position pos |
n1.(DataFlowPrivate::ArgumentNode).argumentOf(call, pos) and
n2.(ParameterNode).isParameterOf(dispatch(call), pos)
)
}
predicate returnStep(Node n1, LocalSourceNode n2) {
exists(DataFlowPrivate::DataFlowCallable callable, DataFlowPrivate::DataFlowCall call |
n1.(DataFlowPrivate::ReturnNode).getEnclosingCallable() = callable and
callable = dispatch(call) and
n2 = DataFlowPrivate::getAnOutNode(call, n1.(DataFlowPrivate::ReturnNode).getKind())
)
}
predicate loadStep(Node n1, LocalSourceNode n2, Content f) {
DataFlowPrivate::readStep(n1, f, n2)
}
predicate loadStoreStep(Node nodeFrom, Node nodeTo, Content f1, Content f2) { none() }
predicate withContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter f) { none() }
predicate withoutContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter f) { none() }
predicate jumpStep(Node n1, LocalSourceNode n2) { DataFlowPrivate::jumpStep(n1, n2) }
predicate hasFeatureBacktrackStoreTarget() { none() }
}
private predicate qualifierSource(RelevantNode n) { qualifierSourceImpl(n, _) }
/**
* Holds if `n` is the qualifier of `call` which targets the virtual member
* function `mf`.
*/
private predicate qualifierOfVirtualCallImpl(
RelevantNode n, CallInstruction call, MemberFunction mf
) {
n.asOperand() = call.getThisArgumentOperand() and
call.getStaticCallTarget() = mf and
mf.isVirtual()
}
private predicate qualifierOfVirtualCall(RelevantNode n) { qualifierOfVirtualCallImpl(n, _, _) }
private import TypeTracking<Location, TtInput>::TypeTrack<qualifierSource/1>::Graph<qualifierOfVirtualCall/1>
private predicate edgePlus(PathNode n1, PathNode n2) = fastTC(edges/2)(n1, n2)
/**
* Gets the most specific implementation of `mf` that may be called when the
* qualifier has runtime type `c`.
*/
private MemberFunction mostSpecific(MemberFunction mf, Class c) {
qualifierOfVirtualCallImpl(_, _, mf) and
mf.getAnOverridingFunction*() = result and
(
result.getDeclaringType() = c
or
not c.getAMemberFunction().getAnOverriddenFunction*() = mf and
result = mostSpecific(mf, c.getABaseClass())
)
}
/**
* Gets a possible pair of end-points `(p1, p2)` where:
* - `p1` is a derived-to-base conversion that converts from some
* class `derived`, and
* - `p2` is the qualifier of a call to a virtual function that may
* target `callable`, and
* - `callable` is the most specific implementation that may be called when
* the qualifier has type `derived`.
*/
private predicate pairCand(
PathNode p1, PathNode p2, DataFlowPrivate::DataFlowCallable callable,
DataFlowPrivate::DataFlowCall call
) {
exists(Class derived, MemberFunction mf |
qualifierSourceImpl(p1.getNode(), derived) and
qualifierOfVirtualCallImpl(p2.getNode(), call.asCallInstruction(), mf) and
p1.isSource() and
p2.isSink() and
callable.asSourceCallable() = mostSpecific(mf, derived)
)
}
/** Gets a possible run-time target of `call`. */
DataFlowPrivate::DataFlowCallable virtualDispatch(DataFlowPrivate::DataFlowCall call) {
exists(PathNode p1, PathNode p2 | p1 = p2 or edgePlus(p1, p2) | pairCand(p1, p2, result, call))
}
}
private DataFlowPrivate::DataFlowCallable noDisp(DataFlowPrivate::DataFlowCall call) { none() }
pragma[nomagic]
private DataFlowPrivate::DataFlowCallable d1(DataFlowPrivate::DataFlowCall call) {
result = TrackVirtualDispatch<noDisp/1>::virtualDispatch(call)
}
pragma[nomagic]
private DataFlowPrivate::DataFlowCallable d2(DataFlowPrivate::DataFlowCall call) {
result = TrackVirtualDispatch<d1/1>::virtualDispatch(call)
}
pragma[nomagic]
private DataFlowPrivate::DataFlowCallable d3(DataFlowPrivate::DataFlowCall call) {
result = TrackVirtualDispatch<d2/1>::virtualDispatch(call)
}
pragma[nomagic]
private DataFlowPrivate::DataFlowCallable d4(DataFlowPrivate::DataFlowCall call) {
result = TrackVirtualDispatch<d3/1>::virtualDispatch(call)
}
pragma[nomagic]
private DataFlowPrivate::DataFlowCallable d5(DataFlowPrivate::DataFlowCall call) {
result = TrackVirtualDispatch<d4/1>::virtualDispatch(call)
}
pragma[nomagic]
private DataFlowPrivate::DataFlowCallable d6(DataFlowPrivate::DataFlowCall call) {
result = TrackVirtualDispatch<d5/1>::virtualDispatch(call)
}
/** Gets a function that might be called by `call`. */
cached
DataFlowPrivate::DataFlowCallable viableCallable(DataFlowPrivate::DataFlowCall call) {
not exists(d6(call)) and
result = nonVirtualDispatch(call)
or
result = d6(call)
}
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate mayBenefitFromCallContext(DataFlowCall call) { mayBenefitFromCallContext(call, _, _) }
predicate mayBenefitFromCallContext(DataFlowPrivate::DataFlowCall call) {
mayBenefitFromCallContext(call, _, _)
}
private predicate localLambdaFlowStep(Node nodeFrom, Node nodeTo) {
localFlowStep(nodeFrom, nodeTo)
or
DataFlowPrivate::additionalLambdaFlowStep(nodeFrom, nodeTo, _)
}
/**
* Holds if `call` is a call through a function pointer, and the pointer
* value is given as the `arg`'th argument to `f`.
*/
private predicate mayBenefitFromCallContext(
VirtualDispatch::DataSensitiveCall call, DataFlowCallable f, int arg
DataFlowPrivate::DataFlowCall call, DataFlowPrivate::DataFlowCallable f, int arg
) {
f = pragma[only_bind_out](call).getEnclosingCallable() and
exists(InitializeParameterInstruction init |
not exists(call.getStaticCallTarget()) and
not exists(call.getStaticCallTarget())
or
exists(call.getStaticCallSourceTarget().(VirtualFunction).getAnOverridingFunction())
|
init.getEnclosingFunction() = f.getUnderlyingCallable() and
call.flowsFrom(instructionNode(init), _) and
localLambdaFlowStep+(instructionNode(init),
operandNode(call.asCallInstruction().getCallTargetOperand())) and
init.getParameter().getIndex() = arg
)
}
@@ -274,9 +362,11 @@ private predicate mayBenefitFromCallContext(
* Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference.
*/
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
DataFlowPrivate::DataFlowCallable viableImplInCallContext(
DataFlowPrivate::DataFlowCall call, DataFlowPrivate::DataFlowCall ctx
) {
result = viableCallable(call) and
exists(int i, DataFlowCallable f |
exists(int i, DataFlowPrivate::DataFlowCallable f |
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
f = ctx.getStaticCallTarget() and
result.asSourceCallable() =
@@ -286,4 +376,8 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
predicate parameterMatch(
DataFlowPrivate::ParameterPosition ppos, DataFlowPrivate::ArgumentPosition apos
) {
ppos = apos
}

View File

@@ -1492,7 +1492,14 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
}
/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) {
preservesValue = false and
exists(ContentSet cs | cs.isSingleton(any(UnionContent uc)) |
storeStep(nodeFrom, cs, nodeTo)
or
readStep(nodeFrom, cs, nodeTo)
)
}
predicate knownSourceModel(Node source, string model) { External::sourceNode(source, _, model) }
@@ -1873,9 +1880,7 @@ module IteratorFlow {
}
}
private module SsaInput implements SsaImpl::InputSig<Location> {
import Ssa::InputSigCommon
private module SsaInput implements SsaImpl::InputSig<Location, IRCfg::BasicBlock> {
class SourceVariable = IteratorFlow::SourceVariable;
/** A call to function that dereferences an iterator. */
@@ -1953,7 +1958,7 @@ module IteratorFlow {
* Holds if `(bb, i)` contains a write to an iterator that may have been obtained
* by calling `begin` (or related functions) on the variable `v`.
*/
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
predicate variableWrite(IRCfg::BasicBlock bb, int i, SourceVariable v, boolean certain) {
certain = false and
exists(GetsIteratorCall beginCall, Instruction writeToDeref, IRBlock bbQual, int iQual |
isIteratorStoreInstruction(beginCall, writeToDeref) and
@@ -1964,12 +1969,12 @@ module IteratorFlow {
}
/** Holds if `(bb, i)` reads the container variable `v`. */
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
predicate variableRead(IRCfg::BasicBlock bb, int i, SourceVariable v, boolean certain) {
Ssa::variableRead(bb, i, v, certain)
}
}
private module IteratorSsa = SsaImpl::Make<Location, SsaInput>;
private module IteratorSsa = SsaImpl::Make<Location, IRCfg, SsaInput>;
private module DataFlowIntegrationInput implements IteratorSsa::DataFlowIntegrationInputSig {
private import codeql.util.Void
@@ -1982,7 +1987,7 @@ module IteratorFlow {
)
}
predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { bb.getInstruction(i) = this }
predicate hasCfgNode(IRCfg::BasicBlock bb, int i) { bb.getInstruction(i) = this }
}
predicate ssaDefHasSource(IteratorSsa::WriteDefinition def) { none() }
@@ -1992,20 +1997,16 @@ module IteratorFlow {
class GuardValue = Void;
class Guard extends Void {
predicate hasValueBranchEdge(
SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue val
) {
predicate hasValueBranchEdge(IRCfg::BasicBlock bb1, IRCfg::BasicBlock bb2, GuardValue val) {
none()
}
predicate valueControlsBranchEdge(
SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue val
) {
predicate valueControlsBranchEdge(IRCfg::BasicBlock bb1, IRCfg::BasicBlock bb2, GuardValue val) {
none()
}
}
predicate guardDirectlyControlsBlock(Guard guard, SsaInput::BasicBlock bb, GuardValue val) {
predicate guardDirectlyControlsBlock(Guard guard, IRCfg::BasicBlock bb, GuardValue val) {
none()
}

View File

@@ -795,7 +795,7 @@ class FinalGlobalValue extends Node, TFinalGlobalValue {
override DataFlowType getType() {
exists(int indirectionIndex |
indirectionIndex = globalUse.getIndirectionIndex() and
result = getTypeImpl(globalUse.getUnderlyingType(), indirectionIndex - 1)
result = getTypeImpl(globalUse.getUnderlyingType(), indirectionIndex)
)
}
@@ -2273,8 +2273,10 @@ class ContentSet instanceof Content {
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
super.hasLocationInfo(path, sl, sc, el, ec)
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}

View File

@@ -498,7 +498,9 @@ class FinalParameterUse extends UseImpl, TFinalParameterUse {
int getArgumentIndex() { result = p.getIndex() }
override Node getNode() { finalParameterNodeHasParameterAndIndex(result, p, indirectionIndex) }
override FinalParameterNode getNode() {
finalParameterNodeHasParameterAndIndex(result, p, indirectionIndex)
}
override int getIndirection() { result = indirectionIndex + 1 }
@@ -891,15 +893,14 @@ private predicate baseSourceVariableIsGlobal(
)
}
private module SsaInput implements Ssa::InputSig<Location> {
import InputSigCommon
private module SsaInput implements Ssa::InputSig<Location, IRCfg::BasicBlock> {
import SourceVariables
/**
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
*/
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
predicate variableWrite(IRCfg::BasicBlock bb, int i, SourceVariable v, boolean certain) {
DataFlowImplCommon::forceCachingInSameStage() and
(
exists(DefImpl def | def.hasIndexInBlock(v, bb, i) |
@@ -917,7 +918,7 @@ private module SsaInput implements Ssa::InputSig<Location> {
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
* `certain` is `true` if the read is guaranteed. For C++, this is always the case.
*/
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
predicate variableRead(IRCfg::BasicBlock bb, int i, SourceVariable v, boolean certain) {
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
if use.isCertain() then certain = true else certain = false
)
@@ -965,7 +966,7 @@ class GlobalDef extends Definition {
GlobalLikeVariable getVariable() { result = impl.getVariable() }
}
private module SsaImpl = Ssa::Make<Location, SsaInput>;
private module SsaImpl = Ssa::Make<Location, IRCfg, SsaInput>;
private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationInputSig {
private import codeql.util.Boolean
@@ -978,7 +979,7 @@ private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationI
)
}
predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { bb.getInstruction(i) = this }
predicate hasCfgNode(IRCfg::BasicBlock bb, int i) { bb.getInstruction(i) = this }
}
Expr getARead(SsaImpl::Definition def) {
@@ -1001,30 +1002,28 @@ private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationI
result instanceof FalseEdge
}
class GuardValue = Boolean;
class GuardValue = IRGuards::GuardValue;
class Guard instanceof IRGuards::IRGuardCondition {
string toString() { result = super.toString() }
predicate hasValueBranchEdge(
SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue branch
) {
predicate hasValueBranchEdge(IRCfg::BasicBlock bb1, IRCfg::BasicBlock bb2, GuardValue branch) {
exists(EdgeKind kind |
super.getBlock() = bb1 and
kind = getConditionalEdge(branch) and
kind = getConditionalEdge(branch.asBooleanValue()) and
bb1.getSuccessor(kind) = bb2
)
}
predicate valueControlsBranchEdge(
SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue branch
IRCfg::BasicBlock bb1, IRCfg::BasicBlock bb2, GuardValue branch
) {
this.hasValueBranchEdge(bb1, bb2, branch)
}
}
predicate guardDirectlyControlsBlock(Guard guard, SsaInput::BasicBlock bb, GuardValue branch) {
guard.(IRGuards::IRGuardCondition).controls(bb, branch)
predicate guardDirectlyControlsBlock(Guard guard, IRCfg::BasicBlock bb, GuardValue branch) {
guard.(IRGuards::IRGuardCondition).valueControls(bb, branch)
}
predicate keepAllPhiInputBackEdges() { any() }
@@ -1051,25 +1050,35 @@ module BarrierGuardWithIntParam<guardChecksNodeSig/4 guardChecksNode> {
)
}
private predicate guardChecks(
DataFlowIntegrationInput::Guard g, SsaImpl::Definition def,
DataFlowIntegrationInput::GuardValue branch, int indirectionIndex
private predicate guardChecksInstr(
IRGuards::Guards_v1::Guard g, IRGuards::GuardsInput::Expr instr, boolean branch,
int indirectionIndex
) {
exists(UseImpl use |
guardChecksNode(g, use.getNode(), branch, indirectionIndex) and
ssaDefReachesCertainUse(def, use)
exists(Node node |
nodeHasInstruction(node, instr, indirectionIndex) and
guardChecksNode(g, node, branch, indirectionIndex)
)
}
private predicate guardChecksWithWrappers(
DataFlowIntegrationInput::Guard g, SsaImpl::Definition def, IRGuards::GuardValue val,
int indirectionIndex
) {
IRGuards::Guards_v1::ValidationWrapperWithState<int, guardChecksInstr/4>::guardChecksDef(g, def,
val, indirectionIndex)
}
Node getABarrierNode(int indirectionIndex) {
// Only get the SynthNodes from the shared implementation, as the ExprNodes cannot
// be matched on SourceVariable.
result.(SsaSynthNode).getSynthNode() =
DataFlowIntegrationImpl::BarrierGuardDefWithState<int, guardChecks/4>::getABarrierNode(indirectionIndex)
DataFlowIntegrationImpl::BarrierGuardDefWithState<int, guardChecksWithWrappers/4>::getABarrierNode(indirectionIndex)
or
// Calculate the guarded UseImpls corresponding to ExprNodes directly.
exists(DataFlowIntegrationInput::Guard g, boolean branch, Definition def, IRBlock bb |
guardChecks(g, def, branch, indirectionIndex) and
exists(
DataFlowIntegrationInput::Guard g, IRGuards::GuardValue branch, Definition def, IRBlock bb
|
guardChecksWithWrappers(g, def, branch, indirectionIndex) and
exists(UseImpl use |
ssaDefReachesCertainUse(def, use) and
use.getBlock() = bb and
@@ -1127,7 +1136,15 @@ predicate ssaFlow(Node nodeFrom, Node nodeTo) {
*/
class PhiNode extends Definition instanceof SsaImpl::PhiNode {
/** Gets a definition that is an input to this phi node. */
final Definition getAnInput() { phiHasInputFromBlock(this, result, _) }
final Definition getAnInput() { this.hasInputFromBlock(result, _) }
/**
* Holds if `input` is an input to this phi node along the edge originating
* in `bb`.
*/
final predicate hasInputFromBlock(Definition input, IRBlock bb) {
phiHasInputFromBlock(this, input, bb)
}
}
/** An static single assignment (SSA) definition. */
@@ -1152,10 +1169,53 @@ class Definition extends SsaImpl::Definition {
exists(SourceVariable sv, IRBlock bb, int i, UseImpl use |
ssaDefReachesRead(sv, this, bb, i) and
use.hasIndexInBlock(bb, i, sv) and
result = use.getNode().asOperand()
use = TDirectUseImpl(result, 0)
)
}
/**
* Holds if this definition defines the parameter `p` upon entry into the
* enclosing function.
*/
pragma[nomagic]
predicate isParameterDefinition(Parameter p) {
this.getIndirectionIndex() = 0 and
getDefImpl(this).getValue().asInstruction().(InitializeParameterInstruction).getParameter() = p
}
/**
* Holds if this definition defines the `indirectionIndex`'th indirection of
* parameter `p` upon entry into the enclosing function.
*/
pragma[nomagic]
predicate isIndirectParameterDefinition(Parameter p, int indirectionIndex) {
this.getIndirectionIndex() = indirectionIndex and
indirectionIndex > 0 and
getDefImpl(this).getValue().asInstruction().(InitializeParameterInstruction).getParameter() = p
}
/**
* Holds if this definition defines the implicit `this` parameter upon entry into
* the enclosing member function.
*/
pragma[nomagic]
predicate isThisDefinition() {
this.getIndirectionIndex() = 0 and
getDefImpl(this).getValue().asInstruction().(InitializeParameterInstruction).hasIndex(-1)
}
/**
* Holds if this definition defines the implicit `*this` parameter (i.e., the
* indirection of the `this` parameter) upon entry into the enclosing member
* function.
*/
pragma[nomagic]
predicate isIndirectThisDefinition(int indirectionIndex) {
this.getIndirectionIndex() = indirectionIndex and
indirectionIndex > 0 and
getDefImpl(this).getValue().asInstruction().(InitializeParameterInstruction).hasIndex(-1)
}
/**
* Gets an `Operand` that represents an indirect use of this definition.
*
@@ -1170,10 +1230,11 @@ class Definition extends SsaImpl::Definition {
* value that was defined by the definition.
*/
Operand getAnIndirectUse(int indirectionIndex) {
indirectionIndex > 0 and
exists(SourceVariable sv, IRBlock bb, int i, UseImpl use |
ssaDefReachesRead(sv, this, bb, i) and
use.hasIndexInBlock(bb, i, sv) and
result = use.getNode().asIndirectOperand(indirectionIndex)
use = TDirectUseImpl(result, indirectionIndex)
)
}

View File

@@ -768,21 +768,3 @@ private module Cached {
}
import Cached
/**
* Inputs to the shared SSA library's parameterized module that is shared
* between the SSA pruning stage, and the final SSA stage.
*/
module InputSigCommon {
class BasicBlock extends IRBlock {
ControlFlowNode getNode(int i) { result = this.getInstruction(i) }
int length() { result = this.getInstructionCount() }
}
class ControlFlowNode = Instruction;
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
}

View File

@@ -2,6 +2,7 @@
* Provides classes that specify the conditions under which control flows along a given edge.
*/
private import codeql.controlflow.SuccessorType
private import internal.EdgeKindInternal
private newtype TEdgeKind =
@@ -28,6 +29,21 @@ abstract private class EdgeKindImpl extends TEdgeKind {
final class EdgeKind = EdgeKindImpl;
private SuccessorType getAMatchingSpecificSuccessorType(EdgeKind k) {
result.(BooleanSuccessor).getValue() = true and k instanceof TrueEdge
or
result.(BooleanSuccessor).getValue() = false and k instanceof FalseEdge
or
result instanceof ExceptionSuccessor and k instanceof ExceptionEdge
}
SuccessorType getAMatchingSuccessorType(EdgeKind k) {
result = getAMatchingSpecificSuccessorType(k)
or
not exists(getAMatchingSpecificSuccessorType(k)) and
result instanceof DirectSuccessor
}
/**
* A "goto" edge, representing the unconditional successor of an `Instruction`
* or `IRBlock`.
@@ -36,11 +52,18 @@ class GotoEdge extends EdgeKindImpl, TGotoEdge {
final override string toString() { result = "Goto" }
}
/**
* A "true" or "false" edge representing a successor of a conditional branch.
*/
abstract private class BooleanEdgeKindImpl extends EdgeKindImpl { }
final class BooleanEdge = BooleanEdgeKindImpl;
/**
* A "true" edge, representing the successor of a conditional branch when the
* condition is non-zero.
*/
class TrueEdge extends EdgeKindImpl, TTrueEdge {
class TrueEdge extends BooleanEdgeKindImpl, TTrueEdge {
final override string toString() { result = "True" }
}
@@ -48,7 +71,7 @@ class TrueEdge extends EdgeKindImpl, TTrueEdge {
* A "false" edge, representing the successor of a conditional branch when the
* condition is zero.
*/
class FalseEdge extends EdgeKindImpl, TFalseEdge {
class FalseEdge extends BooleanEdgeKindImpl, TFalseEdge {
final override string toString() { result = "False" }
}
@@ -79,19 +102,48 @@ class SehExceptionEdge extends ExceptionEdgeImpl, TSehExceptionEdge {
final override string toString() { result = "SEH Exception" }
}
/**
* An edge from a `Switch` instruction to one of the cases, or to the default
* branch.
*/
abstract private class SwitchEdgeKindImpl extends EdgeKindImpl {
/**
* Gets the smallest value of the switch expression for which control will flow along this edge.
*/
string getMinValue() { none() }
/**
* Gets the largest value of the switch expression for which control will flow along this edge.
*/
string getMaxValue() { none() }
/**
* Gets the unique value of the switch expression for which control will
* flow along this edge, if any.
*/
final string getValue() { result = unique( | | [this.getMinValue(), this.getMaxValue()]) }
/** Holds if this edge is the default edge. */
predicate isDefault() { none() }
}
final class SwitchEdge = SwitchEdgeKindImpl;
/**
* A "default" edge, representing the successor of a `Switch` instruction when
* none of the case values matches the condition value.
*/
class DefaultEdge extends EdgeKindImpl, TDefaultEdge {
class DefaultEdge extends SwitchEdgeKindImpl, TDefaultEdge {
final override string toString() { result = "Default" }
final override predicate isDefault() { any() }
}
/**
* A "case" edge, representing the successor of a `Switch` instruction when the
* the condition value matches a corresponding `case` label.
*/
class CaseEdge extends EdgeKindImpl, TCaseEdge {
class CaseEdge extends SwitchEdgeKindImpl, TCaseEdge {
string minValue;
string maxValue;
@@ -103,24 +155,9 @@ class CaseEdge extends EdgeKindImpl, TCaseEdge {
else result = "Case[" + minValue + ".." + maxValue + "]"
}
/**
* Gets the smallest value of the switch expression for which control will flow along this edge.
*/
final string getMinValue() { result = minValue }
final override string getMinValue() { result = minValue }
/**
* Gets the largest value of the switch expression for which control will flow along this edge.
*/
final string getMaxValue() { result = maxValue }
/**
* Gets the unique value of the switch expression for which control will
* flow along this edge, if any.
*/
final string getValue() {
minValue = maxValue and
result = minValue
}
final override string getMaxValue() { result = maxValue }
}
/**

View File

@@ -7,6 +7,7 @@ import Instruction
private import internal.IRBlockImports as Imports
import Imports::EdgeKind
private import Cached
private import codeql.controlflow.BasicBlock as BB
/**
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
@@ -263,6 +264,54 @@ private predicate isEntryBlock(TIRBlock block) {
block = MkIRBlock(any(EnterFunctionInstruction enter))
}
module IRCfg implements BB::CfgSig<Language::Location> {
private import codeql.controlflow.SuccessorType
class ControlFlowNode = Instruction;
final private class FinalIRBlock = IRBlock;
class BasicBlock extends FinalIRBlock {
ControlFlowNode getNode(int i) { result = this.getInstruction(i) }
ControlFlowNode getLastNode() { result = super.getLastInstruction() }
int length() { result = this.getInstructionCount() }
BasicBlock getASuccessor() { result = super.getASuccessor() }
BasicBlock getASuccessor(SuccessorType t) {
exists(EdgeKind k |
result = super.getSuccessor(k) and
t = getAMatchingSuccessorType(k)
)
}
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
predicate dominates(BasicBlock bb) { super.dominates(bb) }
BasicBlock getImmediateDominator() { result.immediatelyDominates(this) }
predicate inDominanceFrontier(BasicBlock df) { super.dominanceFrontier() = df }
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
}
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { isEntryBlock(this) }
}
pragma[nomagic]
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) {
bb1.getASuccessor() = bb2 and
bb1 = bb2.getImmediateDominator() and
forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred))
}
}
cached
private module Cached {
cached

View File

@@ -1084,6 +1084,12 @@ class BinaryInstruction extends Instruction {
or
op1 = this.getRightOperand() and op2 = this.getLeftOperand()
}
/**
* Gets the instruction whose result provides the value of the left or right
* operand of this binary instruction.
*/
Instruction getAnInput() { result = this.getLeft() or result = this.getRight() }
}
/**

View File

@@ -41,7 +41,7 @@ newtype TValueNumber =
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
TUniqueValueNumber(Instruction instr) { uniqueValueNumber(instr) }
/**
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
@@ -129,12 +129,14 @@ private predicate filteredNumberableInstruction(Instruction instr) {
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
)
or
count(instr.getEnclosingIRFunction()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
@@ -144,7 +146,7 @@ private predicate variableAddressValueNumber(
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
@@ -154,7 +156,7 @@ private predicate initializeParameterValueNumber(
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
unique( | | instr.getResultIRType()) = type and
instr.getValue() = value
}
@@ -162,7 +164,7 @@ private predicate constantValueNumber(
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
@@ -171,7 +173,7 @@ private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field,
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
unique( | | instr.getField()) = field and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
@@ -182,7 +184,7 @@ private predicate binaryValueNumber0(
TValueNumber valueNumber
) {
not instr instanceof PointerArithmeticInstruction and
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
(
isLeft = true and
@@ -206,7 +208,7 @@ private predicate pointerArithmeticValueNumber0(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
boolean isLeft, TValueNumber valueNumber
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
instr.getElementSize() = elementSize and
(
@@ -229,7 +231,7 @@ private predicate pointerArithmeticValueNumber(
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
@@ -242,7 +244,7 @@ private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
tvalueNumber(instr.getUnary()) = operand and
unique( | | instr.getBaseClass()) = baseClass and
@@ -254,7 +256,7 @@ private predicate loadTotalOverlapValueNumber0(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber valueNumber,
boolean isAddress
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getResultIRType() = type and
(
isAddress = true and
@@ -277,8 +279,7 @@ private predicate loadTotalOverlapValueNumber(
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
private predicate uniqueValueNumber(Instruction instr) {
not instr.getResultIRType() instanceof IRVoidType and
(
not numberableInstruction(instr)
@@ -294,10 +295,8 @@ cached
TValueNumber tvalueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
uniqueValueNumber(instr) and
result = TUniqueValueNumber(instr)
}
/**
@@ -311,68 +310,64 @@ TValueNumber tvalueNumberOfOperand(Operand op) { result = tvalueNumber(op.getDef
* value number.
*/
private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
or
// The value number of a type-preserving conversion is just the value
// number of the unconverted value.
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
exists(IRFunction irFunc | irFunc = instr.getEnclosingIRFunction() |
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result = TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
or
// The value number of a type-preserving conversion is just the value
// number of the unconverted value.
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
)
}

View File

@@ -7,6 +7,7 @@ import Instruction
private import internal.IRBlockImports as Imports
import Imports::EdgeKind
private import Cached
private import codeql.controlflow.BasicBlock as BB
/**
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
@@ -263,6 +264,54 @@ private predicate isEntryBlock(TIRBlock block) {
block = MkIRBlock(any(EnterFunctionInstruction enter))
}
module IRCfg implements BB::CfgSig<Language::Location> {
private import codeql.controlflow.SuccessorType
class ControlFlowNode = Instruction;
final private class FinalIRBlock = IRBlock;
class BasicBlock extends FinalIRBlock {
ControlFlowNode getNode(int i) { result = this.getInstruction(i) }
ControlFlowNode getLastNode() { result = super.getLastInstruction() }
int length() { result = this.getInstructionCount() }
BasicBlock getASuccessor() { result = super.getASuccessor() }
BasicBlock getASuccessor(SuccessorType t) {
exists(EdgeKind k |
result = super.getSuccessor(k) and
t = getAMatchingSuccessorType(k)
)
}
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
predicate dominates(BasicBlock bb) { super.dominates(bb) }
BasicBlock getImmediateDominator() { result.immediatelyDominates(this) }
predicate inDominanceFrontier(BasicBlock df) { super.dominanceFrontier() = df }
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
}
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { isEntryBlock(this) }
}
pragma[nomagic]
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) {
bb1.getASuccessor() = bb2 and
bb1 = bb2.getImmediateDominator() and
forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred))
}
}
cached
private module Cached {
cached

View File

@@ -1084,6 +1084,12 @@ class BinaryInstruction extends Instruction {
or
op1 = this.getRightOperand() and op2 = this.getLeftOperand()
}
/**
* Gets the instruction whose result provides the value of the left or right
* operand of this binary instruction.
*/
Instruction getAnInput() { result = this.getLeft() or result = this.getRight() }
}
/**

View File

@@ -41,7 +41,7 @@ newtype TValueNumber =
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
TUniqueValueNumber(Instruction instr) { uniqueValueNumber(instr) }
/**
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
@@ -129,12 +129,14 @@ private predicate filteredNumberableInstruction(Instruction instr) {
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
)
or
count(instr.getEnclosingIRFunction()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
@@ -144,7 +146,7 @@ private predicate variableAddressValueNumber(
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
@@ -154,7 +156,7 @@ private predicate initializeParameterValueNumber(
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
unique( | | instr.getResultIRType()) = type and
instr.getValue() = value
}
@@ -162,7 +164,7 @@ private predicate constantValueNumber(
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
@@ -171,7 +173,7 @@ private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field,
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
unique( | | instr.getField()) = field and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
@@ -182,7 +184,7 @@ private predicate binaryValueNumber0(
TValueNumber valueNumber
) {
not instr instanceof PointerArithmeticInstruction and
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
(
isLeft = true and
@@ -206,7 +208,7 @@ private predicate pointerArithmeticValueNumber0(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
boolean isLeft, TValueNumber valueNumber
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
instr.getElementSize() = elementSize and
(
@@ -229,7 +231,7 @@ private predicate pointerArithmeticValueNumber(
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
@@ -242,7 +244,7 @@ private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
tvalueNumber(instr.getUnary()) = operand and
unique( | | instr.getBaseClass()) = baseClass and
@@ -254,7 +256,7 @@ private predicate loadTotalOverlapValueNumber0(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber valueNumber,
boolean isAddress
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getResultIRType() = type and
(
isAddress = true and
@@ -277,8 +279,7 @@ private predicate loadTotalOverlapValueNumber(
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
private predicate uniqueValueNumber(Instruction instr) {
not instr.getResultIRType() instanceof IRVoidType and
(
not numberableInstruction(instr)
@@ -294,10 +295,8 @@ cached
TValueNumber tvalueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
uniqueValueNumber(instr) and
result = TUniqueValueNumber(instr)
}
/**
@@ -311,68 +310,64 @@ TValueNumber tvalueNumberOfOperand(Operand op) { result = tvalueNumber(op.getDef
* value number.
*/
private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
or
// The value number of a type-preserving conversion is just the value
// number of the unconverted value.
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
exists(IRFunction irFunc | irFunc = instr.getEnclosingIRFunction() |
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result = TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
or
// The value number of a type-preserving conversion is just the value
// number of the unconverted value.
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
)
}

View File

@@ -97,7 +97,14 @@ newtype TInstructionTag =
exists(Stmt s | exists(s.getImplicitDestructorCall(index)))
} or
CoAwaitBranchTag() or
BoolToIntConversionTag()
BoolToIntConversionTag() or
SizeofVlaBaseSizeTag() or
SizeofVlaConversionTag(int index) {
exists(VlaDeclStmt v | exists(v.getTransitiveVlaDimensionStmt(index)))
} or
SizeofVlaDimensionTag(int index) {
exists(VlaDeclStmt v | exists(v.getTransitiveVlaDimensionStmt(index)))
}
class InstructionTag extends TInstructionTag {
final string toString() { result = getInstructionTagId(this) }

View File

@@ -123,13 +123,16 @@ private predicate ignoreExprAndDescendants(Expr expr) {
// or
ignoreExprAndDescendants(getRealParent(expr)) // recursive case
or
// va_start doesn't evaluate its argument, so we don't need to translate it.
// va_start does not evaluate its argument, so we do not need to translate it.
exists(BuiltInVarArgsStart vaStartExpr |
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
)
or
// sizeof does not evaluate its argument, so we do not need to translate it.
exists(SizeofExprOperator sizeofExpr | sizeofExpr.getExprOperand().getFullyConverted() = expr)
or
// The children of C11 _Generic expressions are just surface syntax.
exists(C11GenericExpr generic | generic.getAChild() = expr)
exists(C11GenericExpr generic | generic.getAChild().getFullyConverted() = expr)
or
// Do not translate implicit destructor calls for unnamed temporary variables that are
// conditionally constructed (until we have a mechanism for calling these only when the

View File

@@ -187,7 +187,7 @@ Variable getEnclosingVariable(Expr e) {
}
/**
* The IR translation of the "core" part of an expression. This is the part of
* The IR translation of the "core" part of an expression. This is the part of
* the expression that produces the result value of the expression, before any
* lvalue-to-rvalue conversion on the result. Every expression has a single
* `TranslatedCoreExpr`.
@@ -3884,7 +3884,7 @@ class TranslatedNewExpr extends TranslatedNewOrNewArrayExpr {
final override Type getTargetType() { result = expr.getAllocatedType().getUnspecifiedType() }
final override TranslatedInitialization getInitialization() {
result = getTranslatedInitialization(expr.getInitializer())
result = getTranslatedInitialization(expr.getInitializer().getFullyConverted())
}
}
@@ -4094,6 +4094,155 @@ class TranslatedStmtExpr extends TranslatedNonConstantExpr {
TranslatedStmt getStmt() { result = getTranslatedStmt(expr.getStmt()) }
}
private VlaDeclStmt getVlaDeclStmt(Expr expr, int pointerDerefCount) {
expr.(VariableAccess).getTarget() = result.getVariable() and
pointerDerefCount = 0
or
not expr.(PointerDereferenceExpr).getOperand() instanceof AddressOfExpr and
result = getVlaDeclStmt(expr.(PointerDereferenceExpr).getOperand(), pointerDerefCount - 1)
or
// Skip sequences of the form `*&...`
result =
getVlaDeclStmt(expr.(PointerDereferenceExpr).getOperand().(AddressOfExpr).getOperand(),
pointerDerefCount)
or
result = getVlaDeclStmt(expr.(ArrayExpr).getArrayBase(), pointerDerefCount - 1)
}
/**
* The IR translation of `SizeofExprOperator` when its result is non-constant, i.e.,
* when the operand expression refers to a variable length array.
*/
class TranslatedSizeofExpr extends TranslatedNonConstantExpr {
override SizeofExprOperator expr;
VlaDeclStmt vlaDeclStmt;
int vlaDimensions;
int pointerDerefCount;
TranslatedSizeofExpr() {
vlaDeclStmt = getVlaDeclStmt(expr.getExprOperand(), pointerDerefCount) and
vlaDimensions = vlaDeclStmt.getTransitiveNumberOfVlaDimensionStmts() and
pointerDerefCount < vlaDimensions
}
final override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getInstruction(SizeofVlaBaseSizeTag()) and
kind instanceof GotoEdge
}
override Instruction getALastInstructionInternal() {
result = this.getInstruction(SizeofVlaDimensionTag(vlaDimensions - 1))
}
final override TranslatedElement getChildInternal(int id) { none() }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
opcode instanceof Opcode::Constant and
tag = SizeofVlaBaseSizeTag() and
resultType = this.getResultType()
or
exists(int n, Type dimType |
pointerDerefCount <= n and
n < vlaDimensions and
dimType = this.getDimensionExpr(n).getUnderlyingType() and
tag = SizeofVlaConversionTag(n)
|
(
expr.getUnderlyingType() = dimType and
opcode instanceof Opcode::CopyValue
or
not expr.getUnderlyingType() = dimType and
opcode instanceof Opcode::Convert
)
) and
resultType = this.getResultType()
or
opcode instanceof Opcode::Mul and
exists(int n | pointerDerefCount <= n and n < vlaDimensions | tag = SizeofVlaDimensionTag(n)) and
resultType = this.getResultType()
}
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
tag = SizeofVlaBaseSizeTag() and
result = this.getInstruction(SizeofVlaConversionTag(pointerDerefCount)) and
kind instanceof GotoEdge
or
exists(int n | pointerDerefCount <= n and n < vlaDimensions |
tag = SizeofVlaConversionTag(n) and
result = this.getInstruction(SizeofVlaDimensionTag(n))
) and
kind instanceof GotoEdge
or
exists(int n | pointerDerefCount <= n and n < vlaDimensions - 1 |
tag = SizeofVlaDimensionTag(n) and
result = this.getInstruction(SizeofVlaConversionTag(n + 1))
) and
kind instanceof GotoEdge
or
tag = SizeofVlaDimensionTag(vlaDimensions - 1) and
result = this.getParent().getChildSuccessor(this, kind)
}
override string getInstructionConstantValue(InstructionTag tag) {
tag = SizeofVlaBaseSizeTag() and
result = this.getBaseType(vlaDeclStmt).getSize().toString()
}
private Type getBaseType(VlaDeclStmt v) {
not exists(v.getParentVlaDecl()) and
(
result =
this.getBaseType(v.getVariable().getUnderlyingType(), v.getNumberOfVlaDimensionStmts())
or
result = this.getBaseType(v.getType().getUnderlyingType(), v.getNumberOfVlaDimensionStmts())
)
or
result = this.getBaseType(v.getParentVlaDecl())
}
private Type getBaseType(Type type, int n) {
n = 0 and
result = type
or
result = this.getBaseType(type.(DerivedType).getBaseType(), n - 1)
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
exists(int n | pointerDerefCount <= n and n < vlaDimensions |
tag = SizeofVlaConversionTag(n) and
(
operandTag instanceof UnaryOperandTag and
result = getTranslatedExpr(this.getDimensionExpr(n)).getResult()
)
)
or
exists(int n | pointerDerefCount <= n and n < vlaDimensions |
tag = SizeofVlaDimensionTag(n) and
(
operandTag instanceof LeftOperandTag and
(
n - 1 >= pointerDerefCount and
result = this.getInstruction(SizeofVlaDimensionTag(n - 1))
or
n - 1 < pointerDerefCount and
result = this.getInstruction(SizeofVlaBaseSizeTag())
)
or
operandTag instanceof RightOperandTag and
result = this.getInstruction(SizeofVlaConversionTag(n))
)
)
}
private Expr getDimensionExpr(int n) {
result = vlaDeclStmt.getTransitiveVlaDimensionStmt(n).getDimensionExpr().getFullyConverted()
}
final override Instruction getResult() {
result = this.getInstruction(SizeofVlaDimensionTag(vlaDimensions - 1))
}
}
class TranslatedErrorExpr extends TranslatedSingleInstructionExpr {
override ErrorExpr expr;

View File

@@ -50,7 +50,7 @@ CppType getEllipsisVariablePRValueType() {
CppType getEllipsisVariableGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
/**
* Holds if the function returns a value, as opposed to returning `void`.
* Holds if the function `func` returns a value, as opposed to returning `void`.
*/
predicate hasReturnValue(Function func) { not func.getUnspecifiedType() instanceof VoidType }

View File

@@ -601,7 +601,7 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
* The IR translation of an implicit `return` statement generated by the extractor to handle control
* flow that reaches the end of a non-`void`-returning function body. Such control flow
* produces undefined behavior in C++ but not in C. However even in C using the return value is
* undefined behaviour. We make it return uninitialized memory to get as much flow as possible.
* undefined behavior. We make it return uninitialized memory to get as much flow as possible.
*/
class TranslatedNoValueReturnStmt extends TranslatedReturnStmt, TranslatedVariableInitialization {
TranslatedNoValueReturnStmt() {

View File

@@ -7,6 +7,7 @@ import Instruction
private import internal.IRBlockImports as Imports
import Imports::EdgeKind
private import Cached
private import codeql.controlflow.BasicBlock as BB
/**
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
@@ -263,6 +264,54 @@ private predicate isEntryBlock(TIRBlock block) {
block = MkIRBlock(any(EnterFunctionInstruction enter))
}
module IRCfg implements BB::CfgSig<Language::Location> {
private import codeql.controlflow.SuccessorType
class ControlFlowNode = Instruction;
final private class FinalIRBlock = IRBlock;
class BasicBlock extends FinalIRBlock {
ControlFlowNode getNode(int i) { result = this.getInstruction(i) }
ControlFlowNode getLastNode() { result = super.getLastInstruction() }
int length() { result = this.getInstructionCount() }
BasicBlock getASuccessor() { result = super.getASuccessor() }
BasicBlock getASuccessor(SuccessorType t) {
exists(EdgeKind k |
result = super.getSuccessor(k) and
t = getAMatchingSuccessorType(k)
)
}
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
predicate dominates(BasicBlock bb) { super.dominates(bb) }
BasicBlock getImmediateDominator() { result.immediatelyDominates(this) }
predicate inDominanceFrontier(BasicBlock df) { super.dominanceFrontier() = df }
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
}
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { isEntryBlock(this) }
}
pragma[nomagic]
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) {
bb1.getASuccessor() = bb2 and
bb1 = bb2.getImmediateDominator() and
forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred))
}
}
cached
private module Cached {
cached

View File

@@ -1084,6 +1084,12 @@ class BinaryInstruction extends Instruction {
or
op1 = this.getRightOperand() and op2 = this.getLeftOperand()
}
/**
* Gets the instruction whose result provides the value of the left or right
* operand of this binary instruction.
*/
Instruction getAnInput() { result = this.getLeft() or result = this.getRight() }
}
/**

View File

@@ -41,7 +41,7 @@ newtype TValueNumber =
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
TUniqueValueNumber(Instruction instr) { uniqueValueNumber(instr) }
/**
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
@@ -129,12 +129,14 @@ private predicate filteredNumberableInstruction(Instruction instr) {
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
)
or
count(instr.getEnclosingIRFunction()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
@@ -144,7 +146,7 @@ private predicate variableAddressValueNumber(
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
@@ -154,7 +156,7 @@ private predicate initializeParameterValueNumber(
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
unique( | | instr.getResultIRType()) = type and
instr.getValue() = value
}
@@ -162,7 +164,7 @@ private predicate constantValueNumber(
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
@@ -171,7 +173,7 @@ private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field,
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
unique( | | instr.getField()) = field and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
@@ -182,7 +184,7 @@ private predicate binaryValueNumber0(
TValueNumber valueNumber
) {
not instr instanceof PointerArithmeticInstruction and
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
(
isLeft = true and
@@ -206,7 +208,7 @@ private predicate pointerArithmeticValueNumber0(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
boolean isLeft, TValueNumber valueNumber
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
instr.getElementSize() = elementSize and
(
@@ -229,7 +231,7 @@ private predicate pointerArithmeticValueNumber(
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
@@ -242,7 +244,7 @@ private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getOpcode() = opcode and
tvalueNumber(instr.getUnary()) = operand and
unique( | | instr.getBaseClass()) = baseClass and
@@ -254,7 +256,7 @@ private predicate loadTotalOverlapValueNumber0(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber valueNumber,
boolean isAddress
) {
instr.getEnclosingIRFunction() = irFunc and
unique( | | instr.getEnclosingIRFunction()) = irFunc and
instr.getResultIRType() = type and
(
isAddress = true and
@@ -277,8 +279,7 @@ private predicate loadTotalOverlapValueNumber(
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
private predicate uniqueValueNumber(Instruction instr) {
not instr.getResultIRType() instanceof IRVoidType and
(
not numberableInstruction(instr)
@@ -294,10 +295,8 @@ cached
TValueNumber tvalueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
uniqueValueNumber(instr) and
result = TUniqueValueNumber(instr)
}
/**
@@ -311,68 +310,64 @@ TValueNumber tvalueNumberOfOperand(Operand op) { result = tvalueNumber(op.getDef
* value number.
*/
private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
or
// The value number of a type-preserving conversion is just the value
// number of the unconverted value.
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
exists(IRFunction irFunc | irFunc = instr.getEnclosingIRFunction() |
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result = TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
or
// The value number of a type-preserving conversion is just the value
// number of the unconverted value.
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
)
}

View File

@@ -49,7 +49,8 @@ Type getVariableType(Variable v) {
}
/**
* Holds if the database contains a `case` label with the specified minimum and maximum value.
* Holds if the database contains a `switchCase` label with the specified minimum `minValue`
* and maximum `maxValue` value.
*/
predicate hasCaseEdge(SwitchCase switchCase, string minValue, string maxValue) {
minValue = switchCase.getExpr().getFullyConverted().getValue() and

View File

@@ -371,7 +371,7 @@ class FunctionOutput extends TFunctionOutput {
/**
* Holds if this is the output value pointed to by a pointer parameter to a function, or the
* output value referred to by a reference parameter to a function, where the parameter has
* index `index`.
* index `i`.
*
* Example:
* ```
@@ -389,7 +389,7 @@ class FunctionOutput extends TFunctionOutput {
/**
* Holds if this is the output value pointed to by a pointer parameter (through `ind` number
* of indirections) to a function, or the output value referred to by a reference parameter to
* a function, where the parameter has index `index`.
* a function, where the parameter has index `i`.
*
* Example:
* ```

View File

@@ -307,13 +307,12 @@ class SemStoreExpr extends SemUnaryExpr {
}
class SemConditionalExpr extends SemKnownExpr {
SemExpr condition;
SemExpr trueResult;
SemExpr falseResult;
SemConditionalExpr() {
opcode instanceof Opcode::Conditional and
Specific::conditionalExpr(this, type, condition, trueResult, falseResult)
Specific::conditionalExpr(this, type, any(SemExpr condition), trueResult, falseResult)
}
final SemExpr getBranchExpr(boolean branch) {

View File

@@ -259,7 +259,7 @@ module SemanticExprConfig {
}
predicate guardHasBranchEdge(Guard guard, BasicBlock bb1, BasicBlock bb2, boolean branch) {
guard.controlsEdge(bb1, bb2, branch)
guard.controlsBranchEdge(bb1, bb2, branch)
}
Guard comparisonGuard(Expr e) { getSemanticExpr(result) = e }

View File

@@ -21,7 +21,9 @@ class FileWrite extends Expr {
Expr getDest() { fileWrite(this, _, result) }
/**
* Gets the conversion character for this write, if it exists and is known. For example in the following code the write of `value1` has conversion character `"s"`, whereas the write of `value2` has no conversion specifier.
* Gets the conversion character from `source` for this write, if it exists and is known.
* For example in the following code the write of `value1` has conversion character `"s"`, whereas
* the write of `value2` has no conversion specifier.
* ```
* fprintf(file, "%s", value1);
* stream << value2;

View File

@@ -191,11 +191,19 @@ module BoostorgAsio {
class SslContextClass extends Class {
SslContextClass() { this.getQualifiedName() = "boost::asio::ssl::context" }
ConstructorCall getAContructorCall() {
/**
* Gets a constructor call, if any.
*/
ConstructorCall getAConstructorCall() {
this.getAConstructor().getACallToThisFunction() = result and
not result.getLocation().getFile().toString().matches("%/boost/asio/%") and
result.fromSource()
}
/**
* DEPRECATED: Use `getAConstructorCall` instead.
*/
deprecated ConstructorCall getAContructorCall() { result = this.getAConstructorCall() }
}
/**
@@ -368,7 +376,7 @@ module BoostorgAsio {
*/
default predicate isSink(DataFlow::Node sink) {
exists(ConstructorCall cc, SslContextClass c, Expr e | e = sink.asExpr() |
c.getAContructorCall() = cc and
c.getAConstructorCall() = cc and
cc.getArgument(0) = e
)
}
@@ -468,7 +476,7 @@ module BoostorgAsio {
predicate isSource(DataFlow::Node source) {
exists(SslContextClass c, ConstructorCall cc |
cc = source.asExpr() and
c.getAContructorCall() = cc
c.getAConstructorCall() = cc
)
}

View File

@@ -2355,6 +2355,20 @@ class VlaDeclStmt extends Stmt, @stmt_vla_decl {
)
}
/**
* Gets the number of VLA dimension statements in this VLA declaration
* statement and transitively of the VLA declaration used to define its
* base type. if any.
*/
int getTransitiveNumberOfVlaDimensionStmts() {
not exists(this.getParentVlaDecl()) and
result = this.getNumberOfVlaDimensionStmts()
or
result =
this.getNumberOfVlaDimensionStmts() +
this.getParentVlaDecl().getTransitiveNumberOfVlaDimensionStmts()
}
/**
* Gets the `i`th VLA dimension statement in this VLA
* declaration statement.
@@ -2367,6 +2381,19 @@ class VlaDeclStmt extends Stmt, @stmt_vla_decl {
)
}
/**
* Gets the `i`th VLA dimension statement in this VLA declaration
* statement or transitively of the VLA declaration used to define
* its base type.
*/
VlaDimensionStmt getTransitiveVlaDimensionStmt(int i) {
i < this.getNumberOfVlaDimensionStmts() and
result = this.getVlaDimensionStmt(i)
or
result =
this.getParentVlaDecl().getTransitiveVlaDimensionStmt(i - this.getNumberOfVlaDimensionStmts())
}
/**
* Gets the type that this VLA declaration statement relates to,
* if any.
@@ -2378,4 +2405,31 @@ class VlaDeclStmt extends Stmt, @stmt_vla_decl {
* if any.
*/
Variable getVariable() { variable_vla(unresolveElement(result), underlyingElement(this)) }
/**
* Get the VLA declaration used to define the base type of
* this VLA declaration, if any.
*/
VlaDeclStmt getParentVlaDecl() {
exists(Variable v, Type baseType |
v = this.getVariable() and
baseType = this.getBaseType(v.getType(), this.getNumberOfVlaDimensionStmts())
|
result.getType() = baseType
)
or
exists(Type t, Type baseType |
t = this.getType().(TypedefType).getBaseType() and
baseType = this.getBaseType(t, this.getNumberOfVlaDimensionStmts())
|
result.getType() = baseType
)
}
private Type getBaseType(Type type, int n) {
n = 0 and
result = type
or
result = this.getBaseType(type.(DerivedType).getBaseType(), n - 1)
}
}

View File

@@ -222,6 +222,19 @@ extractor_version(
string frontend_version: string ref
)
pch_uses(
int pch: @pch ref,
int compilation: @compilation ref,
int id: @file ref
)
#keyset[pch, compilation]
pch_creations(
int pch: @pch,
int compilation: @compilation ref,
int from: @file ref
)
/** An element for which line-count information is available. */
@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Link PCH creations and uses
compatibility: backwards

View File

@@ -164,12 +164,17 @@ predicate valueOccurrenceCount(string value, int n) {
n > 20
}
predicate occurenceCount(Literal lit, string value, int n) {
predicate occurrenceCount(Literal lit, string value, int n) {
valueOccurrenceCount(value, n) and
value = lit.getValue() and
nonTrivialValue(_, lit)
}
/**
* DEPRECATED: Use `occurrenceCount` instead.
*/
deprecated predicate occurenceCount = occurrenceCount/3;
/*
* Literals repeated frequently
*/
@@ -178,7 +183,7 @@ predicate check(Literal lit, string value, int n, File f) {
// Check that the literal is nontrivial
not trivial(lit) and
// Check that it is repeated a number of times
occurenceCount(lit, value, n) and
occurrenceCount(lit, value, n) and
n > 20 and
f = lit.getFile() and
// Exclude generated files

View File

@@ -1,3 +1,32 @@
## 1.5.1
No user-facing changes.
## 1.5.0
### Major Analysis Improvements
* The queries `cpp/wrong-type-format-argument`, `cpp/comparison-with-wider-type`, `cpp/integer-multiplication-cast-to-long`, `cpp/implicit-function-declaration` and `cpp/suspicious-add-sizeof` have had their precisions reduced from `high` to `medium`. They will also now give alerts for projects built with `build-mode: none`.
* The queries `cpp/wrong-type-format-argument`, `cpp/comparison-with-wider-type`, `cpp/integer-multiplication-cast-to-long` and `cpp/suspicious-add-sizeof` are no longer included in the `code-scanning` suite.
### Bug Fixes
* The predicate `occurenceCount` in the file module `MagicConstants` has been deprecated. Use `occurrenceCount` instead.
* The predicate `additionalAdditionOrSubstractionCheckForLeapYear` in the file module `LeapYear` has been deprecated. Use `additionalAdditionOrSubtractionCheckForLeapYear` instead.
## 1.4.7
### Bug Fixes
* Fixed an inconsistency across languages where most have a `Customizations.qll` file for adding customizations, but not all did.
## 1.4.6
### Minor Analysis Improvements
* The `cpp/short-global-name` query will no longer give alerts for instantiations of template variables, only for the template itself.
* Fixed a false positive in `cpp/overflow-buffer` when the type of the destination buffer is a reference to a class/struct type.
## 1.4.5
### Minor Analysis Improvements

View File

@@ -4,13 +4,9 @@ private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.ir.ValueNumbering
private predicate exprInBooleanContext(Expr e) {
exists(IRGuardCondition gc |
exists(Instruction i |
i.getUnconvertedResultExpression() = e and
gc.comparesEq(valueNumber(i).getAUse(), 0, _, _)
)
or
gc.getUnconvertedResultExpression() = e
exists(IRGuardCondition gc, Instruction i |
i.getUnconvertedResultExpression() = e and
gc.comparesEq(valueNumber(i).getAUse(), 0, _, _)
)
}
@@ -36,20 +32,18 @@ private string getEofValue() {
* Holds if the value of `call` has been checked to not equal `EOF`.
*/
private predicate checkedForEof(ScanfFunctionCall call) {
exists(IRGuardCondition gc |
exists(CallInstruction i | i.getUnconvertedResultExpression() = call |
exists(int val | gc.comparesEq(valueNumber(i).getAUse(), val, _, _) |
// call == EOF
val = getEofValue().toInt()
or
// call == [any positive number]
val > 0
)
exists(IRGuardCondition gc, CallInstruction i | i.getUnconvertedResultExpression() = call |
exists(int val | gc.comparesEq(valueNumber(i).getAUse(), val, _, _) |
// call == EOF
val = getEofValue().toInt()
or
exists(int val | gc.comparesLt(valueNumber(i).getAUse(), val, true, _) |
// call < [any non-negative number] (EOF is guaranteed to be negative)
val >= 0
)
// call == [any positive number]
val > 0
)
or
exists(int val | gc.comparesLt(valueNumber(i).getAUse(), val, true, _) |
// call < [any non-negative number] (EOF is guaranteed to be negative)
val >= 0
)
)
}

View File

@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 8.1
* @precision high
* @precision medium
* @id cpp/integer-multiplication-cast-to-long
* @tags reliability
* security
@@ -179,7 +179,6 @@ predicate overflows(MulExpr me, Type t) {
from MulExpr me, Type t1, Type t2
where
not any(Compilation c).buildModeNone() and
t1 = me.getType().getUnderlyingType() and
t2 = me.getConversion().getType().getUnderlyingType() and
t1.getSize() < t2.getSize() and

View File

@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @precision medium
* @id cpp/wrong-type-format-argument
* @tags reliability
* correctness
@@ -154,7 +154,6 @@ int sizeof_IntType() { exists(IntType it | result = it.getSize()) }
from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual
where
not any(Compilation c).buildModeNone() and
(
formattingFunctionCallExpectedType(ffc, n, expected) and
formattingFunctionCallActualType(ffc, n, arg, actual) and

View File

@@ -128,11 +128,18 @@ abstract class LeapYearFieldAccess extends YearFieldAccess {
/**
* Holds if the top-level binary operation includes an addition or subtraction operator with an operand specified by `valueToCheck`.
*/
predicate additionalAdditionOrSubstractionCheckForLeapYear(int valueToCheck) {
predicate additionalAdditionOrSubtractionCheckForLeapYear(int valueToCheck) {
additionalLogicalCheck(this, "+", valueToCheck) or
additionalLogicalCheck(this, "-", valueToCheck)
}
/**
* DEPRECATED: Use `additionalAdditionOrSubtractionCheckForLeapYear` instead.
*/
deprecated predicate additionalAdditionOrSubstractionCheckForLeapYear(int valueToCheck) {
this.additionalAdditionOrSubtractionCheckForLeapYear(valueToCheck)
}
/**
* Holds if this object is used on a modulus 4 operation, which would likely indicate the start of a leap year check.
*/
@@ -180,13 +187,13 @@ class StructTmLeapYearFieldAccess extends LeapYearFieldAccess {
this.additionalModulusCheckForLeapYear(100) and
// tm_year represents years since 1900
(
this.additionalAdditionOrSubstractionCheckForLeapYear(1900)
this.additionalAdditionOrSubtractionCheckForLeapYear(1900)
or
// some systems may use 2000 for 2-digit year conversions
this.additionalAdditionOrSubstractionCheckForLeapYear(2000)
this.additionalAdditionOrSubtractionCheckForLeapYear(2000)
or
// converting from/to Unix epoch
this.additionalAdditionOrSubstractionCheckForLeapYear(1970)
this.additionalAdditionOrSubtractionCheckForLeapYear(1970)
)
}
}

View File

@@ -136,7 +136,7 @@ private module NetworkToBufferSizeConfig implements DataFlow::ConfigSig {
predicate isBarrier(DataFlow::Node node) {
exists(GuardCondition gc, GVN gvn |
gc.getAChild*() = gvn.getAnExpr() and
gc.(Expr).getAChild*() = gvn.getAnExpr() and
globalValueNumber(node.asExpr()) = gvn and
gc.controls(node.asExpr().getBasicBlock(), _)
)

View File

@@ -14,7 +14,7 @@ import cpp
import semmle.code.cpp.security.boostorg.asio.protocols
predicate isSourceImpl(DataFlow::Node source, ConstructorCall cc) {
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = cc and cc = source.asExpr())
exists(BoostorgAsio::SslContextClass c | c.getAConstructorCall() = cc and cc = source.asExpr())
}
predicate isSinkImpl(DataFlow::Node sink, FunctionCall fcSetOptions) {

View File

@@ -5,7 +5,7 @@
* may lead to unpredictable behavior.
* @kind problem
* @problem.severity warning
* @precision high
* @precision medium
* @id cpp/implicit-function-declaration
* @tags correctness
* maintainability
@@ -38,7 +38,6 @@ predicate isCompiledAsC(File f) {
from FunctionDeclarationEntry fdeIm, FunctionCall fc
where
not any(Compilation c).buildModeNone() and
isCompiledAsC(fdeIm.getFile()) and
not isFromMacroDefinition(fc) and
fdeIm.isImplicit() and

View File

@@ -20,12 +20,14 @@ class RangeFunction extends Function {
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
super.getLocation().hasLocationInfo(path, sl, sc, _, _) and
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
(
this.getBlock().getLocation().hasLocationInfo(path, _, _, el, ec)
this.getBlock().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn)
or
not exists(this.getBlock()) and el = sl + 1 and ec = 1
not exists(this.getBlock()) and endline = startline + 1 and endcolumn = 1
)
}
}

View File

@@ -147,8 +147,8 @@ module ExecTaintConfig implements DataFlow::StateConfigSig {
predicate isBarrier(DataFlow::Node node) { isBarrierImpl(node) }
predicate isBarrierOut(DataFlow::Node node) {
isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
predicate isBarrierOut(DataFlow::Node node, FlowState state) {
isSink(node, state) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
}
predicate observeDiffInformedIncrementalMode() { any() }

View File

@@ -109,7 +109,7 @@ predicate lessThanOrEqual(IRGuardCondition g, Expr e, boolean branch) {
g.comparesEq(left, _, _, true, branch)
|
interestingLessThanOrEqual(left) and
left.getDef().getUnconvertedResultExpression() = e
left.getDef().getConvertedResultExpression() = e
)
}

View File

@@ -26,13 +26,13 @@ predicate isFlowSource(FS::FlowSource source, string sourceType) {
predicate guardChecks(IRGuardCondition g, Expr e, boolean branch) {
exists(Operand op | op.getDef().getConvertedResultExpression() = e |
// `op < k` is true and `k > 0`
g.comparesLt(op, any(int k | k > 0), true, any(BooleanValue bv | bv.getValue() = branch))
g.comparesLt(op, any(int k | k > 0), true, any(GuardValue bv | bv.asBooleanValue() = branch))
or
// `op < _ + k` is true and `k > 0`.
g.comparesLt(op, _, any(int k | k > 0), true, branch)
or
// op == k
g.comparesEq(op, _, true, any(BooleanValue bv | bv.getValue() = branch))
g.comparesEq(op, _, true, any(GuardValue bv | bv.asBooleanValue() = branch))
or
// op == _ + k
g.comparesEq(op, _, _, true, branch)

View File

@@ -6,7 +6,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 7.8
* @precision high
* @precision medium
* @tags reliability
* security
* external/cwe/cwe-190
@@ -51,7 +51,6 @@ int getComparisonSizeAdjustment(Expr e) {
from Loop l, RelationalOperation rel, VariableAccess small, Expr large
where
not any(Compilation c).buildModeNone() and
small = rel.getLesserOperand() and
large = rel.getGreaterOperand() and
rel = l.getCondition().getAChild*() and

View File

@@ -25,10 +25,10 @@ import semmle.code.cpp.controlflow.IRGuards as IRGuards
predicate outOfBoundsExpr(Expr expr, string kind) {
if convertedExprMightOverflowPositively(expr)
then kind = "overflow"
else
if convertedExprMightOverflowNegatively(expr)
then kind = "overflow negatively"
else none()
else (
convertedExprMightOverflowNegatively(expr) and
kind = "overflow negatively"
)
}
predicate isSource(FS::FlowSource source, string sourceType) { sourceType = source.getSourceType() }

View File

@@ -29,7 +29,7 @@ module VerifyResultConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof SslGetVerifyResultCall }
predicate isSink(DataFlow::Node sink) {
exists(GuardCondition guard | guard.getAChild*() = sink.asExpr())
exists(GuardCondition guard | guard.(Expr).getAChild*() = sink.asExpr())
}
predicate observeDiffInformedIncrementalMode() { any() }

View File

@@ -55,30 +55,9 @@ predicate resultIsChecked(SslGetPeerCertificateCall getCertCall, ControlFlowNode
predicate certIsZero(
SslGetPeerCertificateCall getCertCall, ControlFlowNode node1, ControlFlowNode node2
) {
exists(Expr cert | cert = globalValueNumber(getCertCall).getAnExpr() |
exists(GuardCondition guard, Expr zero |
zero.getValue().toInt() = 0 and
node1 = guard and
(
// if (cert == zero) {
guard.comparesEq(cert, zero, 0, true, true) and
node2 = guard.getATrueSuccessor()
or
// if (cert != zero) { }
guard.comparesEq(cert, zero, 0, false, true) and
node2 = guard.getAFalseSuccessor()
)
)
or
(
// if (cert) { }
node1 = cert
or
// if (!cert) {
node1.(NotExpr).getAChild() = cert
) and
node2 = node1.getASuccessor() and
not cert.(GuardCondition).controls(node2, true) // cert may be false
exists(Expr cert |
cert = globalValueNumber(getCertCall).getAnExpr() and
node1.(GuardCondition).ensuresEqEdge(cert, 0, _, node2.getBasicBlock(), true)
)
}

View File

@@ -115,7 +115,7 @@ predicate checksPath(Expr check, Expr checkPath) {
pragma[nomagic]
predicate checkPathControlsUse(Expr check, Expr checkPath, Expr use) {
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
exists(GuardCondition guard | referenceTo(check, guard.(Expr).getAChild*()) |
guard.controls(use.getBasicBlock(), _)
) and
checksPath(pragma[only_bind_into](check), checkPath)
@@ -123,7 +123,7 @@ predicate checkPathControlsUse(Expr check, Expr checkPath, Expr use) {
pragma[nomagic]
predicate fileNameOperationControlsUse(Expr check, Expr checkPath, Expr use) {
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
exists(GuardCondition guard | referenceTo(check, guard.(Expr).getAChild*()) |
guard.controls(use.getBasicBlock(), _)
) and
pragma[only_bind_into](check) = filenameOperation(checkPath)

View File

@@ -31,27 +31,28 @@ private predicate hasConditionalInitialization(
class ConditionallyInitializedVariable extends LocalVariable {
ConditionalInitializationCall call;
ConditionalInitializationFunction f;
VariableAccess initAccess;
Evidence e;
ConditionallyInitializedVariable() {
// Find a call that conditionally initializes this variable
hasConditionalInitialization(f, call, this, initAccess, e) and
// Ignore cases where the variable is assigned prior to the call
not reaches(this.getAnAssignedValue(), initAccess) and
// Ignore cases where the variable is assigned field-wise prior to the call.
not exists(FieldAccess fa |
exists(Assignment a |
fa = getAFieldAccess(this) and
a.getLValue() = fa
exists(VariableAccess initAccess |
hasConditionalInitialization(f, call, this, initAccess, e) and
// Ignore cases where the variable is assigned prior to the call
not reaches(this.getAnAssignedValue(), initAccess) and
// Ignore cases where the variable is assigned field-wise prior to the call.
not exists(FieldAccess fa |
exists(Assignment a |
fa = getAFieldAccess(this) and
a.getLValue() = fa
)
|
reaches(fa, initAccess)
) and
// Ignore cases where the variable is assigned by a prior call to an initialization function
not exists(Call c |
this.getAnAccess() = getAnInitializedArgument(c).(AddressOfExpr).getOperand() and
reaches(c, initAccess)
)
|
reaches(fa, initAccess)
) and
// Ignore cases where the variable is assigned by a prior call to an initialization function
not exists(Call c |
this.getAnAccess() = getAnInitializedArgument(c).(AddressOfExpr).getOperand() and
reaches(c, initAccess)
) and
/*
* Static local variables with constant initializers do not have the initializer expr as part of

View File

@@ -6,7 +6,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 8.8
* @precision high
* @precision medium
* @id cpp/suspicious-add-sizeof
* @tags security
* external/cwe/cwe-468
@@ -24,7 +24,6 @@ private predicate isCharSzPtrExpr(Expr e) {
from Expr sizeofExpr, Expr e
where
not any(Compilation c).buildModeNone() and
// If we see an addWithSizeof then we expect the type of
// the pointer expression to be `char*` or `void*`. Otherwise it
// is probably a mistake.

View File

@@ -41,7 +41,7 @@ predicate deleteMayThrow(DeleteOrDeleteArrayExpr deleteExpr) {
}
/**
* Holds if the function may throw an exception when called. That is, if the body of the function looks
* Holds if the function `f` may throw an exception when called. That is, if the body of the function looks
* like it might throw an exception, and the function does not have a `noexcept` or `throw()` specifier.
*/
predicate functionMayThrow(Function f) {

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Fixed a false positive in `cpp/overflow-buffer` when the type of the destination buffer is a reference to a class/struct type.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The `cpp/short-global-name` query will no longer give alerts for instantiations of template variables, only for the template itself.

View File

@@ -0,0 +1,6 @@
## 1.4.6
### Minor Analysis Improvements
* The `cpp/short-global-name` query will no longer give alerts for instantiations of template variables, only for the template itself.
* Fixed a false positive in `cpp/overflow-buffer` when the type of the destination buffer is a reference to a class/struct type.

View File

@@ -0,0 +1,5 @@
## 1.4.7
### Bug Fixes
* Fixed an inconsistency across languages where most have a `Customizations.qll` file for adding customizations, but not all did.

View File

@@ -0,0 +1,11 @@
## 1.5.0
### Major Analysis Improvements
* The queries `cpp/wrong-type-format-argument`, `cpp/comparison-with-wider-type`, `cpp/integer-multiplication-cast-to-long`, `cpp/implicit-function-declaration` and `cpp/suspicious-add-sizeof` have had their precisions reduced from `high` to `medium`. They will also now give alerts for projects built with `build-mode: none`.
* The queries `cpp/wrong-type-format-argument`, `cpp/comparison-with-wider-type`, `cpp/integer-multiplication-cast-to-long` and `cpp/suspicious-add-sizeof` are no longer included in the `code-scanning` suite.
### Bug Fixes
* The predicate `occurenceCount` in the file module `MagicConstants` has been deprecated. Use `occurrenceCount` instead.
* The predicate `additionalAdditionOrSubstractionCheckForLeapYear` in the file module `LeapYear` has been deprecated. Use `additionalAdditionOrSubtractionCheckForLeapYear` instead.

View File

@@ -0,0 +1,3 @@
## 1.5.1
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.4.5
lastReleaseVersion: 1.5.1

View File

@@ -13,6 +13,6 @@ where
def = definitionOf(e, kind) and
// We need to exclude definitions for elements inside template instantiations,
// as these often lead to multiple links to definitions from the same source location.
// LGTM does not support this behaviour.
// LGTM does not support this behavior.
not e.isFromTemplateInstantiation(_)
select e, def, kind

View File

@@ -47,7 +47,7 @@ where
// for a function parameter
unchecked.getTarget() = param and
// this function parameter is not overwritten
count(param.getAnAssignment()) = 0 and
not exists(param.getAnAssignment()) and
check.getTarget() = param and
// which is once checked
candidateResultChecked(check, eqop) and

View File

@@ -19,16 +19,17 @@ import cpp
* Errors when using a variable declaration inside a loop.
*/
class DangerousWhileLoop extends WhileStmt {
Expr exp;
Declaration dl;
DangerousWhileLoop() {
this = dl.getParentScope().(BlockStmt).getParent*() and
exp = this.getCondition().getAChild*() and
not exp instanceof PointerFieldAccess and
not exp instanceof ValueFieldAccess and
exp.(VariableAccess).getTarget().getName() = dl.getName() and
not exp.getParent*() instanceof FunctionCall
exists(Expr exp |
exp = this.getCondition().getAChild*() and
not exp instanceof PointerFieldAccess and
not exp instanceof ValueFieldAccess and
exp.(VariableAccess).getTarget().getName() = dl.getName() and
not exp.getParent*() instanceof FunctionCall
)
}
Declaration getDeclaration() { result = dl }

View File

@@ -46,7 +46,7 @@ predicate exprMayBeString(Expr exp) {
)
}
/** Holds if expression is constant or operator call `sizeof`. */
/** Holds if expression `exp` is constant or operator call `sizeof`. */
predicate argConstOrSizeof(Expr exp) {
exp.getValue().toInt() > 1 or
exp.(SizeofTypeOperator).getTypeOperand().getSize() > 1

View File

@@ -15,7 +15,7 @@
import cpp
import semmle.code.cpp.commons.Exclusions
/** Holds if a `fc` function call is available before or after a `chdir` function call. */
/** Holds if a `fcp` function call is available before or after a `chdir` function call. */
predicate inExistsChdir(FunctionCall fcp) {
exists(FunctionCall fctmp |
(
@@ -29,7 +29,7 @@ predicate inExistsChdir(FunctionCall fcp) {
)
}
/** Holds if a `fc` function call is available before or after a function call containing a `chdir` call. */
/** Holds if a `fcp` function call is available before or after a function call containing a `chdir` call. */
predicate outExistsChdir(FunctionCall fcp) {
exists(FunctionCall fctmp |
exists(FunctionCall fctmp2 |

Some files were not shown because too many files have changed in this diff Show More