зеркало из https://github.com/github/codeql.git
Merge rc/3.12 into main
This commit is contained in:
Коммит
b1803d0ac2
|
@ -12,7 +12,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
- uses: actions/stale@v9
|
||||
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.'
|
||||
|
|
|
@ -28,9 +28,9 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 7.0.102
|
||||
dotnet-version: 8.0.100
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
|
|
@ -72,15 +72,15 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 7.0.102
|
||||
dotnet-version: 8.0.100
|
||||
- name: Extractor unit tests
|
||||
run: |
|
||||
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"
|
||||
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"
|
||||
shell: bash
|
||||
stubgentest:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
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@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
|
|
@ -44,3 +44,4 @@ 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
|
||||
|
|
|
@ -53,14 +53,6 @@
|
|||
"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"
|
||||
|
@ -462,23 +454,6 @@
|
|||
"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"
|
||||
|
@ -534,4 +509,4 @@
|
|||
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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 C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
|
||||
Actions.RunProcess[@"cmd.exe /C scratch\.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,10 +337,11 @@ 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(@"C:\Project\.nuget");
|
||||
Actions.DownloadFiles.Add(("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", @"C:\Project\.nuget\nuget.exe"));
|
||||
Actions.CreateDirectories.Add(@"scratch\.nuget");
|
||||
Actions.DownloadFiles.Add(("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", @"scratch\.nuget\nuget.exe"));
|
||||
|
||||
var autobuilder = CreateAutoBuilder(true);
|
||||
var solution = new TestSolution(@"C:\Project\test.sln");
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.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.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PackageReference Include="xunit" Version="2.6.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.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.3.2" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.8.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,2 @@
|
|||
description: Revert removal of uniqueness constraint on link_targets/2
|
||||
compatibility: backwards
|
|
@ -52,17 +52,18 @@ class Options extends string {
|
|||
/**
|
||||
* Holds if a call to this function will never return.
|
||||
*
|
||||
* By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`,
|
||||
* `longjmp`, `__builtin_unreachable` and any function with a
|
||||
* `noreturn` attribute or specifier.
|
||||
* 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.
|
||||
*/
|
||||
predicate exits(Function f) {
|
||||
f.getAnAttribute().hasName("noreturn")
|
||||
f.getAnAttribute().hasName(["noreturn", "__noreturn__"])
|
||||
or
|
||||
f.getASpecifier().hasName("noreturn")
|
||||
or
|
||||
f.hasGlobalOrStdName([
|
||||
"exit", "_exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable"
|
||||
"exit", "_exit", "_Exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable"
|
||||
])
|
||||
or
|
||||
CustomOptions::exits(f) // old Options.qll
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `Guards` library has been replaced with the API-compatible `IRGuards` implementation, which has better precision in some cases.
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The deprecated `DefaultTaintTracking` library has been removed.
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added a new predicate `Node.asDefinition` on `DataFlow::Node`s for selecting the dataflow node corresponding to a particular definition.
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `isUserInput`, `userInputArgument`, and `userInputReturned` predicates from `SecurityOptions` have been deprecated. Use `FlowSource` instead.
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Changed the output of `Node.toString` to better reflect how many indirections a given dataflow node has.
|
|
@ -328,6 +328,7 @@ 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, _) }
|
||||
|
||||
/**
|
||||
|
@ -338,10 +339,6 @@ 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`. */
|
||||
|
|
|
@ -7,371 +7,7 @@ import cpp
|
|||
import semmle.code.cpp.controlflow.BasicBlocks
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
import semmle.code.cpp.controlflow.Dominance
|
||||
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
import IRGuards
|
||||
|
||||
/** An `SsaDefinition` with an additional predicate `isLt`. */
|
||||
class GuardedSsa extends SsaDefinition {
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
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`.
|
||||
|
@ -201,12 +203,30 @@ private class GuardConditionFromIR extends GuardCondition {
|
|||
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
||||
*/
|
||||
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
|
||||
exists(IRBlock irb |
|
||||
exists(IRBlock irb, Instruction instr |
|
||||
ir.controls(irb, testIsTrue) and
|
||||
irb.getAnInstruction().getAst().(ControlFlowNode).getBasicBlock() = controlled and
|
||||
not isUnreachedBlock(irb)
|
||||
instr = irb.getAnInstruction() and
|
||||
instr.getAst().(ControlFlowNode).getBasicBlock() = controlled and
|
||||
not isUnreachedBlock(irb) and
|
||||
not this.excludeAsControlledInstruction(instr)
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/**
|
||||
* 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
|
||||
|
@ -12,6 +14,8 @@ 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
|
||||
|
@ -51,7 +55,7 @@ private import TaintTrackingParameter::Private
|
|||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/**
|
||||
* 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
|
||||
|
@ -12,6 +14,8 @@ 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
|
||||
|
@ -51,7 +55,7 @@ private import TaintTrackingParameter::Private
|
|||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* 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;
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
|
|||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
deprecated 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
|
||||
|
@ -48,7 +50,7 @@ private import codeql.util.Unit
|
|||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
@ -189,7 +191,7 @@ abstract 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 private class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
|
@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
deprecated private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
|
@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
|
|||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
deprecated TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
deprecated private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
|
@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
|
|||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
deprecated 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.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
deprecated class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
|
@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
|
|||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
deprecated final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
|
|||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
deprecated module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
|
@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
|
|||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
deprecated predicate flowsTo = hasFlow/3;
|
||||
|
|
|
@ -20,4 +20,6 @@ module CppDataFlow implements InputSig {
|
|||
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
|
||||
|
||||
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
|
||||
|
||||
predicate validParameterAliasStep = Private::validParameterAliasStep/2;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ 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 {
|
||||
|
@ -58,6 +59,41 @@ private module Cached {
|
|||
import Cached
|
||||
private import Nodes0
|
||||
|
||||
/**
|
||||
* A module for calculating the number of stars (i.e., `*`s) needed for various
|
||||
* dataflow node `toString` predicates.
|
||||
*/
|
||||
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)) }
|
||||
}
|
||||
|
||||
import NodeStars
|
||||
|
||||
class Node0Impl extends TIRDataFlowNode0 {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
|
@ -138,11 +174,7 @@ abstract class InstructionNode0 extends Node0Impl {
|
|||
|
||||
override DataFlowType getType() { result = getInstructionType(instr, _) }
|
||||
|
||||
override string toStringImpl() {
|
||||
if instr.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = instr.getAst().toString()
|
||||
}
|
||||
override string toStringImpl() { result = instructionToString(instr) }
|
||||
|
||||
override Location getLocationImpl() {
|
||||
if exists(instr.getAst().getLocation())
|
||||
|
@ -187,11 +219,7 @@ abstract class OperandNode0 extends Node0Impl {
|
|||
|
||||
override DataFlowType getType() { result = getOperandType(op, _) }
|
||||
|
||||
override string toStringImpl() {
|
||||
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = op.getDef().getAst().toString()
|
||||
}
|
||||
override string toStringImpl() { result = operandToString(op) }
|
||||
|
||||
override Location getLocationImpl() {
|
||||
if exists(op.getDef().getAst().getLocation())
|
||||
|
@ -1149,3 +1177,55 @@ 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`.
|
||||
}
|
||||
|
|
|
@ -15,20 +15,21 @@ 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.
|
||||
* - `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.
|
||||
* - `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.
|
||||
*/
|
||||
cached
|
||||
private newtype TIRDataFlowNode =
|
||||
|
@ -37,14 +38,13 @@ private newtype TIRDataFlowNode =
|
|||
indirectionIndex =
|
||||
[getMinIndirectionsForType(var.getUnspecifiedType()) .. Ssa::getMaxIndirectionsForType(var.getUnspecifiedType())]
|
||||
} or
|
||||
TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) {
|
||||
indirectionIndex =
|
||||
[1 .. Ssa::countIndirectionsForCppType(operand.getObjectAddress().getResultLanguageType())]
|
||||
} or
|
||||
TSsaPhiNode(Ssa::PhiNode phi) or
|
||||
TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) {
|
||||
TPostUpdateNodeImpl(Operand operand, int indirectionIndex) {
|
||||
operand = any(FieldAddress fa).getObjectAddressOperand() and
|
||||
indirectionIndex = [0 .. Ssa::countIndirectionsForCppType(Ssa::getLanguageType(operand))]
|
||||
or
|
||||
Ssa::isModifiableByCall(operand, indirectionIndex)
|
||||
} or
|
||||
TSsaPhiNode(Ssa::PhiNode phi) 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() }
|
||||
FieldAddress() { fai = this.getDef() and not Ssa::ignoreOperand(this) }
|
||||
|
||||
/** Gets the field associated with this instruction. */
|
||||
Field getField() { result = fai.getField() }
|
||||
|
@ -260,6 +260,71 @@ 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.
|
||||
*
|
||||
|
@ -372,7 +437,12 @@ 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() { result = this.(PartialDefinitionNode).getDefinedExpr() }
|
||||
Expr asPartialDefinition() {
|
||||
exists(PartialDefinitionNode pdn | this = pdn |
|
||||
pdn.getIndirectionIndex() > 0 and
|
||||
result = pdn.getDefinedExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
|
@ -416,13 +486,6 @@ class Node extends TIRDataFlowNode {
|
|||
}
|
||||
}
|
||||
|
||||
private string toExprString(Node n) {
|
||||
result = n.asExpr(0).toString()
|
||||
or
|
||||
not exists(n.asExpr()) and
|
||||
result = n.asIndirectExpr(0, 1).toString() + " indirection"
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that lifts pre-SSA dataflow nodes to regular dataflow nodes.
|
||||
*/
|
||||
|
@ -485,37 +548,53 @@ Type stripPointer(Type t) {
|
|||
result = t.(FunctionPointerIshType).getBaseType()
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
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 TPostFieldUpdateNode, PartialDefinitionNode {
|
||||
int indirectionIndex;
|
||||
class PostFieldUpdateNode extends PostUpdateNodeImpl {
|
||||
FieldAddress fieldAddress;
|
||||
|
||||
PostFieldUpdateNode() { this = TPostFieldUpdateNode(fieldAddress, indirectionIndex) }
|
||||
|
||||
override Declaration getFunction() { result = fieldAddress.getUse().getEnclosingFunction() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
PostFieldUpdateNode() { operand = fieldAddress.getObjectAddressOperand() }
|
||||
|
||||
FieldAddress getFieldAddress() { result = fieldAddress }
|
||||
|
||||
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() }
|
||||
Field getUpdatedField() { result = this.getFieldAddress().getField() }
|
||||
|
||||
override string toStringImpl() { result = this.getPreUpdateNode() + " [post update]" }
|
||||
}
|
||||
|
@ -700,10 +779,12 @@ class IndirectParameterNode extends Node instanceof IndirectInstruction {
|
|||
override Location getLocationImpl() { result = this.getParameter().getLocation() }
|
||||
|
||||
override string toStringImpl() {
|
||||
result = this.getParameter().toString() + " indirection"
|
||||
or
|
||||
not exists(this.getParameter()) and
|
||||
result = "this indirection"
|
||||
exists(string prefix | prefix = stars(this) |
|
||||
result = prefix + this.getParameter().toString()
|
||||
or
|
||||
not exists(this.getParameter()) and
|
||||
result = prefix + "this"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,13 +832,8 @@ class IndirectReturnNode extends Node {
|
|||
* A node representing the indirection of a value after it
|
||||
* has been returned from a function.
|
||||
*/
|
||||
class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PartialDefinitionNode {
|
||||
ArgumentOperand operand;
|
||||
int indirectionIndex;
|
||||
|
||||
IndirectArgumentOutNode() { this = TIndirectArgumentOutNode(operand, indirectionIndex) }
|
||||
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
class IndirectArgumentOutNode extends PostUpdateNodeImpl {
|
||||
override ArgumentOperand operand;
|
||||
|
||||
int getArgumentIndex() {
|
||||
exists(CallInstruction call | call.getArgumentOperand(result) = operand)
|
||||
|
@ -769,24 +845,16 @@ class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PartialDef
|
|||
|
||||
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() {
|
||||
// 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"
|
||||
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"
|
||||
)
|
||||
}
|
||||
|
||||
override Location getLocationImpl() { result = operand.getLocation() }
|
||||
|
||||
override Expr getDefinedExpr() { result = operand.getDef().getUnconvertedResultExpression() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -891,7 +959,8 @@ private Type getTypeImpl0(Type t, int indirectionIndex) {
|
|||
*
|
||||
* If `indirectionIndex` cannot be stripped off `t`, an `UnknownType` is returned.
|
||||
*/
|
||||
bindingset[indirectionIndex]
|
||||
bindingset[t, indirectionIndex]
|
||||
pragma[inline_late]
|
||||
Type getTypeImpl(Type t, int indirectionIndex) {
|
||||
result = getTypeImpl0(t, indirectionIndex)
|
||||
or
|
||||
|
@ -943,7 +1012,7 @@ private module RawIndirectNodes {
|
|||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
|
||||
result = stars(this) + operandNode(this.getOperand()).toStringImpl()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -985,7 +1054,7 @@ private module RawIndirectNodes {
|
|||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = instructionNode(this.getInstruction()).toStringImpl() + " indirection"
|
||||
result = stars(this) + instructionNode(this.getInstruction()).toStringImpl()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1078,9 +1147,7 @@ class FinalParameterNode extends Node, TFinalParameterNode {
|
|||
result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
if indirectionIndex > 1 then result = p.toString() + " indirection" else result = p.toString()
|
||||
}
|
||||
override string toStringImpl() { result = stars(this) + p.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1142,22 +1209,6 @@ 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
|
||||
|
@ -1165,7 +1216,7 @@ private module GetConvertedResultExpression {
|
|||
// represents the extent.
|
||||
exists(TranslatedNonConstantAllocationSize tas |
|
||||
result = tas.getExtent().getExpr() and
|
||||
instr = tas.getInstruction(any(AllocationExtentConvertTag tag))
|
||||
instr = tas.getInstruction(AllocationExtentConvertTag())
|
||||
)
|
||||
or
|
||||
// There's no instruction that returns `ParenthesisExpr`, but some queries
|
||||
|
@ -1174,6 +1225,39 @@ 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) {
|
||||
|
@ -1182,6 +1266,75 @@ 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
|
||||
|
@ -1557,6 +1710,10 @@ 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();
|
||||
}
|
||||
|
||||
|
@ -1571,6 +1728,8 @@ 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() }
|
||||
|
||||
|
@ -1622,9 +1781,7 @@ class VariableNode extends Node, TVariableNode {
|
|||
result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
if indirectionIndex = 1 then result = v.toString() else result = v.toString() + " indirection"
|
||||
}
|
||||
override string toStringImpl() { result = stars(this) + v.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2084,6 +2241,25 @@ 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;
|
||||
|
@ -2091,11 +2267,7 @@ class FieldContent extends Content, TFieldContent {
|
|||
|
||||
FieldContent() { this = TFieldContent(f, indirectionIndex) }
|
||||
|
||||
override string toString() {
|
||||
indirectionIndex = 1 and result = f.toString()
|
||||
or
|
||||
indirectionIndex > 1 and result = f.toString() + " indirection"
|
||||
}
|
||||
override string toString() { result = contentStars(this) + f.toString() }
|
||||
|
||||
Field getField() { result = f }
|
||||
|
||||
|
@ -2124,11 +2296,7 @@ class UnionContent extends Content, TUnionContent {
|
|||
|
||||
UnionContent() { this = TUnionContent(u, bytes, indirectionIndex) }
|
||||
|
||||
override string toString() {
|
||||
indirectionIndex = 1 and result = u.toString()
|
||||
or
|
||||
indirectionIndex > 1 and result = u.toString() + " indirection"
|
||||
}
|
||||
override string toString() { result = contentStars(this) + u.toString() }
|
||||
|
||||
/** Gets a field of the underlying union of this `UnionContent`, if any. */
|
||||
Field getAField() { result = u.getAField() and getFieldSize(result) = bytes }
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* This file contains the class that implements the _debug_ version of
|
||||
* `toString` for `Instruction` and `Operand` dataflow nodes.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Unit
|
||||
private import Node0ToString
|
||||
private import DataFlowUtil
|
||||
|
||||
private class DebugNode0ToString extends Node0ToString {
|
||||
DebugNode0ToString() {
|
||||
// Silence warning about `this` not being bound.
|
||||
exists(this)
|
||||
}
|
||||
|
||||
override string instructionToString(Instruction i) { result = i.getDumpString() }
|
||||
|
||||
override string operandToString(Operand op) {
|
||||
result = op.getDumpString() + " @ " + op.getUse().getResultId()
|
||||
}
|
||||
|
||||
override string toExprString(Node n) { none() }
|
||||
}
|
|
@ -1,668 +0,0 @@
|
|||
/**
|
||||
* 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()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* This file imports the class that is used to construct the strings used by
|
||||
* `Node.ToString`.
|
||||
*
|
||||
* Normally, this file should just import `NormalNode0ToString` to compute the
|
||||
* efficient `toString`, but for debugging purposes one can import
|
||||
* `DebugPrinting.qll` to better correlate the dataflow nodes with their
|
||||
* underlying instructions and operands.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Unit
|
||||
private import DataFlowUtil
|
||||
import NormalNode0ToString // Change this import to control which version should be used.
|
||||
|
||||
/** An abstract class to control the behavior of `Node.toString`. */
|
||||
abstract class Node0ToString extends Unit {
|
||||
/**
|
||||
* Gets the string that should be used by `OperandNode.toString` to print the
|
||||
* dataflow node whose underlying operand is `op.`
|
||||
*/
|
||||
abstract string operandToString(Operand op);
|
||||
|
||||
/**
|
||||
* Gets the string that should be used by `InstructionNode.toString` to print
|
||||
* the dataflow node whose underlying instruction is `instr`.
|
||||
*/
|
||||
abstract string instructionToString(Instruction i);
|
||||
|
||||
/**
|
||||
* Gets the string representation of the `Expr` associated with `n`, if any.
|
||||
*/
|
||||
abstract string toExprString(Node n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string that should be used by `OperandNode.toString` to print the
|
||||
* dataflow node whose underlying operand is `op.`
|
||||
*/
|
||||
string operandToString(Operand op) { result = any(Node0ToString s).operandToString(op) }
|
||||
|
||||
/**
|
||||
* Gets the string that should be used by `InstructionNode.toString` to print
|
||||
* the dataflow node whose underlying instruction is `instr`.
|
||||
*/
|
||||
string instructionToString(Instruction instr) {
|
||||
result = any(Node0ToString s).instructionToString(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of the `Expr` associated with `n`, if any.
|
||||
*/
|
||||
string toExprString(Node n) { result = any(Node0ToString s).toExprString(n) }
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* This file contains the class that implements the non-debug version of
|
||||
* `toString` for `Instruction` and `Operand` dataflow nodes.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Unit
|
||||
private import Node0ToString
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
|
||||
private class NormalNode0ToString extends Node0ToString {
|
||||
NormalNode0ToString() {
|
||||
// Silence warning about `this` not being bound.
|
||||
exists(this)
|
||||
}
|
||||
|
||||
override string instructionToString(Instruction i) {
|
||||
if i.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = i.getAst().toString()
|
||||
}
|
||||
|
||||
override string operandToString(Operand op) {
|
||||
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = op.getDef().getAst().toString()
|
||||
}
|
||||
|
||||
override string toExprString(Node n) {
|
||||
result = n.asExpr(0).toString()
|
||||
or
|
||||
not exists(n.asExpr()) and
|
||||
result = stars(n) + n.asIndirectExpr(0, 1).toString()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
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) }
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import SsaInternals as Ssa
|
||||
private import PrintIRUtilities
|
||||
|
||||
|
@ -33,9 +34,9 @@ private string getNodeProperty(Node node, string key) {
|
|||
key = "flow" and
|
||||
result =
|
||||
strictconcat(string flow, boolean to, int order1, int order2 |
|
||||
flow = getFromFlow(node, order1, order2) + "->" + starsForNode(node) + "@" and to = false
|
||||
flow = getFromFlow(node, order1, order2) + "->" + stars(node) + "@" and to = false
|
||||
or
|
||||
flow = starsForNode(node) + "@->" + getToFlow(node, order1, order2) and to = true
|
||||
flow = stars(node) + "@->" + getToFlow(node, order1, order2) and to = true
|
||||
|
|
||||
flow, ", " order by to, order1, order2, flow
|
||||
)
|
||||
|
@ -59,8 +60,4 @@ 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) }
|
||||
}
|
||||
|
|
|
@ -7,37 +7,14 @@ private import semmle.code.cpp.ir.IR
|
|||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
private string stars(int k) {
|
||||
k =
|
||||
[0 .. max([
|
||||
any(RawIndirectInstruction n).getIndirectionIndex(),
|
||||
any(RawIndirectOperand n).getIndirectionIndex()
|
||||
]
|
||||
)] and
|
||||
(if k = 0 then result = "" else result = "*" + stars(k - 1))
|
||||
}
|
||||
|
||||
string starsForNode(Node node) {
|
||||
exists(int indirectionIndex |
|
||||
node.(IndirectInstruction).hasInstructionAndIndirectionIndex(_, indirectionIndex) or
|
||||
node.(IndirectOperand).hasOperandAndIndirectionIndex(_, indirectionIndex)
|
||||
|
|
||||
result = stars(indirectionIndex)
|
||||
)
|
||||
or
|
||||
not node instanceof IndirectInstruction and
|
||||
not node instanceof IndirectOperand and
|
||||
result = ""
|
||||
}
|
||||
|
||||
private Instruction getInstruction(Node n, string stars) {
|
||||
result = [n.asInstruction(), n.(RawIndirectInstruction).getInstruction()] and
|
||||
stars = starsForNode(n)
|
||||
stars = stars(n)
|
||||
}
|
||||
|
||||
private Operand getOperand(Node n, string stars) {
|
||||
result = [n.asOperand(), n.(RawIndirectOperand).getOperand()] and
|
||||
stars = starsForNode(n)
|
||||
stars = stars(n)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,15 @@ private module SourceVariables {
|
|||
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||
}
|
||||
|
||||
private int maxNumberOfIndirections() { result = max(SourceVariable sv | | sv.getIndirection()) }
|
||||
|
||||
private string repeatStars(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n = [1 .. maxNumberOfIndirections()] and
|
||||
result = "*" + repeatStars(n - 1)
|
||||
}
|
||||
|
||||
class SourceVariable extends TSourceVariable {
|
||||
SsaInternals0::SourceVariable base;
|
||||
int ind;
|
||||
|
@ -32,13 +41,7 @@ private module SourceVariables {
|
|||
SsaInternals0::SourceVariable getBaseVariable() { result = base }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
ind = 0 and
|
||||
result = this.getBaseVariable().toString()
|
||||
or
|
||||
ind > 0 and
|
||||
result = this.getBaseVariable().toString() + " indirection"
|
||||
}
|
||||
string toString() { result = repeatStars(this.getIndirection()) }
|
||||
|
||||
/**
|
||||
* Gets the number of loads performed on the base source variable
|
||||
|
|
|
@ -417,60 +417,42 @@ class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
|
|||
override CppType getLanguageType() { result = getResultLanguageType(call) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
)
|
||||
}
|
||||
private module IsModifiableAtImpl {
|
||||
pragma[nomagic]
|
||||
private predicate isUnderlyingIndirectionType(Type t) {
|
||||
t = any(Indirection ind).getUnderlyingType()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* 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
|
||||
base = getTypeImpl(pointerType, indirectionIndex)
|
||||
|
|
||||
// The value cannot be modified if it has a const specifier,
|
||||
|
@ -480,28 +462,114 @@ private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
|
|||
// 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
|
||||
// If the `indirectionIndex`'th dereference of a type can be modified
|
||||
// then so can the `indirectionIndex + 1`'th dereference.
|
||||
isModifiableAtImpl(cppType, indirectionIndex - 1)
|
||||
)
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
)
|
||||
}
|
||||
import IsModifiableAtImpl
|
||||
|
||||
abstract class BaseSourceVariableInstruction extends Instruction {
|
||||
/** Gets the base source variable accessed by this instruction. */
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/**
|
||||
* 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
|
||||
|
@ -12,6 +14,8 @@ 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
|
||||
|
@ -51,7 +55,7 @@ private import TaintTrackingParameter::Private
|
|||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/**
|
||||
* 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
|
||||
|
@ -12,6 +14,8 @@ 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
|
||||
|
@ -51,7 +55,7 @@ private import TaintTrackingParameter::Private
|
|||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/**
|
||||
* 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
|
||||
|
@ -12,6 +14,8 @@ 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
|
||||
|
@ -51,7 +55,7 @@ private import TaintTrackingParameter::Private
|
|||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
|
|
|
@ -227,7 +227,7 @@ class CppType extends TCppType {
|
|||
predicate hasType(Type type, boolean isGLValue) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this type represents the C++ type `type`. If `isGLValue` is `true`, then this type
|
||||
* Holds if this type represents the C++ unspecified 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,6 +236,18 @@ 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
pragma[nomagic]
|
||||
private Type stripTopLevelSpecifiersOnly(Type t) {
|
||||
result = stripTopLevelSpecifiersOnly(t.(SpecifiedType).getBaseType())
|
||||
result = stripTopLevelSpecifiersOnly(pragma[only_bind_out](t.(SpecifiedType).getBaseType()))
|
||||
or
|
||||
result = t and
|
||||
not t instanceof SpecifiedType
|
||||
|
|
|
@ -25,4 +25,8 @@ 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() }
|
||||
}
|
||||
|
|
|
@ -173,11 +173,11 @@ private module ModulusAnalysisInstantiated implements ModulusAnalysisSig<Sem> {
|
|||
}
|
||||
|
||||
module ConstantStage =
|
||||
RangeStage<SemLocation, Sem, FloatDelta, ConstantBounds, FloatOverflow, CppLangImplConstant,
|
||||
RangeStage<SemLocation, Sem, FloatDelta, AllBounds, FloatOverflow, CppLangImplConstant,
|
||||
SignAnalysis, ModulusAnalysisInstantiated>;
|
||||
|
||||
module RelativeStage =
|
||||
RangeStage<SemLocation, Sem, FloatDelta, RelativeBounds, FloatOverflow, CppLangImplRelative,
|
||||
RangeStage<SemLocation, Sem, FloatDelta, AllBounds, FloatOverflow, CppLangImplRelative,
|
||||
SignAnalysis, ModulusAnalysisInstantiated>;
|
||||
|
||||
private newtype TSemReason =
|
||||
|
|
|
@ -57,4 +57,8 @@ 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() }
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class SecurityOptions extends string {
|
|||
/**
|
||||
* The argument of the given function is filled in from user input.
|
||||
*/
|
||||
predicate userInputArgument(FunctionCall functionCall, int arg) {
|
||||
deprecated 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.
|
||||
*/
|
||||
predicate userInputReturned(FunctionCall functionCall) {
|
||||
deprecated predicate userInputReturned(FunctionCall functionCall) {
|
||||
exists(string fname |
|
||||
functionCall.getTarget().getName() = fname and
|
||||
(
|
||||
|
@ -91,12 +91,8 @@ 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.
|
||||
*/
|
||||
predicate userInputReturn(string function) { none() }
|
||||
deprecated predicate userInputReturn(string function) { none() }
|
||||
|
||||
/**
|
||||
* The argument of the given function is used for running a process or loading
|
||||
|
@ -117,7 +113,7 @@ class SecurityOptions extends string {
|
|||
* computed from user input. Such expressions are treated as
|
||||
* sources of taint.
|
||||
*/
|
||||
predicate isUserInput(Expr expr, string cause) {
|
||||
deprecated predicate isUserInput(Expr expr, string cause) {
|
||||
exists(FunctionCall fc, int i |
|
||||
this.userInputArgument(fc, i) and
|
||||
expr = fc.getArgument(i) and
|
||||
|
@ -178,17 +174,17 @@ predicate argv(Parameter argv) {
|
|||
predicate isPureFunction(string name) { exists(SecurityOptions opts | opts.isPureFunction(name)) }
|
||||
|
||||
/** Convenience accessor for SecurityOptions.userInputArgument */
|
||||
predicate userInputArgument(FunctionCall functionCall, int arg) {
|
||||
deprecated predicate userInputArgument(FunctionCall functionCall, int arg) {
|
||||
exists(SecurityOptions opts | opts.userInputArgument(functionCall, arg))
|
||||
}
|
||||
|
||||
/** Convenience accessor for SecurityOptions.userInputReturn */
|
||||
predicate userInputReturned(FunctionCall functionCall) {
|
||||
deprecated predicate userInputReturned(FunctionCall functionCall) {
|
||||
exists(SecurityOptions opts | opts.userInputReturned(functionCall))
|
||||
}
|
||||
|
||||
/** Convenience accessor for SecurityOptions.isUserInput */
|
||||
predicate isUserInput(Expr expr, string cause) {
|
||||
deprecated predicate isUserInput(Expr expr, string cause) {
|
||||
exists(SecurityOptions opts | opts.isUserInput(expr, cause))
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ class CustomSecurityOptions extends SecurityOptions {
|
|||
none() // rules to match custom functions replace this line
|
||||
}
|
||||
|
||||
override predicate userInputArgument(FunctionCall functionCall, int arg) {
|
||||
deprecated override predicate userInputArgument(FunctionCall functionCall, int arg) {
|
||||
SecurityOptions.super.userInputArgument(functionCall, arg)
|
||||
or
|
||||
exists(string fname |
|
||||
|
@ -36,7 +36,7 @@ class CustomSecurityOptions extends SecurityOptions {
|
|||
)
|
||||
}
|
||||
|
||||
override predicate userInputReturned(FunctionCall functionCall) {
|
||||
deprecated override predicate userInputReturned(FunctionCall functionCall) {
|
||||
SecurityOptions.super.userInputReturned(functionCall)
|
||||
or
|
||||
exists(string fname |
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/**
|
||||
* 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
|
|
@ -1,654 +0,0 @@
|
|||
/**
|
||||
* 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
|
||||
)
|
||||
)
|
||||
}
|
|
@ -2149,7 +2149,7 @@ includes(
|
|||
);
|
||||
|
||||
link_targets(
|
||||
unique int id: @link_target,
|
||||
int id: @link_target,
|
||||
int binary: @file ref
|
||||
);
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,2 @@
|
|||
description: Remove uniqueness constraint on link_targets/2
|
||||
compatibility: full
|
|
@ -16,7 +16,11 @@ import semmle.code.cpp.dataflow.new.DataFlow
|
|||
import FlowAfterFree
|
||||
import DoubleFree::PathGraph
|
||||
|
||||
predicate isFree(DataFlow::Node n, Expr e) { isFree(n, e, _) }
|
||||
/**
|
||||
* 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, _) }
|
||||
|
||||
/**
|
||||
* `dealloc1` is a deallocation expression and `e` is an expression such
|
||||
|
@ -28,7 +32,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
|
||||
|
@ -42,7 +46,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,
|
||||
|
|
|
@ -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,6 +87,8 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
|
|||
|
|
||||
e = any(StoreInstruction store).getDestinationAddress().getUnconvertedResultExpression()
|
||||
)
|
||||
or
|
||||
n.asExpr() instanceof ArrayExpr
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,14 +96,17 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is a dataflow node such that `n.asExpr() = e` and `e`
|
||||
* is being freed by a deallocation expression `dealloc`.
|
||||
* 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`).
|
||||
*/
|
||||
predicate isFree(DataFlow::Node n, Expr e, DeallocationExpr dealloc) {
|
||||
predicate isFree(DataFlow::Node outgoing, DataFlow::Node incoming, Expr e, DeallocationExpr dealloc) {
|
||||
exists(Expr conv |
|
||||
e = conv.getUnconverted() and
|
||||
conv = dealloc.getFreedExpr().getFullyConverted() and
|
||||
conv = n.asConvertedExpr()
|
||||
incoming = outgoing.(DataFlow::PostUpdateNode).getPreUpdateNode() and
|
||||
conv = incoming.asConvertedExpr()
|
||||
) and
|
||||
// Ignore realloc functions
|
||||
not exists(dealloc.(FunctionCall).getTarget().(AllocationFunction).getReallocPtrArg())
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<!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>
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @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."
|
|
@ -19,6 +19,7 @@ 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]
|
||||
|
@ -60,7 +61,9 @@ 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))
|
||||
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output)) and
|
||||
// Exclude results from incorrectky checked scanf query
|
||||
not incorrectlyCheckedScanf(call)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
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
|
||||
}
|
|
@ -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
|
||||
|
@ -101,35 +101,43 @@ module ParameterSinks {
|
|||
)
|
||||
}
|
||||
|
||||
private CallInstruction getAnAlwaysReachedCallInstruction(IRFunction f) {
|
||||
result.getBlock().postDominates(f.getEntryBlock())
|
||||
private CallInstruction getAnAlwaysReachedCallInstruction() {
|
||||
exists(IRFunction f | result.getBlock().postDominates(f.getEntryBlock()))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callHasTargetAndArgument(Function f, int i, CallInstruction call, Instruction argument) {
|
||||
call.getStaticCallTarget() = f and
|
||||
call.getArgument(i) = argument
|
||||
private predicate callHasTargetAndArgument(Function f, int i, Instruction argument) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = f and
|
||||
call.getArgument(i) = argument and
|
||||
call = getAnAlwaysReachedCallInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate initializeParameterInFunction(Function f, int i, InitializeParameterInstruction init) {
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) = f and
|
||||
init.hasIndex(i)
|
||||
private predicate initializeParameterInFunction(Function f, int i) {
|
||||
exists(InitializeParameterInstruction init |
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) = f and
|
||||
init.hasIndex(i) and
|
||||
init = getAnAlwaysDereferencedParameter()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate alwaysDereferencedArgumentHasValueNumber(ValueNumber vn) {
|
||||
exists(int i, Function f, Instruction argument |
|
||||
callHasTargetAndArgument(f, i, argument) and
|
||||
initializeParameterInFunction(pragma[only_bind_into](f), pragma[only_bind_into](i)) and
|
||||
vn.getAnInstruction() = argument
|
||||
)
|
||||
}
|
||||
|
||||
InitializeParameterInstruction getAnAlwaysDereferencedParameter() {
|
||||
result = getAnAlwaysDereferencedParameter0()
|
||||
or
|
||||
exists(
|
||||
CallInstruction call, int i, InitializeParameterInstruction p, Instruction argument,
|
||||
Function f
|
||||
|
|
||||
callHasTargetAndArgument(f, i, call, argument) and
|
||||
initializeParameterInFunction(f, i, p) and
|
||||
p = getAnAlwaysDereferencedParameter() and
|
||||
result =
|
||||
pragma[only_bind_out](pragma[only_bind_into](valueNumber(argument)).getAnInstruction()) and
|
||||
call = getAnAlwaysReachedCallInstruction(_)
|
||||
exists(ValueNumber vn |
|
||||
alwaysDereferencedArgumentHasValueNumber(vn) and
|
||||
vn.getAnInstruction() = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +178,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()
|
||||
|
|
|
@ -345,6 +345,8 @@ 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.
|
||||
|
|
|
@ -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 = getBufferSize(bw.getDest(), _) and
|
||||
destSize = max(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() +
|
||||
|
|
|
@ -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)
|
||||
not inSystemMacroExpansion(use) and
|
||||
use = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
import cpp
|
||||
import semmle.code.cpp.models.implementations.StdContainer
|
||||
|
||||
/**
|
||||
* Holds if `e` will be consumed by its parent as a glvalue and does not have
|
||||
* an lvalue-to-rvalue conversion. This means that it will be materialized into
|
||||
* a temporary object.
|
||||
*/
|
||||
predicate isTemporary(Expr e) {
|
||||
e instanceof TemporaryObjectExpr
|
||||
or
|
||||
e.isPRValueCategory() and
|
||||
e.getUnspecifiedType() instanceof Class and
|
||||
not e.hasLValueToRValueConversion()
|
||||
}
|
||||
|
||||
/** Holds if `e` is written to a container. */
|
||||
predicate isStoredInContainer(Expr e) {
|
||||
exists(StdSequenceContainerInsert insert, Call call, int index |
|
||||
call = insert.getACallToThisFunction() and
|
||||
index = insert.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceContainerPush push, Call call, int index |
|
||||
call = push.getACallToThisFunction() and
|
||||
index = push.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplace emplace, Call call, int index |
|
||||
call = emplace.getACallToThisFunction() and
|
||||
index = emplace.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplaceBack emplaceBack, Call call, int index |
|
||||
call = emplaceBack.getACallToThisFunction() and
|
||||
index = emplaceBack.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` or a conversion of `e` has an lvalue-to-rvalue conversion.
|
||||
*/
|
||||
private predicate hasLValueToRValueConversion(Expr e) {
|
||||
e.getConversion*().hasLValueToRValueConversion() and
|
||||
not e instanceof ConditionalExpr // ConditionalExpr may be spuriously reported as having an lvalue-to-rvalue conversion
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value of `e` outlives the enclosing full expression. For
|
||||
* example, because the value is stored in a local variable.
|
||||
*/
|
||||
predicate outlivesFullExpr(Expr e) {
|
||||
not hasLValueToRValueConversion(e) and
|
||||
(
|
||||
any(Assignment assign).getRValue() = e
|
||||
or
|
||||
any(Variable v).getInitializer().getExpr() = e
|
||||
or
|
||||
any(ReturnStmt ret).getExpr() = e
|
||||
or
|
||||
exists(ConditionalExpr cond |
|
||||
outlivesFullExpr(cond) and
|
||||
[cond.getThen(), cond.getElse()] = e
|
||||
)
|
||||
or
|
||||
exists(BinaryOperation bin |
|
||||
outlivesFullExpr(bin) and
|
||||
bin.getAnOperand() = e and
|
||||
not bin instanceof ComparisonOperation
|
||||
)
|
||||
or
|
||||
exists(PointerFieldAccess fa |
|
||||
outlivesFullExpr(fa) and
|
||||
fa.getQualifier() = e
|
||||
)
|
||||
or
|
||||
exists(AddressOfExpr ao |
|
||||
outlivesFullExpr(ao) and
|
||||
ao.getOperand() = e
|
||||
)
|
||||
or
|
||||
exists(ClassAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAFieldExpr(_) = e
|
||||
)
|
||||
or
|
||||
exists(ArrayAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAnElementExpr(_) = e
|
||||
)
|
||||
or
|
||||
isStoredInContainer(e)
|
||||
)
|
||||
}
|
|
@ -14,81 +14,7 @@
|
|||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.implementations.StdString
|
||||
import semmle.code.cpp.models.implementations.StdContainer
|
||||
|
||||
/**
|
||||
* Holds if `e` will be consumed by its parent as a glvalue and does not have
|
||||
* an lvalue-to-rvalue conversion. This means that it will be materialized into
|
||||
* a temporary object.
|
||||
*/
|
||||
predicate isTemporary(Expr e) {
|
||||
e instanceof TemporaryObjectExpr
|
||||
or
|
||||
e.isPRValueCategory() and
|
||||
e.getUnspecifiedType() instanceof Class and
|
||||
not e.hasLValueToRValueConversion()
|
||||
}
|
||||
|
||||
/** Holds if `e` is written to a container. */
|
||||
predicate isStoredInContainer(Expr e) {
|
||||
exists(StdSequenceContainerInsert insert, Call call, int index |
|
||||
call = insert.getACallToThisFunction() and
|
||||
index = insert.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceContainerPush push, Call call, int index |
|
||||
call = push.getACallToThisFunction() and
|
||||
index = push.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplace emplace, Call call, int index |
|
||||
call = emplace.getACallToThisFunction() and
|
||||
index = emplace.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplaceBack emplaceBack, Call call, int index |
|
||||
call = emplaceBack.getACallToThisFunction() and
|
||||
index = emplaceBack.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value of `e` outlives the enclosing full expression. For
|
||||
* example, because the value is stored in a local variable.
|
||||
*/
|
||||
predicate outlivesFullExpr(Expr e) {
|
||||
any(Assignment assign).getRValue() = e
|
||||
or
|
||||
any(Variable v).getInitializer().getExpr() = e
|
||||
or
|
||||
any(ReturnStmt ret).getExpr() = e
|
||||
or
|
||||
exists(ConditionalExpr cond |
|
||||
outlivesFullExpr(cond) and
|
||||
[cond.getThen(), cond.getElse()] = e
|
||||
)
|
||||
or
|
||||
exists(BinaryOperation bin |
|
||||
outlivesFullExpr(bin) and
|
||||
bin.getAnOperand() = e
|
||||
)
|
||||
or
|
||||
exists(ClassAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAFieldExpr(_) = e
|
||||
)
|
||||
or
|
||||
exists(ArrayAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAnElementExpr(_) = e
|
||||
)
|
||||
or
|
||||
isStoredInContainer(e)
|
||||
}
|
||||
import Temporaries
|
||||
|
||||
from Call c
|
||||
where
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Calling <code>get</code> on a <code>std::unique_ptr</code> object returns a pointer to the underlying allocations.
|
||||
When the <code>std::unique_ptr</code> object is destroyed, the pointer returned by <code>get</code> is no
|
||||
longer valid. If the pointer is used after the <code>std::unique_ptr</code> object is destroyed, then the behavior is undefined.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Ensure that the pointer returned by <code>get</code> does not outlive the underlying <code>std::unique_ptr</code> object.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example gets a <code>std::unique_ptr</code> object, and then converts the resulting unique pointer to a
|
||||
pointer using <code>get</code> so that it can be passed to the <code>work</code> function.
|
||||
|
||||
However, the <code>std::unique_ptr</code> object is destroyed as soon as the call
|
||||
to <code>get</code> returns. This means that <code>work</code> is given a pointer to invalid memory.
|
||||
</p>
|
||||
|
||||
<sample src="UseOfUniquePointerAfterLifetimeEndsBad.cpp" />
|
||||
|
||||
<p>
|
||||
The following example fixes the above code by ensuring that the pointer returned by the call to <code>get</code> does
|
||||
not outlive the underlying <code>std::unique_ptr</code> objects. This ensures that the pointer passed to <code>work</code>
|
||||
points to valid memory.
|
||||
</p>
|
||||
|
||||
<sample src="UseOfUniquePointerAfterLifetimeEndsGood.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM50-CPP.+Do+not+access+freed+memory">MEM50-CPP. Do not access freed memory</a>.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* @name Use of unique pointer after lifetime ends
|
||||
* @description Referencing the contents of a unique pointer after the underlying object has expired may lead to unexpected behavior.
|
||||
* @kind problem
|
||||
* @precision high
|
||||
* @id cpp/use-of-unique-pointer-after-lifetime-ends
|
||||
* @problem.severity warning
|
||||
* @security-severity 8.8
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-416
|
||||
* external/cwe/cwe-664
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
import Temporaries
|
||||
|
||||
predicate isUniquePointerDerefFunction(Function f) {
|
||||
exists(PointerWrapper wrapper |
|
||||
f = wrapper.getAnUnwrapperFunction() and
|
||||
// We only want unique pointers as the memory behind share pointers may still be
|
||||
// alive after the shared pointer is destroyed.
|
||||
wrapper.(Class).hasQualifiedName(["std", "bsl"], "unique_ptr")
|
||||
)
|
||||
}
|
||||
|
||||
from Call c
|
||||
where
|
||||
outlivesFullExpr(c) and
|
||||
not c.isFromUninstantiatedTemplate(_) and
|
||||
isUniquePointerDerefFunction(c.getTarget()) and
|
||||
isTemporary(c.getQualifier().getFullyConverted())
|
||||
select c,
|
||||
"The underlying unique pointer object is destroyed after the call to '" + c.getTarget() +
|
||||
"' returns."
|
|
@ -0,0 +1,10 @@
|
|||
#include <memory>
|
||||
std::unique_ptr<T> getUniquePointer();
|
||||
void work(const T*);
|
||||
|
||||
// BAD: the unique pointer is deallocated when `get` returns. So `work`
|
||||
// is given a pointer to invalid memory.
|
||||
void work_with_unique_ptr_bad() {
|
||||
const T* combined_string = getUniquePointer().get();
|
||||
work(combined_string);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#include <memory>
|
||||
std::unique_ptr<T> getUniquePointer();
|
||||
void work(const T*);
|
||||
|
||||
// GOOD: the unique pointer outlives the call to `work`. So the pointer
|
||||
// obtainted from `get` is valid.
|
||||
void work_with_unique_ptr_good() {
|
||||
auto combined_string = getUniquePointer();
|
||||
work(combined_string.get());
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: newQuery
|
||||
---
|
||||
* 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.
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `cpp/use-of-unique-pointer-after-lifetime-ends`, to detect uses of the contents unique pointers that will be destroyed immediately.
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* 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,0 +1,110 @@
|
|||
/**
|
||||
* @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."
|
|
@ -126,13 +126,13 @@ class Resource extends MemberVariable {
|
|||
}
|
||||
|
||||
private predicate calledFromDestructor(Function f) {
|
||||
f instanceof Destructor and f.getDeclaringType() = this.getDeclaringType()
|
||||
pragma[only_bind_into](f) instanceof Destructor and
|
||||
f.getDeclaringType() = this.getDeclaringType()
|
||||
or
|
||||
exists(Function mid, FunctionCall fc |
|
||||
exists(Function mid |
|
||||
this.calledFromDestructor(mid) and
|
||||
fc.getEnclosingFunction() = mid and
|
||||
fc.getTarget() = f and
|
||||
f.getDeclaringType() = this.getDeclaringType()
|
||||
mid.calls(f) and
|
||||
pragma[only_bind_out](f.getDeclaringType()) = pragma[only_bind_out](this.getDeclaringType())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,18 +32,41 @@ 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, int pos |
|
||||
ec = e.getEnumConstant(pos) and
|
||||
exists(EnumConstant ec |
|
||||
ec = e.getAnEnumConstant() and
|
||||
not hasInitializer(ec) and
|
||||
exists(EnumConstant ec2, int pos2 |
|
||||
ec2 = e.getEnumConstant(pos2) and
|
||||
pos2 > pos and
|
||||
not hasReferenceInitializer(ec2)
|
||||
)
|
||||
hasNextWithoutReferenceInitializer(e, ec)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
edges
|
||||
| test.cpp:22:27:22:30 | argv indirection | test.cpp:29:13:29:20 | filePath indirection |
|
||||
| test.cpp:22:27:22:30 | **argv | test.cpp:29:13:29:20 | *filePath |
|
||||
nodes
|
||||
| test.cpp:22:27:22:30 | argv indirection | semmle.label | argv indirection |
|
||||
| test.cpp:29:13:29:20 | filePath indirection | semmle.label | filePath indirection |
|
||||
| test.cpp:22:27:22:30 | **argv | semmle.label | **argv |
|
||||
| test.cpp:29:13:29:20 | *filePath | semmle.label | *filePath |
|
||||
subpaths
|
||||
#select
|
||||
| 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. |
|
||||
| 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. |
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
| 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. |
|
|
@ -0,0 +1 @@
|
|||
experimental/Security/CWE/CWE-1240/CustomCryptographicPrimitive.ql
|
|
@ -0,0 +1,6 @@
|
|||
// 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)
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
// 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);
|
||||
}
|
|
@ -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 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: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:21:13:21:18 | call to malloc | test.cpp:21:5:21:24 | ... = ... |
|
||||
| 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: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:50:18:50:25 | call to mk_array [p] | test.cpp:39:27:39:29 | arr [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: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:13:55:18 | call to malloc | test.cpp:55:5:55:24 | ... = ... |
|
||||
| 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: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:69:14:69:19 | call to malloc | test.cpp:69:5:69:25 | ... = ... |
|
||||
| test.cpp:70:5:70:7 | arr indirection [p] | test.cpp:67:10:67:19 | mk_array_p indirection [p] |
|
||||
| test.cpp: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] |
|
||||
| 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] |
|
||||
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 indirection [p] | semmle.label | mk_array indirection [p] |
|
||||
| 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: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 indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:22:5:22:7 | *arr [p] | semmle.label | *arr [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 indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:31:9:31:11 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:31:13:31:13 | p | semmle.label | p |
|
||||
| test.cpp:35:9:35:11 | arr indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:35:9:35:11 | *arr [p] | semmle.label | *arr [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 indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:41:9:41:11 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:41:13:41:13 | p | semmle.label | p |
|
||||
| test.cpp:45:9:45:11 | arr indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:45:9:45:11 | *arr [p] | semmle.label | *arr [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 indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:59:9:59:11 | arr indirection [p] | semmle.label | arr indirection [p] |
|
||||
| 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:59:13:59:13 | p | semmle.label | p |
|
||||
| test.cpp:63:9:63:11 | arr indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:63:9:63:11 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:63:13:63:13 | p | semmle.label | p |
|
||||
| test.cpp:67:10:67:19 | mk_array_p indirection [p] | semmle.label | mk_array_p indirection [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: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 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: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:79:14:79:14 | p | semmle.label | p |
|
||||
| test.cpp:83:9:83:11 | arr indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:83:9:83:11 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:83:14:83:14 | p | semmle.label | 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:87:28:87:30 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:89:9:89:11 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:89:14:89:14 | p | semmle.label | p |
|
||||
| test.cpp:93:9:93:11 | arr indirection [p] | semmle.label | arr indirection [p] |
|
||||
| test.cpp:93:9:93:11 | *arr [p] | semmle.label | *arr [p] |
|
||||
| test.cpp:93:14:93:14 | p | semmle.label | p |
|
||||
| test.cpp:98:18:98:27 | call to mk_array_p indirection [p] | semmle.label | call to mk_array_p indirection [p] |
|
||||
| test.cpp:98:18:98:27 | *call to mk_array_p [p] | semmle.label | *call to mk_array_p [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 |
|
||||
|
|
|
@ -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 indirection | test.cpp:147:4:147:9 | -- ... |
|
||||
| test.cpp:146:26:146:26 | *p | 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 | & ... indirection |
|
||||
| test.cpp:158:17:158:18 | & ... indirection | test.cpp:146:26:146:26 | p indirection |
|
||||
| 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: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 indirection | semmle.label | p indirection |
|
||||
| test.cpp:146:26:146:26 | *p | semmle.label | *p |
|
||||
| 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 | & ... indirection | semmle.label | & ... indirection |
|
||||
| test.cpp:158:17:158:18 | *& ... | semmle.label | *& ... |
|
||||
| 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 |
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
edges
|
||||
| test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | func indirection |
|
||||
| test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | *func |
|
||||
| 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 indirection | semmle.label | func indirection |
|
||||
| test.cpp:45:7:45:10 | *func | semmle.label | *func |
|
||||
| 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 indirection | 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 | 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. |
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
| b.c:5:3:5:34 | return ... | 10 |
|
||||
| c.c:2:3:2:20 | return ... | 5 |
|
||||
| e.c:2:3:2:19 | return ... | 17 |
|
||||
| g.c:3:3:3:12 | return ... | 20 |
|
||||
| i.c:3:3:3:12 | return ... | 30 |
|
||||
| i.c:8:3:8:12 | return ... | 31 |
|
||||
| i.c:13:3:13:12 | return ... | 32 |
|
||||
|
|
|
@ -3,4 +3,4 @@ static int g() {
|
|||
return 20;
|
||||
}
|
||||
#endif
|
||||
// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/f.pch --expect_errors
|
||||
// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/f.pch
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#ifdef SEEN_H
|
||||
static int h() {
|
||||
return 30; // [FALSE POSITIVE] (#pragma hdrstop bug, SEEN_H should not be defined in the precompiled header)
|
||||
return 30;
|
||||
}
|
||||
#endif
|
||||
#ifdef H1
|
||||
|
@ -10,7 +10,7 @@ static int h1() {
|
|||
#endif
|
||||
#ifdef H2
|
||||
static int h2() {
|
||||
return 32; // [FALSE POSITIVE] (#pragma hdrstop bug, H2 should not be defined in the precompiled header)
|
||||
return 32;
|
||||
}
|
||||
#endif
|
||||
// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/h.pch
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
// semmle-extractor-options: -Werror
|
||||
#ifndef __CODEQL_TEST__
|
||||
#error __CODEQL_TEST__ missing
|
||||
#endif
|
||||
|
|
|
@ -10,9 +10,13 @@
|
|||
| arguments.c | 10 | --target |
|
||||
| arguments.c | 11 | --edg |
|
||||
| arguments.c | 12 | linux_x86_64 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
| 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 |
|
||||
|
@ -23,7 +22,6 @@
|
|||
| 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 |
|
||||
|
@ -33,20 +31,17 @@
|
|||
| 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 |
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче