Compare commits

..

4 Commits

Author SHA1 Message Date
github-actions[bot]
4ec2601976 Post-release preparation for codeql-cli-2.15.4 2023-12-13 15:15:21 +00:00
Florin Coada
e637eb720b Merge pull request #15076 from github/changedocs/2.15.4
Release change notes for 2.15.4
2023-12-12 16:51:28 +00:00
Florin Coada
062a85e77b Removed local preview dox-out 2023-12-12 14:32:46 +00:00
Florin Coada
c78dfea3db Release changenotes for 2.15.4 2023-12-12 13:46:32 +00:00
1370 changed files with 59270 additions and 88835 deletions

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
- uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `Stale` label in order to avoid having this issue closed in 7 days.'

View File

@@ -28,9 +28,9 @@ jobs:
steps:
- name: Setup dotnet
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.100
dotnet-version: 7.0.102
- name: Checkout repository
uses: actions/checkout@v4

View File

@@ -72,15 +72,15 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.100
dotnet-version: 7.0.102
- name: Extractor unit tests
run: |
dotnet test -p:RuntimeFrameworkVersion=8.0.0 extractor/Semmle.Util.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.0 extractor/Semmle.Extraction.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.0 autobuilder/Semmle.Autobuild.CSharp.Tests
dotnet test -p:RuntimeFrameworkVersion=8.0.0 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
dotnet test -p:RuntimeFrameworkVersion=7.0.2 extractor/Semmle.Util.Tests
dotnet test -p:RuntimeFrameworkVersion=7.0.2 extractor/Semmle.Extraction.Tests
dotnet test -p:RuntimeFrameworkVersion=7.0.2 autobuilder/Semmle.Autobuild.CSharp.Tests
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
shell: bash
stubgentest:
runs-on: ubuntu-latest

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: macos-latest
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
id: go
@@ -50,7 +50,7 @@ jobs:
runs-on: windows-latest-xl
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
id: go

View File

@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest-xl
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
id: go

View File

@@ -44,4 +44,3 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
# Misc
/misc/scripts/accept-expected-changes-from-ci.py @RasmusWL
/misc/scripts/generate-code-scanning-query-list.py @RasmusWL

View File

@@ -53,6 +53,14 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
],
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
],
"SsaReadPosition Java/C#": [
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
@@ -454,10 +462,23 @@
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll",
"swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll"
],
"TypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
],
"SummaryTypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
],
"AccessPathSyntax": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/AccessPathSyntax.qll"
],
"IncompleteUrlSubstringSanitization": [
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll",
"ruby/ql/src/queries/security/cwe-020/IncompleteUrlSubstringSanitization.qll"
@@ -513,4 +534,4 @@
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
]
}
}

View File

@@ -326,7 +326,7 @@ namespace Semmle.Autobuild.Cpp.Tests
public void TestCppAutobuilderSuccess()
{
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
Actions.RunProcess[@"cmd.exe /C scratch\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program^ Files^ ^(x86^)\Microsoft^ Visual^ Studio^ 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
@@ -337,11 +337,10 @@ namespace Semmle.Autobuild.Cpp.Tests
Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = true;
Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true;
Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CPP_SCRATCH_DIR"] = "scratch";
Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.slx";
Actions.EnumerateDirectories[@"C:\Project"] = "";
Actions.CreateDirectories.Add(@"scratch\.nuget");
Actions.DownloadFiles.Add(("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", @"scratch\.nuget\nuget.exe"));
Actions.CreateDirectories.Add(@"C:\Project\.nuget");
Actions.DownloadFiles.Add(("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", @"C:\Project\.nuget\nuget.exe"));
var autobuilder = CreateAutoBuilder(true);
var solution = new TestSolution(@"C:\Project\test.sln");

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
@@ -11,12 +11,12 @@
<ItemGroup>
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.6.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<AssemblyName>Semmle.Autobuild.Cpp</AssemblyName>
<RootNamespace>Semmle.Autobuild.Cpp</RootNamespace>
<ApplicationIcon />
@@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.8.3" />
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,2 +0,0 @@
description: Revert removal of uniqueness constraint on link_targets/2
compatibility: backwards

View File

@@ -1,16 +1,3 @@
## 0.12.2
### Deprecated APIs
* The `isUserInput`, `userInputArgument`, and `userInputReturned` predicates from `SecurityOptions` have been deprecated. Use `FlowSource` instead.
### Minor Analysis Improvements
* Changed the output of `Node.toString` to better reflect how many indirections a given dataflow node has.
* Added a new predicate `Node.asDefinition` on `DataFlow::Node`s for selecting the dataflow node corresponding to a particular definition.
* The deprecated `DefaultTaintTracking` library has been removed.
* The `Guards` library has been replaced with the API-compatible `IRGuards` implementation, which has better precision in some cases.
## 0.12.1
### New Features

View File

@@ -52,18 +52,17 @@ class Options extends string {
/**
* Holds if a call to this function will never return.
*
* By default, this holds for `exit`, `_exit`, `_Exit`, `abort`,
* `__assert_fail`, `longjmp`, `__builtin_unreachable` and any
* function with a `noreturn` or `__noreturn__` attribute or
* `noreturn` specifier.
* By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`,
* `longjmp`, `__builtin_unreachable` and any function with a
* `noreturn` attribute or specifier.
*/
predicate exits(Function f) {
f.getAnAttribute().hasName(["noreturn", "__noreturn__"])
f.getAnAttribute().hasName("noreturn")
or
f.getASpecifier().hasName("noreturn")
or
f.hasGlobalOrStdName([
"exit", "_exit", "_Exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable"
"exit", "_exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable"
])
or
CustomOptions::exits(f) // old Options.qll

View File

@@ -1,12 +0,0 @@
## 0.12.2
### Deprecated APIs
* The `isUserInput`, `userInputArgument`, and `userInputReturned` predicates from `SecurityOptions` have been deprecated. Use `FlowSource` instead.
### Minor Analysis Improvements
* Changed the output of `Node.toString` to better reflect how many indirections a given dataflow node has.
* Added a new predicate `Node.asDefinition` on `DataFlow::Node`s for selecting the dataflow node corresponding to a particular definition.
* The deprecated `DefaultTaintTracking` library has been removed.
* The `Guards` library has been replaced with the API-compatible `IRGuards` implementation, which has better precision in some cases.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.12.2
lastReleaseVersion: 0.12.1

View File

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

View File

@@ -328,7 +328,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
MetricFunction getMetrics() { result = this }
/** Holds if this function calls the function `f`. */
pragma[nomagic]
predicate calls(Function f) { this.calls(f, _) }
/**
@@ -339,6 +338,10 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
exists(FunctionCall call |
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
)
or
exists(DestructorCall call |
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
)
}
/** Holds if this function accesses a function or variable or enumerator `a`. */

View File

@@ -7,7 +7,371 @@ import cpp
import semmle.code.cpp.controlflow.BasicBlocks
import semmle.code.cpp.controlflow.SSA
import semmle.code.cpp.controlflow.Dominance
import IRGuards
/**
* A Boolean condition that guards one or more basic blocks. This includes
* operands of logical operators but not switch statements.
*/
class GuardCondition extends Expr {
GuardCondition() { is_condition(this) }
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
*
* ```
* [ (testIsTrue) ]
* [ this ----------------succ ---- controlled ]
* [ | | ]
* [ (testIsFalse) | ------ ... ]
* [ other ]
* ```
*
* The predicate holds if all paths to `controlled` go via the `testIsTrue`
* edge of the control-flow graph. In other words, the `testIsTrue` edge
* must dominate `controlled`. This means that `controlled` must be
* dominated by both `this` and `succ` (the target of the `testIsTrue`
* edge). It also means that any other edge into `succ` must be a back-edge
* from a node which is dominated by `succ`.
*
* The short-circuit boolean operations have slightly surprising behavior
* here: because the operation itself only dominates one branch (due to
* being short-circuited) then it will only control blocks dominated by the
* true (for `&&`) or false (for `||`) branch.
*/
cached
predicate controls(BasicBlock controlled, boolean testIsTrue) {
// This condition must determine the flow of control; that is, this
// node must be a top-level condition.
this.controlsBlock(controlled, testIsTrue)
or
exists(BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
this = binop and
lhs = binop.getLeftOperand() and
rhs = binop.getRightOperand() and
lhs.controls(controlled, testIsTrue) and
rhs.controls(controlled, testIsTrue)
)
or
exists(GuardCondition ne, GuardCondition operand |
this = operand and
operand = ne.(NotExpr).getOperand() and
ne.controls(controlled, testIsTrue.booleanNot())
)
}
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
cached
predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
compares_lt(this, left, right, k, isLessThan, testIsTrue)
}
/**
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
* If `isLessThan = false` then this implies `left >= right + k`.
*/
cached
predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
exists(boolean testIsTrue |
compares_lt(this, left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
)
}
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
cached
predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
compares_eq(this, left, right, k, areEqual, testIsTrue)
}
/**
* Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
* If `areEqual = false` then this implies `left != right + k`.
*/
cached
predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
exists(boolean testIsTrue |
compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
)
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`. This helper
* predicate does not necessarily hold for binary logical operations like
* `&&` and `||`. See the detailed explanation on predicate `controls`.
*/
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
exists(BasicBlock thisblock | thisblock.contains(this) |
exists(BasicBlock succ |
testIsTrue = true and succ = this.getATrueSuccessor()
or
testIsTrue = false and succ = this.getAFalseSuccessor()
|
bbDominates(succ, controlled) and
forall(BasicBlock pred | pred.getASuccessor() = succ |
pred = thisblock or bbDominates(succ, pred) or not reachable(pred)
)
)
)
}
}
private predicate is_condition(Expr guard) {
guard.isCondition()
or
is_condition(guard.(BinaryLogicalOperation).getAnOperand())
or
exists(NotExpr cond | is_condition(cond) and cond.getOperand() = guard)
}
/*
* Simplification of equality expressions:
* Simplify conditions in the source to the canonical form l op r + k.
*/
/**
* Holds if `left == right + k` is `areEqual` given that test is `testIsTrue`.
*
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
*/
private predicate compares_eq(
Expr test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) |
areEqual = true and testIsTrue = eq
or
areEqual = false and testIsTrue = eq.booleanNot()
)
or
logical_comparison_eq(test, left, right, k, areEqual, testIsTrue)
or
/* a == b + k => b == a - k */
exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, testIsTrue))
or
complex_eq(test, left, right, k, areEqual, testIsTrue)
or
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
compares_eq(test.(NotExpr).getOperand(), left, right, k, areEqual, isFalse)
)
}
/**
* If `test => part` and `part => left == right + k` then `test => left == right + k`.
* Similarly for the case where `test` is false.
*/
private predicate logical_comparison_eq(
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
exists(boolean partIsTrue, Expr part | test.impliesValue(part, partIsTrue, testIsTrue) |
compares_eq(part, left, right, k, areEqual, partIsTrue)
)
}
/** Rearrange various simple comparisons into `left == right + k` form. */
private predicate simple_comparison_eq(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual
) {
left = cmp.getLeftOperand() and
cmp.getOperator() = "==" and
right = cmp.getRightOperand() and
k = 0 and
areEqual = true
or
left = cmp.getLeftOperand() and
cmp.getOperator() = "!=" and
right = cmp.getRightOperand() and
k = 0 and
areEqual = false
}
private predicate complex_eq(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
sub_eq(cmp, left, right, k, areEqual, testIsTrue)
or
add_eq(cmp, left, right, k, areEqual, testIsTrue)
}
// left - x == right + c => left == right + (c+x)
// left == (right - x) + c => left == right + (c-x)
private predicate sub_eq(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
exists(SubExpr lhs, int c, int x |
compares_eq(cmp, lhs, right, c, areEqual, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRightOperand()) and
k = c + x
)
or
exists(SubExpr rhs, int c, int x |
compares_eq(cmp, left, rhs, c, areEqual, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRightOperand()) and
k = c - x
)
}
// left + x == right + c => left == right + (c-x)
// left == (right + x) + c => left == right + (c+x)
private predicate add_eq(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
) {
exists(AddExpr lhs, int c, int x |
compares_eq(cmp, lhs, right, c, areEqual, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeftOperand())
) and
k = c - x
)
or
exists(AddExpr rhs, int c, int x |
compares_eq(cmp, left, rhs, c, areEqual, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeftOperand())
) and
k = c + x
)
}
/*
* Simplification of inequality expressions:
* Simplify conditions in the source to the canonical form l < r + k.
*/
/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */
private predicate compares_lt(
Expr test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
/* In the simple case, the test is the comparison, so isLt = testIsTrue */
simple_comparison_lt(test, left, right, k) and isLt = true and testIsTrue = true
or
simple_comparison_lt(test, left, right, k) and isLt = false and testIsTrue = false
or
logical_comparison_lt(test, left, right, k, isLt, testIsTrue)
or
complex_lt(test, left, right, k, isLt, testIsTrue)
or
/* (not (left < right + k)) => (left >= right + k) */
exists(boolean isGe | isLt = isGe.booleanNot() |
compares_ge(test, left, right, k, isGe, testIsTrue)
)
or
/* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
compares_lt(test.(NotExpr).getOperand(), left, right, k, isLt, isFalse)
)
}
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
private predicate compares_ge(
Expr test, Expr left, Expr right, int k, boolean isGe, boolean testIsTrue
) {
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, testIsTrue))
}
/**
* If `test => part` and `part => left < right + k` then `test => left < right + k`.
* Similarly for the case where `test` evaluates false.
*/
private predicate logical_comparison_lt(
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
exists(boolean partIsTrue, Expr part | test.impliesValue(part, partIsTrue, testIsTrue) |
compares_lt(part, left, right, k, isLt, partIsTrue)
)
}
/** Rearrange various simple comparisons into `left < right + k` form. */
private predicate simple_comparison_lt(ComparisonOperation cmp, Expr left, Expr right, int k) {
left = cmp.getLeftOperand() and
cmp.getOperator() = "<" and
right = cmp.getRightOperand() and
k = 0
or
left = cmp.getLeftOperand() and
cmp.getOperator() = "<=" and
right = cmp.getRightOperand() and
k = 1
or
right = cmp.getLeftOperand() and
cmp.getOperator() = ">" and
left = cmp.getRightOperand() and
k = 0
or
right = cmp.getLeftOperand() and
cmp.getOperator() = ">=" and
left = cmp.getRightOperand() and
k = 1
}
private predicate complex_lt(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
sub_lt(cmp, left, right, k, isLt, testIsTrue)
or
add_lt(cmp, left, right, k, isLt, testIsTrue)
}
// left - x < right + c => left < right + (c+x)
// left < (right - x) + c => left < right + (c-x)
private predicate sub_lt(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
exists(SubExpr lhs, int c, int x |
compares_lt(cmp, lhs, right, c, isLt, testIsTrue) and
left = lhs.getLeftOperand() and
x = int_value(lhs.getRightOperand()) and
k = c + x
)
or
exists(SubExpr rhs, int c, int x |
compares_lt(cmp, left, rhs, c, isLt, testIsTrue) and
right = rhs.getLeftOperand() and
x = int_value(rhs.getRightOperand()) and
k = c - x
)
}
// left + x < right + c => left < right + (c-x)
// left < (right + x) + c => left < right + (c+x)
private predicate add_lt(
ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
) {
exists(AddExpr lhs, int c, int x |
compares_lt(cmp, lhs, right, c, isLt, testIsTrue) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeftOperand())
) and
k = c - x
)
or
exists(AddExpr rhs, int c, int x |
compares_lt(cmp, left, rhs, c, isLt, testIsTrue) and
(
right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeftOperand())
) and
k = c + x
)
}
/** The `int` value of integer constant expression. */
private int int_value(Expr e) {
e.getUnderlyingType() instanceof IntegralType and
result = e.getValue().toInt()
}
/** An `SsaDefinition` with an additional predicate `isLt`. */
class GuardedSsa extends SsaDefinition {

View File

@@ -5,8 +5,6 @@
import cpp
import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
/**
* Holds if `block` consists of an `UnreachedInstruction`.
@@ -203,30 +201,12 @@ private class GuardConditionFromIR extends GuardCondition {
* `&&` and `||`. See the detailed explanation on predicate `controls`.
*/
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
exists(IRBlock irb, Instruction instr |
exists(IRBlock irb |
ir.controls(irb, testIsTrue) and
instr = irb.getAnInstruction() and
instr.getAst().(ControlFlowNode).getBasicBlock() = controlled and
not isUnreachedBlock(irb) and
not this.excludeAsControlledInstruction(instr)
irb.getAnInstruction().getAst().(ControlFlowNode).getBasicBlock() = controlled and
not isUnreachedBlock(irb)
)
}
private predicate excludeAsControlledInstruction(Instruction instr) {
// Exclude the temporaries generated by a ternary expression.
exists(TranslatedConditionalExpr tce |
instr = tce.getInstruction(ConditionValueFalseStoreTag())
or
instr = tce.getInstruction(ConditionValueTrueStoreTag())
or
instr = tce.getInstruction(ConditionValueTrueTempAddressTag())
or
instr = tce.getInstruction(ConditionValueFalseTempAddressTag())
)
or
// Exclude unreached instructions, as their AST is the whole function and not a block.
instr instanceof UnreachedInstruction
}
}
/**

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -1,6 +1,4 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
@@ -14,8 +12,6 @@ import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
@@ -55,7 +51,7 @@ private import TaintTrackingParameter::Private
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
*/
abstract deprecated class Configuration extends DataFlow::Configuration {
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }

View File

@@ -1,6 +1,4 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
@@ -14,8 +12,6 @@ import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
@@ -55,7 +51,7 @@ private import TaintTrackingParameter::Private
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
*/
abstract deprecated class Configuration extends DataFlow::Configuration {
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }

View File

@@ -0,0 +1,21 @@
/**
* DEPRECATED: Use `semmle.code.cpp.ir.dataflow.TaintTracking` as a replacement.
*
* An IR taint tracking library that uses an IR DataFlow configuration to track
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
*/
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl as DefaultTaintTrackingImpl
deprecated predicate predictableOnlyFlow = DefaultTaintTrackingImpl::predictableOnlyFlow/1;
deprecated predicate tainted = DefaultTaintTrackingImpl::tainted/2;
deprecated predicate taintedIncludingGlobalVars =
DefaultTaintTrackingImpl::taintedIncludingGlobalVars/3;
deprecated predicate globalVarFromId = DefaultTaintTrackingImpl::globalVarFromId/1;
deprecated module TaintedWithPath = DefaultTaintTrackingImpl::TaintedWithPath;

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -10,12 +10,10 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
deprecated import FlowStateString
import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -50,7 +48,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract deprecated class Configuration extends string {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -191,7 +189,7 @@ abstract deprecated class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -212,7 +210,7 @@ abstract deprecated private class ConfigurationRecursionPrevention extends Confi
}
}
deprecated private FlowState relevantState(Configuration config) {
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -221,17 +219,17 @@ deprecated private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
deprecated TMkConfigState(Configuration config, FlowState state) {
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private module Config implements FullStateConfigSig {
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -298,13 +296,13 @@ deprecated private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
deprecated private import Impl<Config> as I
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
deprecated class PathNode instanceof I::PathNode {
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -331,10 +329,10 @@ deprecated class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
deprecated final FlowState getState() { result = getState(super.getState()) }
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -349,9 +347,9 @@ deprecated class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
deprecated module PathGraph = I::PathGraph;
module PathGraph = I::PathGraph;
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -359,10 +357,10 @@ deprecated private predicate hasFlow(Node source, Node sink, Configuration confi
)
}
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated predicate flowsTo = hasFlow/3;
predicate flowsTo = hasFlow/3;

View File

@@ -20,6 +20,4 @@ module CppDataFlow implements InputSig {
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
predicate validParameterAliasStep = Private::validParameterAliasStep/2;
}

View File

@@ -6,7 +6,6 @@ private import semmle.code.cpp.ir.internal.IRCppLanguage
private import SsaInternals as Ssa
private import DataFlowImplCommon as DataFlowImplCommon
private import codeql.util.Unit
private import Node0ToString
cached
private module Cached {
@@ -139,7 +138,11 @@ abstract class InstructionNode0 extends Node0Impl {
override DataFlowType getType() { result = getInstructionType(instr, _) }
override string toStringImpl() { result = instructionToString(instr) }
override string toStringImpl() {
if instr.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
then result = "this"
else result = instr.getAst().toString()
}
override Location getLocationImpl() {
if exists(instr.getAst().getLocation())
@@ -184,7 +187,11 @@ abstract class OperandNode0 extends Node0Impl {
override DataFlowType getType() { result = getOperandType(op, _) }
override string toStringImpl() { result = operandToString(op) }
override string toStringImpl() {
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
then result = "this"
else result = op.getDef().getAst().toString()
}
override Location getLocationImpl() {
if exists(op.getDef().getAst().getLocation())
@@ -1142,55 +1149,3 @@ private int countNumberOfBranchesUsingParameter(SwitchInstruction switch, Parame
)
)
}
/**
* Holds if the data-flow step from `node1` to `node2` can be used to
* determine where side-effects may return from a callable.
* For C/C++, this means that the step from `node1` to `node2` not only
* preserves the value, but also preserves the identity of the value.
* For example, the assignment to `x` that reads the value of `*p` in
* ```cpp
* int* p = ...
* int x = *p;
* ```
* does not preserve the identity of `*p`.
*/
bindingset[node1, node2]
pragma[inline_late]
predicate validParameterAliasStep(Node node1, Node node2) {
// When flow-through summaries are computed we track which parameters flow to out-going parameters.
// In an example such as:
// ```
// modify(int* px) { *px = source(); }
// void modify_copy(int* p) {
// int x = *p;
// modify(&x);
// }
// ```
// since dataflow tracks each indirection as a separate SSA variable dataflow
// sees the above roughly as
// ```
// modify(int* px, int deref_px) { deref_px = source(); }
// void modify_copy(int* p, int deref_p) {
// int x = deref_p;
// modify(&x, x);
// }
// ```
// and when dataflow computes flow from a parameter to a post-update node to
// conclude which parameters are "updated" by the call to `modify_copy` it
// finds flow from `x [post update]` to `deref_p [post update]`.
// To prevent this we exclude steps that don't preserve identity. We do this
// by excluding flow from the right-hand side of `StoreInstruction`s to the
// `StoreInstruction`. This is sufficient because, for flow-through summaries,
// we're only interested in indirect parameters such as `deref_p` in the
// exampe above (i.e., the parameters with a non-zero indirection index), and
// if that ever flows to the right-hand side of a `StoreInstruction` then
// there must have been a dereference to reduce its indirection index down to
// 0.
not exists(Operand operand |
node1.asOperand() = operand and
node2.asInstruction().(StoreInstruction).getSourceValueOperand() = operand
)
// TODO: Also block flow through models that don't preserve identity such
// as `strdup`.
}

View File

@@ -15,21 +15,20 @@ private import ModelUtil
private import SsaInternals as Ssa
private import DataFlowImplCommon as DataFlowImplCommon
private import codeql.util.Unit
private import Node0ToString
/**
* The IR dataflow graph consists of the following nodes:
* - `Node0`, which injects most instructions and operands directly into the
* dataflow graph.
* - `Node0`, which injects most instructions and operands directly into the dataflow graph.
* - `VariableNode`, which is used to model flow through global variables.
* - `PostUpdateNodeImpl`, which is used to model the state of an object after
* an update after a number of loads.
* - `SsaPhiNode`, which represents phi nodes as computed by the shared SSA
* library.
* - `RawIndirectOperand`, which represents the value of `operand` after
* loading the address a number of times.
* - `RawIndirectInstruction`, which represents the value of `instr` after
* loading the address a number of times.
* - `PostFieldUpdateNode`, which is used to model the state of a field after a value has been stored
* into an address after a number of loads.
* - `SsaPhiNode`, which represents phi nodes as computed by the shared SSA library.
* - `IndirectArgumentOutNode`, which represents the value of an argument (and its indirections) after
* it leaves a function call.
* - `RawIndirectOperand`, which represents the value of `operand` after loading the address a number
* of times.
* - `RawIndirectInstruction`, which represents the value of `instr` after loading the address a number
* of times.
*/
cached
private newtype TIRDataFlowNode =
@@ -38,13 +37,14 @@ private newtype TIRDataFlowNode =
indirectionIndex =
[getMinIndirectionsForType(var.getUnspecifiedType()) .. Ssa::getMaxIndirectionsForType(var.getUnspecifiedType())]
} or
TPostUpdateNodeImpl(Operand operand, int indirectionIndex) {
operand = any(FieldAddress fa).getObjectAddressOperand() and
indirectionIndex = [0 .. Ssa::countIndirectionsForCppType(Ssa::getLanguageType(operand))]
or
Ssa::isModifiableByCall(operand, indirectionIndex)
TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) {
indirectionIndex =
[1 .. Ssa::countIndirectionsForCppType(operand.getObjectAddress().getResultLanguageType())]
} or
TSsaPhiNode(Ssa::PhiNode phi) or
TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) {
Ssa::isModifiableByCall(operand, indirectionIndex)
} or
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
} or
@@ -84,7 +84,7 @@ private predicate parameterIsRedefined(Parameter p) {
class FieldAddress extends Operand {
FieldAddressInstruction fai;
FieldAddress() { fai = this.getDef() and not Ssa::ignoreOperand(this) }
FieldAddress() { fai = this.getDef() }
/** Gets the field associated with this instruction. */
Field getField() { result = fai.getField() }
@@ -260,71 +260,6 @@ class Node extends TIRDataFlowNode {
*/
Expr asDefiningArgument() { result = this.asDefiningArgument(_) }
/**
* Gets the definition associated with this node, if any.
*
* For example, consider the following example
* ```cpp
* int x = 42; // 1
* x = 34; // 2
* ++x; // 3
* x++; // 4
* x += 1; // 5
* int y = x += 2; // 6
* ```
* - For (1) the result is `42`.
* - For (2) the result is `x = 34`.
* - For (3) the result is `++x`.
* - For (4) the result is `x++`.
* - For (5) the result is `x += 1`.
* - For (6) there are two results:
* - For the definition generated by `x += 2` the result is `x += 2`
* - For the definition generated by `int y = ...` the result is
* also `x += 2`.
*
* For assignments, `node.asDefinition()` and `node.asExpr()` will both exist
* for the same dataflow node. However, for expression such as `x++` that
* both write to `x` and read the current value of `x`, `node.asDefinition()`
* will give the node corresponding to the value after the increment, and
* `node.asExpr()` will give the node corresponding to the value before the
* increment. For an example of this, consider the following:
*
* ```cpp
* sink(x++);
* ```
* in the above program, there will not be flow from a node `n` such that
* `n.asDefinition() instanceof IncrementOperation` to the argument of `sink`
* since the value passed to `sink` is the value before to the increment.
* However, there will be dataflow from a node `n` such that
* `n.asExpr() instanceof IncrementOperation` since the result of evaluating
* the expression `x++` is passed to `sink`.
*/
Expr asDefinition() {
exists(StoreInstruction store |
store = this.asInstruction() and
result = asDefinitionImpl(store)
)
}
/**
* Gets the indirect definition at a given indirection corresponding to this
* node, if any.
*
* See the comments on `Node.asDefinition` for examples.
*/
Expr asIndirectDefinition(int indirectionIndex) {
exists(StoreInstruction store |
this.(IndirectInstruction).hasInstructionAndIndirectionIndex(store, indirectionIndex) and
result = asDefinitionImpl(store)
)
}
/**
* Gets the indirect definition at some indirection corresponding to this
* node, if any.
*/
Expr asIndirectDefinition() { result = this.asIndirectDefinition(_) }
/**
* Gets the argument that defines this `DefinitionByReferenceNode`, if any.
*
@@ -437,12 +372,7 @@ class Node extends TIRDataFlowNode {
* `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is
* a partial definition of `&x`).
*/
Expr asPartialDefinition() {
exists(PartialDefinitionNode pdn | this = pdn |
pdn.getIndirectionIndex() > 0 and
result = pdn.getDefinedExpr()
)
}
Expr asPartialDefinition() { result = this.(PartialDefinitionNode).getDefinedExpr() }
/**
* Gets an upper bound on the type of this node.
@@ -487,46 +417,12 @@ class Node extends TIRDataFlowNode {
}
private string toExprString(Node n) {
not isDebugMode() and
(
result = n.asExpr(0).toString()
or
not exists(n.asExpr()) and
result = stars(n) + n.asIndirectExpr(0, 1).toString()
)
result = n.asExpr(0).toString()
or
not exists(n.asExpr()) and
result = n.asIndirectExpr(0, 1).toString() + " indirection"
}
private module NodeStars {
private int getNumberOfIndirections(Node n) {
result = n.(RawIndirectOperand).getIndirectionIndex()
or
result = n.(RawIndirectInstruction).getIndirectionIndex()
or
result = n.(VariableNode).getIndirectionIndex()
or
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
or
result = n.(FinalParameterNode).getIndirectionIndex()
}
private int maxNumberOfIndirections() { result = max(getNumberOfIndirections(_)) }
private string repeatStars(int n) {
n = 0 and result = ""
or
n = [1 .. maxNumberOfIndirections()] and
result = "*" + repeatStars(n - 1)
}
/**
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
* output for `n`.
*/
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
}
private import NodeStars
/**
* A class that lifts pre-SSA dataflow nodes to regular dataflow nodes.
*/
@@ -589,50 +485,37 @@ Type stripPointer(Type t) {
result = t.(FunctionPointerIshType).getBaseType()
}
private class PostUpdateNodeImpl extends PartialDefinitionNode, TPostUpdateNodeImpl {
int indirectionIndex;
Operand operand;
PostUpdateNodeImpl() { this = TPostUpdateNodeImpl(operand, indirectionIndex) }
override Declaration getFunction() { result = operand.getUse().getEnclosingFunction() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
/** Gets the operand associated with this node. */
Operand getOperand() { result = operand }
/** Gets the indirection index associated with this node. */
override int getIndirectionIndex() { result = indirectionIndex }
override Location getLocationImpl() { result = operand.getLocation() }
final override Node getPreUpdateNode() {
indirectionIndex > 0 and
hasOperandAndIndex(result, operand, indirectionIndex)
or
indirectionIndex = 0 and
result.asOperand() = operand
}
final override Expr getDefinedExpr() {
result = operand.getDef().getUnconvertedResultExpression()
}
}
/**
* INTERNAL: do not use.
*
* The node representing the value of a field after it has been updated.
*/
class PostFieldUpdateNode extends PostUpdateNodeImpl {
class PostFieldUpdateNode extends TPostFieldUpdateNode, PartialDefinitionNode {
int indirectionIndex;
FieldAddress fieldAddress;
PostFieldUpdateNode() { operand = fieldAddress.getObjectAddressOperand() }
PostFieldUpdateNode() { this = TPostFieldUpdateNode(fieldAddress, indirectionIndex) }
override Declaration getFunction() { result = fieldAddress.getUse().getEnclosingFunction() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
FieldAddress getFieldAddress() { result = fieldAddress }
Field getUpdatedField() { result = this.getFieldAddress().getField() }
Field getUpdatedField() { result = fieldAddress.getField() }
int getIndirectionIndex() { result = indirectionIndex }
override Node getPreUpdateNode() {
hasOperandAndIndex(result, pragma[only_bind_into](fieldAddress).getObjectAddressOperand(),
indirectionIndex)
}
override Expr getDefinedExpr() {
result = fieldAddress.getObjectAddress().getUnconvertedResultExpression()
}
override Location getLocationImpl() { result = fieldAddress.getLocation() }
override string toStringImpl() { result = this.getPreUpdateNode() + " [post update]" }
}
@@ -817,12 +700,10 @@ class IndirectParameterNode extends Node instanceof IndirectInstruction {
override Location getLocationImpl() { result = this.getParameter().getLocation() }
override string toStringImpl() {
exists(string prefix | prefix = stars(this) |
result = prefix + this.getParameter().toString()
or
not exists(this.getParameter()) and
result = prefix + "this"
)
result = this.getParameter().toString() + " indirection"
or
not exists(this.getParameter()) and
result = "this indirection"
}
}
@@ -870,8 +751,13 @@ class IndirectReturnNode extends Node {
* A node representing the indirection of a value after it
* has been returned from a function.
*/
class IndirectArgumentOutNode extends PostUpdateNodeImpl {
override ArgumentOperand operand;
class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PartialDefinitionNode {
ArgumentOperand operand;
int indirectionIndex;
IndirectArgumentOutNode() { this = TIndirectArgumentOutNode(operand, indirectionIndex) }
int getIndirectionIndex() { result = indirectionIndex }
int getArgumentIndex() {
exists(CallInstruction call | call.getArgumentOperand(result) = operand)
@@ -883,16 +769,24 @@ class IndirectArgumentOutNode extends PostUpdateNodeImpl {
Function getStaticCallTarget() { result = this.getCallInstruction().getStaticCallTarget() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = this.getCallInstruction().getEnclosingFunction() }
override Node getPreUpdateNode() { hasOperandAndIndex(result, operand, indirectionIndex) }
override string toStringImpl() {
exists(string prefix | if indirectionIndex > 0 then prefix = "" else prefix = "pointer to " |
// This string should be unique enough to be helpful but common enough to
// avoid storing too many different strings.
result = prefix + this.getStaticCallTarget().getName() + " output argument"
or
not exists(this.getStaticCallTarget()) and
result = prefix + "output argument"
)
// This string should be unique enough to be helpful but common enough to
// avoid storing too many different strings.
result = this.getStaticCallTarget().getName() + " output argument"
or
not exists(this.getStaticCallTarget()) and
result = "output argument"
}
override Location getLocationImpl() { result = operand.getLocation() }
override Expr getDefinedExpr() { result = operand.getDef().getUnconvertedResultExpression() }
}
/**
@@ -997,8 +891,7 @@ private Type getTypeImpl0(Type t, int indirectionIndex) {
*
* If `indirectionIndex` cannot be stripped off `t`, an `UnknownType` is returned.
*/
bindingset[t, indirectionIndex]
pragma[inline_late]
bindingset[indirectionIndex]
Type getTypeImpl(Type t, int indirectionIndex) {
result = getTypeImpl0(t, indirectionIndex)
or
@@ -1050,7 +943,7 @@ private module RawIndirectNodes {
}
override string toStringImpl() {
result = stars(this) + operandNode(this.getOperand()).toStringImpl()
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
}
}
@@ -1092,7 +985,7 @@ private module RawIndirectNodes {
}
override string toStringImpl() {
result = stars(this) + instructionNode(this.getInstruction()).toStringImpl()
result = instructionNode(this.getInstruction()).toStringImpl() + " indirection"
}
}
@@ -1185,7 +1078,9 @@ class FinalParameterNode extends Node, TFinalParameterNode {
result instanceof UnknownDefaultLocation
}
override string toStringImpl() { result = stars(this) + p.toString() }
override string toStringImpl() {
if indirectionIndex > 1 then result = p.toString() + " indirection" else result = p.toString()
}
}
/**
@@ -1247,6 +1142,22 @@ private module GetConvertedResultExpression {
}
private Expr getConvertedResultExpressionImpl0(Instruction instr) {
// For an expression such as `i += 2` we pretend that the generated
// `StoreInstruction` contains the result of the expression even though
// this isn't totally aligned with the C/C++ standard.
exists(TranslatedAssignOperation tao |
result = tao.getExpr() and
instr = tao.getInstruction(any(AssignmentStoreTag tag))
)
or
// Similarly for `i++` and `++i` we pretend that the generated
// `StoreInstruction` is contains the result of the expression even though
// this isn't totally aligned with the C/C++ standard.
exists(TranslatedCrementOperation tco |
result = tco.getExpr() and
instr = tco.getInstruction(any(CrementStoreTag tag))
)
or
// IR construction inserts an additional cast to a `size_t` on the extent
// of a `new[]` expression. The resulting `ConvertInstruction` doesn't have
// a result for `getConvertedResultExpression`. We remap this here so that
@@ -1254,7 +1165,7 @@ private module GetConvertedResultExpression {
// represents the extent.
exists(TranslatedNonConstantAllocationSize tas |
result = tas.getExtent().getExpr() and
instr = tas.getInstruction(AllocationExtentConvertTag())
instr = tas.getInstruction(any(AllocationExtentConvertTag tag))
)
or
// There's no instruction that returns `ParenthesisExpr`, but some queries
@@ -1263,39 +1174,6 @@ private module GetConvertedResultExpression {
result = ttc.getExpr().(ParenthesisExpr) and
instr = ttc.getResult()
)
or
// Certain expressions generate `CopyValueInstruction`s only when they
// are needed. Examples of this include crement operations and compound
// assignment operations. For example:
// ```cpp
// int x = ...
// int y = x++;
// ```
// this generate IR like:
// ```
// r1(glval<int>) = VariableAddress[x] :
// r2(int) = Constant[0] :
// m3(int) = Store[x] : &:r1, r2
// r4(glval<int>) = VariableAddress[y] :
// r5(glval<int>) = VariableAddress[x] :
// r6(int) = Load[x] : &:r5, m3
// r7(int) = Constant[1] :
// r8(int) = Add : r6, r7
// m9(int) = Store[x] : &:r5, r8
// r11(int) = CopyValue : r6
// m12(int) = Store[y] : &:r4, r11
// ```
// When the `CopyValueInstruction` is not generated there is no instruction
// whose `getConvertedResultExpression` maps back to the expression. When
// such an instruction doesn't exist it means that the old value is not
// needed, and in that case the only value that will propagate forward in
// the program is the value that's been updated. So in those cases we just
// use the result of `node.asDefinition()` as the result of `node.asExpr()`.
exists(TranslatedCoreExpr tco |
tco.getInstruction(_) = instr and
tco.producesExprResult() and
result = asDefinitionImpl0(instr)
)
}
private Expr getConvertedResultExpressionImpl(Instruction instr) {
@@ -1304,75 +1182,6 @@ private module GetConvertedResultExpression {
not exists(getConvertedResultExpressionImpl0(instr)) and
result = instr.getConvertedResultExpression()
}
/**
* Gets the result for `node.asDefinition()` (when `node` is the instruction
* node that wraps `store`) in the cases where `store.getAst()` should not be
* used to define the result of `node.asDefinition()`.
*/
private Expr asDefinitionImpl0(StoreInstruction store) {
// For an expression such as `i += 2` we pretend that the generated
// `StoreInstruction` contains the result of the expression even though
// this isn't totally aligned with the C/C++ standard.
exists(TranslatedAssignOperation tao |
store = tao.getInstruction(AssignmentStoreTag()) and
result = tao.getExpr()
)
or
// Similarly for `i++` and `++i` we pretend that the generated
// `StoreInstruction` is contains the result of the expression even though
// this isn't totally aligned with the C/C++ standard.
exists(TranslatedCrementOperation tco |
store = tco.getInstruction(CrementStoreTag()) and
result = tco.getExpr()
)
}
/**
* Holds if the expression returned by `store.getAst()` should not be
* returned as the result of `node.asDefinition()` when `node` is the
* instruction node that wraps `store`.
*/
private predicate excludeAsDefinitionResult(StoreInstruction store) {
// Exclude the store to the temporary generated by a ternary expression.
exists(TranslatedConditionalExpr tce |
store = tce.getInstruction(ConditionValueFalseStoreTag())
or
store = tce.getInstruction(ConditionValueTrueStoreTag())
)
}
/**
* Gets the expression that represents the result of `StoreInstruction` for
* dataflow purposes.
*
* For example, consider the following example
* ```cpp
* int x = 42; // 1
* x = 34; // 2
* ++x; // 3
* x++; // 4
* x += 1; // 5
* int y = x += 2; // 6
* ```
* For (1) the result is `42`.
* For (2) the result is `x = 34`.
* For (3) the result is `++x`.
* For (4) the result is `x++`.
* For (5) the result is `x += 1`.
* For (6) there are two results:
* - For the `StoreInstruction` generated by `x += 2` the result
* is `x += 2`
* - For the `StoreInstruction` generated by `int y = ...` the result
* is also `x += 2`
*/
Expr asDefinitionImpl(StoreInstruction store) {
not exists(asDefinitionImpl0(store)) and
not excludeAsDefinitionResult(store) and
result = store.getAst().(Expr).getUnconverted()
or
result = asDefinitionImpl0(store)
}
}
private import GetConvertedResultExpression
@@ -1748,10 +1557,6 @@ abstract class PostUpdateNode extends Node {
* ```
*/
abstract private class PartialDefinitionNode extends PostUpdateNode {
/** Gets the indirection index of this node. */
abstract int getIndirectionIndex();
/** Gets the expression that is partially defined by this node. */
abstract Expr getDefinedExpr();
}
@@ -1766,8 +1571,6 @@ abstract private class PartialDefinitionNode extends PostUpdateNode {
* `getVariableAccess()` equal to `x`.
*/
class DefinitionByReferenceNode extends IndirectArgumentOutNode {
DefinitionByReferenceNode() { this.getIndirectionIndex() > 0 }
/** Gets the unconverted argument corresponding to this node. */
Expr getArgument() { result = this.getAddressOperand().getDef().getUnconvertedResultExpression() }
@@ -1819,7 +1622,9 @@ class VariableNode extends Node, TVariableNode {
result instanceof UnknownDefaultLocation
}
override string toStringImpl() { result = stars(this) + v.toString() }
override string toStringImpl() {
if indirectionIndex = 1 then result = v.toString() else result = v.toString() + " indirection"
}
}
/**
@@ -2279,25 +2084,6 @@ class Content extends TContent {
abstract predicate impliesClearOf(Content c);
}
private module ContentStars {
private int maxNumberOfIndirections() { result = max(any(Content c).getIndirectionIndex()) }
private string repeatStars(int n) {
n = 0 and result = ""
or
n = [1 .. maxNumberOfIndirections()] and
result = "*" + repeatStars(n - 1)
}
/**
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
* output for `c`.
*/
string contentStars(Content c) { result = repeatStars(c.getIndirectionIndex() - 1) }
}
private import ContentStars
/** A reference through a non-union instance field. */
class FieldContent extends Content, TFieldContent {
Field f;
@@ -2305,7 +2091,11 @@ class FieldContent extends Content, TFieldContent {
FieldContent() { this = TFieldContent(f, indirectionIndex) }
override string toString() { result = contentStars(this) + f.toString() }
override string toString() {
indirectionIndex = 1 and result = f.toString()
or
indirectionIndex > 1 and result = f.toString() + " indirection"
}
Field getField() { result = f }
@@ -2334,7 +2124,11 @@ class UnionContent extends Content, TUnionContent {
UnionContent() { this = TUnionContent(u, bytes, indirectionIndex) }
override string toString() { result = contentStars(this) + u.toString() }
override string toString() {
indirectionIndex = 1 and result = u.toString()
or
indirectionIndex > 1 and result = u.toString() + " indirection"
}
/** Gets a field of the underlying union of this `UnionContent`, if any. */
Field getAField() { result = u.getAField() and getFieldSize(result) = bytes }

View File

@@ -1,9 +0,0 @@
/**
* This file activates debugging mode for dataflow node printing.
*/
private import Node0ToString
private class DebugNode0ToString extends Node0ToString {
final override predicate isDebugMode() { any() }
}

View File

@@ -0,0 +1,668 @@
/**
* INTERNAL: Do not use.
*
* An IR taint tracking library that uses an IR DataFlow configuration to track
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
*/
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.ResolveCall
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.ir.dataflow.TaintTracking
private import semmle.code.cpp.ir.dataflow.TaintTracking2
private import semmle.code.cpp.ir.dataflow.TaintTracking3
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
/**
* A predictable instruction is one where an external user can predict
* the value. For example, a literal in the source code is considered
* predictable.
*/
private predicate predictableInstruction(Instruction instr) {
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
// This could be a conversion on a string literal
predictableInstruction(instr.(UnaryInstruction).getUnary())
}
/**
* Functions that we should only allow taint to flow through (to the return
* value) if all but the source argument are 'predictable'. This is done to
* emulate the old security library's implementation rather than due to any
* strong belief that this is the right approach.
*
* Note that the list itself is not very principled; it consists of all the
* functions listed in the old security library's [default] `isPureFunction`
* that have more than one argument, but are not in the old taint tracking
* library's `returnArgument` predicate.
*/
predicate predictableOnlyFlow(string name) {
name =
[
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
"strtoul"
]
}
private DataFlow::Node getNodeForSource(Expr source) {
isUserInput(source, _) and
result = getNodeForExpr(source)
}
private DataFlow::Node getNodeForExpr(Expr node) {
node = DataFlow::ExprFlowCached::asExprInternal(result)
or
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
not argv(node.(VariableAccess).getTarget())
}
private predicate conflatePointerAndPointee(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Flow from `op` to `*op`.
exists(Operand operand, int indirectionIndex |
nodeHasOperand(nodeFrom, operand, indirectionIndex) and
nodeHasOperand(nodeTo, operand, indirectionIndex - 1)
)
or
// Flow from `instr` to `*instr`.
exists(Instruction instr, int indirectionIndex |
nodeHasInstruction(nodeFrom, instr, indirectionIndex) and
nodeHasInstruction(nodeTo, instr, indirectionIndex - 1)
)
}
private module DefaultTaintTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
conflatePointerAndPointee(nodeFrom, nodeTo)
}
}
private module DefaultTaintTrackingFlow = TaintTracking::Global<DefaultTaintTrackingConfig>;
private module ToGlobalVarTaintTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
predicate isSink(DataFlow::Node sink) { sink.asVariable() instanceof GlobalOrNamespaceVariable }
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
}
predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private module ToGlobalVarTaintTrackingFlow = TaintTracking::Global<ToGlobalVarTaintTrackingConfig>;
private module FromGlobalVarTaintTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
// This set of sources should be reasonably small, which is good for
// performance since the set of sinks is very large.
ToGlobalVarTaintTrackingFlow::flowTo(source)
}
predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
// Additional step for flow out of variables. There is no flow _into_
// variables in this configuration, so this step only serves to take flow
// out of a variable that's a source.
readsVariable(n2.asInstruction(), n1.asVariable())
}
predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private module FromGlobalVarTaintTrackingFlow =
TaintTracking::Global<FromGlobalVarTaintTrackingConfig>;
private predicate readsVariable(LoadInstruction load, Variable var) {
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
}
private predicate writesVariable(StoreInstruction store, Variable var) {
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
}
/**
* A variable that has any kind of upper-bound check anywhere in the program. This is
* biased towards being inclusive because there are a lot of valid ways of doing an
* upper bounds checks if we don't consider where it occurs, for example:
* ```
* if (x < 10) { sink(x); }
*
* if (10 > y) { sink(y); }
*
* if (z > 10) { z = 10; }
* sink(z);
* ```
*/
// TODO: This coarse overapproximation, ported from the old taint tracking
// library, could be replaced with an actual semantic check that a particular
// variable _access_ is guarded by an upper-bound check. We probably don't want
// to do this right away since it could expose a lot of FPs that were
// previously suppressed by this predicate by coincidence.
private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
not oper.getAnOperand().getValue() = "0"
)
}
private predicate nodeIsBarrierEqualityCandidate(
DataFlow::Node node, Operand access, Variable checkedVar
) {
exists(Instruction instr | instr = node.asOperand().getDef() |
readsVariable(instr, checkedVar) and
any(IRGuardCondition guard).ensuresEq(access, _, _, instr.getBlock(), true)
)
}
cached
private module Cached {
cached
predicate nodeIsBarrier(DataFlow::Node node) {
exists(Variable checkedVar, Instruction instr | instr = node.asOperand().getDef() |
readsVariable(instr, checkedVar) and
hasUpperBoundsCheck(checkedVar)
)
or
exists(Variable checkedVar, Operand access |
/*
* This node is guarded by a condition that forces the accessed variable
* to equal something else. For example:
* ```
* x = taintsource()
* if (x == 10) {
* taintsink(x); // not considered tainted
* }
* ```
*/
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
readsVariable(access.getDef(), checkedVar)
)
}
cached
predicate nodeIsBarrierIn(DataFlow::Node node) {
// don't use dataflow into taint sources, as this leads to duplicate results.
exists(Expr source | isUserInput(source, _) |
source = DataFlow::ExprFlowCached::asExprInternal(node)
or
// This case goes together with the similar (but not identical) rule in
// `getNodeForSource`.
node = DataFlow::definitionByReferenceNodeFromArgument(source)
)
or
// don't use dataflow into binary instructions if both operands are unpredictable
exists(BinaryInstruction iTo |
iTo = node.asInstruction() and
not predictableInstruction(iTo.getLeft()) and
not predictableInstruction(iTo.getRight()) and
// propagate taint from either the pointer or the offset, regardless of predictability
not iTo instanceof PointerArithmeticInstruction
)
or
// don't use dataflow through calls to pure functions if two or more operands
// are unpredictable
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
iTo = node.asInstruction() and
isPureFunction(iTo.getStaticCallTarget().getName()) and
iFrom1 = iTo.getAnArgument() and
iFrom2 = iTo.getAnArgument() and
not predictableInstruction(iFrom1) and
not predictableInstruction(iFrom2) and
iFrom1 != iFrom2
)
}
cached
Element adjustedSink(DataFlow::Node sink) {
// TODO: is it more appropriate to use asConvertedExpr here and avoid
// `getConversion*`? Or will that cause us to miss some cases where there's
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
// pretend there was flow to the converted `Expr` for the sake of
// compatibility.
sink.asExpr().getConversion*() = result
or
// For compatibility, send flow from arguments to parameters, even for
// functions with no body.
exists(FunctionCall call, int i |
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
)
or
// For compatibility, send flow into a `Variable` if there is flow to any
// Load or Store of that variable.
exists(CopyInstruction copy |
copy.getSourceValue() = sink.asInstruction() and
(
readsVariable(copy, result) or
writesVariable(copy, result)
) and
not hasUpperBoundsCheck(result)
)
or
// For compatibility, send flow into a `NotExpr` even if it's part of a
// short-circuiting condition and thus might get skipped.
result.(NotExpr).getOperand() = sink.asExpr()
or
// Taint postfix and prefix crement operations when their operand is tainted.
result.(CrementOperation).getAnOperand() = sink.asExpr()
or
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
result.(AssignOperation).getAnOperand() = sink.asExpr()
or
result =
sink.asOperand()
.(SideEffectOperand)
.getUse()
.(ReadSideEffectInstruction)
.getArgumentDef()
.getUnconvertedResultExpression()
}
/**
* Step to return value of a modeled function when an input taints the
* dereference of the return value.
*/
cached
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
n1 = callInput(call, modelIn) and
(
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
or
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
) and
call.getStaticCallTarget() = func and
modelOut.isReturnValueDeref() and
call = n2.asInstruction()
)
}
}
private import Cached
/**
* Holds if `tainted` may contain taint from `source`.
*
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This doesn't include data flow through global variables.
* If you need that you must call `taintedIncludingGlobalVars`.
*/
cached
predicate tainted(Expr source, Element tainted) {
exists(DataFlow::Node sink |
DefaultTaintTrackingFlow::flow(getNodeForSource(source), sink) and
tainted = adjustedSink(sink)
)
}
/**
* Holds if `tainted` may contain taint from `source`, where the taint passed
* through a global variable named `globalVar`.
*
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This version gives the same results as tainted but also includes
* data flow through global variables.
*
* The parameter `globalVar` is the qualified name of the last global variable
* used to move the value from source to tainted. If the taint did not pass
* through a global variable, then `globalVar = ""`.
*/
cached
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
tainted(source, tainted) and
globalVar = ""
or
exists(
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
global = variableNode.getVariable() and
ToGlobalVarTaintTrackingFlow::flow(getNodeForSource(source), variableNode) and
FromGlobalVarTaintTrackingFlow::flow(variableNode, sink) and
tainted = adjustedSink(sink) and
global = globalVarFromId(globalVar)
)
}
/**
* Gets the global variable whose qualified name is `id`. Use this predicate
* together with `taintedIncludingGlobalVars`. Example:
*
* ```
* exists(string varName |
* taintedIncludingGlobalVars(source, tainted, varName) and
* var = globalVarFromId(varName)
* )
* ```
*/
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
/**
* Provides definitions for augmenting source/sink pairs with data-flow paths
* between them. From a `@kind path-problem` query, import this module in the
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
* in place of `tainted`.
*
* Importing this module will also import the query predicates that contain the
* taint paths.
*/
module TaintedWithPath {
private newtype TSingleton = MkSingleton()
/**
* A taint-tracking configuration that matches sources and sinks in the same
* way as the `tainted` predicate.
*
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
* a characteristic predicate.
*/
class TaintTrackingConfiguration extends TSingleton {
/** Override this to specify which elements are sources in this configuration. */
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
/** Override this to specify which elements are sinks in this configuration. */
abstract predicate isSink(Element e);
/** Override this to specify which expressions are barriers in this configuration. */
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
/**
* Override this predicate to `any()` to allow taint to flow through global
* variables.
*/
predicate taintThroughGlobals() { none() }
/** Gets a textual representation of this element. */
string toString() { result = "TaintTrackingConfiguration" }
}
private module AdjustedConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(TaintTrackingConfiguration cfg, Expr e |
cfg.isSource(e) and source = getNodeForExpr(e)
)
}
predicate isSink(DataFlow::Node sink) {
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
}
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
conflatePointerAndPointee(n1, n2)
or
// Steps into and out of global variables
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
)
or
additionalTaintStep(n1, n2)
}
predicate isBarrier(DataFlow::Node node) {
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
}
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
predicate neverSkip(Node node) { none() }
}
private module AdjustedFlow = TaintTracking::Global<AdjustedConfig>;
/*
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
* conversions, and a `Variable` maps to all loads and stores from it. Because
* the path node is part of the tuple that constitutes the alert, this leads
* to duplicate alerts.
*
* To avoid showing duplicates, we edit the graph to replace the final node
* coming from the data-flow library with a node that matches exactly the
* `Element` sink that's requested.
*
* The same is done for sources.
*/
private newtype TPathNode =
TWrapPathNode(AdjustedFlow::PathNode n) or
// There's a single newtype constructor for both sources and sinks since
// that makes it easiest to deal with the case where source = sink.
TEndpointPathNode(Element e) {
exists(DataFlow::Node sourceNode, DataFlow::Node sinkNode |
AdjustedFlow::flow(sourceNode, sinkNode)
|
sourceNode = getNodeForExpr(e) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
or
e = adjustedSink(sinkNode) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
)
}
/** An opaque type used for the nodes of a data-flow path. */
class PathNode extends TPathNode {
/** Gets a textual representation of this element. */
string toString() { none() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
none()
}
}
/**
* INTERNAL: Do not use.
*/
module Private {
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
/** Gets the element that `pathNode` wraps, if any. */
Element getElementFromPathNode(PathNode pathNode) {
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
result = node.asInstruction().getAst()
or
result = node.asOperand().getDef().getAst()
)
or
result = pathNode.(EndpointPathNode).inner()
}
}
private class WrapPathNode extends PathNode, TWrapPathNode {
AdjustedFlow::PathNode inner() { this = TWrapPathNode(result) }
override string toString() { result = this.inner().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private class EndpointPathNode extends PathNode, TEndpointPathNode {
Expr inner() { this = TEndpointPathNode(result) }
override string toString() { result = this.inner().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.inner()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/** A PathNode whose `Element` is a source. It may also be a sink. */
private class InitialPathNode extends EndpointPathNode {
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
}
/** A PathNode whose `Element` is a sink. It may also be a source. */
private class FinalPathNode extends EndpointPathNode {
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
}
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) {
AdjustedFlow::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
AdjustedFlow::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
AdjustedFlow::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
AdjustedFlow::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
/**
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
* from `par` to `ret` within it, in the graph of data flow path explanations.
*/
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
AdjustedFlow::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
AdjustedFlow::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), sinkNode.inner()) and
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
AdjustedFlow::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
AdjustedFlow::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
key = "semmle.label" and val = n.toString()
}
/**
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
* this predicate.
*
* A tainted expression is either directly user input, or is computed from
* user input in a way that users can probably control the exact output of
* the computation.
*/
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
exists(DataFlow::Node flowSource, DataFlow::Node flowSink |
source = sourceNode.(InitialPathNode).inner() and
flowSource = getNodeForExpr(source) and
AdjustedFlow::flow(flowSource, flowSink) and
tainted = adjustedSink(flowSink) and
tainted = sinkNode.(FinalPathNode).inner()
)
}
private predicate isGlobalVariablePathNode(WrapPathNode n) {
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
or
n.inner().getNode().asIndirectVariable() instanceof GlobalOrNamespaceVariable
}
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
edges(a, b) and
not isGlobalVariablePathNode(a) and
not isGlobalVariablePathNode(b)
}
/**
* Holds if `tainted` can be reached from a taint source without passing
* through a global variable.
*/
predicate taintedWithoutGlobals(Element tainted) {
exists(PathNode sourceNode, FinalPathNode sinkNode |
AdjustedConfig::isSource(sourceNode.(WrapPathNode).inner().getNode()) and
edgesWithoutGlobals+(sourceNode, sinkNode) and
tainted = sinkNode.inner()
)
}
}

View File

@@ -1,75 +0,0 @@
/**
* This file contains the abstract class that serves as the base class for
* dataflow node printing.
*
* By default, a non-debug string is produced. However, a debug-friendly
* string can be produced by importing `DebugPrinting.qll`.
*/
private import semmle.code.cpp.ir.IR
private import codeql.util.Unit
/**
* A class to control whether a debugging version of instructions and operands
* should be printed as part of the `toString` output of dataflow nodes.
*
* To enable debug printing import the `DebugPrinting.ql` file. By default,
* non-debug output will be used.
*/
class Node0ToString extends Unit {
abstract predicate isDebugMode();
private string normalInstructionToString(Instruction i) {
not this.isDebugMode() and
if i.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
then result = "this"
else result = i.getAst().toString()
}
private string normalOperandToString(Operand op) {
not this.isDebugMode() and
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
then result = "this"
else result = op.getDef().getAst().toString()
}
/**
* Gets the string that should be used by `InstructionNode.toString`
*/
string instructionToString(Instruction i) {
if this.isDebugMode()
then result = i.getDumpString()
else result = this.normalInstructionToString(i)
}
/**
* Gets the string that should be used by `OperandNode.toString`.
*/
string operandToString(Operand op) {
if this.isDebugMode()
then result = op.getDumpString() + " @ " + op.getUse().getResultId()
else result = this.normalOperandToString(op)
}
}
private class NoDebugNode0ToString extends Node0ToString {
final override predicate isDebugMode() { none() }
}
/**
* Gets the string that should be used by `OperandNode.toString`.
*/
string operandToString(Operand op) { result = any(Node0ToString nts).operandToString(op) }
/**
* Gets the string that should be used by `InstructionNode.toString`
*/
string instructionToString(Instruction i) { result = any(Node0ToString nts).instructionToString(i) }
/**
* Holds if debugging mode is enabled.
*
* In debug mode the `toString` on dataflow nodes is more expensive to compute,
* but gives more precise information about the different dataflow nodes.
*/
predicate isDebugMode() { any(Node0ToString nts).isDebugMode() }

View File

@@ -1,12 +0,0 @@
private import cpp
private import semmle.code.cpp.ir.IR
private import SsaInternals as Ssa
/**
* A property provider that hides all instructions and operands that are not relevant for IR dataflow.
*/
class DataFlowRelevantIRPropertyProvider extends IRPropertyProvider {
override predicate shouldPrintOperand(Operand operand) { not Ssa::ignoreOperand(operand) }
override predicate shouldPrintInstruction(Instruction instr) { not Ssa::ignoreInstruction(instr) }
}

View File

@@ -59,4 +59,8 @@ class LocalFlowPropertyProvider extends IRPropertyProvider {
result = getNodeProperty(node, key)
)
}
override predicate shouldPrintOperand(Operand operand) { not Ssa::ignoreOperand(operand) }
override predicate shouldPrintInstruction(Instruction instr) { not Ssa::ignoreInstruction(instr) }
}

View File

@@ -417,42 +417,60 @@ class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
override CppType getLanguageType() { result = getResultLanguageType(call) }
}
private module IsModifiableAtImpl {
pragma[nomagic]
private predicate isUnderlyingIndirectionType(Type t) {
t = any(Indirection ind).getUnderlyingType()
}
/**
* Holds if the value pointed to by `operand` can potentially be
* modified be the caller.
*/
predicate isModifiableByCall(ArgumentOperand operand, int indirectionIndex) {
exists(CallInstruction call, int index, CppType type |
indirectionIndex = [1 .. countIndirectionsForCppType(type)] and
type = getLanguageType(operand) and
call.getArgumentOperand(index) = operand and
if index = -1
then
// A qualifier is "modifiable" if:
// 1. the member function is not const specified, or
// 2. the member function is `const` specified, but returns a pointer or reference
// type that is non-const.
//
// To see why this is necessary, consider the following function:
// ```
// struct C {
// void* data_;
// void* data() const { return data; }
// };
// ...
// C c;
// memcpy(c.data(), source, 16)
// ```
// the data pointed to by `c.data_` is potentially modified by the call to `memcpy` even though
// `C::data` has a const specifier. So we further place the restriction that the type returned
// by `call` should not be of the form `const T*` (for some deeply const type `T`).
if call.getStaticCallTarget() instanceof Cpp::ConstMemberFunction
then
exists(PointerOrArrayOrReferenceType resultType |
resultType = call.getResultType() and
not resultType.isDeeplyConstBelow()
)
else any()
else
// An argument is modifiable if it's a non-const pointer or reference type.
isModifiableAt(type, indirectionIndex)
)
}
/**
* Holds if the `indirectionIndex`'th dereference of a value of type
* `cppType` is a type that can be modified (either by modifying the value
* itself or one of its fields if it's a class type).
*
* For example, a value of type `const int* const` cannot be modified
* at any indirection index (because it's a constant pointer to constant
* data), and a value of type `int *const *` is modifiable at indirection index
* 2 only.
*
* A value of type `const S2* s2` where `s2` is
* ```cpp
* struct S { int x; }
* ```
* can be modified at indirection index 1. This is to ensure that we generate
* a `PostUpdateNode` for the argument corresponding to the `s2` parameter in
* an example such as:
* ```cpp
* void set_field(const S2* s2)
* {
* s2->s->x = 42;
* }
* ```
*/
bindingset[cppType, indirectionIndex]
pragma[inline_late]
private predicate impl(CppType cppType, int indirectionIndex) {
exists(Type pointerType, Type base |
isUnderlyingIndirectionType(pointerType) and
cppType.hasUnderlyingType(pointerType, _) and
/**
* Holds if `t` is a pointer or reference type that supports at least `indirectionIndex` number
* of indirections, and the `indirectionIndex` indirection cannot be modfiied by passing a
* value of `t` to a function.
*/
private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
indirectionIndex = [1 .. countIndirectionsForCppType(cppType)] and
(
exists(Type pointerType, Type base, Type t |
pointerType = t.getUnderlyingType() and
pointerType = any(Indirection ind).getUnderlyingType() and
cppType.hasType(t, _) and
base = getTypeImpl(pointerType, indirectionIndex)
|
// The value cannot be modified if it has a const specifier,
@@ -462,114 +480,28 @@ private module IsModifiableAtImpl {
// one of the members was modified.
exists(base.stripType().(Cpp::Class).getAField())
)
}
/**
* Holds if `cppType` is modifiable with an indirection index of at least 1.
*
* This predicate factored out into a separate predicate for two reasons:
* - This predicate needs to be recursive because, if a type is modifiable
* at indirection `i`, then it's also modifiable at indirection index `i+1`
* (because the pointer could be completely re-assigned at indirection `i`).
* - We special-case indirection index `0` so that pointer arguments that can
* be modified at some index always have a `PostUpdateNode` at indiretion
* index 0 even though the 0'th indirection can never be modified by a
* callee.
*/
private predicate isModifiableAtImplAtLeast1(CppType cppType, int indirectionIndex) {
indirectionIndex = [1 .. countIndirectionsForCppType(cppType)] and
(
impl(cppType, indirectionIndex)
or
// If the `indirectionIndex`'th dereference of a type can be modified
// then so can the `indirectionIndex + 1`'th dereference.
isModifiableAtImplAtLeast1(cppType, indirectionIndex - 1)
)
}
/**
* Holds if `cppType` is modifiable at indirection index 0.
*
* In reality, the 0'th indirection of a pointer (i.e., the pointer itself)
* can never be modified by a callee, but it is sometimes useful to be able
* to specify the value of the pointer, as its coming out of a function, as
* a source of dataflow since the shared library's reverse-read mechanism
* then ensures that field-flow is accounted for.
*/
private predicate isModifiableAtImplAt0(CppType cppType) { impl(cppType, 0) }
/**
* Holds if `t` is a pointer or reference type that supports at least
* `indirectionIndex` number of indirections, and the `indirectionIndex`
* indirection cannot be modfiied by passing a value of `t` to a function.
*/
private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
isModifiableAtImplAtLeast1(cppType, indirectionIndex)
or
indirectionIndex = 0 and
isModifiableAtImplAt0(cppType)
}
/**
* Holds if `t` is a type with at least `indirectionIndex` number of
* indirections, and the `indirectionIndex` indirection can be modified by
* passing a value of type `t` to a function function.
*/
bindingset[indirectionIndex]
predicate isModifiableAt(CppType cppType, int indirectionIndex) {
isModifiableAtImpl(cppType, indirectionIndex)
or
exists(PointerWrapper pw, Type t |
cppType.hasType(t, _) and
t.stripType() = pw and
not pw.pointsToConst()
)
}
/**
* Holds if the value pointed to by `operand` can potentially be
* modified be the caller.
*/
predicate isModifiableByCall(ArgumentOperand operand, int indirectionIndex) {
exists(CallInstruction call, int index, CppType type |
indirectionIndex = [0 .. countIndirectionsForCppType(type)] and
type = getLanguageType(operand) and
call.getArgumentOperand(index) = operand and
if index = -1
then
// A qualifier is "modifiable" if:
// 1. the member function is not const specified, or
// 2. the member function is `const` specified, but returns a pointer or reference
// type that is non-const.
//
// To see why this is necessary, consider the following function:
// ```
// struct C {
// void* data_;
// void* data() const { return data; }
// };
// ...
// C c;
// memcpy(c.data(), source, 16)
// ```
// the data pointed to by `c.data_` is potentially modified by the call to `memcpy` even though
// `C::data` has a const specifier. So we further place the restriction that the type returned
// by `call` should not be of the form `const T*` (for some deeply const type `T`).
if call.getStaticCallTarget() instanceof Cpp::ConstMemberFunction
then
exists(PointerOrArrayOrReferenceType resultType |
resultType = call.getResultType() and
not resultType.isDeeplyConstBelow()
)
else any()
else
// An argument is modifiable if it's a non-const pointer or reference type.
isModifiableAt(type, indirectionIndex)
)
}
// If the `indirectionIndex`'th dereference of a type can be modified
// then so can the `indirectionIndex + 1`'th dereference.
isModifiableAtImpl(cppType, indirectionIndex - 1)
)
}
import IsModifiableAtImpl
/**
* Holds if `t` is a type with at least `indirectionIndex` number of indirections,
* and the `indirectionIndex` indirection can be modified by passing a value of
* type `t` to a function function.
*/
bindingset[indirectionIndex]
predicate isModifiableAt(CppType cppType, int indirectionIndex) {
isModifiableAtImpl(cppType, indirectionIndex)
or
exists(PointerWrapper pw, Type t |
cppType.hasType(t, _) and
t.stripType() = pw and
not pw.pointsToConst()
)
}
abstract class BaseSourceVariableInstruction extends Instruction {
/** Gets the base source variable accessed by this instruction. */

View File

@@ -1,6 +1,4 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
@@ -14,8 +12,6 @@ import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
@@ -55,7 +51,7 @@ private import TaintTrackingParameter::Private
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
*/
abstract deprecated class Configuration extends DataFlow::Configuration {
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }

View File

@@ -1,6 +1,4 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
@@ -14,8 +12,6 @@ import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
@@ -55,7 +51,7 @@ private import TaintTrackingParameter::Private
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
*/
abstract deprecated class Configuration extends DataFlow::Configuration {
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }

View File

@@ -1,6 +1,4 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
@@ -14,8 +12,6 @@ import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
@@ -55,7 +51,7 @@ private import TaintTrackingParameter::Private
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
*/
abstract deprecated class Configuration extends DataFlow::Configuration {
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }

View File

@@ -227,7 +227,7 @@ class CppType extends TCppType {
predicate hasType(Type type, boolean isGLValue) { none() }
/**
* Holds if this type represents the C++ unspecified type `type`. If `isGLValue` is `true`, then this type
* Holds if this type represents the C++ type `type`. If `isGLValue` is `true`, then this type
* represents a glvalue of type `type`. Otherwise, it represents a prvalue of type `type`.
*/
final predicate hasUnspecifiedType(Type type, boolean isGLValue) {
@@ -236,18 +236,6 @@ class CppType extends TCppType {
type = specifiedType.getUnspecifiedType()
)
}
/**
* Holds if this type represents the C++ type `type` (after resolving
* typedefs). If `isGLValue` is `true`, then this type represents a glvalue
* of type `type`. Otherwise, it represents a prvalue of type `type`.
*/
final predicate hasUnderlyingType(Type type, boolean isGLValue) {
exists(Type typedefType |
this.hasType(typedefType, isGLValue) and
type = typedefType.getUnderlyingType()
)
}
}
/**

View File

@@ -9,9 +9,8 @@
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Taint
pragma[nomagic]
private Type stripTopLevelSpecifiersOnly(Type t) {
result = stripTopLevelSpecifiersOnly(pragma[only_bind_out](t.(SpecifiedType).getBaseType()))
result = stripTopLevelSpecifiersOnly(t.(SpecifiedType).getBaseType())
or
result = t and
not t instanceof SpecifiedType

View File

@@ -25,8 +25,4 @@ module CppLangImplConstant implements LangSig<Sem, FloatDelta> {
* Holds if `e2 >= e1 + delta` (if `upper = false`) or `e2 <= e1 + delta` (if `upper = true`).
*/
predicate additionalBoundFlowStep(SemExpr e2, SemExpr e1, float delta, boolean upper) { none() }
predicate includeConstantBounds() { any() }
predicate includeRelativeBounds() { none() }
}

View File

@@ -173,11 +173,11 @@ private module ModulusAnalysisInstantiated implements ModulusAnalysisSig<Sem> {
}
module ConstantStage =
RangeStage<SemLocation, Sem, FloatDelta, AllBounds, FloatOverflow, CppLangImplConstant,
RangeStage<SemLocation, Sem, FloatDelta, ConstantBounds, FloatOverflow, CppLangImplConstant,
SignAnalysis, ModulusAnalysisInstantiated>;
module RelativeStage =
RangeStage<SemLocation, Sem, FloatDelta, AllBounds, FloatOverflow, CppLangImplRelative,
RangeStage<SemLocation, Sem, FloatDelta, RelativeBounds, FloatOverflow, CppLangImplRelative,
SignAnalysis, ModulusAnalysisInstantiated>;
private newtype TSemReason =

View File

@@ -57,8 +57,4 @@ module CppLangImplRelative implements LangSig<Sem, FloatDelta> {
* Holds if `e2 >= e1 + delta` (if `upper = false`) or `e2 <= e1 + delta` (if `upper = true`).
*/
predicate additionalBoundFlowStep(SemExpr e2, SemExpr e1, float delta, boolean upper) { none() }
predicate includeConstantBounds() { none() }
predicate includeRelativeBounds() { any() }
}

View File

@@ -45,7 +45,7 @@ class SecurityOptions extends string {
/**
* The argument of the given function is filled in from user input.
*/
deprecated predicate userInputArgument(FunctionCall functionCall, int arg) {
predicate userInputArgument(FunctionCall functionCall, int arg) {
exists(string fname |
functionCall.getTarget().hasGlobalOrStdName(fname) and
exists(functionCall.getArgument(arg)) and
@@ -73,7 +73,7 @@ class SecurityOptions extends string {
/**
* The return value of the given function is filled in from user input.
*/
deprecated predicate userInputReturned(FunctionCall functionCall) {
predicate userInputReturned(FunctionCall functionCall) {
exists(string fname |
functionCall.getTarget().getName() = fname and
(
@@ -91,8 +91,12 @@ class SecurityOptions extends string {
/**
* DEPRECATED: Users should override `userInputReturned()` instead.
*
* note: this function is not formally tagged as `deprecated` since the
* new `userInputReturned` uses it to provide compatibility with older
* custom SecurityOptions.qll files.
*/
deprecated predicate userInputReturn(string function) { none() }
predicate userInputReturn(string function) { none() }
/**
* The argument of the given function is used for running a process or loading
@@ -113,7 +117,7 @@ class SecurityOptions extends string {
* computed from user input. Such expressions are treated as
* sources of taint.
*/
deprecated predicate isUserInput(Expr expr, string cause) {
predicate isUserInput(Expr expr, string cause) {
exists(FunctionCall fc, int i |
this.userInputArgument(fc, i) and
expr = fc.getArgument(i) and
@@ -174,17 +178,17 @@ predicate argv(Parameter argv) {
predicate isPureFunction(string name) { exists(SecurityOptions opts | opts.isPureFunction(name)) }
/** Convenience accessor for SecurityOptions.userInputArgument */
deprecated predicate userInputArgument(FunctionCall functionCall, int arg) {
predicate userInputArgument(FunctionCall functionCall, int arg) {
exists(SecurityOptions opts | opts.userInputArgument(functionCall, arg))
}
/** Convenience accessor for SecurityOptions.userInputReturn */
deprecated predicate userInputReturned(FunctionCall functionCall) {
predicate userInputReturned(FunctionCall functionCall) {
exists(SecurityOptions opts | opts.userInputReturned(functionCall))
}
/** Convenience accessor for SecurityOptions.isUserInput */
deprecated predicate isUserInput(Expr expr, string cause) {
predicate isUserInput(Expr expr, string cause) {
exists(SecurityOptions opts | opts.isUserInput(expr, cause))
}

View File

@@ -23,7 +23,7 @@ class CustomSecurityOptions extends SecurityOptions {
none() // rules to match custom functions replace this line
}
deprecated override predicate userInputArgument(FunctionCall functionCall, int arg) {
override predicate userInputArgument(FunctionCall functionCall, int arg) {
SecurityOptions.super.userInputArgument(functionCall, arg)
or
exists(string fname |
@@ -36,7 +36,7 @@ class CustomSecurityOptions extends SecurityOptions {
)
}
deprecated override predicate userInputReturned(FunctionCall functionCall) {
override predicate userInputReturned(FunctionCall functionCall) {
SecurityOptions.super.userInputReturned(functionCall)
or
exists(string fname |

View File

@@ -0,0 +1,10 @@
/**
* Support for tracking tainted data through the program. This is an alias for
* `semmle.code.cpp.ir.dataflow.DefaultTaintTracking` provided for backwards
* compatibility.
*
* Prefer to use `semmle.code.cpp.dataflow.TaintTracking` or
* `semmle.code.cpp.ir.dataflow.TaintTracking` when designing new queries.
*/
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking

View File

@@ -0,0 +1,654 @@
/**
* DEPRECATED: we now use `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`,
* which is based on the IR but designed to behave similarly to this old
* library.
*
* Provides the implementation of `semmle.code.cpp.security.TaintTracking`. Do
* not import this file directly.
*/
import cpp
import Security
/** Expressions that change the value of a variable */
private predicate valueSource(Expr expr) {
exists(AssignExpr ae | expr = ae.getLValue())
or
exists(FunctionCall fc, int i |
userInputArgument(fc, i) and
expr = fc.getArgument(i)
)
or
exists(FunctionCall c, int arg |
copyValueBetweenArguments(c.getTarget(), _, arg) and
expr = c.getArgument(arg)
)
or
exists(FunctionCall c, int arg |
c.getTarget().getParameter(arg).getType() instanceof ReferenceType and
expr = c.getArgument(arg)
)
}
/** Expressions that are inside an expression that changes the value of a variable */
private predicate insideValueSource(Expr expr) {
valueSource(expr)
or
insideValueSource(expr.getParent()) and
// A modification of array[offset] does not modify offset
not expr.getParent().(ArrayExpr).getArrayOffset() = expr
}
private predicate isPointer(Type type) {
type instanceof PointerType or
isPointer(type.(ReferenceType).getBaseType())
}
/**
* Tracks data flow from src to dest.
* If this is used in the left side of an assignment src and dest should be swapped
*/
private predicate moveToDependingOnSide(Expr src, Expr dest) {
exists(ParenthesisExpr e |
src = e.getAChild() and
dest = e
)
or
exists(ArrayExpr e |
src = e.getArrayBase() and
dest = e
)
or
exists(PointerDereferenceExpr e |
src = e.getOperand() and
dest = e
)
or
exists(AddressOfExpr e |
src = e.getOperand() and
dest = e
)
or
// if var+offset is tainted, then so is var
exists(VariableAccess base, BinaryOperation binop |
dest = binop and
(base = binop.getLeftOperand() or base = binop.getRightOperand()) and
isPointer(base.getType()) and
base.getTarget() instanceof LocalScopeVariable and
src = base and
// flow through pointer-pointer subtraction is dubious, the result should be
// a number bounded by the size of the pointed-to thing.
not binop instanceof PointerDiffExpr
)
or
exists(UnaryOperation unop |
dest = unop and
unop.getAnOperand() = src
)
or
exists(BinaryOperation binop |
dest = binop and
binop.getLeftOperand() = src and
predictable(binop.getRightOperand())
)
or
exists(BinaryOperation binop |
dest = binop and
binop.getRightOperand() = src and
predictable(binop.getLeftOperand())
)
or
exists(Cast cast |
dest = cast and
src = cast.getExpr()
)
or
exists(ConditionalExpr cond |
cond = dest and
(
cond.getThen() = src or
cond.getElse() = src
)
)
}
/**
* Track value flow between functions.
* Handles the following cases:
* - If an argument to a function is tainted, all the usages of the parameter inside the function are tainted
* - If a function obtains input from the user internally and returns it, all calls to the function are tainted
* - If an argument to a function is tainted and that parameter is returned, all calls to the function are not tainted
* (this is done to avoid false positives). Because of this we need to track if the tainted element came from an argument
* or not, and for that we use destFromArg
*/
deprecated private predicate betweenFunctionsValueMoveTo(
Element src, Element dest, boolean destFromArg
) {
not unreachable(src) and
not unreachable(dest) and
(
exists(Call call, int i |
src = call.getArgument(i) and
resolveCallWithParam(call, _, i, dest) and
destFromArg = true
)
or
// Only move the return of the function to the function itself if the value didn't came from an
// argument, or else we would taint all the calls to one function if one argument is tainted
// somewhere
exists(Function f, ReturnStmt ret |
ret.getEnclosingFunction() = f and
src = ret.getExpr() and
destFromArg = false and
dest = f
)
or
exists(Call call, Function f |
f = resolveCall(call) and
src = f and
dest = call and
destFromArg = false
)
or
// If a parameter of type reference is tainted inside a function, taint the argument too
exists(Call call, int pi, Parameter p |
resolveCallWithParam(call, _, pi, p) and
p.getType() instanceof ReferenceType and
src = p and
dest = call.getArgument(pi) and
destFromArg = false
)
)
}
// predicate folding for proper join-order
// bad magic: pushes down predicate that ruins join-order
pragma[nomagic]
deprecated private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
called = resolveCall(call) and
p = called.getParameter(i)
}
/** A variable for which flow through is allowed. */
deprecated library class FlowVariable extends Variable {
FlowVariable() {
(
this instanceof LocalScopeVariable or
this instanceof GlobalOrNamespaceVariable
) and
not argv(this)
}
}
/** A local scope variable for which flow through is allowed. */
deprecated library class FlowLocalScopeVariable extends Variable {
FlowLocalScopeVariable() { this instanceof LocalScopeVariable }
}
deprecated private predicate insideFunctionValueMoveTo(Element src, Element dest) {
not unreachable(src) and
not unreachable(dest) and
(
// Taint all variable usages when one is tainted
// This function taints global variables but doesn't taint from a global variable (see globalVariableValueMoveTo)
exists(FlowLocalScopeVariable v |
src = v and
dest = v.getAnAccess() and
not insideValueSource(dest)
)
or
exists(FlowVariable v |
src = v.getAnAccess() and
dest = v and
insideValueSource(src)
)
or
// Taint all union usages when one is tainted
// This function taints global variables but doesn't taint from a global variable (see globalVariableValueMoveTo)
exists(FlowLocalScopeVariable v, FieldAccess a |
unionAccess(v, _, a) and
src = v and
dest = a and
not insideValueSource(dest)
)
or
exists(FlowVariable v, FieldAccess a |
unionAccess(v, _, a) and
src = a and
dest = v and
insideValueSource(src)
)
or
// If a pointer is tainted, taint the original variable
exists(FlowVariable p, FlowVariable v, AddressOfExpr e |
p.getAnAssignedValue() = e and
e.getOperand() = v.getAnAccess() and
src = p and
dest = v
)
or
// If a reference is tainted, taint the original variable
exists(FlowVariable r, FlowVariable v |
r.getType() instanceof ReferenceType and
r.getInitializer().getExpr() = v.getAnAccess() and
src = r and
dest = v
)
or
exists(Variable var |
var = dest and
var.getInitializer().getExpr() = src
)
or
exists(AssignExpr ae |
src = ae.getRValue() and
dest = ae.getLValue()
)
or
exists(CommaExpr comma |
comma = dest and
comma.getRightOperand() = src
)
or
exists(FunctionCall c, int sourceArg, int destArg |
copyValueBetweenArguments(c.getTarget(), sourceArg, destArg) and
// Only consider copies from `printf`-like functions if the format is a string
(
exists(FormattingFunctionCall ffc, FormatLiteral format |
ffc = c and
format = ffc.getFormat() and
format.getConversionChar(sourceArg - ffc.getTarget().getNumberOfParameters()) = ["s", "S"]
)
or
not c.(FormattingFunctionCall).getFormat() instanceof FormatLiteral
or
not c instanceof FormattingFunctionCall
) and
src = c.getArgument(sourceArg) and
dest = c.getArgument(destArg)
)
or
exists(FunctionCall c, int sourceArg |
returnArgument(c.getTarget(), sourceArg) and
src = c.getArgument(sourceArg) and
dest = c
)
or
exists(FormattingFunctionCall formattingSend, int arg, FormatLiteral format |
dest = formattingSend and
formattingSend.getArgument(arg) = src and
format = formattingSend.getFormat() and
format.getConversionChar(arg - formattingSend.getTarget().getNumberOfParameters()) =
["s", "S", "@"]
)
or
// Expressions computed from tainted data are also tainted
exists(FunctionCall call | dest = call and isPureFunction(call.getTarget().getName()) |
call.getAnArgument() = src and
forall(Expr arg | arg = call.getAnArgument() | arg = src or predictable(arg)) and
// flow through `strlen` tends to cause dubious results, if the length is
// bounded.
not call.getTarget().getName() = "strlen"
)
or
exists(Element a, Element b |
moveToDependingOnSide(a, b) and
if insideValueSource(a) then (src = b and dest = a) else (src = a and dest = b)
)
)
}
/**
* Handles data flow from global variables to its usages.
* The tainting for the global variable itself is done at insideFunctionValueMoveTo.
*/
private predicate globalVariableValueMoveTo(GlobalOrNamespaceVariable src, Expr dest) {
not unreachable(dest) and
(
exists(GlobalOrNamespaceVariable v |
src = v and
dest = v.getAnAccess() and
not insideValueSource(dest)
)
or
exists(GlobalOrNamespaceVariable v, FieldAccess a |
unionAccess(v, _, a) and
src = v and
dest = a and
not insideValueSource(dest)
)
)
}
private predicate unionAccess(Variable v, Field f, FieldAccess a) {
f.getDeclaringType() instanceof Union and
a.getTarget() = f and
a.getQualifier() = v.getAnAccess()
}
deprecated GlobalOrNamespaceVariable globalVarFromId(string id) {
if result instanceof NamespaceVariable
then id = result.getNamespace() + "::" + result.getName()
else id = result.getName()
}
/**
* A variable that has any kind of upper-bound check anywhere in the program. This is
* biased towards being inclusive because there are a lot of valid ways of doing an
* upper bounds checks if we don't consider where it occurs, for example:
* ```
* if (x < 10) { sink(x); }
*
* if (10 > y) { sink(y); }
*
* if (z > 10) { z = 10; }
* sink(z);
* ```
*/
private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
not oper.getAnOperand().getValue() = "0"
)
}
cached
deprecated private predicate taintedWithArgsAndGlobalVars(
Element src, Element dest, boolean destFromArg, string globalVar
) {
isUserInput(src, _) and
not unreachable(src) and
dest = src and
destFromArg = false and
globalVar = ""
or
exists(Element other, boolean otherFromArg, string otherGlobalVar |
taintedWithArgsAndGlobalVars(src, other, otherFromArg, otherGlobalVar)
|
not unreachable(dest) and
not hasUpperBoundsCheck(dest) and
(
// Direct flow from one expression to another.
betweenFunctionsValueMoveTo(other, dest, destFromArg) and
(destFromArg = true or otherFromArg = false) and
globalVar = otherGlobalVar
or
insideFunctionValueMoveTo(other, dest) and
destFromArg = otherFromArg and
globalVar = otherGlobalVar
or
exists(GlobalOrNamespaceVariable v |
v = other and
globalVariableValueMoveTo(v, dest) and
destFromArg = false and
v = globalVarFromId(globalVar)
)
)
)
}
/**
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This doesn't include data flow through global variables.
* If you need that you must call taintedIncludingGlobalVars.
*/
deprecated predicate tainted(Expr source, Element tainted) {
taintedWithArgsAndGlobalVars(source, tainted, _, "")
}
/**
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This version gives the same results as tainted but also includes
* data flow through global variables.
*
* The parameter `globalVar` is the name of the last global variable used to move the
* value from source to tainted.
*/
deprecated predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
taintedWithArgsAndGlobalVars(source, tainted, _, globalVar)
}
/**
* A predictable expression is one where an external user can predict
* the value. For example, a literal in the source code is considered
* predictable.
*/
private predicate predictable(Expr expr) {
expr instanceof Literal
or
exists(BinaryOperation binop | binop = expr |
predictable(binop.getLeftOperand()) and predictable(binop.getRightOperand())
)
or
exists(UnaryOperation unop | unop = expr | predictable(unop.getOperand()))
}
private int maxArgIndex(Function f) {
result =
max(FunctionCall fc, int toMax |
fc.getTarget() = f and toMax = fc.getNumberOfArguments() - 1
|
toMax
)
}
/** Functions that copy the value of one argument to another */
private predicate copyValueBetweenArguments(Function f, int sourceArg, int destArg) {
f.hasGlobalOrStdName("memcpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("__builtin___memcpy_chk") and sourceArg = 1 and destArg = 0
or
f.hasGlobalOrStdName("memmove") and sourceArg = 1 and destArg = 0
or
f.hasGlobalOrStdName("strcat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("_mbscat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalOrStdName("wcscat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalOrStdName("strncat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("_mbsncat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("wcsncat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalOrStdName("strcpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("_mbscpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalOrStdName("wcscpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalOrStdName("strncpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("_mbsncpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalOrStdName("wcsncpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("inet_aton") and sourceArg = 0 and destArg = 1
or
f.hasGlobalName("inet_pton") and sourceArg = 1 and destArg = 2
or
f.hasGlobalOrStdName("strftime") and sourceArg in [2 .. maxArgIndex(f)] and destArg = 0
or
exists(FormattingFunction ff | ff = f |
sourceArg in [ff.getFormatParameterIndex() .. maxArgIndex(f)] and
destArg = ff.getOutputParameterIndex(false)
)
}
/** Functions where if one of the arguments is tainted, the result should be tainted */
private predicate returnArgument(Function f, int sourceArg) {
f.hasGlobalName("memcpy") and sourceArg = 0
or
f.hasGlobalName("__builtin___memcpy_chk") and sourceArg = 0
or
f.hasGlobalOrStdName("memmove") and sourceArg = 0
or
f.hasGlobalOrStdName("strcat") and sourceArg = 0
or
f.hasGlobalName("_mbscat") and sourceArg = 0
or
f.hasGlobalOrStdName("wcsncat") and sourceArg = 0
or
f.hasGlobalOrStdName("strncat") and sourceArg = 0
or
f.hasGlobalName("_mbsncat") and sourceArg = 0
or
f.hasGlobalOrStdName("wcsncat") and sourceArg = 0
or
f.hasGlobalOrStdName("strcpy") and sourceArg = 0
or
f.hasGlobalName("_mbscpy") and sourceArg = 0
or
f.hasGlobalOrStdName("wcscpy") and sourceArg = 0
or
f.hasGlobalOrStdName("strncpy") and sourceArg = 0
or
f.hasGlobalName("_mbsncpy") and sourceArg = 0
or
f.hasGlobalOrStdName("wcsncpy") and sourceArg = 0
or
f.hasGlobalName("inet_ntoa") and sourceArg = 0
or
f.hasGlobalName("inet_addr") and sourceArg = 0
or
f.hasGlobalName("inet_network") and sourceArg = 0
or
f.hasGlobalName("inet_ntoa") and sourceArg = 0
or
f.hasGlobalName("inet_makeaddr") and
(sourceArg = 0 or sourceArg = 1)
or
f.hasGlobalName("inet_lnaof") and sourceArg = 0
or
f.hasGlobalName("inet_netof") and sourceArg = 0
or
f.hasGlobalName("gethostbyname") and sourceArg = 0
or
f.hasGlobalName("gethostbyaddr") and sourceArg = 0
}
/**
* Resolve potential target function(s) for `call`.
*
* If `call` is a call through a function pointer (`ExprCall`) or
* targets a virtual method, simple data flow analysis is performed
* in order to identify target(s).
*/
deprecated Function resolveCall(Call call) {
result = call.getTarget()
or
result = call.(DataSensitiveCallExpr).resolve()
}
/** A data sensitive call expression. */
abstract deprecated library class DataSensitiveCallExpr extends Expr {
DataSensitiveCallExpr() { not unreachable(this) }
abstract Expr getSrc();
cached
abstract Function resolve();
/**
* Whether `src` can flow to this call expression.
*
* Searches backwards from `getSrc()` to `src`.
*/
predicate flowsFrom(Element src, boolean allowFromArg) {
src = this.getSrc() and allowFromArg = true
or
exists(Element other, boolean allowOtherFromArg | this.flowsFrom(other, allowOtherFromArg) |
exists(boolean otherFromArg | betweenFunctionsValueMoveToStatic(src, other, otherFromArg) |
otherFromArg = true and allowOtherFromArg = true and allowFromArg = true
or
otherFromArg = false and allowFromArg = false
)
or
insideFunctionValueMoveTo(src, other) and allowFromArg = allowOtherFromArg
or
globalVariableValueMoveTo(src, other) and allowFromArg = true
)
}
}
/** Call through a function pointer. */
deprecated library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
override Expr getSrc() { result = this.getExpr() }
override Function resolve() {
exists(FunctionAccess fa | this.flowsFrom(fa, true) | result = fa.getTarget())
}
}
/** Call to a virtual function. */
deprecated library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr,
FunctionCall
{
DataSensitiveOverriddenFunctionCall() {
exists(this.getTarget().(VirtualFunction).getAnOverridingFunction())
}
override Expr getSrc() { result = this.getQualifier() }
override MemberFunction resolve() {
exists(NewExpr new |
this.flowsFrom(new, true) and
memberFunctionFromNewExpr(new, result) and
result.overrides*(this.getTarget().(VirtualFunction))
)
}
}
private predicate memberFunctionFromNewExpr(NewExpr new, MemberFunction f) {
f = new.getAllocatedType().(Class).getAMemberFunction()
}
/** Same as `betweenFunctionsValueMoveTo`, but calls are resolved to their static target. */
private predicate betweenFunctionsValueMoveToStatic(Element src, Element dest, boolean destFromArg) {
not unreachable(src) and
not unreachable(dest) and
(
exists(FunctionCall call, Function called, int i |
src = call.getArgument(i) and
called = call.getTarget() and
dest = called.getParameter(i) and
destFromArg = true
)
or
// Only move the return of the function to the function itself if the value didn't came from an
// argument, or else we would taint all the calls to one function if one argument is tainted
// somewhere
exists(Function f, ReturnStmt ret |
ret.getEnclosingFunction() = f and
src = ret.getExpr() and
destFromArg = false and
dest = f
)
or
exists(FunctionCall call, Function f |
call.getTarget() = f and
src = f and
dest = call and
destFromArg = false
)
or
// If a parameter of type reference is tainted inside a function, taint the argument too
exists(FunctionCall call, Function f, int pi, Parameter p |
call.getTarget() = f and
f.getParameter(pi) = p and
p.getType() instanceof ReferenceType and
src = p and
dest = call.getArgument(pi) and
destFromArg = false
)
)
}

View File

@@ -2149,7 +2149,7 @@ includes(
);
link_targets(
int id: @link_target,
unique int id: @link_target,
int binary: @file ref
);

View File

@@ -1,2 +0,0 @@
description: Remove uniqueness constraint on link_targets/2
compatibility: full

View File

@@ -1,13 +1,3 @@
## 0.9.1
### New Queries
* The `cpp/incorrectly-checked-scanf` query has been added. This finds results where the return value of scanf is not checked correctly. Some of these were previously found by `cpp/missing-check-scanf` and will no longer be reported there.
### Minor Analysis Improvements
* The `cpp/badly-bounded-write` query could report false positives when a pointer was first initialized with a literal and later assigned a dynamically allocated array. These false positives now no longer occur.
## 0.9.0
### Breaking Changes

View File

@@ -16,11 +16,7 @@ import semmle.code.cpp.dataflow.new.DataFlow
import FlowAfterFree
import DoubleFree::PathGraph
/**
* Holds if `n` is a dataflow node that represents a pointer going into a
* deallocation function, and `e` is the corresponding expression.
*/
predicate isFree(DataFlow::Node n, Expr e) { isFree(_, n, e, _) }
predicate isFree(DataFlow::Node n, Expr e) { isFree(n, e, _) }
/**
* `dealloc1` is a deallocation expression and `e` is an expression such
@@ -32,7 +28,7 @@ predicate isFree(DataFlow::Node n, Expr e) { isFree(_, n, e, _) }
*/
bindingset[dealloc1, e]
predicate isExcludeFreePair(DeallocationExpr dealloc1, Expr e) {
exists(DeallocationExpr dealloc2 | isFree(_, _, e, dealloc2) |
exists(DeallocationExpr dealloc2 | isFree(_, e, dealloc2) |
dealloc1.(FunctionCall).getTarget().hasGlobalName("MmFreePagesFromMdl") and
// From https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmfreepagesfrommdl:
// "After calling MmFreePagesFromMdl, the caller must also call ExFreePool
@@ -46,7 +42,7 @@ module DoubleFree = FlowFromFree<isFree/2, isExcludeFreePair/2>;
from DoubleFree::PathNode source, DoubleFree::PathNode sink, DeallocationExpr dealloc, Expr e2
where
DoubleFree::flowPath(source, sink) and
isFree(source.getNode(), _, _, dealloc) and
isFree(source.getNode(), _, dealloc) and
isFree(sink.getNode(), e2)
select sink.getNode(), source, sink,
"Memory pointed to by '" + e2.toString() + "' may already have been freed by $@.", dealloc,

View File

@@ -50,12 +50,12 @@ predicate strictlyDominates(IRBlock b1, int i1, IRBlock b2, int i2) {
module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
module FlowFromFreeConfig implements DataFlow::StateConfigSig {
class FlowState instanceof Expr {
FlowState() { isFree(_, _, this, _) }
FlowState() { isFree(_, this, _) }
string toString() { result = super.toString() }
}
predicate isSource(DataFlow::Node node, FlowState state) { isFree(node, _, state, _) }
predicate isSource(DataFlow::Node node, FlowState state) { isFree(node, state, _) }
pragma[inline]
predicate isSink(DataFlow::Node sink, FlowState state) {
@@ -64,7 +64,7 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
DeallocationExpr dealloc
|
isASink(sink, e) and
isFree(source, _, state, dealloc) and
isFree(source, state, dealloc) and
e != state and
source.hasIndexInBlock(b1, i1) and
sink.hasIndexInBlock(b2, i2) and
@@ -87,8 +87,6 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
|
e = any(StoreInstruction store).getDestinationAddress().getUnconvertedResultExpression()
)
or
n.asExpr() instanceof ArrayExpr
}
}
@@ -96,17 +94,14 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
}
/**
* Holds if `outgoing` is a dataflow node that represents the pointer passed to
* `dealloc` after the call returns (i.e., the post-update node associated with
* the argument to `dealloc`), and `incoming` is the corresponding argument
* node going into `dealloc` (i.e., the pre-update node of `outgoing`).
* Holds if `n` is a dataflow node such that `n.asExpr() = e` and `e`
* is being freed by a deallocation expression `dealloc`.
*/
predicate isFree(DataFlow::Node outgoing, DataFlow::Node incoming, Expr e, DeallocationExpr dealloc) {
predicate isFree(DataFlow::Node n, Expr e, DeallocationExpr dealloc) {
exists(Expr conv |
e = conv.getUnconverted() and
conv = dealloc.getFreedExpr().getFullyConverted() and
incoming = outgoing.(DataFlow::PostUpdateNode).getPreUpdateNode() and
conv = incoming.asConvertedExpr()
conv = n.asConvertedExpr()
) and
// Ignore realloc functions
not exists(dealloc.(FunctionCall).getTarget().(AllocationFunction).getReallocPtrArg())

View File

@@ -1,30 +0,0 @@
{
int i, j;
// BAD: The result is only checked against zero
if (scanf("%d %d", &i, &j)) {
use(i);
use(j);
}
// BAD: The result is only checked against zero
if (scanf("%d %d", &i, &j) == 0) {
i = 0;
j = 0;
}
use(i);
use(j);
if (scanf("%d %d", &i, &j) == 2) {
// GOOD: the result is checked against 2
}
// GOOD: the result is compared directly
int r = scanf("%d %d", &i, &j);
if (r < 2) {
return;
}
if (r == 1) {
j = 0;
}
}

View File

@@ -1,37 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This query finds calls of <tt>scanf</tt>-like functions with
improper return-value checking. Specifically, it flags uses of <code>scanf</code> where the return value is only checked against zero.
</p>
<p>
Functions in the <tt>scanf</tt> family return either <tt>EOF</tt> (a negative value)
in case of IO failure, or the number of items successfully read from the
input. Consequently, a simple check that the return value is nonzero
is not enough.
</p>
</overview>
<recommendation>
<p>
Ensure that all uses of <tt>scanf</tt> check the return value against the expected number of arguments
rather than just against zero.
</p>
</recommendation>
<example>
<p>The following examples show different ways of guarding a <tt>scanf</tt> output. In the BAD examples, the results are only checked against zero. In the GOOD examples, the results are checked against the expected number of matches instead.</p>
<sample src="IncorrectCheckScanf.cpp" />
</example>
<references>
<li>SEI CERT C++ Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR62-CPP.+Detect+errors+when+converting+a+string+to+a+number">ERR62-CPP. Detect errors when converting a string to a number</a>.</li>
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors">ERR33-C. Detect and handle standard library errors</a>.</li>
<li>cppreference.com: <a href="https://en.cppreference.com/w/c/io/fscanf">scanf, fscanf, sscanf, scanf_s, fscanf_s, sscanf_s</a>.</li>
</references>
</qhelp>

View File

@@ -1,21 +0,0 @@
/**
* @name Incorrect return-value check for a 'scanf'-like function
* @description Failing to account for EOF in a call to a scanf-like function can lead to
* undefined behavior.
* @kind problem
* @problem.severity warning
* @security-severity 7.5
* @precision high
* @id cpp/incorrectly-checked-scanf
* @tags security
* correctness
* external/cwe/cwe-253
*/
import cpp
import semmle.code.cpp.commons.Scanf
import ScanfChecks
from ScanfFunctionCall call
where incorrectlyCheckedScanf(call)
select call, "The result of scanf is only checked against 0, but it can also return EOF."

View File

@@ -19,7 +19,6 @@ import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.dataflow.new.DataFlow::DataFlow
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.ValueNumbering
import ScanfChecks
/** Holds if `n` reaches an argument to a call to a `scanf`-like function. */
pragma[nomagic]
@@ -61,9 +60,7 @@ predicate isSink(ScanfFunctionCall call, int index, Node n, Expr input) {
* argument that has not been previously initialized.
*/
predicate isRelevantScanfCall(ScanfFunctionCall call, int index, Expr output) {
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output)) and
// Exclude results from incorrectky checked scanf query
not incorrectlyCheckedScanf(call)
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output))
}
/**

View File

@@ -1,29 +0,0 @@
private import cpp
private import semmle.code.cpp.commons.Scanf
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.ir.ValueNumbering
private predicate exprInBooleanContext(Expr e) {
exists(IRGuardCondition gc |
exists(Instruction i, ConstantInstruction zero |
zero.getValue() = "0" and
i.getUnconvertedResultExpression() = e and
gc.comparesEq(valueNumber(i).getAUse(), zero.getAUse(), 0, _, _)
)
or
gc.getUnconvertedResultExpression() = e
)
}
private predicate isLinuxKernel() {
// For the purpose of sscanf, we check the header guards for the files that it is defined in (which have changed)
exists(Macro macro | macro.getName() in ["_LINUX_KERNEL_SPRINTF_H_", "_LINUX_KERNEL_H"])
}
/**
* Holds if `call` is a `scanf`-like call were the result is only checked against 0, but it can also return EOF.
*/
predicate incorrectlyCheckedScanf(ScanfFunctionCall call) {
exprInBooleanContext(call) and
not isLinuxKernel() // scanf in the linux kernel can't return EOF
}

View File

@@ -30,7 +30,7 @@ private predicate externalCallNeverDereferences(FormattingFunctionCall call, int
}
predicate isUse0(Expr e) {
not isFree(_, _, e, _) and
not isFree(_, e, _) and
(
e = any(PointerDereferenceExpr pde).getOperand()
or
@@ -170,6 +170,6 @@ module UseAfterFree = FlowFromFree<isUse/2, isExcludeFreeUsePair/2>;
from UseAfterFree::PathNode source, UseAfterFree::PathNode sink, DeallocationExpr dealloc
where
UseAfterFree::flowPath(source, sink) and
isFree(source.getNode(), _, _, dealloc)
isFree(source.getNode(), _, dealloc)
select sink.getNode(), source, sink, "Memory may have been previously freed by $@.", dealloc,
dealloc.toString()

View File

@@ -345,8 +345,6 @@ private module PossibleYearArithmeticOperationCheckConfig implements DataFlow::C
)
}
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
// flow from anything on the RHS of an assignment to a time/date structure to that
// assignment.

View File

@@ -24,7 +24,7 @@ import semmle.code.cpp.security.BufferWrite
from BufferWrite bw, int destSize
where
bw.hasExplicitLimit() and // has an explicit size limit
destSize = max(getBufferSize(bw.getDest(), _)) and
destSize = getBufferSize(bw.getDest(), _) and
bw.getExplicitLimit() > destSize // but it's larger than the destination
select bw,
"This '" + bw.getBWDesc() + "' operation is limited to " + bw.getExplicitLimit() +

View File

@@ -35,10 +35,10 @@ predicate isSource(FS::FlowSource source, string sourceType) { sourceType = sour
predicate isSink(DataFlow::Node sink, string kind) {
exists(Expr use |
use = sink.asExpr() and
not use.getUnspecifiedType() instanceof PointerType and
outOfBoundsExpr(use, kind) and
not inSystemMacroExpansion(use) and
use = sink.asExpr()
not inSystemMacroExpansion(use)
)
}

View File

@@ -1,9 +0,0 @@
## 0.9.1
### New Queries
* The `cpp/incorrectly-checked-scanf` query has been added. This finds results where the return value of scanf is not checked correctly. Some of these were previously found by `cpp/missing-check-scanf` and will no longer be reported there.
### Minor Analysis Improvements
* The `cpp/badly-bounded-write` query could report false positives when a pointer was first initialized with a literal and later assigned a dynamically allocated array. These false positives now no longer occur.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.9.1
lastReleaseVersion: 0.9.0

View File

@@ -1,110 +0,0 @@
/**
* @name Implementation of a cryptographic primitive
* @description Writing your own cryptographic primitives is prone to errors and omissions that weaken cryptographic protection.
* @kind problem
* @problem.severity warning
* @security-severity 7.5
* @precision medium
* @id cpp/crypto-primitive
* @tags security
* experimental
* external/cwe/cwe-1240
*/
import cpp
/**
* Gets a word that might be in the name of an encryption function.
*/
string encryptionWord() {
exists(string word |
// `(?<!P)` is negative lookbehind, i.e. the match is not preceded by `P`.
// `(?!P)` is negative lookahead, i.e. the match is not followed by `P`.
word =
[
"Crypt", "Cipher", "Aes", "Rijndael",
//"(?<!Wi|Co|No)Des(?!truct)",
"(?<!C)Rc[0-9]", "(?<!Cha|Unive)Rsa", "Blowfish", "Twofish", "Idea", "Kyber", "(?<!V)Aria",
//"Asn[0-9]",
"Camellia",
//"(?<!Bit|Type)Cast",
"Chacha", "ChaCha", "Poly[0-9]", "Ripemd", "Whirlpool", "Sbox", "SBox", "Cblock", "CBlock",
"Sub.?Bytes?", "Mix.?Columns?", "ECDH", "ECDSA", "EdDSA", "ECMQV", "ECQV", "Curve[0-9][0-9]"
] and
(
result = word or
result = word.toLowerCase() + "(?![a-z])" or // avoid matching middles of words
result = word.toUpperCase() + "(?![A-Z])" // avoid matching middles of words
)
)
}
/**
* Holds if `f` is a function whose name suggests it may be doing encryption
* (but may or may not actually implement an encryption primitive itself).
*/
predicate likelyEncryptionFunction(Function f) {
exists(string fName | fName = f.getName() |
fName.regexpMatch(".*(" + concat(encryptionWord(), "|") + ").*")
)
}
/**
* Holds if `t` is a type that is common in encryption-like computations. That
* is, an integral type or array of integral type elements.
*/
predicate computeHeuristicType(Type t) {
t instanceof IntegralType or
computeHeuristicType(t.(ArrayType).getBaseType().getUnspecifiedType())
}
/**
* Holds if `e` is an operation that is common in encryption-like computations.
* Looking for clusters of these tends to find things like encrpytion,
* compression, random number generation, graphics processing and other compute
* heavy algorithms.
*/
predicate computeHeuristic(Expr e) {
(
e instanceof BitwiseXorExpr or
e instanceof AssignXorExpr or
e instanceof LShiftExpr or
e instanceof AssignLShiftExpr or
e instanceof RShiftExpr or
e instanceof AssignRShiftExpr or
e instanceof ArrayExpr
) and
computeHeuristicType(e.getUnspecifiedType())
}
/**
* Gets the name of an established cryptography library or likely third party directory.
*/
string encryptionLibraryName() {
result =
[
"libssh", "openssl", "boringssl", "mbed", "libsodium", "libsrtp", "third.?party", "library",
"deps"
]
}
/**
* Holds if `f` is a file that is likely to be inside an established
* cryptography library.
*/
predicate isLibrary(File f) {
f.getAbsolutePath().regexpMatch("(?i).*(" + concat(encryptionLibraryName(), "|") + ").*")
or
// assume that any result that would be found outside the source location is in a crypto library
not exists(f.getFile().getRelativePath())
}
from Function f, int amount
where
likelyEncryptionFunction(f) and
amount = strictcount(Expr e | computeHeuristic(e) and e.getEnclosingFunction() = f) and
amount >= 8 and
not isLibrary(f.getFile())
select f,
"This function, \"" + f.getName() +
"\", may be a custom implementation of a cryptographic primitive."

View File

@@ -126,13 +126,13 @@ class Resource extends MemberVariable {
}
private predicate calledFromDestructor(Function f) {
pragma[only_bind_into](f) instanceof Destructor and
f.getDeclaringType() = this.getDeclaringType()
f instanceof Destructor and f.getDeclaringType() = this.getDeclaringType()
or
exists(Function mid |
exists(Function mid, FunctionCall fc |
this.calledFromDestructor(mid) and
mid.calls(f) and
pragma[only_bind_out](f.getDeclaringType()) = pragma[only_bind_out](this.getDeclaringType())
fc.getEnclosingFunction() = mid and
fc.getTarget() = f and
f.getDeclaringType() = this.getDeclaringType()
)
}

View File

@@ -32,41 +32,18 @@ predicate hasReferenceInitializer(EnumConstant c) {
)
}
/**
* Gets the `rnk`'th (1-based) enumeration constant in `e` that does not have a
* reference initializer (i.e., an initializer that refers to an enumeration
* constant from the same enumeration).
*/
EnumConstant getNonReferenceInitializedEnumConstantByRank(Enum e, int rnk) {
result =
rank[rnk](EnumConstant cand, int pos, string filepath, int startline, int startcolumn |
e.getEnumConstant(pos) = cand and
not hasReferenceInitializer(cand) and
cand.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _)
|
cand order by pos, filepath, startline, startcolumn
)
}
/**
* Holds if `ec` is not the last enumeration constant in `e` that has a non-
* reference initializer.
*/
predicate hasNextWithoutReferenceInitializer(Enum e, EnumConstant ec) {
exists(int rnk |
ec = getNonReferenceInitializedEnumConstantByRank(e, rnk) and
exists(getNonReferenceInitializedEnumConstantByRank(e, rnk + 1))
)
}
// There exists another constant whose value is implicit, but it's
// not the last one: the last value is okay to use to get the highest
// enum value automatically. It can be followed by aliases though.
predicate enumThatHasConstantWithImplicitValue(Enum e) {
exists(EnumConstant ec |
ec = e.getAnEnumConstant() and
exists(EnumConstant ec, int pos |
ec = e.getEnumConstant(pos) and
not hasInitializer(ec) and
hasNextWithoutReferenceInitializer(e, ec)
exists(EnumConstant ec2, int pos2 |
ec2 = e.getEnumConstant(pos2) and
pos2 > pos and
not hasReferenceInitializer(ec2)
)
)
}

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.9.2-dev
version: 0.9.1-dev
groups:
- cpp
- queries

View File

@@ -1,8 +1,8 @@
edges
| test.cpp:22:27:22:30 | **argv | test.cpp:29:13:29:20 | *filePath |
| test.cpp:22:27:22:30 | argv indirection | test.cpp:29:13:29:20 | filePath indirection |
nodes
| test.cpp:22:27:22:30 | **argv | semmle.label | **argv |
| test.cpp:29:13:29:20 | *filePath | semmle.label | *filePath |
| test.cpp:22:27:22:30 | argv indirection | semmle.label | argv indirection |
| test.cpp:29:13:29:20 | filePath indirection | semmle.label | filePath indirection |
subpaths
#select
| test.cpp:29:13:29:20 | *filePath | test.cpp:22:27:22:30 | **argv | test.cpp:29:13:29:20 | *filePath | Using user-supplied data in a `wordexp` command, without disabling command substitution, can make code vulnerable to command injection. |
| test.cpp:29:13:29:20 | filePath indirection | test.cpp:22:27:22:30 | argv indirection | test.cpp:29:13:29:20 | filePath indirection | Using user-supplied data in a `wordexp` command, without disabling command substitution, can make code vulnerable to command injection. |

View File

@@ -1,4 +0,0 @@
| tests_crypto.cpp:11:6:11:18 | encryptString | This function, "encryptString", may be a custom implementation of a cryptographic primitive. |
| tests_crypto.cpp:30:6:30:14 | MyEncrypt | This function, "MyEncrypt", may be a custom implementation of a cryptographic primitive. |
| tests_crypto.cpp:51:6:51:16 | mix_columns | This function, "mix_columns", may be a custom implementation of a cryptographic primitive. |
| tests_crypto.cpp:83:6:83:18 | init_aes_sbox | This function, "init_aes_sbox", may be a custom implementation of a cryptographic primitive. |

View File

@@ -1 +0,0 @@
experimental/Security/CWE/CWE-1240/CustomCryptographicPrimitive.ql

View File

@@ -1,6 +0,0 @@
// Cryptography 'library' snippets. Nothing in this file should be flagged by the query, because
// it's in a library.
void do_aes_encrypt(unsigned int *v) {
COMPUTE(v)
}

View File

@@ -1,97 +0,0 @@
// Cryptography snippets. All (non-stub) functions in this file should be flagged by the query.
typedef unsigned char uint8_t;
int strlen(const char *string);
// ---
// the following function is homebrew crypto written for this test. This is a bad algorithm
// on multiple levels and should never be used in cryptography.
void encryptString(char *string, unsigned int key) {
char *ptr = string;
int len = strlen(string);
while (len >= 4) {
// encrypt block by XOR-ing with the key
ptr[0] = ptr[0] ^ (key >> 0);
ptr[1] = ptr[1] ^ (key >> 8);
ptr[2] = ptr[2] ^ (key >> 16);
ptr[3] = ptr[3] ^ (key >> 24);
// move on
ptr += 4;
len -= 4;
}
}
// the following function is homebrew crypto written for this test. This is a bad algorithm
// on multiple levels and should never be used in cryptography.
void MyEncrypt(const unsigned int *dataIn, unsigned int *dataOut, unsigned int dataSize, unsigned int key[2]) {
unsigned int state[2];
unsigned int t;
state[0] = key[0];
state[1] = key[1];
for (unsigned int i = 0; i < dataSize; i++) {
// mix state
t = state[0];
state[0] = (state[0] << 1) | (state[1] >> 31);
state[1] = (state[1] << 1) | (t >> 31);
// encrypt data
dataOut[i] = dataIn[i] ^ state[0];
}
}
// the following function resembles an implementation of the AES "mix columns"
// step. It is not accurate, efficient or safe and should never be used in
// cryptography.
void mix_columns(const uint8_t inputs[4], uint8_t outputs[4]) {
// The "mix columns" step takes four bytes as inputs. Each byte represents a
// polynomial with 8 one-bit coefficients, e.g. input bits 00001101
// represent the polynomial x^3 + x^2 + 1. Arithmetic is reduced modulo
// x^8 + x^4 + x^3 + x + 1 (= 0x11b).
//
// The "mix columns" step multiplies each input by 2 (in the field described
// above) to produce four more values. The output is then four values
// produced by XOR-ing specific combinations of five of these eight values.
// The exact values selected here do not match the actual AES algorithm.
//
// We avoid control flow decisions that depend on the inputs.
uint8_t vs[4];
vs[0] = inputs[0] << 1; // multiply by two
vs[0] ^= (inputs[0] >> 7) * 0x1b; // reduce modulo 0x11b; the top bit was removed in the shift.
vs[1] = inputs[1] << 1;
vs[1] ^= (inputs[1] >> 7) * 0x1b;
vs[2] = inputs[2] << 1;
vs[2] ^= (inputs[2] >> 7) * 0x1b;
vs[3] = inputs[3] << 1;
vs[3] ^= (inputs[3] >> 7) * 0x1b;
outputs[0] = inputs[0] ^ inputs[1] ^ inputs[2] ^ vs[0] ^ vs[1];
outputs[1] = inputs[1] ^ inputs[2] ^ inputs[3] ^ vs[1] ^ vs[2];
outputs[2] = inputs[2] ^ inputs[3] ^ inputs[0] ^ vs[2] ^ vs[3];
outputs[3] = inputs[3] ^ inputs[0] ^ inputs[1] ^ vs[3] ^ vs[0];
}
// the following function resembles initialization of an S-box as may be done
// in an implementation of DES, AES and other encryption algorithms. It is not
// accurate, efficient or safe and should never be used in cryptography.
void init_aes_sbox(unsigned char data[256]) {
// initialize `data` in a loop using lots of ^, ^= and << operations and
// a few fixed constants.
unsigned int state = 0x12345678;
for (int i = 0; i < 256; i++)
{
state ^= (i ^ 0x86) << 24;
state ^= (i ^ 0xb9) << 16;
state ^= (i ^ 0x11) << 8;
state ^= (i ^ 0x23) << 0;
state = (state << 1) ^ (state >> 31);
data[i] = state & 0xff;
}
}

View File

@@ -1,138 +0,0 @@
// Non-cryptography snippets. Nothing in this file should be flagged by the query.
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned long size_t;
// a very cut down stub for `std::cout`
namespace std
{
template<class charT> struct char_traits;
template <class charT, class traits = char_traits<charT> >
class basic_ostream {
public:
typedef charT char_type;
};
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
typedef basic_ostream<char> ostream;
extern ostream cout;
}
// this macro expands to some compute operations that look a bit like cryptography
#define COMPUTE(v) \
v[0] ^= v[1] ^ v[2] ^ v[3] ^ v[4]; \
v[1] ^= v[2] ^ v[3] ^ v[4] ^ v[5]; \
v[2] ^= v[3] ^ v[4] ^ v[5] ^ v[6]; \
v[3] ^= v[4] ^ v[5] ^ v[6] ^ v[7];
// ---
#include "library/tests_library.h"
bool isEnabledAes() {
// This function has "Aes" in it's name, but does not contain enough compute to
// be an encryption implementation.
return false;
}
uint32_t lookup[256];
uint8_t computeCRC32(const uint8_t *data, size_t dataLen) {
// This function has "RC3" in its name, but is not an implementation of the (broken) RC3 encryption algorithm.
uint32_t result = 0xFFFFFFFF;
for (size_t i = 0; i < dataLen; i++) {
result = (result >> 8) + lookup[(result ^ data[i]) & 0xFF];
result = (result >> 8) + lookup[(result ^ data[i]) & 0xFF]; // artificial extra compute
result = (result >> 8) + lookup[(result ^ data[i]) & 0xFF]; // artificial extra compute
result = (result >> 8) + lookup[(result ^ data[i]) & 0xFF]; // artificial extra compute
}
return result ^ 0xFFFFFFFF;
}
void convert_image_universal(uint32_t *img, int width, int height) {
// This function has "rsa" in its name, but is nothing to do with the RSA encryption algorithm.
uint32_t *pixel_ptr = img;
uint32_t num_pixels = width * height;
// convert pixels RGBA -> ARGB (with probably unhelpful loop unrolling)
while (num_pixels >= 4) {
pixel_ptr[0] = (pixel_ptr[0] >> 8) ^ (pixel_ptr[0] << 24);
pixel_ptr[1] = (pixel_ptr[1] >> 8) ^ (pixel_ptr[1] << 24);
pixel_ptr[2] = (pixel_ptr[2] >> 8) ^ (pixel_ptr[2] << 24);
pixel_ptr[3] = (pixel_ptr[3] >> 8) ^ (pixel_ptr[3] << 24);
num_pixels -= 4;
}
if (num_pixels >= 2) {
pixel_ptr[0] = (pixel_ptr[0] >> 8) ^ (pixel_ptr[0] << 24);
pixel_ptr[1] = (pixel_ptr[1] >> 8) ^ (pixel_ptr[1] << 24);
num_pixels -= 2;
}
if (num_pixels >= 1) {
pixel_ptr[2] = (pixel_ptr[2] >> 8) ^ (pixel_ptr[2] << 24);
}
}
const char* yes_no_setting() { return "no"; }
void output_encrypt_decrypt_algorithms() {
// This function has "encrypt" and "decrypt" in its name, but no encryption is done.
// This function uses `<<` heavily, but not as an integer shift left.
const char *indent = " ";
std::cout << "Supported algorithms:\n";
std::cout << indent << "DES (" << yes_no_setting() << ")\n";
std::cout << indent << "3DES (" << yes_no_setting() << ")\n";
std::cout << indent << "AES (" << yes_no_setting() << ")\n";
std::cout << indent << "RSA (" << yes_no_setting() << ")\n";
std::cout << indent << "Blowfish (" << yes_no_setting() << ")\n";
std::cout << indent << "Twofish (" << yes_no_setting() << ")\n";
std::cout << indent << "Chacha (" << yes_no_setting() << ")\n";
}
void wideStringCharsAt(int *v) {
// This function has "des" and "rsa" in the name.
COMPUTE(v)
}
void bitcastVariable(int *v) {
// This function has "aria" and "cast" in the name.
COMPUTE(v)
}
void dividesVariance(int *v) {
// This function has "des" and "aria" in the name.
COMPUTE(v)
}
void broadcastNodes(int *v) {
// This function has "cast" and "des" in the name.
COMPUTE(v)
}
#define ROTATE(val, amount) ( (val << amount) | (val >> (32 - amount)) )
static inline void hashMix(const int *data, int &state) {
// This function looks like part of a hashing function. It's not necessarily intended to
// be a cryptographic hash, so should not be flagged.
state ^= data[0];
ROTATE(state, 1);
state ^= data[1];
ROTATE(state, 7);
state ^= data[2];
ROTATE(state, 11);
state ^= data[3];
ROTATE(state, 3);
state ^= data[4];
ROTATE(state, 13);
state ^= data[5];
ROTATE(state, 5);
state ^= data[6];
ROTATE(state, 2);
state ^= data[7];
ROTATE(state, 17);
}

View File

@@ -1,87 +1,87 @@
edges
| test.cpp:4:17:4:22 | call to malloc | test.cpp:6:9:6:11 | arr |
| test.cpp:4:17:4:22 | call to malloc | test.cpp:10:9:10:11 | arr |
| test.cpp:19:9:19:16 | *mk_array [p] | test.cpp:28:19:28:26 | call to mk_array [p] |
| test.cpp:19:9:19:16 | *mk_array [p] | test.cpp:50:18:50:25 | call to mk_array [p] |
| test.cpp:21:5:21:7 | *arr [post update] [p] | test.cpp:22:5:22:7 | *arr [p] |
| test.cpp:21:5:21:24 | ... = ... | test.cpp:21:5:21:7 | *arr [post update] [p] |
| test.cpp:19:9:19:16 | mk_array indirection [p] | test.cpp:28:19:28:26 | call to mk_array [p] |
| test.cpp:19:9:19:16 | mk_array indirection [p] | test.cpp:50:18:50:25 | call to mk_array [p] |
| test.cpp:21:5:21:24 | ... = ... | test.cpp:21:9:21:9 | arr indirection [post update] [p] |
| test.cpp:21:9:21:9 | arr indirection [post update] [p] | test.cpp:22:5:22:7 | arr indirection [p] |
| test.cpp:21:13:21:18 | call to malloc | test.cpp:21:5:21:24 | ... = ... |
| test.cpp:22:5:22:7 | *arr [p] | test.cpp:19:9:19:16 | *mk_array [p] |
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:31:9:31:11 | *arr [p] |
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:35:9:35:11 | *arr [p] |
| test.cpp:31:9:31:11 | *arr [p] | test.cpp:31:13:31:13 | p |
| test.cpp:35:9:35:11 | *arr [p] | test.cpp:35:13:35:13 | p |
| test.cpp:39:27:39:29 | arr [p] | test.cpp:41:9:41:11 | *arr [p] |
| test.cpp:39:27:39:29 | arr [p] | test.cpp:45:9:45:11 | *arr [p] |
| test.cpp:41:9:41:11 | *arr [p] | test.cpp:41:13:41:13 | p |
| test.cpp:45:9:45:11 | *arr [p] | test.cpp:45:13:45:13 | p |
| test.cpp:22:5:22:7 | arr indirection [p] | test.cpp:19:9:19:16 | mk_array indirection [p] |
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:31:9:31:11 | arr indirection [p] |
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:35:9:35:11 | arr indirection [p] |
| test.cpp:31:9:31:11 | arr indirection [p] | test.cpp:31:13:31:13 | p |
| test.cpp:35:9:35:11 | arr indirection [p] | test.cpp:35:13:35:13 | p |
| test.cpp:39:27:39:29 | arr [p] | test.cpp:41:9:41:11 | arr indirection [p] |
| test.cpp:39:27:39:29 | arr [p] | test.cpp:45:9:45:11 | arr indirection [p] |
| test.cpp:41:9:41:11 | arr indirection [p] | test.cpp:41:13:41:13 | p |
| test.cpp:45:9:45:11 | arr indirection [p] | test.cpp:45:13:45:13 | p |
| test.cpp:50:18:50:25 | call to mk_array [p] | test.cpp:39:27:39:29 | arr [p] |
| test.cpp:55:5:55:7 | *arr [post update] [p] | test.cpp:56:5:56:7 | *arr [p] |
| test.cpp:55:5:55:24 | ... = ... | test.cpp:55:5:55:7 | *arr [post update] [p] |
| test.cpp:55:5:55:24 | ... = ... | test.cpp:55:9:55:9 | arr indirection [post update] [p] |
| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:56:5:56:7 | arr indirection [p] |
| test.cpp:55:13:55:18 | call to malloc | test.cpp:55:5:55:24 | ... = ... |
| test.cpp:56:5:56:7 | *arr [p] | test.cpp:59:9:59:11 | *arr [p] |
| test.cpp:56:5:56:7 | *arr [p] | test.cpp:63:9:63:11 | *arr [p] |
| test.cpp:59:9:59:11 | *arr [p] | test.cpp:59:13:59:13 | p |
| test.cpp:63:9:63:11 | *arr [p] | test.cpp:63:13:63:13 | p |
| test.cpp:67:10:67:19 | **mk_array_p [p] | test.cpp:76:20:76:29 | *call to mk_array_p [p] |
| test.cpp:67:10:67:19 | **mk_array_p [p] | test.cpp:98:18:98:27 | *call to mk_array_p [p] |
| test.cpp:69:5:69:7 | *arr [post update] [p] | test.cpp:70:5:70:7 | *arr [p] |
| test.cpp:69:5:69:25 | ... = ... | test.cpp:69:5:69:7 | *arr [post update] [p] |
| test.cpp:56:5:56:7 | arr indirection [p] | test.cpp:59:9:59:11 | arr indirection [p] |
| test.cpp:56:5:56:7 | arr indirection [p] | test.cpp:63:9:63:11 | arr indirection [p] |
| test.cpp:59:9:59:11 | arr indirection [p] | test.cpp:59:13:59:13 | p |
| test.cpp:63:9:63:11 | arr indirection [p] | test.cpp:63:13:63:13 | p |
| test.cpp:67:10:67:19 | mk_array_p indirection [p] | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] |
| test.cpp:67:10:67:19 | mk_array_p indirection [p] | test.cpp:98:18:98:27 | call to mk_array_p indirection [p] |
| test.cpp:69:5:69:25 | ... = ... | test.cpp:69:10:69:10 | arr indirection [post update] [p] |
| 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 [p] | test.cpp:67:10:67:19 | **mk_array_p [p] |
| test.cpp:76:20:76:29 | *call to mk_array_p [p] | test.cpp:79:9:79:11 | *arr [p] |
| test.cpp:76:20:76:29 | *call to mk_array_p [p] | test.cpp:83:9:83:11 | *arr [p] |
| test.cpp:79:9:79:11 | *arr [p] | test.cpp:79:14:79:14 | p |
| test.cpp:83:9:83:11 | *arr [p] | test.cpp:83:14:83:14 | p |
| test.cpp:87:28:87:30 | *arr [p] | test.cpp:89:9:89:11 | *arr [p] |
| test.cpp:87:28:87:30 | *arr [p] | test.cpp:93:9:93:11 | *arr [p] |
| test.cpp:89:9:89:11 | *arr [p] | test.cpp:89:14:89:14 | p |
| test.cpp:93:9:93:11 | *arr [p] | test.cpp:93:14:93:14 | p |
| test.cpp:98:18:98:27 | *call to mk_array_p [p] | test.cpp:87:28:87:30 | *arr [p] |
| test.cpp:70:5:70:7 | arr indirection [p] | test.cpp:67:10:67:19 | mk_array_p 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 |
| test.cpp:83:9:83:11 | arr indirection [p] | test.cpp:83:14:83:14 | p |
| test.cpp:87:28:87:30 | arr indirection [p] | test.cpp:89:9:89:11 | arr indirection [p] |
| test.cpp:87:28:87:30 | arr indirection [p] | test.cpp:93:9:93:11 | arr indirection [p] |
| test.cpp:89:9:89:11 | arr indirection [p] | test.cpp:89:14:89:14 | p |
| test.cpp:93:9:93:11 | arr indirection [p] | test.cpp:93:14:93:14 | p |
| test.cpp:98:18:98:27 | call to mk_array_p indirection [p] | test.cpp:87:28:87:30 | arr indirection [p] |
nodes
| test.cpp:4:17:4:22 | call to malloc | semmle.label | call to malloc |
| test.cpp:6:9:6:11 | arr | semmle.label | arr |
| test.cpp:10:9:10:11 | arr | semmle.label | arr |
| test.cpp:19:9:19:16 | *mk_array [p] | semmle.label | *mk_array [p] |
| test.cpp:21:5:21:7 | *arr [post update] [p] | semmle.label | *arr [post update] [p] |
| test.cpp:19:9:19:16 | mk_array indirection [p] | semmle.label | mk_array indirection [p] |
| test.cpp:21:5:21:24 | ... = ... | semmle.label | ... = ... |
| test.cpp:21:9:21:9 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] |
| test.cpp:21:13:21:18 | call to malloc | semmle.label | call to malloc |
| test.cpp:22:5:22:7 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:22:5:22:7 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:28:19:28:26 | call to mk_array [p] | semmle.label | call to mk_array [p] |
| test.cpp:31:9:31:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:31:9:31:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:31:13:31:13 | p | semmle.label | p |
| test.cpp:35:9:35:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:35:9:35:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:35:13:35:13 | p | semmle.label | p |
| test.cpp:39:27:39:29 | arr [p] | semmle.label | arr [p] |
| test.cpp:41:9:41:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:41:9:41:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:41:13:41:13 | p | semmle.label | p |
| test.cpp:45:9:45:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:45:9:45:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:45:13:45:13 | p | semmle.label | p |
| test.cpp:50:18:50:25 | call to mk_array [p] | semmle.label | call to mk_array [p] |
| test.cpp:55:5:55:7 | *arr [post update] [p] | semmle.label | *arr [post update] [p] |
| test.cpp:55:5:55:24 | ... = ... | semmle.label | ... = ... |
| test.cpp:55:9:55:9 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] |
| test.cpp:55:13:55:18 | call to malloc | semmle.label | call to malloc |
| test.cpp:56:5:56:7 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:59:9:59:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:56:5:56:7 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:59:9:59:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:59:13:59:13 | p | semmle.label | p |
| test.cpp:63:9:63:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:63:9:63:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:63:13:63:13 | p | semmle.label | p |
| test.cpp:67:10:67:19 | **mk_array_p [p] | semmle.label | **mk_array_p [p] |
| test.cpp:69:5:69:7 | *arr [post update] [p] | semmle.label | *arr [post update] [p] |
| test.cpp:67:10:67:19 | mk_array_p indirection [p] | semmle.label | mk_array_p indirection [p] |
| test.cpp:69:5:69:25 | ... = ... | semmle.label | ... = ... |
| test.cpp:69:10:69:10 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] |
| test.cpp:69:14:69:19 | call to malloc | semmle.label | call to malloc |
| test.cpp:70:5:70:7 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:76:20:76:29 | *call to mk_array_p [p] | semmle.label | *call to mk_array_p [p] |
| test.cpp:79:9:79:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:70:5:70:7 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | semmle.label | call to mk_array_p indirection [p] |
| test.cpp:79:9:79:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:79:14:79:14 | p | semmle.label | p |
| test.cpp:83:9:83:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:83:9:83:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:83:14:83:14 | p | semmle.label | p |
| test.cpp:87:28:87:30 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:89:9:89:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:87:28:87:30 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:89:9:89:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:89:14:89:14 | p | semmle.label | p |
| test.cpp:93:9:93:11 | *arr [p] | semmle.label | *arr [p] |
| test.cpp:93:9:93:11 | arr indirection [p] | semmle.label | arr indirection [p] |
| test.cpp:93:14:93:14 | p | semmle.label | p |
| test.cpp:98:18:98:27 | *call to mk_array_p [p] | semmle.label | *call to mk_array_p [p] |
| test.cpp:98:18:98:27 | call to mk_array_p indirection [p] | semmle.label | call to mk_array_p indirection [p] |
subpaths
#select
| test.cpp:10:9:10:11 | arr | test.cpp:4:17:4:22 | call to malloc | test.cpp:10:9:10:11 | arr | Off-by one error allocated at $@ bounded by $@. | test.cpp:4:17:4:22 | call to malloc | call to malloc | test.cpp:4:24:4:27 | size | size |

View File

@@ -35,10 +35,10 @@ 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 | test.cpp:147:4:147:9 | -- ... |
| test.cpp:146:26:146:26 | p indirection | test.cpp:147:4:147: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 | *& ... |
| test.cpp:158:17:158:18 | *& ... | test.cpp:146:26:146:26 | *p |
| 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 |
| test.cpp:218:23:218:28 | buffer | test.cpp:220:5:220:11 | access to array |
| test.cpp:218:23:218:28 | buffer | test.cpp:221:5:221:11 | access to array |
| test.cpp:229:25:229:29 | array | test.cpp:231:5:231:10 | access to array |
@@ -121,11 +121,11 @@ nodes
| test.cpp:138:13:138:15 | arr | semmle.label | arr |
| 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 | semmle.label | *p |
| test.cpp:146:26:146:26 | p indirection | semmle.label | p indirection |
| test.cpp:147:4:147: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 | *& ... | semmle.label | *& ... |
| test.cpp:158:17:158:18 | & ... indirection | semmle.label | & ... indirection |
| test.cpp:218:23:218:28 | buffer | semmle.label | buffer |
| test.cpp:220:5:220:11 | access to array | semmle.label | access to array |
| test.cpp:221:5:221:11 | access to array | semmle.label | access to array |

View File

@@ -1,5 +1,5 @@
edges
| test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | *func |
| test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | func indirection |
| test.cpp:74:24:74:30 | medical | test.cpp:78:24:78:27 | temp |
| test.cpp:74:24:74:30 | medical | test.cpp:81:22:81:28 | medical |
| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp |
@@ -10,7 +10,7 @@ edges
| test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
| test.cpp:99:61:99:70 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
nodes
| test.cpp:45:7:45:10 | *func | semmle.label | *func |
| test.cpp:45:7:45:10 | func indirection | semmle.label | func indirection |
| test.cpp:45:18:45:23 | buffer | semmle.label | buffer |
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
@@ -25,7 +25,7 @@ nodes
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
| test.cpp:99:61:99:70 | theZipcode | semmle.label | theZipcode |
subpaths
| test.cpp:81:22:81:28 | medical | test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | *func | test.cpp:81:17:81:20 | call to func |
| test.cpp:81:22:81:28 | medical | test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | func indirection | test.cpp:81:17:81:20 | call to func |
#select
| test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:57:9:57:18 | theZipcode | this source of private data. |
| test.cpp:74:24:74:30 | medical | test.cpp:74:24:74:30 | medical | test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@. | test.cpp:74:24:74:30 | medical | this source of private data. |

View File

@@ -1,4 +1 @@
// semmle-extractor-options: -Werror
#ifndef __CODEQL_TEST__
#error __CODEQL_TEST__ missing
#endif

View File

@@ -10,13 +10,9 @@
| arguments.c | 10 | --target |
| arguments.c | 11 | --edg |
| arguments.c | 12 | linux_x86_64 |
| arguments.c | 13 | --edg |
| arguments.c | 14 | -D |
| arguments.c | 15 | --edg |
| arguments.c | 16 | __CODEQL_TEST__ |
| arguments.c | 17 | --gcc |
| arguments.c | 18 | --predefined_macros |
| arguments.c | 19 | <tools>/qltest/predefined_macros |
| arguments.c | 20 | -w |
| arguments.c | 21 | -Werror |
| arguments.c | 22 | arguments.c |
| arguments.c | 13 | --gcc |
| arguments.c | 14 | --predefined_macros |
| arguments.c | 15 | <tools>/qltest/predefined_macros |
| arguments.c | 16 | -w |
| arguments.c | 17 | -Werror |
| arguments.c | 18 | arguments.c |

View File

@@ -11,6 +11,7 @@
| test.c:2:31:72:1 | { ... } | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 50 | 52 |
| test.c:2:31:72:1 | { ... } | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 53 | 53 |
| test.c:2:31:72:1 | { ... } | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 53 | 55 |
| test.c:2:31:72:1 | { ... } | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 56 | 63 |
| test.c:2:31:72:1 | { ... } | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 59 | 61 |
| test.c:2:31:72:1 | { ... } | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 64 | 71 |
| test.c:2:31:72:1 | { ... } | test.c:2:14:2:14 | x | > | test.c:7:13:7:13 | 0 | 7 | 9 |
@@ -22,6 +23,7 @@
| test.c:34:11:34:11 | x | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 50 | 52 |
| test.c:34:11:34:11 | x | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 53 | 53 |
| test.c:34:11:34:11 | x | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 53 | 55 |
| test.c:34:11:34:11 | x | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 56 | 63 |
| test.c:34:11:34:11 | x | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 59 | 61 |
| test.c:34:11:34:11 | x | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 64 | 71 |
| test.c:34:11:34:11 | x | test.c:2:14:2:14 | x | > | test.c:34:15:34:15 | 0 | 34 | 36 |
@@ -31,17 +33,20 @@
| test.c:42:16:42:16 | j | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 50 | 52 |
| test.c:42:16:42:16 | j | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 53 | 53 |
| test.c:42:16:42:16 | j | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 53 | 55 |
| test.c:42:16:42:16 | j | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 56 | 63 |
| test.c:42:16:42:16 | j | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 59 | 61 |
| test.c:42:16:42:16 | j | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 64 | 71 |
| test.c:47:5:47:10 | ... += ... | test.c:2:28:2:28 | z | < | test.c:52:16:52:16 | 0 | 50 | 50 |
| test.c:47:5:47:10 | ... += ... | test.c:2:28:2:28 | z | < | test.c:52:16:52:16 | 0 | 59 | 61 |
| test.c:47:5:47:10 | ... += ... | test.c:2:28:2:28 | z | > | test.c:52:16:52:16 | 0 | 53 | 53 |
| test.c:47:5:47:10 | ... += ... | test.c:2:28:2:28 | z | > | test.c:52:16:52:16 | 0 | 53 | 55 |
| test.c:47:5:47:10 | ... += ... | test.c:2:28:2:28 | z | > | test.c:52:16:52:16 | 0 | 56 | 63 |
| test.c:50:16:50:16 | j | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 50 | 50 |
| test.c:50:16:50:16 | j | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 50 | 52 |
| test.c:50:16:50:16 | j | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 53 | 53 |
| test.c:50:16:50:16 | j | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 53 | 55 |
| test.c:50:16:50:16 | j | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 56 | 63 |
| test.c:50:16:50:16 | j | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 59 | 61 |
| test.c:51:9:51:14 | ... = ... | test.c:4:10:4:10 | y | < | test.c:53:20:53:20 | 0 | 56 | 63 |
| test.c:51:9:51:14 | ... = ... | test.c:4:10:4:10 | y | > | test.c:53:20:53:20 | 0 | 53 | 55 |
| test.c:74:19:89:1 | { ... } | test.c:74:16:74:16 | a | > | test.c:79:17:79:19 | 100 | 79 | 81 |
| test.cpp:9:19:9:19 | i | test.cpp:9:12:9:12 | i | < | test.cpp:9:23:9:24 | 10 | 9 | 9 |

View File

@@ -167,20 +167,3 @@ int ptr_test(int *x, int *y) {
return 0;
}
int foo(const char*, int);
int ternary_test(const char *path, int mode)
{
return (foo(path, mode) == 0 ? 1 : 0);
}
void abort(void);
int abort_test(int x) {
if (x) {
x += 1;
} else {
abort();
}
}

View File

@@ -34,8 +34,6 @@ astGuards
| test.c:159:9:159:19 | ... == ... |
| test.c:162:9:162:18 | ... < ... |
| test.c:165:9:165:18 | ... < ... |
| test.c:175:13:175:32 | ... == ... |
| test.c:181:9:181:9 | x |
| test.cpp:18:8:18:10 | call to get |
| test.cpp:31:7:31:13 | ... == ... |
| test.cpp:42:13:42:20 | call to getABool |
@@ -160,10 +158,6 @@ astGuardsCompare
| 165 | x >= y+-42 when ... < ... is false |
| 165 | y < x+43 when ... < ... is false |
| 165 | y >= x+43 when ... < ... is true |
| 175 | 0 != call to foo+0 when ... == ... is false |
| 175 | 0 == call to foo+0 when ... == ... is true |
| 175 | call to foo != 0+0 when ... == ... is false |
| 175 | call to foo == 0+0 when ... == ... is true |
astGuardsControl
| test.c:7:9:7:13 | ... > ... | false | 10 | 11 |
| test.c:7:9:7:13 | ... > ... | true | 7 | 9 |
@@ -254,11 +248,6 @@ astGuardsControl
| test.c:159:9:159:19 | ... == ... | true | 159 | 160 |
| test.c:162:9:162:18 | ... < ... | true | 162 | 163 |
| test.c:165:9:165:18 | ... < ... | true | 165 | 166 |
| test.c:175:13:175:32 | ... == ... | false | 175 | 175 |
| test.c:175:13:175:32 | ... == ... | true | 175 | 175 |
| test.c:181:9:181:9 | x | false | 183 | 184 |
| test.c:181:9:181:9 | x | true | 181 | 182 |
| test.c:181:9:181:9 | x | true | 186 | 180 |
| test.cpp:18:8:18:10 | call to get | true | 19 | 19 |
| test.cpp:31:7:31:13 | ... == ... | false | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | false | 34 | 34 |
@@ -431,10 +420,6 @@ astGuardsEnsure
| test.c:165:9:165:18 | ... < ... | test.c:165:9:165:9 | x | < | test.c:165:13:165:18 | ... - ... | 0 | 165 | 166 |
| test.c:165:9:165:18 | ... < ... | test.c:165:13:165:13 | y | >= | test.c:165:9:165:9 | x | 43 | 165 | 166 |
| test.c:165:9:165:18 | ... < ... | test.c:165:13:165:18 | ... - ... | >= | test.c:165:9:165:9 | x | 1 | 165 | 166 |
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | != | test.c:175:32:175:32 | 0 | 0 | 175 | 175 |
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | == | test.c:175:32:175:32 | 0 | 0 | 175 | 175 |
| test.c:175:13:175:32 | ... == ... | test.c:175:32:175:32 | 0 | != | test.c:175:13:175:15 | call to foo | 0 | 175 | 175 |
| test.c:175:13:175:32 | ... == ... | test.c:175:32:175:32 | 0 | == | test.c:175:13:175:15 | call to foo | 0 | 175 | 175 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 |
@@ -473,8 +458,6 @@ irGuards
| test.c:159:9:159:19 | CompareEQ: ... == ... |
| test.c:162:9:162:18 | CompareLT: ... < ... |
| test.c:165:9:165:18 | CompareLT: ... < ... |
| test.c:175:13:175:32 | CompareEQ: ... == ... |
| test.c:181:9:181:9 | Load: x |
| test.cpp:18:8:18:12 | CompareNE: (bool)... |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... |
| test.cpp:42:13:42:20 | Call: call to getABool |
@@ -583,10 +566,6 @@ irGuardsCompare
| 165 | x >= y+-42 when CompareLT: ... < ... is false |
| 165 | y < x+43 when CompareLT: ... < ... is false |
| 165 | y >= x+43 when CompareLT: ... < ... is true |
| 175 | 0 != call to foo+0 when CompareEQ: ... == ... is false |
| 175 | 0 == call to foo+0 when CompareEQ: ... == ... is true |
| 175 | call to foo != 0+0 when CompareEQ: ... == ... is false |
| 175 | call to foo == 0+0 when CompareEQ: ... == ... is true |
irGuardsControl
| test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 |
| test.c:7:9:7:13 | CompareGT: ... > ... | true | 8 | 8 |
@@ -670,10 +649,6 @@ irGuardsControl
| test.c:159:9:159:19 | CompareEQ: ... == ... | true | 159 | 160 |
| test.c:162:9:162:18 | CompareLT: ... < ... | true | 162 | 163 |
| test.c:165:9:165:18 | CompareLT: ... < ... | true | 165 | 166 |
| test.c:175:13:175:32 | CompareEQ: ... == ... | false | 175 | 175 |
| test.c:175:13:175:32 | CompareEQ: ... == ... | true | 175 | 175 |
| test.c:181:9:181:9 | Load: x | false | 184 | 184 |
| test.c:181:9:181:9 | Load: x | true | 182 | 182 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | true | 19 | 19 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | false | 34 | 34 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | true | 30 | 30 |
@@ -829,10 +804,6 @@ irGuardsEnsure
| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:9:165:9 | Load: x | < | test.c:165:13:165:18 | PointerSub: ... - ... | 0 | 165 | 166 |
| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:13:165:13 | Load: y | >= | test.c:165:9:165:9 | Load: x | 43 | 165 | 166 |
| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:13:165:18 | PointerSub: ... - ... | >= | test.c:165:9:165:9 | Load: x | 1 | 165 | 166 |
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | != | test.c:175:32:175:32 | Constant: 0 | 0 | 175 | 175 |
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | == | test.c:175:32:175:32 | Constant: 0 | 0 | 175 | 175 |
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:32:175:32 | Constant: 0 | != | test.c:175:13:175:15 | Call: call to foo | 0 | 175 | 175 |
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:32:175:32 | Constant: 0 | == | test.c:175:13:175:15 | Call: call to foo | 0 | 175 | 175 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:10 | Call: call to get | != | test.cpp:18:8:18:12 | Constant: (bool)... | 0 | 19 | 19 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:12 | Constant: (bool)... | != | test.cpp:18:8:18:10 | Call: call to get | 0 | 19 | 19 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | test.cpp:31:12:31:13 | Constant: - ... | 0 | 34 | 34 |

View File

@@ -24,6 +24,7 @@
| test.c:126:12:126:26 | call to test3_condition |
| test.c:131:7:131:7 | b |
| test.c:137:7:137:7 | 0 |
| test.c:138:9:138:9 | i |
| test.c:146:7:146:8 | ! ... |
| test.c:146:8:146:8 | x |
| test.cpp:18:8:18:10 | call to get |

View File

@@ -12,6 +12,7 @@
| test.c:26:11:26:15 | ... > ... | false | 42 | 44 |
| test.c:26:11:26:15 | ... > ... | false | 45 | 45 |
| test.c:26:11:26:15 | ... > ... | false | 45 | 47 |
| test.c:26:11:26:15 | ... > ... | false | 48 | 55 |
| test.c:26:11:26:15 | ... > ... | false | 51 | 53 |
| test.c:26:11:26:15 | ... > ... | false | 56 | 58 |
| test.c:26:11:26:15 | ... > ... | false | 58 | 58 |
@@ -24,6 +25,7 @@
| test.c:34:16:34:21 | ... < ... | false | 42 | 44 |
| test.c:34:16:34:21 | ... < ... | false | 45 | 45 |
| test.c:34:16:34:21 | ... < ... | false | 45 | 47 |
| test.c:34:16:34:21 | ... < ... | false | 48 | 55 |
| test.c:34:16:34:21 | ... < ... | false | 51 | 53 |
| test.c:34:16:34:21 | ... < ... | false | 56 | 58 |
| test.c:34:16:34:21 | ... < ... | false | 58 | 58 |
@@ -34,11 +36,13 @@
| test.c:42:16:42:21 | ... < ... | true | 42 | 44 |
| test.c:42:16:42:21 | ... < ... | true | 45 | 45 |
| test.c:42:16:42:21 | ... < ... | true | 45 | 47 |
| test.c:42:16:42:21 | ... < ... | true | 48 | 55 |
| test.c:42:16:42:21 | ... < ... | true | 51 | 53 |
| test.c:44:12:44:16 | ... > ... | false | 42 | 42 |
| test.c:44:12:44:16 | ... > ... | false | 51 | 53 |
| test.c:44:12:44:16 | ... > ... | true | 45 | 45 |
| test.c:44:12:44:16 | ... > ... | true | 45 | 47 |
| test.c:44:12:44:16 | ... > ... | true | 48 | 55 |
| test.c:45:16:45:20 | ... > ... | false | 48 | 55 |
| test.c:45:16:45:20 | ... > ... | true | 45 | 47 |
| test.c:58:9:58:14 | ... == ... | false | 58 | 58 |
| test.c:58:9:58:14 | ... == ... | false | 62 | 62 |
@@ -77,12 +81,10 @@
| test.c:126:12:126:26 | call to test3_condition | true | 126 | 128 |
| test.c:131:7:131:7 | b | true | 131 | 132 |
| test.c:137:7:137:7 | 0 | false | 142 | 136 |
| test.c:138:9:138:9 | i | true | 138 | 139 |
| test.c:146:7:146:8 | ! ... | true | 146 | 147 |
| test.c:146:8:146:8 | x | false | 146 | 147 |
| test.cpp:18:8:18:10 | call to get | true | 19 | 19 |
| test.cpp:31:7:31:13 | ... == ... | false | 30 | 30 |
| test.cpp:18:8:18:10 | call to get | false | 20 | 16 |
| test.cpp:31:7:31:13 | ... == ... | false | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | true | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | true | 31 | 32 |
| test.cpp:42:13:42:20 | call to getABool | false | 53 | 53 |
| test.cpp:42:13:42:20 | call to getABool | true | 43 | 45 |

View File

@@ -20,6 +20,7 @@
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 42 | 44 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 45 | 45 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 45 | 47 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 48 | 55 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 51 | 53 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 56 | 58 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 58 | 58 |
@@ -35,6 +36,7 @@
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 42 | 44 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 45 | 45 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 45 | 47 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 48 | 55 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 51 | 53 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 56 | 58 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 58 | 58 |
@@ -47,6 +49,7 @@
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 42 | 44 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 45 | 45 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 45 | 47 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 48 | 55 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 51 | 53 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 56 | 58 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 58 | 58 |
@@ -58,6 +61,7 @@
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 42 | 44 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 45 | 45 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 45 | 47 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 48 | 55 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 51 | 53 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 56 | 58 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 58 | 58 |
@@ -68,22 +72,26 @@
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 42 | 44 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 45 | 45 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 45 | 47 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 48 | 55 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 51 | 53 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 42 | 42 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 42 | 44 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 45 | 45 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 45 | 47 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 48 | 55 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 51 | 53 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | < | test.c:44:16:44:16 | 0 | 1 | 42 | 42 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | < | test.c:44:16:44:16 | 0 | 1 | 51 | 53 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 45 | 45 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 45 | 47 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 48 | 55 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 45 | 45 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 45 | 47 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | >= | test.c:44:12:44:12 | z | 0 | 42 | 42 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 48 | 55 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | >= | test.c:44:12:44:12 | z | 0 | 51 | 53 |
| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:16 | y | < | test.c:45:20:45:20 | 0 | 1 | 48 | 55 |
| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:16 | y | >= | test.c:45:20:45:20 | 0 | 1 | 45 | 47 |
| test.c:45:16:45:20 | ... > ... | test.c:45:20:45:20 | 0 | < | test.c:45:16:45:16 | y | 0 | 45 | 47 |
| test.c:45:16:45:20 | ... > ... | test.c:45:20:45:20 | 0 | >= | test.c:45:16:45:16 | y | 0 | 48 | 55 |
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | test.c:58:14:58:14 | 0 | 0 | 58 | 58 |
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | test.c:58:14:58:14 | 0 | 0 | 62 | 62 |
| test.c:58:9:58:14 | ... == ... | test.c:58:14:58:14 | 0 | != | test.c:58:9:58:9 | x | 0 | 58 | 58 |
@@ -146,11 +154,7 @@
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 |
| test.c:109:19:109:23 | ... < ... | test.c:109:19:109:19 | y | >= | test.c:109:23:109:23 | 0 | 0 | 113 | 113 |
| test.c:109:19:109:23 | ... < ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 31 | 32 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 |

View File

@@ -0,0 +1,74 @@
#include "../shared.h"
using SinkFunction = void (*)(int);
void notSink(int notSinkParam);
void callsSink(int sinkParam) { // $ ir-path=31:23 ir-path=32:26 ir-path=34:17
sink(sinkParam); // $ ast=31:28 ast=32:31 ast=34:22 ir-sink
}
struct {
SinkFunction sinkPtr, notSinkPtr;
} globalStruct;
union {
SinkFunction sinkPtr, notSinkPtr;
} globalUnion;
SinkFunction globalSinkPtr;
void assignGlobals() {
globalStruct.sinkPtr = callsSink;
globalUnion.sinkPtr = callsSink;
globalSinkPtr = callsSink;
};
void testStruct() {
globalStruct.sinkPtr(atoi(getenv("TAINTED"))); // $ MISSING: ir-path,ast
globalStruct.notSinkPtr(atoi(getenv("TAINTED"))); // clean
globalUnion.sinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
globalUnion.notSinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
globalSinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
}
class B {
public:
virtual void f(const char*) = 0;
};
class D1 : public B {};
class D2 : public D1 {
public:
void f(const char* p) override {}
};
class D3 : public D2 {
public:
void f(const char* p) override { // $ ir-path=58:10 ir-path=60:17 ir-path=61:28 ir-path=62:29 ir-path=63:33 SPURIOUS: ir-path=73:30
sink(p); // $ ast=58:10 ast=60:17 ast=61:28 ast=62:29 ast=63:33 ir-sink SPURIOUS: ast=73:30
}
};
void test_dynamic_cast() {
B* b = new D3();
b->f(getenv("VAR")); // $ ast ir-path
((D2*)b)->f(getenv("VAR")); // $ ast ir-path
static_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
dynamic_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
reinterpret_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
B* b2 = new D2();
b2->f(getenv("VAR"));
((D2*)b2)->f(getenv("VAR"));
static_cast<D2*>(b2)->f(getenv("VAR"));
dynamic_cast<D2*>(b2)->f(getenv("VAR"));
reinterpret_cast<D2*>(b2)->f(getenv("VAR"));
dynamic_cast<D3*>(b2)->f(getenv("VAR")); // $ SPURIOUS: ast ir-path
}

View File

@@ -0,0 +1,4 @@
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:9,8-47)
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:20,49-74)
testFailures
failures

View File

@@ -0,0 +1,100 @@
/**
* This test provides the possibility to annotate elements when they are on a path of a taint flow to a sink.
* This is different when compared to the tests in `../annotate_sink`, where only sink invocations are annotated.
*/
import cpp
import semmle.code.cpp.security.TaintTrackingImpl as AstTaintTracking
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
import IRDefaultTaintTracking::TaintedWithPath as TaintedWithPath
import TaintedWithPath::Private
import TestUtilities.InlineExpectationsTest
predicate isSinkArgument(Element sink) {
exists(FunctionCall call |
call.getTarget().getName() = "sink" and
sink = call.getAnArgument()
)
}
predicate astTaint(Expr source, Element sink) { AstTaintTracking::tainted(source, sink) }
class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
override predicate isSink(Element e) { isSinkArgument(e) }
}
predicate irTaint(Element source, TaintedWithPath::PathNode predNode, string tag) {
exists(TaintedWithPath::PathNode sinkNode |
TaintedWithPath::taintedWithPath(source, _, _, sinkNode) and
predNode = getAPredecessor*(sinkNode) and
// Make sure the path is actually reachable from this predecessor.
// Otherwise, we could pick `predNode` to be b when `source` is
// `source1` in this dataflow graph:
// source1 ---> a ---> c ---> sinkNode
// ^
// source2 ---> b --/
source = getElementFromPathNode(getAPredecessor*(predNode)) and
if predNode = sinkNode then tag = "ir-sink" else tag = "ir-path"
)
}
module IRDefaultTaintTrackingTest implements TestSig {
string getARelevantTag() { result = ["ir-path", "ir-sink"] }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(Element elem, TaintedWithPath::PathNode node, int n |
irTaint(_, node, tag) and
elem = getElementFromPathNode(node) and
n = count(int startline | getAPredecessor(node).hasLocationInfo(_, startline, _, _, _)) and
location = elem.getLocation() and
element = elem.toString()
|
// Zero predecessors means it's a source, and 1 predecessor means it has a unique predecessor.
// In either of these cases we leave out the location.
n = [0, 1] and value = ""
or
// If there is more than one predecessor for this node
// we specify the source location explicitly.
n > 1 and
exists(TaintedWithPath::PathNode pred | pred = getAPredecessor(node) |
value =
getElementFromPathNode(pred).getLocation().getStartLine().toString() + ":" +
getElementFromPathNode(pred).getLocation().getStartColumn()
)
)
}
}
module AstTaintTrackingTest implements TestSig {
string getARelevantTag() { result = "ast" }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(Expr source, Element tainted, int n |
tag = "ast" and
astTaint(source, tainted) and
(
isSinkArgument(tainted)
or
exists(Element sink |
isSinkArgument(sink) and
astTaint(tainted, sink)
)
) and
n = strictcount(Expr otherSource | astTaint(otherSource, tainted)) and
(
n = 1 and value = ""
or
// If there is more than one source for this sink
// we specify the source location explicitly.
n > 1 and
value =
source.getLocation().getStartLine().toString() + ":" +
source.getLocation().getStartColumn()
) and
location = tainted.getLocation() and
element = tainted.toString()
)
}
}
import MakeTest<MergeTests<IRDefaultTaintTrackingTest, AstTaintTrackingTest>>

View File

@@ -0,0 +1,129 @@
#include "../shared.h"
struct S {
void(*f)(const char*);
void apply(char* p) {
f(p);
}
void (*get())(const char*) {
return f;
}
};
void calls_sink_with_argv(const char* a) { // $ ir-path=96:26 ir-path=102:26
sink(a); // $ ast=96:26 ast=98:18 ir-sink
}
extern int i;
class BaseWithPureVirtual {
public:
virtual void f(const char*) = 0;
};
class DerivedCallsSink : public BaseWithPureVirtual {
public:
void f(const char* p) override { // $ ir-path
sink(p); // $ ast=108:10 ir-sink SPURIOUS: ast=111:10
}
};
class DerivedDoesNotCallSink : public BaseWithPureVirtual {
public:
void f(const char* p) override {}
};
class DerivedCallsSinkDiamond1 : virtual public BaseWithPureVirtual {
public:
void f(const char* p) override { // $ ir-path
sink(p); // $ ast ir-sink
}
};
class DerivedDoesNotCallSinkDiamond2 : virtual public BaseWithPureVirtual {
public:
void f(const char* p) override {}
};
class DerivesMultiple : public DerivedCallsSinkDiamond1, public DerivedDoesNotCallSinkDiamond2 {
void f(const char* p) override { // $ ir-path=53:37 ir-path=115:11
DerivedCallsSinkDiamond1::f(p); // $ ir-path
}
};
template<typename T>
class CRTP {
public:
void f(const char* p) { // $ ir-path
static_cast<T*>(this)->g(p); // $ ir-path
}
};
class CRTPCallsSink : public CRTP<CRTPCallsSink> {
public:
void g(const char* p) { // $ ir-path
sink(p); // $ ast ir-sink
}
};
class Derived1 : public BaseWithPureVirtual {};
class Derived2 : public Derived1 {
public:
void f(const char* p) override {}
};
class Derived3 : public Derived2 {
public:
void f(const char* p) override { // $ ir-path=124:19 ir-path=126:43 ir-path=128:44
sink(p); // $ ast=124:19 ast=126:43 ast=128:44 ir-sink
}
};
class CRTPDoesNotCallSink : public CRTP<CRTPDoesNotCallSink> {
public:
void g(const char* p) {}
};
int main(int argc, char *argv[]) {
sink(argv[0]); // $ ast,ir-path,ir-sink
sink(reinterpret_cast<int>(argv)); // $ ast,ir-sink
calls_sink_with_argv(argv[1]); // $ ast,ir-path
char*** p = &argv; // $ ast,ir-path
sink(*p[0]); // $ ast ir-sink=96:26 ir-sink=98:18 ir-sink=98:17
calls_sink_with_argv(*p[i]); // $ ir-path=96:26 ir-path=98:18 ir-path=98:17 MISSING:ast
sink(*(argv + 1)); // $ ast ir-path ir-sink
BaseWithPureVirtual* b = new DerivedCallsSink;
b->f(argv[1]); // $ ast,ir-path
b = new DerivedDoesNotCallSink;
b->f(argv[0]); // $ SPURIOUS: ast
BaseWithPureVirtual* b2 = new DerivesMultiple;
b2->f(argv[i]); // $ ast,ir-path
CRTP<CRTPDoesNotCallSink> crtp_not_call_sink;
crtp_not_call_sink.f(argv[0]); // clean
CRTP<CRTPCallsSink> crtp_calls_sink;
crtp_calls_sink.f(argv[0]); // $ ast,ir-path
Derived1* calls_sink = new Derived3;
calls_sink->f(argv[1]); // $ ast,ir-path
static_cast<Derived2*>(calls_sink)->f(argv[1]); // $ ast,ir-path
dynamic_cast<Derived2*>(calls_sink)->f(argv[1]); // $ ast,ir-path
}

View File

@@ -0,0 +1,237 @@
#include "../shared.h"
int main() {
sink(_strdup(getenv("VAR"))); // $ ir MISSING: ast
sink(strdup(getenv("VAR"))); // $ ast,ir
sink(unmodeled_function(getenv("VAR"))); // clean by assumption
char untainted_buf[100] = "";
char buf[100] = "VAR = ";
sink(strcat(buf, getenv("VAR"))); // $ ast,ir
sink(buf); // $ ast,ir
sink(untainted_buf); // the two buffers would be conflated if we added flow through all partial chi inputs
return 0;
}
typedef unsigned int inet_addr_retval;
inet_addr_retval inet_addr(const char *dotted_address);
void sink(inet_addr_retval);
void test_indirect_arg_to_model() {
// This test is non-sensical but carefully arranged so we get data flow into
// inet_addr not through the function argument but through its associated
// read side effect.
void *env_pointer = getenv("VAR"); // env_pointer is tainted, not its data.
inet_addr_retval a = inet_addr((const char *)&env_pointer);
sink(a); // $ ast,ir
}
namespace std {
template< class T >
T&& move( T&& t ) noexcept;
}
void test_std_move() {
sink(std::move(getenv("VAR"))); // $ ir MISSING: ast
}
void flow_to_outparam(char ** ret, char *arg) {
*ret = arg;
}
void test_outparams() {
char *p2 = nullptr;
flow_to_outparam(&p2, getenv("VAR"));
sink(p2); // $ ir MISSING: ast
}
struct XY {
int x;
int y;
};
void taint_y(XY *xyp) {
int tainted = getenv("VAR")[0];
xyp->y = tainted;
}
void test_conflated_fields3() {
XY xy;
xy.x = 0;
taint_y(&xy);
sink(xy.x); // not tainted
}
struct Point {
int x;
int y;
void callSink() {
sink(this->x); // $ ir MISSING: ast
sink(this->y); // not tainted
}
};
void test_conflated_fields1() {
Point p;
p.x = getenv("VAR")[0];
sink(p.x); // $ ir MISSING: ast
sink(p.y); // not tainted
p.callSink();
}
void taint_x(Point *pp) {
pp->x = getenv("VAR")[0];
}
void y_to_sink(Point *pp) {
sink(pp->y); // not tainted
}
void test_conflated_fields2() {
Point p;
taint_x(&p);
y_to_sink(&p);
}
void sink(Point*);
void sink(Point);
void test_field_to_obj_taint_object(Point p) {
p.x = getenv("VAR")[0];
sink(p); // not tainted
sink(p.x); // $ ir MISSING: ast
}
void test_field_to_obj_taint_object_addrof(Point p) {
taint_x(&p);
sink(p); // not tainted
sink(&p); // not tainted
sink(p.x); // $ ir MISSING: ast
}
void test_field_to_obj_taint_pointer(Point* pp) {
pp->x = getenv("VAR")[0];
sink(pp);// not tainted
sink(*pp); // not tainted
}
void call_sink_on_object(Point* pp) {
sink(pp);// not tainted
sink(*pp);// not tainted
}
void test_field_to_obj_taint_call_sink(Point* pp) {
pp->x = getenv("VAR")[0];
call_sink_on_object(pp);
}
void test_field_to_obj_taint_through_setter(Point* pp) {
taint_x(pp);
sink(pp);// not tainted
sink(*pp); // not tainted
}
Point* getPoint();
void test_field_to_obj_local_variable() {
Point* pp = getPoint();
pp->x = getenv("VAR")[0];
sink(pp); // not tainted
sink(*pp); // not tainted
}
void test_field_to_obj_taint_array(Point* pp, int i) {
pp[0].x = getenv("VAR")[0];
sink(pp[i]); // not tainted
sink(pp);// not tainted
sink(*pp); // not tainted
}
void test_field_to_obj_test_pointer_arith(Point* pp) {
(pp + sizeof(*pp))->x = getenv("VAR")[0];
sink(pp);// not tainted
sink(pp + sizeof(*pp));// not tainted
}
void sink(char **);
void test_pointers1()
{
char buffer[1024];
char *s = getenv("VAR");
char *ptr1, **ptr2;
char *ptr3, **ptr4;
ptr1 = buffer;
ptr2 = &ptr1;
memcpy(buffer, s, 1024);
ptr3 = buffer;
ptr4 = &ptr3;
sink(buffer); // $ ast,ir
sink(ptr1); // $ ast MISSING: ir
sink(ptr2); // $ SPURIOUS: ast
sink(*ptr2); // $ ast MISSING: ir
sink(ptr3); // $ ast,ir
sink(ptr4); // $ SPURIOUS: ast,ir
sink(*ptr4); // $ ast,ir
}
void test_pointers2()
{
char buffer[1024];
char *s = getenv("VAR");
char *ptr1, **ptr2;
char *ptr3, **ptr4;
ptr1 = buffer;
ptr2 = &ptr1;
memcpy(*ptr2, s, 1024);
ptr3 = buffer;
ptr4 = &ptr3;
sink(buffer); // $ MISSING: ast,ir
sink(ptr1); // $ ast MISSING: ir
sink(ptr2); // $ SPURIOUS: ast,ir
sink(*ptr2); // $ ast,ir
sink(ptr3); // $ MISSING: ast,ir
sink(ptr4); // clean
sink(*ptr4); // $ MISSING: ast,ir
}
// --- recv ---
int recv(int s, char* buf, int len, int flags);
void test_recv() {
char buffer[1024];
recv(0, buffer, sizeof(buffer), 0);
sink(buffer); // $ ast,ir
sink(*buffer); // $ ast,ir
}
// --- send and related functions ---
struct iovec {
void *iov_base;
unsigned iov_len;
};
int readv(int, const struct iovec*, int);
void sink(const iovec* iovs);
void sink(iovec);
void test_readv_and_writev(iovec* iovs) {
readv(0, iovs, 16);
sink(iovs); // $ast,ir
sink(iovs[0]); // $ast,ir
sink(*iovs); // $ast,ir
char* p = (char*)iovs[1].iov_base;
sink(p); // $ MISSING: ast,ir
sink(*p); // $ MISSING: ast,ir
}

View File

@@ -0,0 +1,159 @@
#include "../shared.h"
typedef unsigned long size_t;
namespace std
{
template<class charT> struct char_traits;
typedef size_t streamsize;
template <class T> class allocator {
public:
allocator() throw();
};
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string {
public:
explicit basic_string(const Allocator& a = Allocator());
basic_string(const charT* s, const Allocator& a = Allocator());
const charT* c_str() const;
};
typedef basic_string<char> string;
template <class charT, class traits = char_traits<charT> >
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
public:
basic_istream<charT,traits>& operator>>(int& n);
};
template <class charT, class traits = char_traits<charT> >
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
public:
typedef charT char_type;
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
basic_ostream<charT, traits>& operator<<(int n);
};
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
template<class charT, class traits = char_traits<charT>>
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
public:
};
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
class basic_stringstream : public basic_iostream<charT, traits> {
public:
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
basic_string<charT, traits, Allocator> str() const;
};
using stringstream = basic_stringstream<char>;
}
char *source() { return getenv("USERDATA"); }
void sink(const std::string &s) {};
void sink(const std::stringstream &s) {};
void test_string()
{
char *a = source();
std::string b("123");
std::string c(source());
sink(a); // $ ast,ir
sink(b); // clean
sink(c); // $ ir MISSING: ast
sink(b.c_str()); // clean
sink(c.c_str()); // $ ir MISSING: ast
}
void test_stringstream()
{
std::stringstream ss1, ss2, ss3, ss4, ss5;
std::string t(source());
ss1 << "1234";
ss2 << source();
ss3 << "123" << source();
ss4 << source() << "456";
ss5 << t;
sink(ss1);
sink(ss2); // $ ir MISSING: ast
sink(ss3); // $ ir MISSING: ast
sink(ss4); // $ ir MISSING: ast
sink(ss5); // $ ir MISSING: ast
sink(ss1.str());
sink(ss2.str()); // $ ir MISSING: ast
sink(ss3.str()); // $ ir MISSING: ast
sink(ss4.str()); // $ ir MISSING: ast
sink(ss5.str()); // $ ir MISSING: ast
}
void test_stringstream_int(int source)
{
std::stringstream ss1, ss2;
ss1 << 1234;
ss2 << source;
sink(ss1); // clean
sink(ss2); // $ MISSING: ast,ir
sink(ss1.str()); // clean
sink(ss2.str()); // $ MISSING: ast,ir
}
using namespace std;
char *user_input() {
return source();
}
void sink(const char *filename, const char *mode);
void test_strings2()
{
string path1 = user_input();
sink(path1.c_str(), "r"); // $ ir MISSING: ast
string path2;
path2 = user_input();
sink(path2.c_str(), "r"); // $ ir MISSING: ast
string path3(user_input());
sink(path3.c_str(), "r"); // $ ir MISSING: ast
}
void test_string3()
{
const char *cs = source();
// convert char * -> std::string
std::string ss(cs);
sink(cs); // $ ast,ir
sink(ss); // $ ir MISSING: ast
}
void test_string4()
{
const char *cs = source();
// convert char * -> std::string
std::string ss(cs);
// convert back std::string -> char *
cs = ss.c_str();
sink(cs); // $ ast,ir
sink(ss); // $ ir MISSING: ast
}

View File

@@ -0,0 +1,4 @@
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:10,8-47)
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:21,3-28)
testFailures
failures

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