mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'main' into signature_model_refactor
This commit is contained in:
2436
cpp/downgrades/c16b29b27f71247023321cc0d0360998b318837c/old.dbscheme
Normal file
2436
cpp/downgrades/c16b29b27f71247023321cc0d0360998b318837c/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
description: Link PCH creations and uses
|
||||
compatibility: full
|
||||
pch_uses.rel: delete
|
||||
pch_creations.rel: delete
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
11
cpp/ql/lib/Customizations.qll
Normal file
11
cpp/ql/lib/Customizations.qll
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
4
cpp/ql/lib/change-notes/2025-09-18-guards.md
Normal file
4
cpp/ql/lib/change-notes/2025-09-18-guards.md
Normal 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`.
|
||||
7
cpp/ql/lib/change-notes/released/5.4.1.md
Normal file
7
cpp/ql/lib/change-notes/released/5.4.1.md
Normal 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.
|
||||
10
cpp/ql/lib/change-notes/released/5.5.0.md
Normal file
10
cpp/ql/lib/change-notes/released/5.5.0.md
Normal 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.
|
||||
9
cpp/ql/lib/change-notes/released/5.6.0.md
Normal file
9
cpp/ql/lib/change-notes/released/5.6.0.md
Normal 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`.
|
||||
3
cpp/ql/lib/change-notes/released/5.6.1.md
Normal file
3
cpp/ql/lib/change-notes/released/5.6.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 5.6.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 5.4.0
|
||||
lastReleaseVersion: 5.6.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
31
cpp/ql/lib/ext/ComPtr.model.yml
Normal file
31
cpp/ql/lib/ext/ComPtr.model.yml
Normal 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"]
|
||||
12
cpp/ql/lib/ext/ComPtrRef.model.yml
Normal file
12
cpp/ql/lib/ext/ComPtrRef.model.yml
Normal 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"]
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
*
|
||||
|
||||
26
cpp/ql/lib/semmle/code/cpp/PchFile.qll
Normal file
26
cpp/ql/lib/semmle/code/cpp/PchFile.qll
Normal 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) }
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
* ```
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Link PCH creations and uses
|
||||
compatibility: backwards
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(), _)
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
6
cpp/ql/src/change-notes/released/1.4.6.md
Normal file
6
cpp/ql/src/change-notes/released/1.4.6.md
Normal 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.
|
||||
5
cpp/ql/src/change-notes/released/1.4.7.md
Normal file
5
cpp/ql/src/change-notes/released/1.4.7.md
Normal 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.
|
||||
11
cpp/ql/src/change-notes/released/1.5.0.md
Normal file
11
cpp/ql/src/change-notes/released/1.5.0.md
Normal 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.
|
||||
3
cpp/ql/src/change-notes/released/1.5.1.md
Normal file
3
cpp/ql/src/change-notes/released/1.5.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.5.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.5
|
||||
lastReleaseVersion: 1.5.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user