Compare commits

..

2 Commits

1696 changed files with 73357 additions and 149777 deletions

View File

@@ -31,12 +31,3 @@ updates:
- "golang.org/x/*"
reviewers:
- "github/codeql-go"
- package-ecosystem: "gomod"
directory: "go/ql/test"
schedule:
interval: "monthly"
ignore:
- dependency-name: "*"
reviewers:
- "github/codeql-go"

View File

@@ -9,42 +9,26 @@ on:
- "*/ql/lib/**/*.ql"
- "*/ql/lib/**/*.qll"
- "*/ql/lib/**/*.yml"
- "shared/**/*.ql"
- "shared/**/*.qll"
- "!**/experimental/**"
- "!ql/**"
- ".github/workflows/check-change-note.yml"
jobs:
check-change-note:
env:
REPO: ${{ github.repository }}
PULL_REQUEST_NUMBER: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: ubuntu-latest
steps:
- name: Fail if no change note found. To fix, either add one, or add the `no-change-note-required` label.
if: |
github.event.pull_request.draft == false &&
!contains(github.event.pull_request.labels.*.name, 'no-change-note-required')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
change_note_files=$(gh api "repos/$REPO/pulls/$PULL_REQUEST_NUMBER/files" --paginate --jq '.[].filename | select(test("/change-notes/.*[.]md$"))')
if [ -z "$change_note_files" ]; then
echo "No change note found. Either add one, or add the 'no-change-note-required' label."
exit 1
fi
echo "Change notes found:"
echo "$change_note_files"
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
grep true -c
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md', 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text, or released/x.y.z.md for released change-notes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bad_change_note_file_names=$(gh api "repos/$REPO/pulls/$PULL_REQUEST_NUMBER/files" --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))][] | select((test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$")) | not)')
if [ -n "$bad_change_note_file_names" ]; then
echo "The following change note file names are invalid:"
echo "$bad_change_note_file_names"
exit 1
fi
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$"))' |
grep true -c

View File

@@ -91,7 +91,7 @@ jobs:
run: |
# Generate (Asp)NetCore stubs
STUBS_PATH=stubs_output
python3 scripts/stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger 6.5.0 "$STUBS_PATH"
python3 ql/src/Stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger 6.5.0 "$STUBS_PATH"
rm -rf ql/test/resources/stubs/_frameworks
# Update existing stubs in the repo with the freshly generated ones
mv "$STUBS_PATH/output/stubs/_frameworks" ql/test/resources/stubs/

View File

@@ -89,32 +89,9 @@ jobs:
- name: Save PR number
run: |
mkdir -p pr
echo ${PR_NUMBER} > pr/NR
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
echo ${{ github.event.pull_request.number }} > pr/NR
- name: Upload PR number
uses: actions/upload-artifact@v3
with:
name: pr
path: pr/
- name: Save comment ID (if it exists)
run: |
# Find the latest comment starting with COMMENT_PREFIX
COMMENT_PREFIX=":warning: The head of this PR and the base branch were compared for differences in the framework coverage reports."
COMMENT_ID=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate | jq --arg prefix "${COMMENT_PREFIX}" 'map(select(.body|startswith($prefix)) | .id) | max // empty')
if [[ -z ${COMMENT_ID} ]]
then
echo "Comment not found. Not uploading 'comment/ID' artifact."
else
mkdir -p comment
echo ${COMMENT_ID} > comment/ID
fi
env:
GITHUB_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
- name: Upload comment ID (if it exists)
uses: actions/upload-artifact@v3
with:
name: comment
path: comment/
if-no-files-found: ignore

View File

@@ -6,7 +6,7 @@ provide:
- "*/ql/consistency-queries/qlpack.yml"
- "*/ql/automodel/src/qlpack.yml"
- "*/ql/automodel/test/qlpack.yml"
- "shared/**/qlpack.yml"
- "shared/*/qlpack.yml"
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
- "go/ql/config/legacy-support/qlpack.yml"
- "go/build/codeql-extractor-go/codeql-extractor.yml"
@@ -29,7 +29,6 @@ provide:
- "swift/extractor-pack/codeql-extractor.yml"
- "swift/integration-tests/qlpack.yml"
- "ql/extractor-pack/codeql-extractor.yml"
- ".github/codeql/extensions/**/codeql-pack.yml"
versionPolicies:
default:

View File

@@ -1,19 +0,0 @@
class BuiltinType extends @builtintype {
string toString() { none() }
}
from BuiltinType type, string name, int kind, int kind_new, int size, int sign, int alignment
where
builtintypes(type, name, kind, size, sign, alignment) and
if
type instanceof @fp16 or
type instanceof @std_bfloat16 or
type instanceof @std_float16 or
type instanceof @complex_std_float32 or
type instanceof @complex_float32x or
type instanceof @complex_std_float64 or
type instanceof @complex_float64x or
type instanceof @complex_std_float128
then kind_new = 2
else kind_new = kind
select type, name, kind_new, size, sign, alignment

View File

@@ -1,3 +0,0 @@
description: Introduce new floating-point types from C23 and C++23
compatibility: backwards
builtintypes.rel: run builtintypes.qlo

View File

@@ -1,2 +0,0 @@
description: Removed @assignpaddexpr and @assignpsubexpr from @assign_bitwise_expr
compatibility: full

View File

@@ -1,3 +0,0 @@
description: Introduce extractor version numbers
compatibility: breaking
extractor_version.rel: delete

View File

@@ -1,25 +1,3 @@
## 0.11.0
### Breaking Changes
* The `Container` and `Folder` classes now derive from `ElementBase` instead of `Locatable`, and no longer expose the `getLocation` predicate. Use `getURL` instead.
### New Features
* Added a new class `AdditionalCallTarget` for specifying additional call targets.
### Minor Analysis Improvements
* More field accesses are identified as `ImplicitThisFieldAccess`.
* Added support for new floating-point types in C23 and C++23.
## 0.10.1
### Minor Analysis Improvements
* Deleted the deprecated `AnalysedString` class, use the new name `AnalyzedString`.
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.
## 0.10.0
### Minor Analysis Improvements

View File

@@ -1,6 +1,5 @@
## 0.10.1
### Minor Analysis Improvements
---
category: minorAnalysis
---
* Deleted the deprecated `AnalysedString` class, use the new name `AnalyzedString`.
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added taint models for `realloc` and related functions.

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
* The expressions `AssignPointerAddExpr` and `AssignPointerSubExpr` are no longer subtypes of `AssignBitwiseOperation`.

View File

@@ -1,14 +0,0 @@
## 0.11.0
### Breaking Changes
* The `Container` and `Folder` classes now derive from `ElementBase` instead of `Locatable`, and no longer expose the `getLocation` predicate. Use `getURL` instead.
### New Features
* Added a new class `AdditionalCallTarget` for specifying additional call targets.
### Minor Analysis Improvements
* More field accesses are identified as `ImplicitThisFieldAccess`.
* Added support for new floating-point types in C23 and C++23.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.11.0
lastReleaseVersion: 0.10.0

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 0.11.1-dev
version: 0.10.1-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
@@ -7,7 +7,6 @@ library: true
upgrades: upgrades
dependencies:
codeql/dataflow: ${workspace}
codeql/rangeanalysis: ${workspace}
codeql/ssa: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}

View File

@@ -32,7 +32,7 @@ private module Input implements InputSig {
private module Impl = Make<Input>;
/** A file or folder. */
class Container extends ElementBase, Impl::Container {
class Container extends Locatable, Impl::Container {
override string toString() { result = Impl::Container.super.toString() }
}
@@ -47,6 +47,11 @@ class Container extends ElementBase, Impl::Container {
* To get the full path, use `getAbsolutePath`.
*/
class Folder extends Container, Impl::Folder {
override Location getLocation() {
result.getContainer() = this and
result.hasLocationInfo(_, 0, 0, 0, 0)
}
override string getAPrimaryQlClass() { result = "Folder" }
}
@@ -62,7 +67,7 @@ class Folder extends Container, Impl::Folder {
* The base name further decomposes into the _stem_ and _extension_ -- see
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
*/
class File extends Container, Locatable, Impl::File {
class File extends Container, Impl::File {
override string getAPrimaryQlClass() { result = "File" }
override Location getLocation() {

View File

@@ -819,30 +819,6 @@ private predicate floatingPointTypeMapping(
or
// _Complex _Float16
kind = 53 and base = 2 and domain = TComplexDomain() and realKind = 52 and extended = false
or
// __fp16
kind = 54 and base = 2 and domain = TRealDomain() and realKind = 54 and extended = false
or
// __bf16
kind = 55 and base = 2 and domain = TRealDomain() and realKind = 55 and extended = false
or
// std::float16_t
kind = 56 and base = 2 and domain = TRealDomain() and realKind = 56 and extended = false
or
// _Complex _Float32
kind = 57 and base = 2 and domain = TComplexDomain() and realKind = 45 and extended = false
or
// _Complex _Float32x
kind = 58 and base = 2 and domain = TComplexDomain() and realKind = 46 and extended = true
or
// _Complex _Float64
kind = 59 and base = 2 and domain = TComplexDomain() and realKind = 47 and extended = false
or
// _Complex _Float64x
kind = 60 and base = 2 and domain = TComplexDomain() and realKind = 48 and extended = true
or
// _Complex _Float128
kind = 61 and base = 2 and domain = TComplexDomain() and realKind = 49 and extended = false
}
/**

View File

@@ -306,13 +306,15 @@ private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.get
* }
* };
* ```
* Note: the C++ front-end often automatically desugars `field` to
* `this->field`, so most accesses of `this->field` are instances
* of `PointerFieldAccess` (with `ThisExpr` as the qualifier), not
* `ImplicitThisFieldAccess`.
*/
class ImplicitThisFieldAccess extends FieldAccess {
override string getAPrimaryQlClass() { result = "ImplicitThisFieldAccess" }
ImplicitThisFieldAccess() {
this.getQualifier().(ThisExpr).isCompilerGenerated() or not exists(this.getQualifier())
}
ImplicitThisFieldAccess() { not exists(this.getQualifier()) }
}
/**
@@ -330,7 +332,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
// access without a qualifier. The only other unqualified field accesses it
// emits are for compiler-generated constructors and destructors. When we
// filter those out, there are only pointer-to-field literals left.
not this.isCompilerGenerated() and not exists(this.getQualifier())
not this.isCompilerGenerated()
}
override predicate isConstant() { any() }

View File

@@ -1,15 +0,0 @@
/**
* INTERNAL: Do not use. Provides predicates for getting the CodeQL and frontend
* version used during database extraction.
*/
/** Get the extractor CodeQL version */
string getExtractorCodeQLVersion() { extractor_version(result, _) }
/** Get the extractor frontend version */
string getExtractorFrontendVersion() { extractor_version(_, result) }
predicate isExtractorFrontendVersion65OrHigher() {
// Version numbers we not included in the database before 6.5.
exists(getExtractorCodeQLVersion())
}

View File

@@ -31,11 +31,6 @@ abstract class MustFlowConfiguration extends string {
*/
abstract predicate isSink(Operand sink);
/**
* Holds if data flow through `instr` is prohibited.
*/
predicate isBarrier(Instruction instr) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
@@ -53,21 +48,18 @@ abstract class MustFlowConfiguration extends string {
*/
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
this.isSource(source.getInstruction()) and
source.getASuccessor*() = sink
source.getASuccessor+() = sink
}
}
/** Holds if `node` flows from a source. */
pragma[nomagic]
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
not config.isBarrier(node) and
(
config.isSource(node)
or
exists(Instruction mid |
step(mid, node, config) and
flowsFromSource(mid, pragma[only_bind_into](config))
)
config.isSource(node)
or
exists(Instruction mid |
step(mid, node, config) and
flowsFromSource(mid, pragma[only_bind_into](config))
)
}

View File

@@ -7,12 +7,9 @@ private import DataFlowImplCommon as DataFlowImplCommon
/**
* Gets a function that might be called by `call`.
*
* This predicate does not take additional call targets
* from `AdditionalCallTarget` into account.
*/
cached
DataFlowCallable defaultViableCallable(DataFlowCall call) {
DataFlowCallable viableCallable(DataFlowCall call) {
DataFlowImplCommon::forceCachingInSameStage() and
result = call.getStaticCallTarget()
or
@@ -32,17 +29,6 @@ DataFlowCallable defaultViableCallable(DataFlowCall call) {
result = 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 = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
}
/**
* Provides virtual dispatch support compatible with the original
* implementation of `semmle.code.cpp.security.TaintTracking`.

View File

@@ -14,7 +14,6 @@ private import DataFlowPrivate
private import ModelUtil
private import SsaInternals as Ssa
private import DataFlowImplCommon as DataFlowImplCommon
private import codeql.util.Unit
/**
* The IR dataflow graph consists of the following nodes:
@@ -44,12 +43,11 @@ private newtype TIRDataFlowNode =
TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) {
Ssa::isModifiableByCall(operand, indirectionIndex)
} or
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
TRawIndirectOperand(Operand op, int indirectionIndex) {
Ssa::hasRawIndirectOperand(op, indirectionIndex)
} or
TRawIndirectInstruction0(Node0Impl node, int indirectionIndex) {
not exists(node.asOperand()) and
Ssa::hasRawIndirectInstruction(node.asInstruction(), indirectionIndex)
TRawIndirectInstruction(Instruction instr, int indirectionIndex) {
Ssa::hasRawIndirectInstruction(instr, indirectionIndex)
} or
TFinalParameterNode(Parameter p, int indirectionIndex) {
exists(Ssa::FinalParameterUse use |
@@ -919,146 +917,48 @@ Type getTypeImpl(Type t, int indirectionIndex) {
result instanceof UnknownType
}
private module RawIndirectNodes {
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an operand in the IR
* after `index` number of loads.
*/
private class RawIndirectOperand0 extends Node, TRawIndirectOperand0 {
Node0Impl node;
int indirectionIndex;
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an operand in the IR
* after `index` number of loads.
*/
class RawIndirectOperand extends Node, TRawIndirectOperand {
Operand operand;
int indirectionIndex;
RawIndirectOperand0() { this = TRawIndirectOperand0(node, indirectionIndex) }
RawIndirectOperand() { this = TRawIndirectOperand(operand, indirectionIndex) }
/** Gets the underlying instruction. */
Operand getOperand() { result = node.asOperand() }
/** Gets the underlying instruction. */
Operand getOperand() { result = operand }
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
override Declaration getFunction() {
result = this.getOperand().getDef().getEnclosingFunction()
}
override Declaration getFunction() { result = this.getOperand().getDef().getEnclosingFunction() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override DataFlowType getType() {
exists(int sub, DataFlowType type, boolean isGLValue |
type = getOperandType(this.getOperand(), isGLValue) and
if isGLValue = true then sub = 1 else sub = 0
|
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
)
}
final override Location getLocationImpl() {
if exists(this.getOperand().getLocation())
then result = this.getOperand().getLocation()
else result instanceof UnknownDefaultLocation
}
override string toStringImpl() {
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
}
override DataFlowType getType() {
exists(int sub, DataFlowType type, boolean isGLValue |
type = getOperandType(operand, isGLValue) and
if isGLValue = true then sub = 1 else sub = 0
|
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
)
}
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an instruction in the IR
* after `index` number of loads.
*/
private class RawIndirectInstruction0 extends Node, TRawIndirectInstruction0 {
Node0Impl node;
int indirectionIndex;
RawIndirectInstruction0() { this = TRawIndirectInstruction0(node, indirectionIndex) }
/** Gets the underlying instruction. */
Instruction getInstruction() { result = node.asInstruction() }
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override DataFlowType getType() {
exists(int sub, DataFlowType type, boolean isGLValue |
type = getInstructionType(this.getInstruction(), isGLValue) and
if isGLValue = true then sub = 1 else sub = 0
|
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
)
}
final override Location getLocationImpl() {
if exists(this.getInstruction().getLocation())
then result = this.getInstruction().getLocation()
else result instanceof UnknownDefaultLocation
}
override string toStringImpl() {
result = instructionNode(this.getInstruction()).toStringImpl() + " indirection"
}
final override Location getLocationImpl() {
if exists(this.getOperand().getLocation())
then result = this.getOperand().getLocation()
else result instanceof UnknownDefaultLocation
}
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an operand in the IR
* after a number of loads.
*/
class RawIndirectOperand extends Node {
int indirectionIndex;
Operand operand;
RawIndirectOperand() {
exists(Node0Impl node | operand = node.asOperand() |
this = TRawIndirectOperand0(node, indirectionIndex)
or
this = TRawIndirectInstruction0(node, indirectionIndex)
)
}
/** Gets the operand associated with this node. */
Operand getOperand() { result = operand }
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
}
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an instruction in the IR
* after a number of loads.
*/
class RawIndirectInstruction extends Node {
int indirectionIndex;
Instruction instr;
RawIndirectInstruction() {
exists(Node0Impl node | instr = node.asInstruction() |
this = TRawIndirectOperand0(node, indirectionIndex)
or
this = TRawIndirectInstruction0(node, indirectionIndex)
)
}
/** Gets the instruction associated with this node. */
Instruction getInstruction() { result = instr }
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
override string toStringImpl() {
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
}
}
import RawIndirectNodes
/**
* INTERNAL: do not use.
*
@@ -1120,6 +1020,48 @@ class UninitializedNode extends Node {
LocalVariable getLocalVariable() { result = v }
}
/**
* INTERNAL: Do not use.
*
* A node that represents the indirect value of an instruction in the IR
* after `index` number of loads.
*/
class RawIndirectInstruction extends Node, TRawIndirectInstruction {
Instruction instr;
int indirectionIndex;
RawIndirectInstruction() { this = TRawIndirectInstruction(instr, indirectionIndex) }
/** Gets the underlying instruction. */
Instruction getInstruction() { result = instr }
/** Gets the underlying indirection index. */
int getIndirectionIndex() { result = indirectionIndex }
override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override DataFlowType getType() {
exists(int sub, DataFlowType type, boolean isGLValue |
type = getInstructionType(instr, isGLValue) and
if isGLValue = true then sub = 1 else sub = 0
|
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
)
}
final override Location getLocationImpl() {
if exists(this.getInstruction().getLocation())
then result = this.getInstruction().getLocation()
else result instanceof UnknownDefaultLocation
}
override string toStringImpl() {
result = instructionNode(this.getInstruction()).toStringImpl() + " indirection"
}
}
private module GetConvertedResultExpression {
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
@@ -1657,29 +1599,26 @@ private module Cached {
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
private predicate indirectionOperandFlow(RawIndirectOperand nodeFrom, Node nodeTo) {
nodeFrom != nodeTo and
(
// Reduce the indirection count by 1 if we're passing through a `LoadInstruction`.
exists(int ind, LoadInstruction load |
hasOperandAndIndex(nodeFrom, load.getSourceAddressOperand(), ind) and
nodeHasInstruction(nodeTo, load, ind - 1)
)
or
// If an operand flows to an instruction, then the indirection of
// the operand also flows to the indirection of the instruction.
exists(Operand operand, Instruction instr, int indirectionIndex |
simpleInstructionLocalFlowStep(operand, instr) and
hasOperandAndIndex(nodeFrom, operand, pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeTo, instr, pragma[only_bind_into](indirectionIndex))
)
or
// If there's indirect flow to an operand, then there's also indirect
// flow to the operand after applying some pointer arithmetic.
exists(PointerArithmeticInstruction pointerArith, int indirectionIndex |
hasOperandAndIndex(nodeFrom, pointerArith.getAnOperand(),
pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeTo, pointerArith, pragma[only_bind_into](indirectionIndex))
)
// Reduce the indirection count by 1 if we're passing through a `LoadInstruction`.
exists(int ind, LoadInstruction load |
hasOperandAndIndex(nodeFrom, load.getSourceAddressOperand(), ind) and
nodeHasInstruction(nodeTo, load, ind - 1)
)
or
// If an operand flows to an instruction, then the indirection of
// the operand also flows to the indirection of the instruction.
exists(Operand operand, Instruction instr, int indirectionIndex |
simpleInstructionLocalFlowStep(operand, instr) and
hasOperandAndIndex(nodeFrom, operand, pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeTo, instr, pragma[only_bind_into](indirectionIndex))
)
or
// If there's indirect flow to an operand, then there's also indirect
// flow to the operand after applying some pointer arithmetic.
exists(PointerArithmeticInstruction pointerArith, int indirectionIndex |
hasOperandAndIndex(nodeFrom, pointerArith.getAnOperand(),
pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeTo, pointerArith, pragma[only_bind_into](indirectionIndex))
)
}
@@ -1705,7 +1644,6 @@ private module Cached {
private predicate indirectionInstructionFlow(
RawIndirectInstruction nodeFrom, IndirectOperand nodeTo
) {
nodeFrom != nodeTo and
// If there's flow from an instruction to an operand, then there's also flow from the
// indirect instruction to the indirect operand.
exists(Operand operand, Instruction instr, int indirectionIndex |
@@ -2299,43 +2237,3 @@ module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardCheck
)
}
}
/**
* A unit class for adding additional call steps.
*
* Extend this class to add additional call steps to the data flow graph.
*
* For example, if the following subclass is added:
* ```ql
* class MyAdditionalCallTarget extends DataFlow::AdditionalCallTarget {
* override Function viableTarget(Call call) {
* call.getTarget().hasName("f") and
* result.hasName("g")
* }
* }
* ```
* then flow from `source()` to `x` in `sink(x)` is reported in the following example:
* ```cpp
* void sink(int);
* int source();
* void f(int);
*
* void g(int x) {
* sink(x);
* }
*
* void test() {
* int x = source();
* f(x);
* }
* ```
*
* Note: To prevent reevaluation of cached dataflow-related predicates any
* subclass of `AdditionalCallTarget` must be imported in all dataflow queries.
*/
class AdditionalCallTarget extends Unit {
/**
* Gets a viable target for `call`.
*/
abstract DataFlowCallable viableTarget(Call call);
}

View File

@@ -31,35 +31,26 @@ DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
)
}
/**
* Gets the node that represents the output of `call` with kind `output` at
* indirection index `indirectionIndex`.
*/
private Node callOutputWithIndirectionIndex(
CallInstruction call, FunctionOutput output, int indirectionIndex
) {
// The return value
simpleOutNode(result, call) and
output.isReturnValue() and
indirectionIndex = 0
or
// The side effect of a call on the value pointed to by an argument or qualifier
exists(int index |
result.(IndirectArgumentOutNode).getArgumentIndex() = index and
result.(IndirectArgumentOutNode).getIndirectionIndex() = indirectionIndex - 1 and
result.(IndirectArgumentOutNode).getCallInstruction() = call and
output.isParameterDerefOrQualifierObject(index, indirectionIndex - 1)
)
or
result = getIndirectReturnOutNode(call, indirectionIndex) and
output.isReturnValueDeref(indirectionIndex)
}
/**
* Gets the instruction that holds the `output` for `call`.
*/
Node callOutput(CallInstruction call, FunctionOutput output) {
result = callOutputWithIndirectionIndex(call, output, _)
// The return value
simpleOutNode(result, call) and
output.isReturnValue()
or
// The side effect of a call on the value pointed to by an argument or qualifier
exists(int index, int indirectionIndex |
result.(IndirectArgumentOutNode).getArgumentIndex() = index and
result.(IndirectArgumentOutNode).getIndirectionIndex() = indirectionIndex and
result.(IndirectArgumentOutNode).getCallInstruction() = call and
output.isParameterDerefOrQualifierObject(index, indirectionIndex)
)
or
exists(int ind |
result = getIndirectReturnOutNode(call, ind) and
output.isReturnValueDeref(ind)
)
}
DataFlow::Node callInput(CallInstruction call, FunctionInput input, int d) {
@@ -85,15 +76,19 @@ private IndirectReturnOutNode getIndirectReturnOutNode(CallInstruction call, int
*/
bindingset[d]
Node callOutput(CallInstruction call, FunctionOutput output, int d) {
exists(DataFlow::Node n, int indirectionIndex |
n = callOutputWithIndirectionIndex(call, output, indirectionIndex) and d > 0
|
exists(DataFlow::Node n | n = callOutput(call, output) and d > 0 |
// The return value
result = callOutputWithIndirectionIndex(call, output, indirectionIndex + d)
result = getIndirectReturnOutNode(n.asInstruction(), d)
or
// If there isn't an indirect out node for the call with indirection `d` then
// we conflate this with the underlying `CallInstruction`.
not exists(getIndirectReturnOutNode(call, indirectionIndex + d)) and
not exists(getIndirectReturnOutNode(call, d)) and
n = result
or
// The side effect of a call on the value pointed to by an argument or qualifier
exists(Operand operand, int indirectionIndex |
Ssa::outNodeHasAddressAndIndex(n, operand, indirectionIndex) and
Ssa::outNodeHasAddressAndIndex(result, operand, indirectionIndex + d)
)
)
}

View File

@@ -228,7 +228,7 @@ private class PointerWrapperTypeIndirection extends Indirection instanceof Point
override predicate isAdditionalDereference(Instruction deref, Operand address) {
exists(CallInstruction call |
operandForFullyConvertedCall(getAUse(deref), call) and
this = call.getStaticCallTarget().getClassAndName(["operator*", "operator->", "get"]) and
this = call.getStaticCallTarget().getClassAndName("operator*") and
address = call.getThisArgumentOperand()
)
}

View File

@@ -1,6 +1,5 @@
private import cpp
import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.internal.ExtractorVersion
private import semmle.code.cpp.ir.IRConfiguration
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.implementation.internal.OperandTag
@@ -362,12 +361,6 @@ predicate ignoreLoad(Expr expr) {
or
expr instanceof FunctionAccess
or
// The load is duplicated from the operand.
isExtractorFrontendVersion65OrHigher() and expr instanceof ParenthesisExpr
or
// The load is duplicated from the right operand.
isExtractorFrontendVersion65OrHigher() and expr instanceof CommaExpr
or
expr.(PointerDereferenceExpr).getOperand().getFullyConverted().getType().getUnspecifiedType()
instanceof FunctionPointerType
or

View File

@@ -1,5 +1,4 @@
private import cpp
private import semmle.code.cpp.internal.ExtractorVersion
private import semmle.code.cpp.ir.implementation.IRType
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.implementation.internal.OperandTag
@@ -650,9 +649,7 @@ class TranslatedPrefixCrementOperation extends TranslatedCrementOperation {
override PrefixCrementOperation expr;
override Instruction getResult() {
// The following distinction is needed to work around extractor limitations
// in old versions of the extractor.
if expr.isPRValueCategory() and not isExtractorFrontendVersion65OrHigher()
if expr.isPRValueCategory()
then
// If this is C, then the result of a prefix crement is a prvalue for the
// new value assigned to the operand. If this is C++, then the result is
@@ -1507,9 +1504,7 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr {
}
final override Instruction getResult() {
// The following distinction is needed to work around extractor limitations
// in old versions of the extractor.
if expr.isPRValueCategory() and not isExtractorFrontendVersion65OrHigher()
if expr.isPRValueCategory()
then
// If this is C, then the result of an assignment is a prvalue for the new
// value assigned to the left operand. If this is C++, then the result is
@@ -1647,9 +1642,7 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr {
}
final override Instruction getResult() {
// The following distinction is needed to work around extractor limitations
// in old versions of the extractor.
if expr.isPRValueCategory() and not isExtractorFrontendVersion65OrHigher()
if expr.isPRValueCategory()
then
// If this is C, then the result of an assignment is a prvalue for the new
// value assigned to the left operand. If this is C++, then the result is
@@ -2198,16 +2191,8 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
not this.elseIsVoid() and tag = ConditionValueFalseStoreTag()
) and
opcode instanceof Opcode::Store and
if isExtractorFrontendVersion65OrHigher()
then
not expr.hasLValueToRValueConversion() and
resultType = this.getResultType()
or
expr.hasLValueToRValueConversion() and
resultType = getTypeForPRValue(expr.getType())
else resultType = this.getResultType()
resultType = this.getResultType()
or
(not expr.hasLValueToRValueConversion() or not isExtractorFrontendVersion65OrHigher()) and
tag = ConditionValueResultLoadTag() and
opcode instanceof Opcode::Load and
resultType = this.getResultType()
@@ -2237,16 +2222,8 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
)
or
tag = ConditionValueResultTempAddressTag() and
if isExtractorFrontendVersion65OrHigher()
then
not expr.hasLValueToRValueConversion() and
result = this.getInstruction(ConditionValueResultLoadTag())
or
expr.hasLValueToRValueConversion() and
result = this.getParent().getChildSuccessor(this)
else result = this.getInstruction(ConditionValueResultLoadTag())
result = this.getInstruction(ConditionValueResultLoadTag())
or
(not expr.hasLValueToRValueConversion() or not isExtractorFrontendVersion65OrHigher()) and
tag = ConditionValueResultLoadTag() and
result = this.getParent().getChildSuccessor(this)
)
@@ -2275,24 +2252,18 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
result = this.getElse().getResult()
)
or
(not expr.hasLValueToRValueConversion() or not isExtractorFrontendVersion65OrHigher()) and
tag = ConditionValueResultLoadTag() and
operandTag instanceof AddressOperandTag and
result = this.getInstruction(ConditionValueResultTempAddressTag())
(
operandTag instanceof AddressOperandTag and
result = this.getInstruction(ConditionValueResultTempAddressTag())
)
)
}
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
not this.resultIsVoid() and
tag = ConditionValueTempVar() and
if isExtractorFrontendVersion65OrHigher()
then
not expr.hasLValueToRValueConversion() and
type = this.getResultType()
or
expr.hasLValueToRValueConversion() and
type = getTypeForPRValue(expr.getType())
else type = this.getResultType()
type = this.getResultType()
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
@@ -2307,14 +2278,7 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
final override Instruction getResult() {
not this.resultIsVoid() and
if isExtractorFrontendVersion65OrHigher()
then
expr.hasLValueToRValueConversion() and
result = this.getInstruction(ConditionValueResultTempAddressTag())
or
not expr.hasLValueToRValueConversion() and
result = this.getInstruction(ConditionValueResultLoadTag())
else result = this.getInstruction(ConditionValueResultLoadTag())
result = this.getInstruction(ConditionValueResultLoadTag())
}
override Instruction getChildSuccessor(TranslatedElement child) {
@@ -3274,18 +3238,10 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) {
expr instanceof AssignExpr
or
expr instanceof AssignOperation and
(
not expr.isPRValueCategory() // is C++
or
isExtractorFrontendVersion65OrHigher()
)
not expr.isPRValueCategory() // is C++
or
expr instanceof PrefixCrementOperation and
(
not expr.isPRValueCategory() // is C++
or
isExtractorFrontendVersion65OrHigher()
)
not expr.isPRValueCategory() // is C++
or
// Because the load is on the `e` in `e++`.
expr instanceof PostfixCrementOperation

View File

@@ -19,7 +19,6 @@ private import implementations.Strtok
private import implementations.Strset
private import implementations.Strcrement
private import implementations.Strnextc
private import implementations.Strtol
private import implementations.StdContainer
private import implementations.StdPair
private import implementations.StdMap
@@ -35,7 +34,6 @@ private import implementations.Accept
private import implementations.Poll
private import implementations.Select
private import implementations.MySql
private import implementations.ODBC
private import implementations.SqLite3
private import implementations.PostgreSql
private import implementations.System

View File

@@ -5,7 +5,6 @@
*/
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.models.interfaces.Taint
/**
* An allocation function (such as `malloc`) that has an argument for the size
@@ -122,7 +121,7 @@ private class CallocAllocationFunction extends AllocationFunction {
* An allocation function (such as `realloc`) that has an argument for the size
* in bytes, and an argument for an existing pointer that is to be reallocated.
*/
private class ReallocAllocationFunction extends AllocationFunction, TaintFunction {
private class ReallocAllocationFunction extends AllocationFunction {
int sizeArg;
int reallocArg;
@@ -152,10 +151,6 @@ private class ReallocAllocationFunction extends AllocationFunction, TaintFunctio
override int getSizeArg() { result = sizeArg }
override int getReallocPtrArg() { result = reallocArg }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(this.getReallocPtrArg()) and output.isReturnValueDeref()
}
}
/**

View File

@@ -157,7 +157,7 @@ private class Getaddrinfo extends TaintFunction, ArrayFunction, RemoteFlowSource
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam in [0, 1] }
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
output.isParameterDeref(3, 2) and
output.isParameterDeref(3) and
description = "address returned by " + this.getName()
}
}

View File

@@ -9,17 +9,18 @@ import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
private class MemsetFunctionModel extends ArrayFunction, DataFlowFunction, AliasFunction,
/**
* The standard function `memset` and its assorted variants
*/
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
SideEffectFunction
{
MemsetFunctionModel() {
MemsetFunction() {
this.hasGlobalOrStdOrBslName("memset")
or
this.hasGlobalOrStdName("wmemset")
or
this.hasGlobalName([
bzero(), "__builtin_memset", "__builtin_memset_chk", "RtlZeroMemory", "RtlSecureZeroMemory"
])
this.hasGlobalName([bzero(), "__builtin_memset", "__builtin_memset_chk"])
}
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
@@ -59,8 +60,3 @@ private class MemsetFunctionModel extends ArrayFunction, DataFlowFunction, Alias
}
private string bzero() { result = ["bzero", "explicit_bzero"] }
/**
* The standard function `memset` and its assorted variants
*/
class MemsetFunction extends Function instanceof MemsetFunctionModel { }

View File

@@ -1,28 +0,0 @@
/**
* Provides implementation classes modeling the ODBC C/C++ API.
* See `semmle.code.cpp.models.Models` for usage information.
*/
private import semmle.code.cpp.models.interfaces.Sql
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
/**
* The `SQLExecDirect`, and `SQLPrepare` from the ODBC C/C++ API:
* https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlexecdirect-function?view=sql-server-ver16
* https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlprepare-function?view=sql-server-ver16
*
* Note, `SQLExecute` is not included because it operates on a SQLHSTMT type, not a string.
* The SQLHSTMT parameter for `SQLExecute` is set through a `SQLPrepare`, which is modeled.
* The other source of input to a `SQLExecute` is via a `SQLBindParameter`, which sanitizes user input,
* and would be considered a barrier to SQL injection.
*/
private class ODBCExecutionFunction extends SqlExecutionFunction {
ODBCExecutionFunction() { this.hasGlobalName(["SQLExecDirect", "SQLPrepare"]) }
override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
}
// NOTE: no need to define a barrier explicitly.
// `SQLBindParameter` is the typical means for sanitizing user input.
// https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function?view=sql-server-ver16
// First a query is establisehed via `SQLPrepare`, then parameters are bound via `SQLBindParameter`, before
// the query is executed via `SQLExecute`. We are not modeling SQLExecute, so we do not need to model SQLBindParameter.

View File

@@ -147,32 +147,19 @@ private class SnprintfImpl extends Snprintf {
/**
* The Microsoft `StringCchPrintf` function and variants.
* See: https://learn.microsoft.com/en-us/windows/win32/api/strsafe/
* and
* https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms860435(v=msdn.10)
*/
private class StringCchPrintf extends FormattingFunction {
StringCchPrintf() {
this instanceof TopLevelFunction and
exists(string baseName |
baseName in [
"StringCchPrintf", //StringCchPrintf(pszDest, cchDest, pszFormat, …)
"StringCchPrintfEx", //StringCchPrintfEx(pszDest,cchDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, ...);
"StringCchPrintf_l", //StringCchPrintf_l(pszDest, cbDest, pszFormat, locale, …)
"StringCchPrintf_lEx", //StringCchPrintf_lEx(pszDest, cchDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, locale, …)
"StringCbPrintf", //StringCbPrintf(pszDest, cbDest, pszFormat, …)
"StringCbPrintfEx", //StringCbPrintfEx(pszDest, cbDest, ppszDestEnd, pcbRemaining, dwFlags, pszFormat, …)
"StringCbPrintf_l", //StringCbPrintf_l(pszDest, cbDest, pszFormat, locale, …)
"StringCbPrintf_lEx" //StringCbPrintf_lEx(pszDest, cbDest, ppszDestEnd, pcbRemaining, dwFlags, pszFormat, locale, …)
]
|
this.hasGlobalName(baseName + ["", "A", "W"])
) and
this.hasGlobalName([
"StringCchPrintf", "StringCchPrintfEx", "StringCchPrintf_l", "StringCchPrintf_lEx",
"StringCbPrintf", "StringCbPrintfEx", "StringCbPrintf_l", "StringCbPrintf_lEx"
]) and
not exists(this.getDefinition().getFile().getRelativePath())
}
override int getFormatParameterIndex() {
if this.getName().matches("%Ex" + ["", "A", "W"]) then result = 5 else result = 2
if this.getName().matches("%Ex") then result = 5 else result = 2
}
override int getOutputParameterIndex(boolean isStream) { result = 0 and isStream = false }

View File

@@ -13,7 +13,7 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
PureStrFunction() {
this.hasGlobalOrStdOrBslName([
atoi(), "strcasestr", "strchnul", "strchr", "strchrnul", "strstr", "strpbrk", "strrchr",
"strspn", strrev(), strcmp(), strlwr(), strupr()
"strspn", strtol(), strrev(), strcmp(), strlwr(), strupr()
])
}
@@ -70,6 +70,8 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
private string atoi() { result = ["atof", "atoi", "atol", "atoll"] }
private string strtol() { result = ["strtod", "strtof", "strtol", "strtoll", "strtoq", "strtoul"] }
private string strlwr() {
result = ["_strlwr", "_wcslwr", "_mbslwr", "_strlwr_l", "_wcslwr_l", "_mbslwr_l"]
}

View File

@@ -58,7 +58,7 @@ private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, Rem
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
override predicate hasRemoteFlowSink(FunctionInput input, string description) {
input.isParameterDeref(1, 1) and description = "buffer sent by " + this.getName()
input.isParameterDeref(1) and description = "buffer sent by " + this.getName()
}
override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }

View File

@@ -32,8 +32,6 @@ private class Strtok extends ArrayFunction, AliasFunction, TaintFunction, SideEf
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and output.isReturnValue()
or
input.isParameterDeref(0) and output.isReturnValueDeref()
}
override predicate hasOnlySpecificReadSideEffects() { none() }

View File

@@ -1,54 +0,0 @@
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
private string strtol() { result = ["strtod", "strtof", "strtol", "strtoll", "strtoq", "strtoul"] }
/**
* The standard function `strtol` and its assorted variants
*/
private class Strtol extends AliasFunction, ArrayFunction, TaintFunction, SideEffectFunction {
Strtol() { this.hasGlobalOrStdOrBslName(strtol()) }
override predicate hasArrayInput(int bufParam) {
// All the functions given by `strtol()` takes a `const char*` input as the first parameter
bufParam = 0
}
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
(
input.isParameter(0)
or
input.isParameterDeref(0)
) and
output.isReturnValue()
or
input.isParameter(0) and
output.isParameterDeref(1)
}
override predicate parameterNeverEscapes(int i) {
// Parameter 0 does escape into parameter 1.
i = 1
}
override predicate parameterEscapesOnlyViaReturn(int i) { none() }
override predicate parameterIsAlwaysReturned(int i) { none() }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
i = 0 and
buffer = true
}
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 1 and buffer = false and mustWrite = false
}
}

View File

@@ -8,7 +8,7 @@ import semmle.code.cpp.Parameter
private newtype TFunctionInput =
TInParameter(ParameterIndex i) or
TInParameterDeref(ParameterIndex i, int indirectionIndex) { indirectionIndex = [1, 2] } or
TInParameterDeref(ParameterIndex i) or
TInQualifierObject() or
TInQualifierAddress() or
TInReturnValueDeref()
@@ -245,18 +245,15 @@ class InParameter extends FunctionInput, TInParameter {
*/
class InParameterDeref extends FunctionInput, TInParameterDeref {
ParameterIndex index;
int indirectionIndex;
InParameterDeref() { this = TInParameterDeref(index, indirectionIndex) }
InParameterDeref() { this = TInParameterDeref(index) }
override string toString() { result = "InParameterDeref " + index.toString() }
/** Gets the zero-based index of the parameter. */
ParameterIndex getIndex() { result = index }
override predicate isParameterDeref(ParameterIndex i, int indirection) {
i = index and indirectionIndex = indirection
}
override predicate isParameterDeref(ParameterIndex i) { i = index }
}
/**
@@ -324,10 +321,10 @@ class InReturnValueDeref extends FunctionInput, TInReturnValueDeref {
}
private newtype TFunctionOutput =
TOutParameterDeref(ParameterIndex i, int indirectionIndex) { indirectionIndex = [1, 2] } or
TOutParameterDeref(ParameterIndex i) or
TOutQualifierObject() or
TOutReturnValue() or
TOutReturnValueDeref(int indirections) { indirections = [1, 2] }
TOutReturnValueDeref()
/**
* An output from a function. This can be:
@@ -501,16 +498,17 @@ class FunctionOutput extends TFunctionOutput {
*/
class OutParameterDeref extends FunctionOutput, TOutParameterDeref {
ParameterIndex index;
int indirectionIndex;
OutParameterDeref() { this = TOutParameterDeref(index, indirectionIndex) }
OutParameterDeref() { this = TOutParameterDeref(index) }
override string toString() { result = "OutParameterDeref " + index.toString() }
ParameterIndex getIndex() { result = index }
override predicate isParameterDeref(ParameterIndex i) { i = index }
override predicate isParameterDeref(ParameterIndex i, int ind) {
i = index and ind = indirectionIndex
this.isParameterDeref(i) and ind = 1
}
}
@@ -574,8 +572,4 @@ class OutReturnValueDeref extends FunctionOutput, TOutReturnValueDeref {
override string toString() { result = "OutReturnValueDeref" }
override predicate isReturnValueDeref() { any() }
override predicate isReturnValueDeref(int indirectionIndex) {
this = TOutReturnValueDeref(indirectionIndex)
}
}

View File

@@ -17,7 +17,9 @@ private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
* `upper` is true, and can be traced back to a guard represented by `reason`.
*/
predicate bounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
exists(SemanticExprConfig::Expr semExpr | semExpr.getUnconvertedResultExpression() = e |
exists(SemanticExprConfig::Expr semExpr |
semExpr.getUnconverted().getUnconvertedResultExpression() = e
|
semBounded(semExpr, b, delta, upper, reason)
)
}
@@ -28,7 +30,9 @@ predicate bounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
* The `Expr` may be a conversion.
*/
predicate convertedBounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
exists(SemanticExprConfig::Expr semExpr | semExpr.getConvertedResultExpression() = e |
exists(SemanticExprConfig::Expr semExpr |
semExpr.getConverted().getConvertedResultExpression() = e
|
semBounded(semExpr, b, delta, upper, reason)
)
}

View File

@@ -100,7 +100,7 @@ predicate exprMightOverflowNegatively(Expr expr) {
lowerBound(expr) < exprMinVal(expr)
or
exists(SemanticExprConfig::Expr semExpr |
semExpr.getAst() = expr and
semExpr.getUnconverted().getAst() = expr and
ConstantStage::potentiallyOverflowingExpr(false, semExpr) and
not ConstantStage::initialBounded(semExpr, _, _, false, _, _, _)
)
@@ -126,7 +126,7 @@ predicate exprMightOverflowPositively(Expr expr) {
upperBound(expr) > exprMaxVal(expr)
or
exists(SemanticExprConfig::Expr semExpr |
semExpr.getAst() = expr and
semExpr.getUnconverted().getAst() = expr and
ConstantStage::potentiallyOverflowingExpr(true, semExpr) and
not ConstantStage::initialBounded(semExpr, _, _, true, _, _, _)
)

View File

@@ -12,6 +12,9 @@ class SemBasicBlock extends Specific::BasicBlock {
/** Holds if this block (transitively) dominates `otherblock`. */
final predicate bbDominates(SemBasicBlock otherBlock) { Specific::bbDominates(this, otherBlock) }
/** Holds if this block has dominance information. */
final predicate hasDominanceInformation() { Specific::hasDominanceInformation(this) }
/** Gets an expression that is evaluated in this basic block. */
final SemExpr getAnExpr() { result.getBasicBlock() = this }

View File

@@ -4,7 +4,6 @@
private import Semantic
private import SemanticExprSpecific::SemanticExprConfig as Specific
private import SemanticType
/**
* An language-neutral expression.
@@ -242,21 +241,8 @@ class SemConvertExpr extends SemUnaryExpr {
SemConvertExpr() { opcode instanceof Opcode::Convert }
}
private import semmle.code.cpp.ir.IR as IR
/** A conversion instruction which is guaranteed to not overflow. */
private class SafeConversion extends IR::ConvertInstruction {
SafeConversion() {
exists(SemType tFrom, SemType tTo |
tFrom = getSemanticType(super.getUnary().getResultIRType()) and
tTo = getSemanticType(super.getResultIRType()) and
conversionCannotOverflow(tFrom, tTo)
)
}
}
class SemCopyValueExpr extends SemUnaryExpr {
SemCopyValueExpr() { opcode instanceof Opcode::CopyValue or this instanceof SafeConversion }
SemCopyValueExpr() { opcode instanceof Opcode::CopyValue }
}
class SemNegateExpr extends SemUnaryExpr {

View File

@@ -12,10 +12,87 @@ private import semmle.code.cpp.ir.ValueNumbering
module SemanticExprConfig {
class Location = Cpp::Location;
/** A `ConvertInstruction` or a `CopyValueInstruction`. */
private class Conversion extends IR::UnaryInstruction {
Conversion() {
this instanceof IR::CopyValueInstruction
or
this instanceof IR::ConvertInstruction
}
/** Holds if this instruction converts a value of type `tFrom` to a value of type `tTo`. */
predicate converts(SemType tFrom, SemType tTo) {
tFrom = getSemanticType(this.getUnary().getResultIRType()) and
tTo = getSemanticType(this.getResultIRType())
}
}
/**
* Gets a conversion-like instruction that consumes `op`, and
* which is guaranteed to not overflow.
*/
private IR::Instruction safeConversion(IR::Operand op) {
exists(Conversion conv, SemType tFrom, SemType tTo |
conv.converts(tFrom, tTo) and
conversionCannotOverflow(tFrom, tTo) and
conv.getUnaryOperand() = op and
result = conv
)
}
/** Holds if `i1 = i2` or if `i2` is a safe conversion that consumes `i1`. */
private predicate idOrSafeConversion(IR::Instruction i1, IR::Instruction i2) {
not i1.getResultIRType() instanceof IR::IRVoidType and
(
i1 = i2
or
i2 = safeConversion(i1.getAUse()) and
i1.getBlock() = i2.getBlock()
)
}
module Equiv = QlBuiltins::EquivalenceRelation<IR::Instruction, idOrSafeConversion/2>;
/**
* The expressions on which we perform range analysis.
*/
class Expr = IR::Instruction;
class Expr extends Equiv::EquivalenceClass {
/** Gets the n'th instruction in this equivalence class. */
private IR::Instruction getInstruction(int n) {
result =
rank[n + 1](IR::Instruction instr, int i, IR::IRBlock block |
this = Equiv::getEquivalenceClass(instr) and block.getInstruction(i) = instr
|
instr order by i
)
}
/** Gets a textual representation of this element. */
string toString() { result = this.getUnconverted().toString() }
/** Gets the basic block of this expression. */
IR::IRBlock getBlock() { result = this.getUnconverted().getBlock() }
/** Gets the unconverted instruction associated with this expression. */
IR::Instruction getUnconverted() { result = this.getInstruction(0) }
/**
* Gets the final instruction associated with this expression. This
* represents the result after applying all the safe conversions.
*/
IR::Instruction getConverted() {
exists(int n |
result = this.getInstruction(n) and
not exists(this.getInstruction(n + 1))
)
}
/** Gets the type of the result produced by this instruction. */
IR::IRType getResultIRType() { result = this.getConverted().getResultIRType() }
/** Gets the location of the source code for this expression. */
Location getLocation() { result = this.getUnconverted().getLocation() }
}
SemBasicBlock getExprBasicBlock(Expr e) { result = getSemanticBasicBlock(e.getBlock()) }
@@ -62,12 +139,12 @@ module SemanticExprConfig {
predicate stringLiteral(Expr expr, SemType type, string value) {
anyConstantExpr(expr, type, value) and
expr instanceof IR::StringConstantInstruction
expr.getUnconverted() instanceof IR::StringConstantInstruction
}
predicate binaryExpr(Expr expr, Opcode opcode, SemType type, Expr leftOperand, Expr rightOperand) {
exists(IR::BinaryInstruction instr |
instr = expr and
instr = expr.getUnconverted() and
type = getSemanticType(instr.getResultIRType()) and
leftOperand = getSemanticExpr(instr.getLeft()) and
rightOperand = getSemanticExpr(instr.getRight()) and
@@ -77,14 +154,14 @@ module SemanticExprConfig {
}
predicate unaryExpr(Expr expr, Opcode opcode, SemType type, Expr operand) {
exists(IR::UnaryInstruction instr | instr = expr |
exists(IR::UnaryInstruction instr | instr = expr.getUnconverted() |
type = getSemanticType(instr.getResultIRType()) and
operand = getSemanticExpr(instr.getUnary()) and
// REVIEW: Merge the two operand types.
opcode.toString() = instr.getOpcode().toString()
)
or
exists(IR::StoreInstruction instr | instr = expr |
exists(IR::StoreInstruction instr | instr = expr.getUnconverted() |
type = getSemanticType(instr.getResultIRType()) and
operand = getSemanticExpr(instr.getSourceValue()) and
opcode instanceof Opcode::Store
@@ -93,13 +170,13 @@ module SemanticExprConfig {
predicate nullaryExpr(Expr expr, Opcode opcode, SemType type) {
exists(IR::LoadInstruction load |
load = expr and
load = expr.getUnconverted() and
type = getSemanticType(load.getResultIRType()) and
opcode instanceof Opcode::Load
)
or
exists(IR::InitializeParameterInstruction init |
init = expr and
init = expr.getUnconverted() and
type = getSemanticType(init.getResultIRType()) and
opcode instanceof Opcode::InitializeParameter
)
@@ -122,6 +199,8 @@ module SemanticExprConfig {
dominator.dominates(dominated)
}
predicate hasDominanceInformation(BasicBlock block) { any() }
private predicate id(Cpp::Locatable x, Cpp::Locatable y) { x = y }
private predicate idOf(Cpp::Locatable x, int y) = equivalenceRelation(id/2)(x, y)
@@ -130,7 +209,17 @@ module SemanticExprConfig {
newtype TSsaVariable =
TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() }
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() } or
TSsaPointerArithmeticGuard(ValueNumber instr) {
exists(Guard g, IR::Operand use |
use = instr.getAUse() and use.getIRType() instanceof IR::IRAddressType
|
g.comparesLt(use, _, _, _, _) or
g.comparesLt(_, use, _, _, _) or
g.comparesEq(use, _, _, _, _) or
g.comparesEq(_, use, _, _, _)
)
}
class SsaVariable extends TSsaVariable {
string toString() { none() }
@@ -139,6 +228,8 @@ module SemanticExprConfig {
IR::Instruction asInstruction() { none() }
ValueNumber asPointerArithGuard() { none() }
IR::Operand asOperand() { none() }
}
@@ -154,6 +245,18 @@ module SemanticExprConfig {
final override IR::Instruction asInstruction() { result = instr }
}
class SsaPointerArithmeticGuard extends SsaVariable, TSsaPointerArithmeticGuard {
ValueNumber vn;
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(vn) }
final override string toString() { result = vn.toString() }
final override Location getLocation() { result = vn.getLocation() }
final override ValueNumber asPointerArithGuard() { result = vn }
}
class SsaOperand extends SsaVariable, TSsaOperand {
IR::Operand op;
@@ -186,7 +289,11 @@ module SemanticExprConfig {
)
}
Expr getAUse(SsaVariable v) { result.(IR::LoadInstruction).getSourceValue() = v.asInstruction() }
Expr getAUse(SsaVariable v) {
result.getUnconverted().(IR::LoadInstruction).getSourceValue() = v.asInstruction()
or
result.getUnconverted() = v.asPointerArithGuard().getAnInstruction()
}
SemType getSsaVariableType(SsaVariable v) {
result = getSemanticType(v.asInstruction().getResultIRType())
@@ -225,7 +332,10 @@ module SemanticExprConfig {
final override Location getLocation() { result = block.getLocation() }
final override predicate hasRead(SsaVariable v) {
exists(IR::Operand operand | operand.getDef() = v.asInstruction() |
exists(IR::Operand operand |
operand.getDef() = v.asInstruction() or
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
|
not operand instanceof IR::PhiInputOperand and
operand.getUse().getBlock() = block
)
@@ -243,7 +353,10 @@ module SemanticExprConfig {
final override Location getLocation() { result = succ.getLocation() }
final override predicate hasRead(SsaVariable v) {
exists(IR::PhiInputOperand operand | operand.getDef() = v.asInstruction() |
exists(IR::PhiInputOperand operand |
operand.getDef() = v.asInstruction() or
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
|
operand.getPredecessorBlock() = pred and
operand.getUse().getBlock() = succ
)
@@ -320,7 +433,7 @@ module SemanticExprConfig {
}
/** Gets the expression associated with `instr`. */
SemExpr getSemanticExpr(IR::Instruction instr) { result = instr }
SemExpr getSemanticExpr(IR::Instruction instr) { result = Equiv::getEquivalenceClass(instr) }
}
predicate getSemanticExpr = SemanticExprConfig::getSemanticExpr/1;

View File

@@ -35,4 +35,32 @@ predicate semImplies_v2(SemGuard g1, boolean b1, SemGuard g2, boolean b2) {
Specific::implies_v2(g1, b1, g2, b2)
}
/**
* Holds if `guard` directly controls the position `controlled` with the
* value `testIsTrue`.
*/
pragma[nomagic]
predicate semGuardDirectlyControlsSsaRead(
SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue
) {
guard.directlyControls(controlled.(SemSsaReadPositionBlock).getBlock(), testIsTrue)
or
exists(SemSsaReadPositionPhiInputEdge controlledEdge | controlledEdge = controlled |
guard.directlyControls(controlledEdge.getOrigBlock(), testIsTrue) or
guard.hasBranchEdge(controlledEdge.getOrigBlock(), controlledEdge.getPhiBlock(), testIsTrue)
)
}
/**
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
*/
predicate semGuardControlsSsaRead(SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue) {
semGuardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
or
exists(SemGuard guard0, boolean testIsTrue0 |
semImplies_v2(guard0, testIsTrue0, guard, testIsTrue) and
semGuardControlsSsaRead(guard0, controlled, testIsTrue0)
)
}
SemGuard semGetComparisonGuard(SemRelationalExpr e) { result = Specific::comparisonGuard(e) }

View File

@@ -8,18 +8,6 @@ class SemLocation instanceof Location {
*/
string toString() { result = super.toString() }
/** Gets the 1-based line number (inclusive) where this location starts. */
int getStartLine() { result = super.getStartLine() }
/** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn() { result = super.getStartColumn() }
/** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine() { result = super.getEndLine() }
/** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn() { result = super.getEndColumn() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to

View File

@@ -23,8 +23,6 @@ class SemSsaExplicitUpdate extends SemSsaVariable {
SemSsaExplicitUpdate() { Specific::explicitUpdate(this, sourceExpr) }
final SemExpr getSourceExpr() { result = sourceExpr }
final SemExpr getDefiningExpr() { result = sourceExpr }
}
class SemSsaPhiNode extends SemSsaVariable {
@@ -63,3 +61,36 @@ class SemSsaReadPositionBlock extends SemSsaReadPosition {
SemExpr getAnExpr() { result = this.getBlock().getAnExpr() }
}
/**
* Holds if `inp` is an input to `phi` along a back edge.
*/
predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge) {
edge.phiInput(phi, inp) and
// Conservatively assume that every edge is a back edge if we don't have dominance information.
(
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
irreducibleSccEdge(edge.getOrigBlock(), phi.getBasicBlock()) or
not edge.getOrigBlock().hasDominanceInformation()
)
}
/**
* Holds if the edge from b1 to b2 is part of a multiple-entry cycle in an irreducible control flow
* graph.
*
* An ireducible control flow graph is one where the usual dominance-based back edge detection does
* not work, because there is a cycle with multiple entry points, meaning there are
* mutually-reachable basic blocks where neither dominates the other. For such a graph, we first
* remove all detectable back-edges using the normal condition that the predecessor block is
* dominated by the successor block, then mark all edges in a cycle in the resulting graph as back
* edges.
*/
private predicate irreducibleSccEdge(SemBasicBlock b1, SemBasicBlock b2) {
trimmedEdge(b1, b2) and trimmedEdge+(b2, b1)
}
private predicate trimmedEdge(SemBasicBlock pred, SemBasicBlock succ) {
pred.getASuccessor() = succ and
not succ.bbDominates(pred)
}

View File

@@ -1,7 +1,5 @@
private import RangeAnalysisStage
private import RangeAnalysisImpl
private import codeql.rangeanalysis.RangeAnalysis
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExpr
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticType
module FloatDelta implements DeltaSig {
class Delta = float;
@@ -22,7 +20,7 @@ module FloatDelta implements DeltaSig {
Delta fromFloat(float f) { result = f }
}
module FloatOverflow implements OverflowSig<Sem, FloatDelta> {
module FloatOverflow implements OverflowSig<FloatDelta> {
predicate semExprDoesNotOverflow(boolean positively, SemExpr expr) {
exists(float lb, float ub, float delta |
typeBounds(expr.getSemType(), lb, ub) and

View File

@@ -0,0 +1,29 @@
private import RangeAnalysisStage
module IntDelta implements DeltaSig {
class Delta = int;
bindingset[d]
bindingset[result]
float toFloat(Delta d) { result = d }
bindingset[d]
bindingset[result]
int toInt(Delta d) { result = d }
bindingset[n]
bindingset[result]
Delta fromInt(int n) { result = n }
bindingset[f]
Delta fromFloat(float f) {
result =
min(float diff, float res |
diff = (res - f) and res = f.ceil()
or
diff = (f - res) and res = f.floor()
|
res order by diff
)
}
}

View File

@@ -0,0 +1,326 @@
/**
* Provides inferences of the form: `e` equals `b + v` modulo `m` where `e` is
* an expression, `b` is a `Bound` (typically zero or the value of an SSA
* variable), and `v` is an integer in the range `[0 .. m-1]`.
*/
/*
* The main recursion has base cases in both `ssaModulus` (for guarded reads) and `semExprModulus`
* (for constant values). The most interesting recursive case is `phiModulusRankStep`, which
* handles phi inputs.
*/
private import ModulusAnalysisSpecific::Private
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import ConstantAnalysis
private import RangeUtils
private import RangeAnalysisStage
module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
pragma[nomagic]
private predicate valueFlowStepSsaEqFlowCond(
SemSsaReadPosition pos, SemSsaVariable v, SemExpr e, int delta
) {
exists(SemGuard guard, boolean testIsTrue |
guard = U::semEqFlowCond(v, e, D::fromInt(delta), true, testIsTrue) and
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
)
}
/**
* Holds if `e + delta` equals `v` at `pos`.
*/
pragma[nomagic]
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
U::semSsaUpdateStep(v, e, D::fromInt(delta)) and pos.hasReadOfVar(v)
or
pos.hasReadOfVar(v) and
valueFlowStepSsaEqFlowCond(pos, v, e, delta)
}
/**
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
* `ConstantIntegerExpr`s.
*/
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
exists(SemAddExpr a | a = add |
larg = a.getLeftOperand() and
rarg = a.getRightOperand()
) and
not larg instanceof SemConstantIntegerExpr and
not rarg instanceof SemConstantIntegerExpr
}
/**
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
* a `ConstantIntegerExpr`.
*/
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
exists(SemSubExpr s | s = sub |
larg = s.getLeftOperand() and
rarg = s.getRightOperand()
) and
not rarg instanceof SemConstantIntegerExpr
}
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
private SemExpr modExpr(SemExpr arg, int mod) {
exists(SemRemExpr rem |
result = rem and
arg = rem.getLeftOperand() and
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
mod >= 2
)
or
exists(SemConstantIntegerExpr c |
mod = 2.pow([1 .. 30]) and
c.getIntValue() = mod - 1 and
result.(SemBitAndExpr).hasOperands(arg, c)
)
}
/**
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
* its `testIsTrue` branch.
*/
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
result.isEquality(rem, c, polarity) and
c.getIntValue() = r and
rem = modExpr(v.getAUse(), mod) and
(
testIsTrue = polarity and val = r
or
testIsTrue = polarity.booleanNot() and
mod = 2 and
val = 1 - r and
(r = 0 or r = 1)
)
)
}
/**
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
*/
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
exists(SemGuard guard, boolean testIsTrue |
pos.hasReadOfVar(v) and
guard = moduloCheck(v, val, mod, testIsTrue) and
semGuardControlsSsaRead(guard, pos, testIsTrue)
)
}
/** Holds if `factor` is a power of 2 that divides `mask`. */
bindingset[mask]
private predicate andmaskFactor(int mask, int factor) {
mask % factor = 0 and
factor = 2.pow([1 .. 30])
}
/** Holds if `e` is evenly divisible by `factor`. */
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
or
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
or
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
)
}
/**
* Gets the remainder of `val` modulo `mod`.
*
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
* the range `[0 .. mod-1]`.
*/
bindingset[val, mod]
private int remainder(int val, int mod) {
mod = 0 and result = val
or
mod > 1 and result = ((val % mod) + mod) % mod
}
/**
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
*/
private predicate phiSelfModulus(
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
) {
exists(Bounds::SemSsaBound phibound, int v, int m |
edge.phiInput(phi, inp) and
phibound.getAVariable() = phi and
ssaModulus(inp, edge, phibound, v, m) and
mod = m.gcd(v) and
mod != 1
)
}
/**
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
*/
private predicate phiModulusInit(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
edge.phiInput(phi, inp) and
ssaModulus(inp, edge, b, val, mod)
)
}
/**
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
*/
pragma[nomagic]
private predicate phiModulusRankStep(
SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod, int rix
) {
/*
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
* class for the phi node.
*/
rix = 0 and
phiModulusInit(phi, b, val, mod)
or
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
mod != 1 and
val = remainder(v1, mod)
|
/*
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
*/
exists(int v2, int m2 |
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
ssaModulus(inp, edge, b, v2, m2) and
mod = m1.gcd(m2).gcd(v1 - v2)
)
or
/*
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
* common denominator of `m1` and `m2`.
*/
exists(int m2 |
rankedPhiInput(phi, inp, edge, rix) and
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
phiSelfModulus(phi, inp, edge, m2) and
mod = m1.gcd(m2)
)
)
}
/**
* Holds if `phi` is equal to `b + val` modulo `mod`.
*/
private predicate phiModulus(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
exists(int r |
maxPhiInputRank(phi, r) and
phiModulusRankStep(phi, b, val, mod, r)
)
}
/**
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
*/
private predicate ssaModulus(
SemSsaVariable v, SemSsaReadPosition pos, Bounds::SemBound b, int val, int mod
) {
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
or
b.(Bounds::SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
or
exists(SemExpr e, int val0, int delta |
semExprModulus(e, b, val0, mod) and
valueFlowStepSsa(v, pos, e, delta) and
val = remainder(val0 + delta, mod)
)
or
moduloGuardedRead(v, pos, val, mod) and b instanceof Bounds::SemZeroBound
}
/**
* Holds if `e` is equal to `b + val` modulo `mod`.
*
* There are two cases for the modulus:
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
*/
cached
predicate semExprModulus(SemExpr e, Bounds::SemBound b, int val, int mod) {
not ignoreExprModulus(e) and
(
e = b.getExpr(D::fromInt(val)) and mod = 0
or
evenlyDivisibleExpr(e, mod) and
val = 0 and
b instanceof Bounds::SemZeroBound
or
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
ssaModulus(v, bb, b, val, mod) and
e = v.getAUse() and
bb.getAnExpr() = e
)
or
exists(SemExpr mid, int val0, int delta |
semExprModulus(mid, b, val0, mod) and
U::semValueFlowStep(e, mid, D::fromInt(delta)) and
val = remainder(val0 + delta, mod)
)
or
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
cond = e and
condExprBranchModulus(cond, true, b, v1, m1) and
condExprBranchModulus(cond, false, b, v2, m2) and
mod = m1.gcd(m2).gcd(v1 - v2) and
mod != 1 and
val = remainder(v1, mod)
)
or
exists(Bounds::SemBound b1, Bounds::SemBound b2, int v1, int v2, int m1, int m2 |
addModulus(e, true, b1, v1, m1) and
addModulus(e, false, b2, v2, m2) and
mod = m1.gcd(m2) and
mod != 1 and
val = remainder(v1 + v2, mod)
|
b = b1 and b2 instanceof Bounds::SemZeroBound
or
b = b2 and b1 instanceof Bounds::SemZeroBound
)
or
exists(int v1, int v2, int m1, int m2 |
subModulus(e, true, b, v1, m1) and
subModulus(e, false, any(Bounds::SemZeroBound zb), v2, m2) and
mod = m1.gcd(m2) and
mod != 1 and
val = remainder(v1 - v2, mod)
)
)
}
private predicate condExprBranchModulus(
SemConditionalExpr cond, boolean branch, Bounds::SemBound b, int val, int mod
) {
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
}
private predicate addModulus(SemExpr add, boolean isLeft, Bounds::SemBound b, int val, int mod) {
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
semExprModulus(larg, b, val, mod) and isLeft = true
or
semExprModulus(rarg, b, val, mod) and isLeft = false
)
}
private predicate subModulus(SemExpr sub, boolean isLeft, Bounds::SemBound b, int val, int mod) {
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
semExprModulus(larg, b, val, mod) and isLeft = true
or
semExprModulus(rarg, b, val, mod) and isLeft = false
)
}
}

View File

@@ -0,0 +1,8 @@
/**
* C++-specific implementation of modulus analysis.
*/
module Private {
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
predicate ignoreExprModulus(SemExpr e) { none() }
}

View File

@@ -3,11 +3,18 @@
*/
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import RangeAnalysisStage
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
private import RangeAnalysisImpl
private import codeql.rangeanalysis.RangeAnalysis
module CppLangImplConstant implements LangSig<Sem, FloatDelta> {
module CppLangImplConstant implements LangSig<FloatDelta> {
/**
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadCopy(SemExpr e) { none() }
/**
* Ignore the bound on this expression.
*
@@ -16,6 +23,40 @@ module CppLangImplConstant implements LangSig<Sem, FloatDelta> {
*/
predicate ignoreExprBound(SemExpr e) { none() }
/**
* Ignore any inferred zero lower bound on this expression.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreZeroLowerBound(SemExpr e) { none() }
/**
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
/**
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
/**
* Adds additional results to `ssaRead()` that are specific to Java.
*
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
* in exactly the same way as the old Java implementation. Once the new implementation matches the
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
* or not they come from a post-increment/decrement expression.
*/
SemExpr specificSsaRead(SemSsaVariable v, float delta) { none() }
/**
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
*/

View File

@@ -1,114 +1,13 @@
private import RangeAnalysisStage
private import RangeAnalysisConstantSpecific
private import RangeAnalysisRelativeSpecific
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
private import RangeUtils
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExpr
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticCFG
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticGuard
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticBound as SemanticBound
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticLocation
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticSSA
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticType as SemanticType
private import SemanticType
private import codeql.rangeanalysis.RangeAnalysis
private import ConstantAnalysis as ConstantAnalysis
module Sem implements Semantic {
class Expr = SemExpr;
class ConstantIntegerExpr = ConstantAnalysis::SemConstantIntegerExpr;
class BinaryExpr = SemBinaryExpr;
class AddExpr = SemAddExpr;
class SubExpr = SemSubExpr;
class MulExpr = SemMulExpr;
class DivExpr = SemDivExpr;
class RemExpr = SemRemExpr;
class BitAndExpr = SemBitAndExpr;
class BitOrExpr = SemBitOrExpr;
class ShiftLeftExpr = SemShiftLeftExpr;
class ShiftRightExpr = SemShiftRightExpr;
class ShiftRightUnsignedExpr = SemShiftRightUnsignedExpr;
class RelationalExpr = SemRelationalExpr;
class UnaryExpr = SemUnaryExpr;
class ConvertExpr = SemConvertExpr;
class BoxExpr = SemBoxExpr;
class UnboxExpr = SemUnboxExpr;
class NegateExpr = SemNegateExpr;
class PreIncExpr = SemAddOneExpr;
class PreDecExpr = SemSubOneExpr;
class PostIncExpr extends SemUnaryExpr {
PostIncExpr() { none() }
}
class PostDecExpr extends SemUnaryExpr {
PostDecExpr() { none() }
}
class CopyValueExpr extends SemUnaryExpr {
CopyValueExpr() { this instanceof SemCopyValueExpr or this instanceof SemStoreExpr }
}
class ConditionalExpr = SemConditionalExpr;
class BasicBlock = SemBasicBlock;
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
class Guard = SemGuard;
predicate implies_v2 = semImplies_v2/4;
class Type = SemType;
class IntegerType = SemIntegerType;
class FloatingPointType = SemFloatingPointType;
class AddressType = SemAddressType;
class SsaVariable = SemSsaVariable;
class SsaPhiNode = SemSsaPhiNode;
class SsaExplicitUpdate = SemSsaExplicitUpdate;
class SsaReadPosition = SemSsaReadPosition;
class SsaReadPositionPhiInputEdge = SemSsaReadPositionPhiInputEdge;
class SsaReadPositionBlock = SemSsaReadPositionBlock;
predicate conversionCannotOverflow(Type fromType, Type toType) {
SemanticType::conversionCannotOverflow(fromType, toType)
}
}
module SignAnalysis implements SignAnalysisSig<Sem> {
private import SignAnalysisCommon as SA
import SA::SignAnalysis<FloatDelta, Util>
}
module ConstantBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
module ConstantBounds implements BoundSig<FloatDelta> {
class SemBound instanceof SemanticBound::SemBound {
SemBound() {
this instanceof SemanticBound::SemZeroBound
@@ -126,11 +25,11 @@ module ConstantBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
SemSsaVariable getVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
}
}
module RelativeBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
module RelativeBounds implements BoundSig<FloatDelta> {
class SemBound instanceof SemanticBound::SemBound {
SemBound() { not this instanceof SemanticBound::SemZeroBound }
@@ -144,42 +43,17 @@ module RelativeBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
SemSsaVariable getVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
}
}
module AllBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
class SemBound instanceof SemanticBound::SemBound {
string toString() { result = super.toString() }
SemLocation getLocation() { result = super.getLocation() }
SemExpr getExpr(float delta) { result = super.getExpr(delta) }
}
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
SemSsaVariable getVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
}
}
private module ModulusAnalysisInstantiated implements ModulusAnalysisSig<Sem> {
class ModBound = AllBounds::SemBound;
private import codeql.rangeanalysis.ModulusAnalysis as MA
import MA::ModulusAnalysis<SemLocation, Sem, FloatDelta, AllBounds, Util>
}
module Util = RangeUtil<FloatDelta, CppLangImplConstant>;
module ConstantStage =
RangeStage<SemLocation, Sem, FloatDelta, ConstantBounds, FloatOverflow, CppLangImplConstant,
SignAnalysis, ModulusAnalysisInstantiated, Util>;
RangeStage<FloatDelta, ConstantBounds, FloatOverflow, CppLangImplConstant,
RangeUtil<FloatDelta, CppLangImplConstant>>;
module RelativeStage =
RangeStage<SemLocation, Sem, FloatDelta, RelativeBounds, FloatOverflow, CppLangImplRelative,
SignAnalysis, ModulusAnalysisInstantiated, Util>;
RangeStage<FloatDelta, RelativeBounds, FloatOverflow, CppLangImplRelative,
RangeUtil<FloatDelta, CppLangImplRelative>>;
private newtype TSemReason =
TSemNoReason() or

View File

@@ -3,12 +3,21 @@
*/
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import RangeAnalysisStage
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.IntDelta
private import RangeAnalysisImpl
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
private import codeql.rangeanalysis.RangeAnalysis
module CppLangImplRelative implements LangSig<Sem, FloatDelta> {
module CppLangImplRelative implements LangSig<FloatDelta> {
/**
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadCopy(SemExpr e) { none() }
/**
* Ignore the bound on this expression.
*
@@ -48,6 +57,40 @@ module CppLangImplRelative implements LangSig<Sem, FloatDelta> {
t instanceof SemFloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
}
/**
* Ignore any inferred zero lower bound on this expression.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreZeroLowerBound(SemExpr e) { none() }
/**
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
/**
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
/**
* Adds additional results to `ssaRead()` that are specific to Java.
*
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
* in exactly the same way as the old Java implementation. Once the new implementation matches the
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
* or not they come from a post-increment/decrement expression.
*/
SemExpr specificSsaRead(SemSsaVariable v, float delta) { none() }
/**
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
*/

View File

@@ -4,35 +4,40 @@
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import RangeAnalysisRelativeSpecific
private import codeql.rangeanalysis.RangeAnalysis
private import RangeAnalysisImpl
private import RangeAnalysisStage as Range
private import ConstantAnalysis
module RangeUtil<DeltaSig D, LangSig<Sem, D> Lang> implements UtilSig<Sem, D> {
module RangeUtil<Range::DeltaSig D, Range::LangSig<D> Lang> implements Range::UtilSig<D> {
/**
* Gets an expression that equals `v - d`.
*/
private SemExpr semSsaRead(SemSsaVariable v, D::Delta delta) {
SemExpr semSsaRead(SemSsaVariable v, D::Delta delta) {
// There are various language-specific extension points that can be removed once we no longer
// expect to match the original Java implementation's results exactly.
result = v.getAUse() and delta = D::fromInt(0)
or
exists(D::Delta d1, SemConstantIntegerExpr c |
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
delta = D::fromFloat(D::toFloat(d1) - c.getIntValue())
delta = D::fromFloat(D::toFloat(d1) - c.getIntValue()) and
not Lang::ignoreSsaReadArithmeticExpr(result)
)
or
exists(SemSubExpr sub, D::Delta d1, SemConstantIntegerExpr c |
result = sub and
sub.getLeftOperand() = semSsaRead(v, d1) and
sub.getRightOperand() = c and
delta = D::fromFloat(D::toFloat(d1) + c.getIntValue())
delta = D::fromFloat(D::toFloat(d1) + c.getIntValue()) and
not Lang::ignoreSsaReadArithmeticExpr(result)
)
or
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
delta = D::fromFloat(0)
delta = D::fromFloat(0) and
not Lang::ignoreSsaReadAssignment(v)
or
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta)
result = Lang::specificSsaRead(v, delta)
or
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
not Lang::ignoreSsaReadCopy(result)
or
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
}
@@ -133,33 +138,27 @@ module RangeUtil<DeltaSig D, LangSig<Sem, D> Lang> implements UtilSig<Sem, D> {
or
not exists(Lang::getAlternateTypeForSsaVariable(var)) and result = var.getType()
}
import Ranking
}
import Ranking
module Ranking {
/**
* Holds if `rix` is the number of input edges to `phi`.
*/
predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
rix = max(int r | rankedPhiInput(phi, _, _, r))
}
/**
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
* in an arbitrary 1-based numbering of the input edges to `phi`.
*/
predicate rankedPhiInput(
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
) {
edge.phiInput(phi, inp) and
edge =
rank[r](SemSsaReadPositionPhiInputEdge e |
e.phiInput(phi, _)
|
e order by e.getOrigBlock().getUniqueId()
)
}
/**
* Holds if `rix` is the number of input edges to `phi`.
*/
predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
rix = max(int r | rankedPhiInput(phi, _, _, r))
}
/**
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
* in an arbitrary 1-based numbering of the input edges to `phi`.
*/
predicate rankedPhiInput(
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
) {
edge.phiInput(phi, inp) and
edge =
rank[r](SemSsaReadPositionPhiInputEdge e |
e.phiInput(phi, _)
|
e order by e.getOrigBlock().getUniqueId()
)
}

View File

@@ -6,17 +6,14 @@
* three-valued domain `{negative, zero, positive}`.
*/
private import codeql.rangeanalysis.RangeAnalysis
private import RangeAnalysisImpl
private import RangeAnalysisStage
private import SignAnalysisSpecific as Specific
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import ConstantAnalysis
private import RangeUtils
private import Sign
module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
private import codeql.rangeanalysis.internal.RangeUtils::MakeUtils<Sem, D>
module SignAnalysis<DeltaSig D, UtilSig<D> Utils> {
/**
* An SSA definition for which the analysis can compute the sign.
*
@@ -294,17 +291,17 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
) {
exists(boolean testIsTrue, SemRelationalExpr comp |
pos.hasReadOfVar(v) and
guardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
not unknownSign(lowerbound)
|
testIsTrue = true and
comp.getLesserOperand() = lowerbound and
comp.getGreaterOperand() = ssaRead(v, D::fromInt(0)) and
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
(if comp.isStrict() then isStrict = true else isStrict = false)
or
testIsTrue = false and
comp.getGreaterOperand() = lowerbound and
comp.getLesserOperand() = ssaRead(v, D::fromInt(0)) and
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
(if comp.isStrict() then isStrict = false else isStrict = true)
)
}
@@ -318,17 +315,17 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
) {
exists(boolean testIsTrue, SemRelationalExpr comp |
pos.hasReadOfVar(v) and
guardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
not unknownSign(upperbound)
|
testIsTrue = true and
comp.getGreaterOperand() = upperbound and
comp.getLesserOperand() = ssaRead(v, D::fromInt(0)) and
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
(if comp.isStrict() then isStrict = true else isStrict = false)
or
testIsTrue = false and
comp.getLesserOperand() = upperbound and
comp.getGreaterOperand() = ssaRead(v, D::fromInt(0)) and
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
(if comp.isStrict() then isStrict = false else isStrict = true)
)
}
@@ -343,8 +340,8 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
exists(SemGuard guard, boolean testIsTrue, boolean polarity, SemExpr e |
pos.hasReadOfVar(pragma[only_bind_into](v)) and
guardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
e = ssaRead(pragma[only_bind_into](v), D::fromInt(0)) and
semGuardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
e = Utils::semSsaRead(pragma[only_bind_into](v), D::fromInt(0)) and
guard.isEquality(eqbound, e, polarity) and
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
not unknownSign(eqbound)
@@ -510,16 +507,4 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
not semExprSign(e) = TPos() and
not semExprSign(e) = TZero()
}
/**
* Holds if `e` may have positive values. This does not rule out the
* possibility for negative values.
*/
predicate semMayBePositive(SemExpr e) { semExprSign(e) = TPos() }
/**
* Holds if `e` may have negative values. This does not rule out the
* possibility for positive values.
*/
predicate semMayBeNegative(SemExpr e) { semExprSign(e) = TNeg() }
}

View File

@@ -23,7 +23,9 @@
* configuration (see `InvalidPointerToDerefConfig`).
*
* The dataflow traversal defines the set of sources as any dataflow node `n` such that there exists a pointer-arithmetic
* instruction `pai` found by `AllocationToInvalidPointer.qll` and a `n.asInstruction() = pai`.
* instruction `pai` found by `AllocationToInvalidPointer.qll` and a `n.asInstruction() >= pai + deltaDerefSourceAndPai`.
* Here, `deltaDerefSourceAndPai` is the constant difference between the source we track for finding a dereference and the
* pointer-arithmetic instruction.
*
* The set of sinks is defined as any dataflow node `n` such that `addr <= n.asInstruction() + deltaDerefSinkAndDerefAddress`
* for some address operand `addr` and constant difference `deltaDerefSinkAndDerefAddress`. Since an address operand is
@@ -35,8 +37,9 @@
* `deltaDerefSinkAndDerefAddress >= 0`. The load attached to `*p` is the "operation". To ensure that the path makes
* intuitive sense, we only pick operations that are control-flow reachable from the dereference sink.
*
* We use the `deltaDerefSinkAndDerefAddress` to compute how many elements the dereference is beyond the end position of
* the allocation. This is done in the `operationIsOffBy` predicate (which is the only predicate exposed by this file).
* To compute how many elements the dereference is beyond the end position of the allocation, we sum the two deltas
* `deltaDerefSourceAndPai` and `deltaDerefSinkAndDerefAddress`. This is done in the `operationIsOffBy` predicate
* (which is the only predicate exposed by this file).
*
* Handling false positives:
*
@@ -93,7 +96,7 @@ int invalidPointerToDereferenceFieldFlowBranchLimit() { result = 0 }
private module InvalidPointerToDerefBarrier {
private module BarrierConfig implements DataFlow::ConfigSig {
additional predicate isSource(DataFlow::Node source, PointerArithmeticInstruction pai) {
invalidPointerToDerefSource(_, pai, _) and
invalidPointerToDerefSource(_, pai, _, _) and
// source <= pai
bounded2(source.asInstruction(), pai, any(int d | d <= 0))
}
@@ -166,11 +169,11 @@ private module InvalidPointerToDerefBarrier {
*/
private module InvalidPointerToDerefConfig implements DataFlow::StateConfigSig {
class FlowState extends PointerArithmeticInstruction {
FlowState() { invalidPointerToDerefSource(_, this, _) }
FlowState() { invalidPointerToDerefSource(_, this, _, _) }
}
predicate isSource(DataFlow::Node source, FlowState pai) {
invalidPointerToDerefSource(_, pai, source)
invalidPointerToDerefSource(_, pai, source, _)
}
pragma[inline]
@@ -195,17 +198,24 @@ private import DataFlow::GlobalWithState<InvalidPointerToDerefConfig>
/**
* Holds if `allocSource` is dataflow node that represents an allocation that flows to the
* left-hand side of the pointer-arithmetic instruction represented by `derefSource`.
* left-hand side of the pointer-arithmetic `pai`, and `derefSource <= pai + derefSourcePaiDelta`.
*
* For example, if `pai` is a pointer-arithmetic operation `p + size` in an expression such
* as `(p + size) + 1` and `derefSource` is the node representing `(p + size) + 1`. In this
* case `derefSourcePaiDelta` is 1.
*/
private predicate invalidPointerToDerefSource(
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
int deltaDerefSourceAndPai
) {
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
// and the instruction computing the address for which we will search for a dereference.
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, _) and
derefSource.asInstruction() = pai
// derefSource <= pai + deltaDerefSourceAndPai
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
deltaDerefSourceAndPai >= 0
}
/**
@@ -248,9 +258,11 @@ private Instruction getASuccessor(Instruction instr) {
instr.getBlock().getASuccessor+() = result.getBlock()
}
private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFlow::Node derefSink) {
private predicate paiForDereferenceSink(
PointerArithmeticInstruction pai, DataFlow::Node derefSink, int deltaDerefSourceAndPai
) {
exists(DataFlow::Node derefSource |
invalidPointerToDerefSource(_, pai, derefSource) and
invalidPointerToDerefSource(_, pai, derefSource, deltaDerefSourceAndPai) and
flow(derefSource, derefSink)
)
}
@@ -262,10 +274,10 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl
*/
private predicate derefSinkToOperation(
DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation,
string description, int deltaDerefSinkAndDerefAddress
string description, int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress
) {
exists(Instruction operationInstr, AddressOperand addr |
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink), deltaDerefSourceAndPai) and
isInvalidPointerDerefSink(derefSink, addr, operationInstr, description,
deltaDerefSinkAndDerefAddress) and
operationInstr = getASuccessor(derefSink.asInstruction()) and
@@ -286,7 +298,11 @@ predicate operationIsOffBy(
DataFlow::Node allocation, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
DataFlow::Node derefSink, string description, DataFlow::Node operation, int delta
) {
invalidPointerToDerefSource(allocation, pai, derefSource) and
flow(derefSource, derefSink) and
derefSinkToOperation(derefSink, pai, operation, description, delta)
exists(int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress |
invalidPointerToDerefSource(allocation, pai, derefSource, deltaDerefSourceAndPai) and
flow(derefSource, derefSink) and
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSourceAndPai,
deltaDerefSinkAndDerefAddress) and
delta = deltaDerefSourceAndPai + deltaDerefSinkAndDerefAddress
)
}

View File

@@ -372,8 +372,7 @@ private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
private predicate mk_PointerFieldAccess(HashCons qualifier, Field target, PointerFieldAccess access) {
analyzablePointerFieldAccess(access) and
target = access.getTarget() and
qualifier = hashCons(access.getQualifier().getFullyConverted()) and
not access instanceof ImplicitThisFieldAccess
qualifier = hashCons(access.getQualifier().getFullyConverted())
}
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {

View File

@@ -197,11 +197,6 @@ svnchurn(
* C++ dbscheme
*/
extractor_version(
string codeql_version: string ref,
string frontend_version: string ref
)
@location = @location_stmt | @location_expr | @location_default ;
/**
@@ -617,14 +612,6 @@ case @builtintype.kind of
| 51 = @char8_t
| 52 = @float16 // _Float16
| 53 = @complex_float16 // _Complex _Float16
| 54 = @fp16 // __fp16
| 55 = @std_bfloat16 // __bf16
| 56 = @std_float16 // std::float16_t
| 57 = @complex_std_float32 // _Complex _Float32
| 58 = @complex_float32x // _Complex _Float32x
| 59 = @complex_std_float64 // _Complex _Float64
| 60 = @complex_float64x // _Complex _Float64x
| 61 = @complex_std_float128 // _Complex _Float128
;
builtintypes(
@@ -1334,16 +1321,11 @@ funbind(
| @assignxorexpr
| @assignlshiftexpr
| @assignrshiftexpr
;
@assign_pointer_expr = @assignpaddexpr
| @assignpaddexpr
| @assignpsubexpr
;
@assign_op_expr = @assign_arith_expr
| @assign_bitwise_expr
| @assign_pointer_expr
;
@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
@assign_expr = @assignexpr | @assign_op_expr | @blockassignexpr

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
description: Introduce extractor version numbers
compatibility: full

View File

@@ -1,2 +0,0 @@
description: Introduce new floating-point types from C23 and C++23
compatibility: full

View File

@@ -1,2 +0,0 @@
description: Removed @assignpaddexpr and @assignpsubexpr from @assign_bitwise_expr
compatibility: full

View File

@@ -1,15 +1,3 @@
## 0.8.2
No user-facing changes.
## 0.8.1
### New Queries
* The query `cpp/redundant-null-check-simple` has been promoted to Code Scanning. The query finds cases where a pointer is compared to null after it has already been dereferenced. Such comparisons likely indicate a bug at the place where the pointer is dereferenced, or where the pointer is compared to null.
Note: This query was incorrectly noted as being promoted to Code Scanning in CodeQL version 2.14.6.
## 0.8.0
### Query Metadata Changes

View File

@@ -13,8 +13,7 @@
*/
import cpp
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow
import semmle.code.cpp.controlflow.StackVariableReachability
/**
* Auxiliary predicate: Types that don't require initialization
@@ -34,6 +33,31 @@ predicate allocatedType(Type t) {
allocatedType(t.getUnspecifiedType())
}
/**
* A declaration of a local variable that leaves the
* variable uninitialized.
*/
DeclStmt declWithNoInit(LocalVariable v) {
result.getADeclaration() = v and
not exists(v.getInitializer()) and
/* The type of the variable is not stack-allocated. */
exists(Type t | t = v.getType() | not allocatedType(t))
}
class UninitialisedLocalReachability extends StackVariableReachability {
UninitialisedLocalReachability() { this = "UninitialisedLocal" }
override predicate isSource(ControlFlowNode node, StackVariable v) { node = declWithNoInit(v) }
override predicate isSink(ControlFlowNode node, StackVariable v) { useOfVarActual(v, node) }
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
// only report the _first_ possibly uninitialized use
useOfVarActual(v, node) or
definitionBarrier(v, node)
}
}
pragma[noinline]
predicate containsInlineAssembly(Function f) { exists(AsmStmt s | s.getEnclosingFunction() = f) }
@@ -58,33 +82,8 @@ VariableAccess commonException() {
containsInlineAssembly(result.getEnclosingFunction())
}
predicate isSinkImpl(Instruction sink, VariableAccess va) {
exists(LoadInstruction load |
va = load.getUnconvertedResultExpression() and
not va = commonException() and
sink = load.getSourceValue()
)
}
class MustFlow extends MustFlowConfiguration {
MustFlow() { this = "MustFlow" }
override predicate isSource(Instruction source) {
source instanceof UninitializedInstruction and
exists(Type t | t = source.getResultType() | not allocatedType(t))
}
override predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
override predicate allowInterproceduralFlow() { none() }
override predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
}
from
VariableAccess va, LocalVariable v, MustFlow conf, MustFlowPathNode source, MustFlowPathNode sink
from UninitialisedLocalReachability r, LocalVariable v, VariableAccess va
where
conf.hasFlowPath(source, sink) and
isSinkImpl(sink.getInstruction(), va) and
v = va.getTarget()
r.reaches(_, v, va) and
not va = commonException()
select va, "The variable $@ may not be initialized at this access.", v, v.getName()

View File

@@ -8,6 +8,7 @@
* @id cpp/invalid-pointer-deref
* @tags reliability
* security
* experimental
* external/cwe/cwe-119
* external/cwe/cwe-125
* external/cwe/cwe-193

View File

@@ -15,7 +15,6 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.models.interfaces.FlowSource
import semmle.code.cpp.models.implementations.Memset
import ExposedSystemData::PathGraph
import SystemData
@@ -29,10 +28,6 @@ module ExposedSystemDataConfig implements DataFlow::ConfigSig {
fc.getArgument(arg).getAChild*() = sink.asIndirectExpr()
)
}
predicate isBarrier(DataFlow::Node node) {
node.asIndirectArgument() = any(MemsetFunction func).getACallToThisFunction().getAnArgument()
}
}
module ExposedSystemData = TaintTracking::Global<ExposedSystemDataConfig>;

View File

@@ -28,7 +28,6 @@ import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.models.interfaces.FlowSource
import semmle.code.cpp.security.OutputWrite
import semmle.code.cpp.models.implementations.Memset
import PotentiallyExposedSystemData::PathGraph
import SystemData
@@ -50,10 +49,6 @@ module PotentiallyExposedSystemDataConfig implements DataFlow::ConfigSig {
else child = sink.asExpr()
)
}
predicate isBarrier(DataFlow::Node node) {
node.asIndirectArgument() = any(MemsetFunction func).getACallToThisFunction().getAnArgument()
}
}
module PotentiallyExposedSystemData = TaintTracking::Global<PotentiallyExposedSystemDataConfig>;

View File

@@ -1,7 +1,6 @@
## 0.8.1
### New Queries
---
category: newQuery
---
* The query `cpp/redundant-null-check-simple` has been promoted to Code Scanning. The query finds cases where a pointer is compared to null after it has already been dereferenced. Such comparisons likely indicate a bug at the place where the pointer is dereferenced, or where the pointer is compared to null.
Note: This query was incorrectly noted as being promoted to Code Scanning in CodeQL version 2.14.6.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added SQL API models for `ODBC`.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The `cpp/uninitialized-local` query has been improved to produce fewer false positives.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added models for the `sprintf` variants from the `StrSafe.h` header.

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.8.2
lastReleaseVersion: 0.8.0

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.8.3-dev
version: 0.8.1-dev
groups:
- cpp
- queries

View File

@@ -763,7 +763,7 @@ StaticMemberAccess.cpp:
# 7| ValueCategory = lvalue
# 7| getRValue(): [VariableAccess] i
# 7| Type = [IntType] int
# 7| ValueCategory = prvalue(load)
# 7| ValueCategory = prvalue
# 7| getQualifier(): [VariableAccess] xref
# 7| Type = [LValueReferenceType] X &
# 7| ValueCategory = prvalue(load)
@@ -1298,7 +1298,7 @@ union_etc.cpp:
# 6| getExpr(): [AssignExpr] ... = ...
# 6| Type = [IntType] int
# 6| ValueCategory = lvalue
# 6| getLValue(): [ImplicitThisFieldAccess,PointerFieldAccess] x
# 6| getLValue(): [PointerFieldAccess] x
# 6| Type = [IntType] int
# 6| ValueCategory = lvalue
# 6| getQualifier(): [ThisExpr] this
@@ -1394,7 +1394,7 @@ union_etc.cpp:
# 26| ValueCategory = lvalue
# 26| getRValue(): [AssignExpr] ... = ...
# 26| Type = [IntType] int
# 26| ValueCategory = prvalue(load)
# 26| ValueCategory = prvalue
# 26| getLValue(): [ValueFieldAccess] e
# 26| Type = [IntType] int
# 26| ValueCategory = lvalue
@@ -1406,7 +1406,7 @@ union_etc.cpp:
# 26| ValueCategory = lvalue
# 26| getRValue(): [AssignExpr] ... = ...
# 26| Type = [IntType] int
# 26| ValueCategory = prvalue(load)
# 26| ValueCategory = prvalue
# 26| getLValue(): [ValueFieldAccess] i
# 26| Type = [IntType] int
# 26| ValueCategory = lvalue
@@ -1488,7 +1488,7 @@ union_etc.cpp:
# 33| getExpr(): [AssignExpr] ... = ...
# 33| Type = [IntType] int
# 33| ValueCategory = lvalue
# 33| getLValue(): [ImplicitThisFieldAccess,PointerFieldAccess] q
# 33| getLValue(): [PointerFieldAccess] q
# 33| Type = [IntType] int
# 33| ValueCategory = lvalue
# 33| getQualifier(): [ThisExpr] this

View File

@@ -675,7 +675,6 @@
| test.c:398:9:398:22 | CopyValue: ... , ... | positive strictlyPositive |
| test.c:398:14:398:14 | Load: y | positive strictlyPositive |
| test.c:398:14:398:19 | Add: ... += ... | positive strictlyPositive |
| test.c:398:14:398:19 | Load: ... += ... | positive strictlyPositive |
| test.c:398:14:398:19 | Store: ... += ... | positive strictlyPositive |
| test.c:398:19:398:19 | Constant: (unsigned int)... | positive strictlyPositive |
| test.c:398:22:398:22 | Load: y | positive strictlyPositive |

View File

@@ -29,6 +29,7 @@ edges
| test.cpp:69:10:69:10 | arr indirection [post update] [p] | test.cpp:70:5:70:7 | arr indirection [p] |
| test.cpp:69:14:69:19 | call to malloc | test.cpp:69:5:69:25 | ... = ... |
| test.cpp:70:5:70:7 | arr indirection [p] | test.cpp:67:10:67:19 | mk_array_p indirection [p] |
| test.cpp:70:5:70:7 | arr indirection [p] | test.cpp:70:5:70:7 | arr indirection [p] |
| test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | test.cpp:79:9:79:11 | arr indirection [p] |
| test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | test.cpp:83:9:83:11 | arr indirection [p] |
| test.cpp:79:9:79:11 | arr indirection [p] | test.cpp:79:14:79:14 | p |

View File

@@ -35,7 +35,9 @@ edges
| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr |
| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr |
| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf |
| test.cpp:146:26:146:26 | p indirection | test.cpp:147:4:147:9 | -- ... |
| test.cpp:146:26:146:26 | p indirection | test.cpp:148:6:148:9 | * ... |
| test.cpp:146:26:146:26 | p indirection | test.cpp:149:6:149:9 | * ... |
| test.cpp:146:26:146:26 | p indirection | test.cpp:150:6:150:9 | * ... |
| test.cpp:156:12:156:14 | buf | test.cpp:156:12:156:18 | ... + ... |
| test.cpp:156:12:156:18 | ... + ... | test.cpp:158:17:158:18 | & ... indirection |
| test.cpp:158:17:158:18 | & ... indirection | test.cpp:146:26:146:26 | p indirection |
@@ -122,7 +124,9 @@ nodes
| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
| test.cpp:146:26:146:26 | p indirection | semmle.label | p indirection |
| test.cpp:147:4:147:9 | -- ... | semmle.label | -- ... |
| test.cpp:148:6:148:9 | * ... | semmle.label | * ... |
| test.cpp:149:6:149:9 | * ... | semmle.label | * ... |
| test.cpp:150:6:150:9 | * ... | semmle.label | * ... |
| test.cpp:156:12:156:14 | buf | semmle.label | buf |
| test.cpp:156:12:156:18 | ... + ... | semmle.label | ... + ... |
| test.cpp:158:17:158:18 | & ... indirection | semmle.label | & ... indirection |
@@ -175,7 +179,9 @@ subpaths
| test.cpp:88:5:88:27 | PointerAdd: access to array | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:88:5:88:31 | Store: ... = ... | write |
| test.cpp:128:9:128:14 | PointerAdd: access to array | test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:125:11:125:13 | arr | arr | test.cpp:128:9:128:18 | Store: ... = ... | write |
| test.cpp:136:9:136:16 | PointerAdd: ... += ... | test.cpp:143:18:143:21 | asdf | test.cpp:138:13:138:15 | arr | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:142:10:142:13 | asdf | asdf | test.cpp:138:12:138:15 | Load: * ... | read |
| test.cpp:156:12:156:18 | PointerAdd: ... + ... | test.cpp:156:12:156:14 | buf | test.cpp:147:4:147:9 | -- ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:154:7:154:9 | buf | buf | test.cpp:147:3:147:13 | Store: ... = ... | write |
| test.cpp:156:12:156:18 | PointerAdd: ... + ... | test.cpp:156:12:156:14 | buf | test.cpp:148:6:148:9 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:154:7:154:9 | buf | buf | test.cpp:147:3:147:13 | Store: ... = ... | write |
| test.cpp:156:12:156:18 | PointerAdd: ... + ... | test.cpp:156:12:156:14 | buf | test.cpp:149:6:149:9 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:154:7:154:9 | buf | buf | test.cpp:148:3:148:13 | Store: ... = ... | write |
| test.cpp:156:12:156:18 | PointerAdd: ... + ... | test.cpp:156:12:156:14 | buf | test.cpp:150:6:150:9 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:154:7:154:9 | buf | buf | test.cpp:149:3:149:13 | Store: ... = ... | write |
| test.cpp:221:5:221:11 | PointerAdd: access to array | test.cpp:218:23:218:28 | buffer | test.cpp:221:5:221:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:217:19:217:24 | buffer | buffer | test.cpp:221:5:221:15 | Store: ... = ... | write |
| test.cpp:232:5:232:10 | PointerAdd: access to array | test.cpp:229:25:229:29 | array | test.cpp:232:5:232:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:228:10:228:14 | array | array | test.cpp:232:5:232:19 | Store: ... = ... | write |
| test.cpp:261:27:261:30 | PointerAdd: access to array | test.cpp:286:19:286:25 | buffer2 | test.cpp:261:27:261:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:285:19:285:25 | buffer2 | buffer2 | test.cpp:261:27:261:30 | Load: access to array | read |

View File

@@ -1,6 +1,6 @@
| FieldAccess.cpp:11:12:11:13 | p1 | ptr, this |
| FieldAccess.cpp:12:12:12:13 | p2 | ptr, this |
| FieldAccess.cpp:25:12:25:13 | x1 | ptr, this |
| FieldAccess.cpp:11:12:11:13 | p1 | ptr |
| FieldAccess.cpp:12:12:12:13 | p2 | ptr |
| FieldAccess.cpp:25:12:25:13 | x1 | ptr |
| FieldAccess.cpp:29:18:29:19 | x2 | ptr |
| FieldAccess.cpp:34:3:34:3 | d | this |
| FieldAccess.cpp:45:13:45:14 | x1 | ptr |
@@ -19,10 +19,10 @@
| FieldAccess.cpp:91:7:91:7 | x | val |
| FieldAccess.cpp:91:13:91:13 | y | ref |
| FieldAccess.cpp:92:8:92:8 | x | ptr |
| FieldAccess.cpp:92:12:92:12 | y | ptr, this |
| FieldAccess.cpp:92:12:92:12 | y | ptr |
| FieldAccess.cpp:93:8:93:8 | x | ptr |
| FieldAccess.cpp:93:18:93:18 | y | ptr |
| FieldAccess.cpp:94:11:94:11 | y | ptr |
| FieldAccess.cpp:94:20:94:20 | y | val |
| FieldAccess.cpp:113:5:113:5 | x | ptr, this |
| FieldAccess.cpp:113:5:113:5 | x | ptr |
| FieldAccess.cpp:116:3:116:3 | v | this |

View File

@@ -1,2 +1,2 @@
static int clang421 = __has_feature(attribute_deprecated_with_message);
// semmle-extractor-options: --gnu_version 40201 --clang_version 30400
// semmle-extractor-options: --gnu_version 40201 --edg --clang

View File

@@ -1,2 +1,2 @@
static int clang450 = __has_feature(attribute_deprecated_with_message);
// semmle-extractor-options: --gnu_version 40500 --clang_version 30500
// semmle-extractor-options: --gnu_version 40500 --edg --clang

View File

@@ -1,2 +1,2 @@
static int gcc421 = __has_feature(attribute_deprecated_with_message);
// semmle-extractor-options: --gnu_version 40201
// semmle-extractor-options: --gnu_version 40201 --edg --clang

View File

@@ -1,2 +1,2 @@
static int gcc450 = __has_feature(attribute_deprecated_with_message);
// semmle-extractor-options: --gnu_version 40500
// semmle-extractor-options: --gnu_version 40500 --edg --clang

View File

@@ -41,9 +41,6 @@ postWithInFlow
| example.c:26:9:26:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
| example.c:26:19:26:24 | coords [inner post update] | PostUpdateNode should not be the target of local flow. |
| example.c:28:23:28:25 | pos [inner post update] | PostUpdateNode should not be the target of local flow. |
| flowOut.cpp:5:5:5:12 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| flowOut.cpp:5:6:5:12 | toTaint [inner post update] | PostUpdateNode should not be the target of local flow. |
| flowOut.cpp:18:17:18:17 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
| globals.cpp:13:5:13:19 | flowTestGlobal1 [post update] | PostUpdateNode should not be the target of local flow. |
| globals.cpp:23:5:23:19 | flowTestGlobal2 [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. |

View File

@@ -14,14 +14,17 @@ localCallNodes
postIsNotPre
postHasUniquePre
uniquePostUpdate
| example.c:24:13:24:18 | coords indirection | Node has multiple PostUpdateNodes. |
postIsInSameCallable
reverseRead
argHasPostUpdate
postWithInFlow
| test.cpp:384:10:384:13 | memcpy output argument | PostUpdateNode should not be the target of local flow. |
| test.cpp:384:10:384:13 | memcpy output argument | PostUpdateNode should not be the target of local flow. |
| test.cpp:391:10:391:13 | memcpy output argument | PostUpdateNode should not be the target of local flow. |
| test.cpp:391:10:391:13 | memcpy output argument | PostUpdateNode should not be the target of local flow. |
| test.cpp:400:10:400:13 | memcpy output argument | PostUpdateNode should not be the target of local flow. |
| test.cpp:400:10:400:13 | memcpy output argument | PostUpdateNode should not be the target of local flow. |
| test.cpp:407:10:407:13 | memcpy output argument | PostUpdateNode should not be the target of local flow. |
| test.cpp:407:10:407:13 | memcpy output argument | PostUpdateNode should not be the target of local flow. |
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition

View File

@@ -1,20 +0,0 @@
int source();
void sink(int);
void source_ref(int *toTaint) { // $ ir-def=*toTaint ast-def=toTaint
*toTaint = source();
}
void modify_copy(int* ptr) { // $ ast-def=ptr
int deref = *ptr;
int* other = &deref;
source_ref(other);
}
void test_output() {
int x = 0;
modify_copy(&x);
sink(x); // $ SPURIOUS: ir
}

View File

@@ -1,19 +1,6 @@
private import semmle.code.cpp.ir.dataflow.DataFlow
private import DataFlow
private class TestAdditionalCallTarget extends AdditionalCallTarget {
override Function viableTarget(Call call) {
// To test that call targets specified by `AdditionalCallTarget` are
// resolved correctly this subclass resolves all calls to
// `call_template_argument<f>(x)` as if the user had written `f(x)`.
exists(FunctionTemplateInstantiation inst |
inst.getTemplate().hasName("call_template_argument") and
call.getTarget() = inst and
result = inst.getTemplateArgument(0).(FunctionAccess).getTarget()
)
}
}
module IRConfig implements ConfigSig {
predicate isSource(Node src) {
src.asExpr() instanceof NewExpr

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