mirror of
https://github.com/github/codeql.git
synced 2026-05-16 12:17:07 +02:00
Compare commits
1 Commits
rc/3.1
...
updateExte
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73e6550fd0 |
21
.github/workflows/check-change-note.yml
vendored
21
.github/workflows/check-change-note.yml
vendored
@@ -1,21 +0,0 @@
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled, unlabeled, opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- "*/ql/src/**/*.ql"
|
||||
- "*/ql/src/**/*.qll"
|
||||
- "!**/experimental/**"
|
||||
|
||||
jobs:
|
||||
check-change-note:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Fail if no change note found. To fix, either add one, or add the `no-change-note-required` label.
|
||||
if: |
|
||||
github.event.pull_request.draft == false &&
|
||||
!contains(github.event.pull_request.labels.*.name, 'no-change-note-required')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate |
|
||||
jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' --exit-status
|
||||
17
.github/workflows/codeql-analysis.yml
vendored
17
.github/workflows/codeql-analysis.yml
vendored
@@ -2,15 +2,7 @@ name: "Code scanning - action"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'rc/*'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'rc/*'
|
||||
paths:
|
||||
- 'csharp/**'
|
||||
schedule:
|
||||
- cron: '0 9 * * 1'
|
||||
|
||||
@@ -22,7 +14,16 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
|
||||
60
.github/workflows/generate-query-help-docs.yml
vendored
Normal file
60
.github/workflows/generate-query-help-docs.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Generate CodeQL query help documentation using Sphinx
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
description:
|
||||
description: A description of the purpose of this job. For human consumption.
|
||||
required: false
|
||||
push:
|
||||
branches:
|
||||
- 'lgtm.com'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/generate-query-help-docs.yml'
|
||||
- 'docs/codeql/query-help/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone github/codeql
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: codeql
|
||||
- name: Clone github/codeql-go
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'github/codeql-go'
|
||||
path: codeql-go
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Download CodeQL CLI
|
||||
uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c
|
||||
with:
|
||||
repo: "github/codeql-cli-binaries"
|
||||
version: "latest"
|
||||
file: "codeql-linux64.zip"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Unzip CodeQL CLI
|
||||
run: unzip -d codeql-cli codeql-linux64.zip
|
||||
- name: Set up query help docs folder
|
||||
run: |
|
||||
cp -r codeql/docs/codeql/** .
|
||||
- name: Query help to markdown
|
||||
run: |
|
||||
PATH="$PATH:codeql-cli/codeql" python codeql/docs/codeql/query-help-markdown.py
|
||||
- name: Run Sphinx for query help
|
||||
uses: ammaraskar/sphinx-action@8b4f60114d7fd1faeba1a712269168508d4750d2 # v0.4
|
||||
with:
|
||||
docs-folder: "query-help/"
|
||||
pre-build-command: "python -m pip install --upgrade recommonmark"
|
||||
build-command: "sphinx-build -b dirhtml . _build"
|
||||
- name: Upload HTML artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: query-help-html
|
||||
path: query-help/_build
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -17,9 +17,6 @@
|
||||
# Byte-compiled python files
|
||||
*.pyc
|
||||
|
||||
# python virtual environment folder
|
||||
.venv/
|
||||
|
||||
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
|
||||
/codeql/
|
||||
|
||||
|
||||
20
CODEOWNERS
20
CODEOWNERS
@@ -4,9 +4,17 @@
|
||||
/javascript/ @github/codeql-javascript
|
||||
/python/ @github/codeql-python
|
||||
|
||||
# Make @xcorail (GitHub Security Lab) a code owner for experimental queries so he gets pinged when we promote a query out of experimental
|
||||
/cpp/**/experimental/**/* @github/codeql-c-analysis @xcorail
|
||||
/csharp/**/experimental/**/* @github/codeql-csharp @xcorail
|
||||
/java/**/experimental/**/* @github/codeql-java @xcorail
|
||||
/javascript/**/experimental/**/* @github/codeql-javascript @xcorail
|
||||
/python/**/experimental/**/* @github/codeql-python @xcorail
|
||||
# Assign query help for docs review
|
||||
/cpp/**/*.qhelp @hubwriter
|
||||
/csharp/**/*.qhelp @jf205
|
||||
/java/**/*.qhelp @felicitymay
|
||||
/javascript/**/*.qhelp @mchammer01
|
||||
/python/**/*.qhelp @felicitymay
|
||||
/docs/language/ @shati-patel @jf205
|
||||
|
||||
# Exclude help for experimental queries from docs review
|
||||
/cpp/**/experimental/**/*.qhelp @github/codeql-c-analysis
|
||||
/csharp/**/experimental/**/*.qhelp @github/codeql-csharp
|
||||
/java/**/experimental/**/*.qhelp @github/codeql-java
|
||||
/javascript/**/experimental/**/*.qhelp @github/codeql-javascript
|
||||
/python/**/experimental/**/*.qhelp @github/codeql-python
|
||||
|
||||
@@ -49,11 +49,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
- The query must have at least one true positive result on some revision of a real project.
|
||||
|
||||
6. **Query help files and unit tests**
|
||||
|
||||
- Query help (`.qhelp`) files and unit tests are optional (but strongly encouraged!) for queries in the `experimental` directories. For more information about contributing query help files and unit tests, see [Supported CodeQL queries and libraries](docs/supported-queries.md).
|
||||
|
||||
Experimental queries and libraries may not be actively maintained as the supported libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings.
|
||||
Experimental queries and libraries may not be actively maintained as the [supported](docs/supported-queries.md) libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings.
|
||||
|
||||
After the experimental query is merged, we welcome pull requests to improve it. Before a query can be moved out of the `experimental` subdirectory, it must satisfy [the requirements for being a supported query](docs/supported-queries.md).
|
||||
|
||||
|
||||
@@ -356,7 +356,6 @@
|
||||
],
|
||||
"Inline Test Expectations": [
|
||||
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"java/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"python/ql/test/TestUtilities/InlineExpectationsTest.qll"
|
||||
],
|
||||
"C++ ExternalAPIs": [
|
||||
@@ -425,10 +424,5 @@
|
||||
"java/ql/src/IDEContextual.qll",
|
||||
"javascript/ql/src/IDEContextual.qll",
|
||||
"python/ql/src/analysis/IDEContextual.qll"
|
||||
],
|
||||
"SSA C#": [
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm
|
||||
* A new query (`cpp/unsigned-difference-expression-compared-zero`) is run but not yet displayed on LGTM. The query finds unsigned subtractions used in relational comparisons with the value 0. This query was originally submitted as an experimental query by @ihsinme in https://github.com/github/codeql/pull/4745.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* A new query (`cpp/memset-may-be-deleted`) is added to the default query suite. The query finds calls to `memset` that may be removed by the compiler. This behavior can make information-leak vulnerabilities easier to exploit. This query was originally [submitted as an experimental query by @ihsinme](https://github.com/github/codeql/pull/4953).
|
||||
@@ -10,7 +10,6 @@
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Likely Bugs/OO/UnsafeUseOfThis.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql: /Correctness/Dangerous Conversions
|
||||
# Consistent Use
|
||||
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
|
||||
+ semmlecode-cpp-queries/Likely Bugs/InconsistentCheckReturnNull.ql: /Correctness/Consistent Use
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
char password[MAX_PASSWORD_LENGTH];
|
||||
// read and verify password
|
||||
memset(password, 0, MAX_PASSWORD_LENGTH);
|
||||
@@ -1,3 +0,0 @@
|
||||
char password[MAX_PASSWORD_LENGTH];
|
||||
// read and verify password
|
||||
memset_s(password, MAX_PASSWORD_LENGTH, 0, MAX_PASSWORD_LENGTH);
|
||||
@@ -1,45 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Calling <code>memset</code> or <code>bzero</code> on a buffer to clear its contents may get optimized
|
||||
away by the compiler if the buffer is not subsequently used. This is not desirable behavior if the buffer
|
||||
contains sensitive data that could somehow be retrieved by an attacker.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Use alternative platform-supplied functions that will not get optimized away. Examples of such
|
||||
functions include <code>memset_s</code>, <code>SecureZeroMemory</code>, and <code>bzero_explicit</code>.
|
||||
Alternatively, passing the <code>-fno-builtin-memset</code> option to the GCC/Clang compiler usually
|
||||
also prevents the optimization. Finally, you can use the public-domain <code>secure_memzero</code> function
|
||||
(see references below). This function, however, is not guaranteed to work on all platforms and compilers.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following program fragment uses <code>memset</code> to erase sensitive information after it is no
|
||||
longer needed:</p>
|
||||
<sample src="MemsetMayBeDeleted-bad.c" />
|
||||
<p>Because of dead store elimination, the call to <code>memset</code> may be removed by the compiler
|
||||
(since the buffer is not subsequently used), resulting in potentially sensitive data remaining in memory.
|
||||
</p>
|
||||
|
||||
<p>The best solution to this problem is to use the <code>memset_s</code> function instead of
|
||||
<code>memset</code>:</p>
|
||||
<sample src="MemsetMayBeDeleted-good.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations">MSC06-C. Beware of compiler optimizations</a>.
|
||||
</li>
|
||||
<li>
|
||||
USENIX: The Advanced Computing Systems Association:
|
||||
<a href="https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-yang.pdf">Dead Store Elimination (Still) Considered Harmfuls</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* @name Call to `memset` may be deleted
|
||||
* @description Using the `memset` function to clear private data in a variable that has no subsequent use
|
||||
* can make information-leak vulnerabilities easier to exploit because the compiler can remove the call.
|
||||
* @kind problem
|
||||
* @id cpp/memset-may-be-deleted
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-14
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.EscapesTree
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
class MemsetFunction extends Function {
|
||||
MemsetFunction() {
|
||||
this.hasGlobalOrStdOrBslName("memset")
|
||||
or
|
||||
this.hasGlobalOrStdName("wmemset")
|
||||
or
|
||||
this.hasGlobalName(["bzero", "__builtin_memset"])
|
||||
}
|
||||
}
|
||||
|
||||
predicate isNonEscapingArgument(Expr escaped) {
|
||||
exists(Call call, AliasFunction aliasFunction, int i |
|
||||
aliasFunction = call.getTarget() and
|
||||
call.getArgument(i) = escaped.getUnconverted() and
|
||||
(
|
||||
aliasFunction.parameterNeverEscapes(i)
|
||||
or
|
||||
aliasFunction.parameterEscapesOnlyViaReturn(i) and
|
||||
(call instanceof ExprInVoidContext or call.getConversion*() instanceof BoolConversion)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate callToMemsetWithRelevantVariable(
|
||||
LocalVariable v, VariableAccess acc, FunctionCall call, MemsetFunction memset
|
||||
) {
|
||||
not v.isStatic() and
|
||||
// Reference-typed variables get special treatment in `variableAddressEscapesTree` so we leave them
|
||||
// out of this query.
|
||||
not v.getUnspecifiedType() instanceof ReferenceType and
|
||||
call.getTarget() = memset and
|
||||
acc = v.getAnAccess() and
|
||||
// `v` escapes as the argument to `memset`
|
||||
variableAddressEscapesTree(acc, call.getArgument(0).getFullyConverted())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate relevantVariable(LocalVariable v, FunctionCall call, MemsetFunction memset) {
|
||||
exists(VariableAccess acc, VariableAccess anotherAcc |
|
||||
callToMemsetWithRelevantVariable(v, acc, call, memset) and
|
||||
// `v` is not only just used in the call to `memset`.
|
||||
anotherAcc = v.getAnAccess() and
|
||||
acc != anotherAcc and
|
||||
not anotherAcc.isUnevaluated()
|
||||
)
|
||||
}
|
||||
|
||||
from FunctionCall call, LocalVariable v, MemsetFunction memset
|
||||
where
|
||||
relevantVariable(v, call, memset) and
|
||||
not isFromMacroDefinition(call) and
|
||||
// `v` doesn't escape anywhere else.
|
||||
forall(Expr escape | variableAddressEscapesTree(v.getAnAccess(), escape) |
|
||||
isNonEscapingArgument(escape)
|
||||
) and
|
||||
// There is no later use of `v`.
|
||||
not v.getAnAccess() = call.getASuccessor*() and
|
||||
// Not using the `-fno-builtin-memset` flag
|
||||
exists(Compilation c |
|
||||
c.getAFileCompiled() = call.getFile() and
|
||||
not c.getAnArgument() = "-fno-builtin-memset"
|
||||
)
|
||||
select call, "Call to " + memset.getName() + " may be deleted by the compiler."
|
||||
@@ -46,7 +46,7 @@ class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(RemoteFlowSourceFunction remoteFlow |
|
||||
exists(RemoteFlowFunction remoteFlow |
|
||||
remoteFlow = source.asExpr().(Call).getTarget() and
|
||||
remoteFlow.hasRemoteFlowSource(_, _)
|
||||
)
|
||||
|
||||
@@ -34,10 +34,6 @@ class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) {
|
||||
exists(PrintStdoutCall call | call.getAnArgument() = tainted)
|
||||
}
|
||||
|
||||
override predicate isBarrier(Expr e) {
|
||||
super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
from QueryString query, Element printedArg, PathNode sourceNode, PathNode sinkNode
|
||||
|
||||
@@ -27,10 +27,6 @@ class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) {
|
||||
exists(SQLLikeFunction runSql | runSql.outermostWrapperFunctionCall(tainted, _))
|
||||
}
|
||||
|
||||
override predicate isBarrier(Expr e) {
|
||||
super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
unsigned limit = get_limit();
|
||||
unsigned total = 0;
|
||||
while (limit - total > 0) { // wrong: if `total` is greater than `limit` this will underflow and continue executing the loop.
|
||||
total += get_data();
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds relational comparisons between the result of an unsigned subtraction and the value <code>0</code>.
|
||||
Such comparisons are likely to be wrong as the value of an unsigned subtraction can never be negative. So the
|
||||
relational comparison ends up checking whether the result of the subtraction is equal to <code>0</code>.
|
||||
This is probably not what the programmer intended.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>If a relational comparison is intended, consider casting the result of the subtraction to a signed type.
|
||||
If the intention was to test for equality, consider replacing the relational comparison with an equality test.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="UnsignedDifferenceExpressionComparedZero.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>SEI CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules">INT02-C. Understand integer conversion rules</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* @name Unsigned difference expression compared to zero
|
||||
* @description A subtraction with an unsigned result can never be negative. Using such an expression in a relational comparison with `0` is likely to be wrong.
|
||||
* @kind problem
|
||||
* @id cpp/unsigned-difference-expression-compared-zero
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* correctness
|
||||
* external/cwe/cwe-191
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/** Holds if `sub` is guarded by a condition which ensures that `left >= right`. */
|
||||
pragma[noinline]
|
||||
predicate isGuarded(SubExpr sub, Expr left, Expr right) {
|
||||
exists(GuardCondition guard |
|
||||
guard.controls(sub.getBasicBlock(), true) and
|
||||
guard.ensuresLt(left, right, 0, sub.getBasicBlock(), false)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `sub` will never be negative. */
|
||||
predicate nonNegative(SubExpr sub) {
|
||||
not exprMightOverflowNegatively(sub.getFullyConverted())
|
||||
or
|
||||
// The subtraction is guarded by a check of the form `left >= right`.
|
||||
exists(GVN left, GVN right |
|
||||
// This is basically a poor man's version of a directional unbind operator.
|
||||
strictcount([left, globalValueNumber(sub.getLeftOperand())]) = 1 and
|
||||
strictcount([right, globalValueNumber(sub.getRightOperand())]) = 1 and
|
||||
isGuarded(sub, left.getAnExpr(), right.getAnExpr())
|
||||
)
|
||||
}
|
||||
|
||||
from RelationalOperation ro, SubExpr sub
|
||||
where
|
||||
not isFromMacroDefinition(ro) and
|
||||
not isFromMacroDefinition(sub) and
|
||||
ro.getLesserOperand().getValue().toInt() = 0 and
|
||||
ro.getGreaterOperand() = sub and
|
||||
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and
|
||||
not nonNegative(sub)
|
||||
select ro, "Unsigned subtraction can never be negative."
|
||||
@@ -353,9 +353,7 @@ class InitializationFunction extends Function {
|
||||
// Destination range is zeroed out on failure, assuming first two parameters are valid
|
||||
"memcpy_s",
|
||||
// This zeroes the memory unconditionally
|
||||
"SeCreateAccessState",
|
||||
// Argument initialization is optional, but always succeeds
|
||||
"KeGetCurrentProcessorNumberEx"
|
||||
"SeCreateAccessState"
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-242
|
||||
* external/cwe/cwe-676
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
unsigned long sizeArray;
|
||||
|
||||
// BAD: let's consider several values, taking ULONG_MAX =18446744073709551615
|
||||
// sizeArray = 60; (sizeArray - 10) = 50; true
|
||||
// sizeArray = 10; (sizeArray - 10) = 0; false
|
||||
// sizeArray = 1; (sizeArray - 10) = 18446744073709551607; true
|
||||
// sizeArray = 0; (sizeArray - 10) = 18446744073709551606; true
|
||||
if (sizeArray - 10 > 0)
|
||||
|
||||
// GOOD: Prevent overflow by checking the input
|
||||
if (sizeArray > 10)
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The code compares the unsigned difference with zero.
|
||||
It is highly probable that the condition is wrong if the difference expression has the unsigned type.
|
||||
The condition holds in all the cases when difference is not equal to zero.
|
||||
It means that we may use condition not equal. But the programmer probably wanted to compare the difference of elements.</p>
|
||||
|
||||
<p>False positives include code in which the first difference element is always greater than or equal to the second.
|
||||
For comparison, ">" such conditions are equivalent to "! =", And are recommended for replacement.
|
||||
For comparison "> =", the conditions are always true and are recommended to be excluded.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Use a simple comparison of two elements, instead of comparing their difference to zero.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of comparison.</p>
|
||||
<sample src="UnsignedDifferenceExpressionComparedZero.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules">INT02-C. Understand integer conversion rules</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @name Unsigned difference expression compared to zero
|
||||
* @description It is highly probable that the condition is wrong if the difference expression has the unsigned type.
|
||||
* The condition holds in all the cases when difference is not equal to zero. It means that we may use condition not equal.
|
||||
* But the programmer probably wanted to compare the difference of elements.
|
||||
* @kind problem
|
||||
* @id cpp/unsigned-difference-expression-compared-zero
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-191
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
from RelationalOperation ro, SubExpr sub
|
||||
where
|
||||
not isFromMacroDefinition(ro) and
|
||||
ro.getLesserOperand().getValue().toInt() = 0 and
|
||||
ro.getGreaterOperand() = sub and
|
||||
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned()
|
||||
select ro, "Difference in condition is always greater than or equal to zero"
|
||||
@@ -16,6 +16,6 @@ import DataFlow::PathGraph
|
||||
|
||||
from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where b.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"This write into the external location '" + sink.getNode() +
|
||||
"' may contain unencrypted data from $@", source, "this source."
|
||||
select sink.getNode(),
|
||||
"This write into the external location '" + sink + "' may contain unencrypted data from $@",
|
||||
source, "this source."
|
||||
|
||||
@@ -12,21 +12,6 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
|
||||
/**
|
||||
* A function call that potentially does not return (such as `exit`).
|
||||
*/
|
||||
class CallMayNotReturn extends FunctionCall {
|
||||
CallMayNotReturn() {
|
||||
// call that is known to not return
|
||||
not exists(this.(ControlFlowNode).getASuccessor())
|
||||
or
|
||||
// call to another function that may not return
|
||||
exists(CallMayNotReturn exit | getTarget() = exit.getEnclosingFunction())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `realloc` of the form `v = realloc(v, size)`, for some variable `v`.
|
||||
@@ -35,27 +20,50 @@ class ReallocCallLeak extends FunctionCall {
|
||||
Variable v;
|
||||
|
||||
ReallocCallLeak() {
|
||||
exists(AssignExpr ex |
|
||||
this.getTarget().hasGlobalOrStdName("realloc") and
|
||||
exists(AssignExpr ex, VariableAccess va1, VariableAccess va2 |
|
||||
this.getTarget().hasName("realloc") and
|
||||
this = ex.getRValue() and
|
||||
hashCons(ex.getLValue()) = hashCons(this.getArgument(0)) and
|
||||
v.getAnAccess() = this.getArgument(0)
|
||||
va1 = ex.getLValue() and
|
||||
va2 = this.getArgument(0) and
|
||||
va1 = v.getAnAccess() and
|
||||
va2 = v.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if failure of this allocation may be handled by termination, for
|
||||
* example a call to `exit()`.
|
||||
*/
|
||||
predicate mayHandleByTermination() {
|
||||
exists(GuardCondition guard, CallMayNotReturn exit |
|
||||
this.(ControlFlowNode).getASuccessor*() = guard and
|
||||
guard.getAChild*() = v.getAnAccess() and
|
||||
guard.controls(exit.getBasicBlock(), _)
|
||||
predicate isExistsIfWithExitCall() {
|
||||
exists(IfStmt ifc |
|
||||
this.getArgument(0) = v.getAnAccess() and
|
||||
ifc.getCondition().getAChild*() = v.getAnAccess() and
|
||||
ifc.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
ifc.getLocation().getStartLine() >= this.getArgument(0).getLocation().getStartLine() and
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("exit") and
|
||||
fc.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
(ifc.getThen().getAChild*() = fc or ifc.getElse().getAChild*() = fc)
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc, FunctionCall ftmp1, FunctionCall ftmp2 |
|
||||
ftmp1.getTarget().hasName("exit") and
|
||||
ftmp2.(ControlFlowNode).getASuccessor*() = ftmp1 and
|
||||
fc = ftmp2.getEnclosingFunction().getACallToThisFunction() and
|
||||
fc.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
(ifc.getThen().getAChild*() = fc or ifc.getElse().getAChild*() = fc)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsAssertWithArgumentCall() {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("__assert_fail") and
|
||||
this.getEnclosingFunction() = fc.getEnclosingFunction() and
|
||||
fc.getLocation().getStartLine() > this.getArgument(0).getLocation().getEndLine() and
|
||||
fc.getArgument(0).toString().matches("%" + this.getArgument(0).toString() + "%")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from ReallocCallLeak rcl
|
||||
where not rcl.mayHandleByTermination()
|
||||
where
|
||||
not rcl.isExistsIfWithExitCall() and
|
||||
not rcl.isExistsAssertWithArgumentCall()
|
||||
select rcl, "possible loss of original pointer on unsuccessful call realloc"
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// BAD: on memory allocation error, the program terminates.
|
||||
void badFunction(const int *source, std::size_t length) noexcept {
|
||||
int * dest = new int[length];
|
||||
std::memset(dest, 0, length);
|
||||
// ..
|
||||
}
|
||||
// GOOD: memory allocation error will be handled.
|
||||
void goodFunction(const int *source, std::size_t length) noexcept {
|
||||
try {
|
||||
int * dest = new int[length];
|
||||
} catch(std::bad_alloc) {
|
||||
// ...
|
||||
}
|
||||
std::memset(dest, 0, length);
|
||||
// ..
|
||||
}
|
||||
// BAD: memory allocation error will not be handled.
|
||||
void badFunction(const int *source, std::size_t length) noexcept {
|
||||
try {
|
||||
int * dest = new (std::nothrow) int[length];
|
||||
} catch(std::bad_alloc) {
|
||||
// ...
|
||||
}
|
||||
std::memset(dest, 0, length);
|
||||
// ..
|
||||
}
|
||||
// GOOD: memory allocation error will be handled.
|
||||
void goodFunction(const int *source, std::size_t length) noexcept {
|
||||
int * dest = new (std::nothrow) int[length];
|
||||
if (!dest) {
|
||||
return;
|
||||
}
|
||||
std::memset(dest, 0, length);
|
||||
// ..
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>When using the <code>new</code> operator to allocate memory, you need to pay attention to the different ways of detecting errors. <code>::operator new(std::size_t)</code> throws an exception on error, whereas <code>::operator new(std::size_t, const std::nothrow_t &)</code> returns zero on error. The programmer can get confused and check the error that occurs when allocating memory incorrectly. That can lead to an unhandled program termination or to a violation of the program logic.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Use the correct error detection method corresponding with the memory allocation.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates various approaches to detecting memory allocation errors using the <code>new</code> operator.</p>
|
||||
<sample src="WrongInDetectingAndHandlingMemoryAllocationErrors.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C++ Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM52-CPP.+Detect+and+handle+memory+allocation+errors">MEM52-CPP. Detect and handle memory allocation errors</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* @name Detect And Handle Memory Allocation Errors
|
||||
* @description --::operator new(std::size_t) throws an exception on error, and ::operator new(std::size_t, const std::nothrow_t &) returns zero on error.
|
||||
* --the programmer can get confused when check the error that occurs when allocating memory incorrectly.
|
||||
* @kind problem
|
||||
* @id cpp/detect-and-handle-memory-allocation-errors
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-570
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Lookup if condition compare with 0
|
||||
*/
|
||||
class IfCompareWithZero extends IfStmt {
|
||||
IfCompareWithZero() {
|
||||
this.getCondition().(EQExpr).getAChild().getValue() = "0"
|
||||
or
|
||||
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
|
||||
this.hasElse()
|
||||
or
|
||||
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
|
||||
this.getThen().getAChild*() instanceof ReturnStmt
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lookup for calls to `operator new`, with incorrect error handling.
|
||||
*/
|
||||
class WrongCheckErrorOperatorNew extends FunctionCall {
|
||||
Expr exp;
|
||||
|
||||
WrongCheckErrorOperatorNew() {
|
||||
this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and
|
||||
(
|
||||
this.getTarget().hasGlobalOrStdName("operator new")
|
||||
or
|
||||
this.getTarget().hasGlobalOrStdName("operator new[]")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if handler `try ... catch` exists.
|
||||
*/
|
||||
predicate isExistsTryCatchBlock() {
|
||||
exists(TryStmt ts | this.getEnclosingStmt() = ts.getStmt().getAChild*())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if results call `operator new` check in `operator if`.
|
||||
*/
|
||||
predicate isExistsIfCondition() {
|
||||
exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it |
|
||||
// call `operator new` directly from the condition of `operator if`.
|
||||
this = ifc.getCondition().getAChild*()
|
||||
or
|
||||
// check results call `operator new` with variable appropriation
|
||||
postDominates(ifc, this) and
|
||||
aex.getAChild() = exp and
|
||||
ifc.getCondition().getAChild().(VariableAccess).getTarget() =
|
||||
aex.getLValue().(VariableAccess).getTarget()
|
||||
or
|
||||
// check results call `operator new` with declaration variable
|
||||
postDominates(ifc, this) and
|
||||
exp = it.getExpr() and
|
||||
it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(std::nothrow)` exists in call `operator new`.
|
||||
*/
|
||||
predicate isExistsNothrow() { this.getAChild().toString() = "nothrow" }
|
||||
}
|
||||
|
||||
from WrongCheckErrorOperatorNew op
|
||||
where
|
||||
// use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if`
|
||||
op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock()
|
||||
or
|
||||
// use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block
|
||||
not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition()
|
||||
select op, "memory allocation error check is incorrect or missing"
|
||||
@@ -1,9 +0,0 @@
|
||||
// BAD: if buffer does not have a terminal zero, then access outside the allocated memory is possible.
|
||||
|
||||
buffer[strlen(buffer)] = 0;
|
||||
|
||||
|
||||
// GOOD: we will eliminate dangerous behavior if we use a different method of calculating the length.
|
||||
size_t len;
|
||||
...
|
||||
buffer[len] = 0
|
||||
@@ -1,31 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Potentially dangerous use of the strlen function to calculate the length of a string.
|
||||
The expression <code>buffer[strlen(buffer)] = 0</code> is potentially dangerous, if the variable buffer does not have a terminal zero, then access beyond the bounds of the allocated memory is possible, which will lead to undefined behavior.
|
||||
If terminal zero is present, then the specified expression is meaningless.</p>
|
||||
|
||||
<p>False positives include heavily nested strlen. This situation is unlikely.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend using another method for calculating the string length</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of the strlen function.</p>
|
||||
<sample src="AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/STR32-C.+Do+not+pass+a+non-null-terminated+character+sequence+to+a+library+function+that+expects+a+string">STR32-C. Do not pass a non-null-terminated character sequence to a library function that expects a string</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @name Access Of Memory Location After End Of Buffer
|
||||
* @description The expression `buffer [strlen (buffer)] = 0` is potentially dangerous, if the variable `buffer` does not have a terminal zero, then access beyond the bounds of the allocated memory is possible, which will lead to undefined behavior.
|
||||
* If terminal zero is present, then the specified expression is meaningless.
|
||||
* @kind problem
|
||||
* @id cpp/access-memory-location-after-end-buffer
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-788
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
from StrlenCall fc, AssignExpr expr, ArrayExpr exprarr
|
||||
where
|
||||
exprarr = expr.getLValue() and
|
||||
expr.getRValue().getValue().toInt() = 0 and
|
||||
globalValueNumber(exprarr.getArrayOffset()) = globalValueNumber(fc) and
|
||||
not exists(Expr exptmp |
|
||||
(
|
||||
DataFlow::localExprFlow(fc, exptmp) or
|
||||
exptmp.getAChild*() = fc.getArgument(0).(VariableAccess).getTarget().getAnAccess()
|
||||
) and
|
||||
dominates(exptmp, expr) and
|
||||
postDominates(exptmp, fc) and
|
||||
not exptmp.getEnclosingStmt() = fc.getEnclosingStmt() and
|
||||
not exptmp.getEnclosingStmt() = expr.getEnclosingStmt()
|
||||
) and
|
||||
globalValueNumber(fc.getArgument(0)) = globalValueNumber(exprarr.getArrayBase())
|
||||
select expr, "potential unsafe or redundant assignment."
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
strncat(dest, source, sizeof(dest) - strlen(dest)); // BAD: writes a zero byte past the `dest` buffer.
|
||||
|
||||
strncat(dest, source, sizeof(dest) - strlen(dest) -1); // GOOD: Reserves space for the zero byte.
|
||||
@@ -1,32 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The standard library function <code>strncat(dest, source, count)</code> appends the <code>source</code> string to the <code>dest</code> string. <code>count</code> specifies the maximum number of characters to append and must be less than the remaining space in the target buffer. Calls of the form <code> strncat (dest, source, sizeof (dest) - strlen (dest)) </code> set the third argument to one more than possible. So when the <code>dest</code> is full, the expression <code> sizeof (dest) - strlen (dest) </code> will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the <code>dest</code> buffer.</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend subtracting one from the third argument. For example, replace <code>strncat(dest, source, sizeof(dest)-strlen(dest))</code> with <code>strncat(dest, source, sizeof(dest)-strlen(dest)-1)</code>.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of the <code>strncat</code> function.</p>
|
||||
<sample src="AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator</a>.
|
||||
</li>
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* @name Access Of Memory Location After The End Of A Buffer Using Strncat
|
||||
* @description Calls of the form `strncat(dest, source, sizeof (dest) - strlen (dest))` set the third argument to one more than possible. So when `dest` is full, the expression `sizeof(dest) - strlen (dest)` will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the `dest` buffer.
|
||||
* @kind problem
|
||||
* @id cpp/access-memory-location-after-end-buffer
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-788
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`.
|
||||
*/
|
||||
class WrongCallStrncat extends FunctionCall {
|
||||
Expr leftsomeExpr;
|
||||
|
||||
WrongCallStrncat() {
|
||||
this.getTarget().hasGlobalOrStdName("strncat") and
|
||||
// the expression of the first argument in `strncat` and `strnlen` is identical
|
||||
globalValueNumber(this.getArgument(0)) =
|
||||
globalValueNumber(this.getArgument(2).(SubExpr).getRightOperand().(StrlenCall).getStringExpr()) and
|
||||
// using a string constant often speaks of manually calculating the length of the required buffer.
|
||||
(
|
||||
not this.getArgument(1) instanceof StringLiteral and
|
||||
not this.getArgument(1) instanceof CharLiteral
|
||||
) and
|
||||
// for use in predicates
|
||||
leftsomeExpr = this.getArgument(2).(SubExpr).getLeftOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the left side of the expression `someExpr` equal to `sizeof(buf)`.
|
||||
*/
|
||||
predicate isExpressionEqualSizeof() {
|
||||
// the left side of the expression `someExpr` is `sizeof(buf)`.
|
||||
globalValueNumber(this.getArgument(0)) =
|
||||
globalValueNumber(leftsomeExpr.(SizeofExprOperator).getExprOperand())
|
||||
or
|
||||
// value of the left side of the expression `someExpr` equal `sizeof(buf)` value, and `buf` is array.
|
||||
leftsomeExpr.getValue().toInt() = this.getArgument(0).getType().getSize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the left side of the expression `someExpr` equal to variable containing the length of the memory allocated for the buffer.
|
||||
*/
|
||||
predicate isVariableEqualValueSizegBuffer() {
|
||||
// the left side of expression `someExpr` is the variable that was used in the function of allocating memory for the buffer`.
|
||||
exists(AllocationExpr alc |
|
||||
leftsomeExpr.(VariableAccess).getTarget() =
|
||||
alc.(FunctionCall).getArgument(0).(VariableAccess).getTarget()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from WrongCallStrncat sc
|
||||
where
|
||||
sc.isExpressionEqualSizeof() or
|
||||
sc.isVariableEqualValueSizegBuffer()
|
||||
select sc, "if the used buffer is full, writing out of the buffer is possible"
|
||||
@@ -50,5 +50,5 @@ class CStyleComment extends Comment {
|
||||
* ```
|
||||
*/
|
||||
class CppStyleComment extends Comment {
|
||||
CppStyleComment() { this.getContents().matches("//%") }
|
||||
CppStyleComment() { this.getContents().prefix(2) = "//" }
|
||||
}
|
||||
|
||||
@@ -139,19 +139,6 @@ class Declaration extends Locatable, @declaration {
|
||||
this.hasQualifiedName("std", "", name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this declaration has the given name in the global namespace,
|
||||
* the `std` namespace or the `bsl` namespace.
|
||||
* We treat `std` and `bsl` as the same in some of our models.
|
||||
*/
|
||||
predicate hasGlobalOrStdOrBslName(string name) {
|
||||
this.hasGlobalName(name)
|
||||
or
|
||||
this.hasQualifiedName("std", "", name)
|
||||
or
|
||||
this.hasQualifiedName("bsl", "", name)
|
||||
}
|
||||
|
||||
/** Gets a specifier of this declaration. */
|
||||
Specifier getASpecifier() { none() } // overridden in subclasses
|
||||
|
||||
|
||||
@@ -270,12 +270,7 @@ private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 `static_assert` or C11 `_Static_assert` construct. For example each
|
||||
* line in the following example contains a static assert:
|
||||
* ```
|
||||
* static_assert(sizeof(MyStruct) <= 4096);
|
||||
* static_assert(sizeof(MyStruct) <= 4096, "MyStruct is too big!");
|
||||
* ```
|
||||
* A C++11 `static_assert` or C11 `_Static_assert` construct.
|
||||
*/
|
||||
class StaticAssert extends Locatable, @static_assert {
|
||||
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }
|
||||
|
||||
@@ -334,18 +334,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class of which this function, called `memberName`, is a member.
|
||||
*
|
||||
* Prefer to use `getDeclaringType()` or `getName()` directly if you do not
|
||||
* need to reason about both.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Class getClassAndName(string memberName) {
|
||||
this.hasName(memberName) and
|
||||
this.getDeclaringType() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements `ControlFlowNode.getControlFlowScope`. The `Function` is
|
||||
* used to represent the exit node of the control flow graph, so it is
|
||||
@@ -467,7 +455,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
// ... and likewise for destructors.
|
||||
this.(Destructor).getADestruction().mayBeGloballyImpure()
|
||||
else
|
||||
// Unless it's a function that we know is side-effect free, it may
|
||||
// Unless it's a function that we know is side-effect-free, it may
|
||||
// have side-effects.
|
||||
not this.hasGlobalOrStdName([
|
||||
"strcmp", "wcscmp", "_mbscmp", "strlen", "wcslen", "_mbslen", "_mbslen_l", "_mbstrlen",
|
||||
@@ -680,7 +668,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
|
||||
/**
|
||||
* A C/C++ non-member function (a function that is not a member of any
|
||||
* class). For example, in the following code, `MyFunction` is a
|
||||
* class). For example the in the following code, `MyFunction` is a
|
||||
* `TopLevelFunction` but `MyMemberFunction` is not:
|
||||
* ```
|
||||
* void MyFunction() {
|
||||
|
||||
@@ -7,21 +7,8 @@ import semmle.code.cpp.Type
|
||||
import semmle.code.cpp.metrics.MetricNamespace
|
||||
|
||||
/**
|
||||
* A C++ namespace. For example the (single) namespace `A` in the following
|
||||
* code:
|
||||
* ```
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
* A C++ namespace.
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* Note that namespaces are somewhat nebulous entities, as they do not in
|
||||
* general have a single well-defined location in the source code. The
|
||||
* related notion of a `NamespaceDeclarationEntry` is rather more concrete,
|
||||
@@ -109,22 +96,10 @@ class Namespace extends NameQualifyingElement, @namespace {
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration of (part of) a C++ namespace. This corresponds to a single
|
||||
* `namespace N { ... }` occurrence in the source code. For example the two
|
||||
* mentions of `A` in the following code:
|
||||
* ```
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
* A declaration of (part of) a C++ namespace.
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* This corresponds to a single `namespace N { ... }` occurrence in the
|
||||
* source code.
|
||||
*/
|
||||
class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
|
||||
/**
|
||||
@@ -168,9 +143,8 @@ class UsingEntry extends Locatable, @using {
|
||||
|
||||
/**
|
||||
* A C++ `using` declaration. For example:
|
||||
* ```
|
||||
* using std::string;
|
||||
* ```
|
||||
*
|
||||
* `using std::string;`
|
||||
*/
|
||||
class UsingDeclarationEntry extends UsingEntry {
|
||||
UsingDeclarationEntry() {
|
||||
@@ -188,9 +162,8 @@ class UsingDeclarationEntry extends UsingEntry {
|
||||
|
||||
/**
|
||||
* A C++ `using` directive. For example:
|
||||
* ```
|
||||
* using namespace std;
|
||||
* ```
|
||||
*
|
||||
* `using namespace std;`
|
||||
*/
|
||||
class UsingDirectiveEntry extends UsingEntry {
|
||||
UsingDirectiveEntry() {
|
||||
|
||||
@@ -2,14 +2,9 @@ import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Element
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor directive. For example each of the following lines of
|
||||
* code contains a `PreprocessorDirective`:
|
||||
* ```
|
||||
* #pragma once
|
||||
* #ifdef MYDEFINE
|
||||
* #include "myfile.h"
|
||||
* #line 1 "source.c"
|
||||
* ```
|
||||
* A C/C++ preprocessor directive.
|
||||
*
|
||||
* For example: `#ifdef`, `#line`, or `#pragma`.
|
||||
*/
|
||||
class PreprocessorDirective extends Locatable, @preprocdirect {
|
||||
override string toString() { result = "Preprocessor directive" }
|
||||
@@ -103,9 +98,9 @@ class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBr
|
||||
* A C/C++ preprocessor branching directive: `#if`, `#ifdef`, `#ifndef`, or
|
||||
* `#elif`.
|
||||
*
|
||||
* A branching directive has a condition and that condition may be evaluated
|
||||
* at compile-time. As a result, the preprocessor will either take the
|
||||
* branch, or not take the branch.
|
||||
* A branching directive can have its condition evaluated at compile-time,
|
||||
* and as a result, the preprocessor will either take the branch, or not
|
||||
* take the branch.
|
||||
*
|
||||
* However, there are also situations in which a branch's condition isn't
|
||||
* evaluated. The obvious case of this is when the directive is contained
|
||||
@@ -141,13 +136,8 @@ class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#if` directive. For example there is a
|
||||
* `PreprocessorIf` on the first line of the following code:
|
||||
* ```
|
||||
* #if defined(MYDEFINE)
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* A C/C++ preprocessor `#if` directive.
|
||||
*
|
||||
* For the related notion of a directive which causes branching (which
|
||||
* includes `#if`, plus also `#ifdef`, `#ifndef`, and `#elif`), see
|
||||
* `PreprocessorBranch`.
|
||||
@@ -157,13 +147,8 @@ class PreprocessorIf extends PreprocessorBranch, @ppd_if {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#ifdef` directive. For example there is a
|
||||
* `PreprocessorIfdef` on the first line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* A C/C++ preprocessor `#ifdef` directive.
|
||||
*
|
||||
* The syntax `#ifdef X` is shorthand for `#if defined(X)`.
|
||||
*/
|
||||
class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
|
||||
@@ -173,13 +158,8 @@ class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#ifndef` directive. For example there is a
|
||||
* `PreprocessorIfndef` on the first line of the following code:
|
||||
* ```
|
||||
* #ifndef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* A C/C++ preprocessor `#ifndef` directive.
|
||||
*
|
||||
* The syntax `#ifndef X` is shorthand for `#if !defined(X)`.
|
||||
*/
|
||||
class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
|
||||
@@ -187,80 +167,42 @@ class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#else` directive. For example there is a
|
||||
* `PreprocessorElse` on the fifth line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE1
|
||||
* // ...
|
||||
* #elif MYDEFINE2
|
||||
* // ...
|
||||
* #else
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* A C/C++ preprocessor `#else` directive.
|
||||
*/
|
||||
class PreprocessorElse extends PreprocessorBranchDirective, @ppd_else {
|
||||
override string toString() { result = "#else" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#elif` directive. For example there is a
|
||||
* `PreprocessorElif` on the third line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE1
|
||||
* // ...
|
||||
* #elif MYDEFINE2
|
||||
* // ...
|
||||
* #else
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* A C/C++ preprocessor `#elif` directive.
|
||||
*/
|
||||
class PreprocessorElif extends PreprocessorBranch, @ppd_elif {
|
||||
override string toString() { result = "#elif " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#endif` directive. For example there is a
|
||||
* `PreprocessorEndif` on the third line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* A C/C++ preprocessor `#endif` directive.
|
||||
*/
|
||||
class PreprocessorEndif extends PreprocessorBranchDirective, @ppd_endif {
|
||||
override string toString() { result = "#endif" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#warning` directive. For example:
|
||||
* ```
|
||||
* #warning "This configuration is not supported."
|
||||
* ```
|
||||
* A C/C++ preprocessor `#warning` directive.
|
||||
*/
|
||||
class PreprocessorWarning extends PreprocessorDirective, @ppd_warning {
|
||||
override string toString() { result = "#warning " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#error` directive. For example:
|
||||
* ```
|
||||
* #error "This configuration is not implemented."
|
||||
* ```
|
||||
* A C/C++ preprocessor `#error` directive.
|
||||
*/
|
||||
class PreprocessorError extends PreprocessorDirective, @ppd_error {
|
||||
override string toString() { result = "#error " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#undef` directive. For example there is a
|
||||
* `PreprocessorUndef` on the second line of the following code:
|
||||
* ```
|
||||
* #ifdef MYMACRO
|
||||
* #undef MYMACRO
|
||||
* #endif
|
||||
* ```
|
||||
* A C/C++ preprocessor `#undef` directive.
|
||||
*/
|
||||
class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
|
||||
override string toString() { result = "#undef " + this.getHead() }
|
||||
@@ -272,10 +214,7 @@ class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#pragma` directive. For example:
|
||||
* ```
|
||||
* #pragma once
|
||||
* ```
|
||||
* A C/C++ preprocessor `#pragma` directive.
|
||||
*/
|
||||
class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
|
||||
override string toString() {
|
||||
@@ -284,10 +223,7 @@ class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#line` directive. For example:
|
||||
* ```
|
||||
* #line 1 "source.c"
|
||||
* ```
|
||||
* A C/C++ preprocessor `#line` directive.
|
||||
*/
|
||||
class PreprocessorLine extends PreprocessorDirective, @ppd_line {
|
||||
override string toString() { result = "#line " + this.getHead() }
|
||||
|
||||
@@ -171,11 +171,8 @@ class StdAttribute extends Attribute, @stdattribute {
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute introduced by Microsoft's `__declspec(name)` syntax. For
|
||||
* example the attribute on the following declaration:
|
||||
* ```
|
||||
* __declspec(dllimport) void myFunction();
|
||||
* ```
|
||||
* An attribute introduced by Microsoft's `__declspec(name)` syntax, for
|
||||
* example: `__declspec(dllimport)`.
|
||||
*/
|
||||
class Declspec extends Attribute, @declspec { }
|
||||
|
||||
@@ -189,13 +186,8 @@ class MicrosoftAttribute extends Attribute, @msattribute {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 `alignas` construct. For example the attribute in the following
|
||||
* code:
|
||||
* ```
|
||||
* struct alignas(16) MyStruct {
|
||||
* int x;
|
||||
* };
|
||||
* ```
|
||||
* A C++11 `alignas` construct.
|
||||
*
|
||||
* Though it doesn't use the attribute syntax, `alignas(...)` is presented
|
||||
* as an `Attribute` for consistency with the `[[align(...)]]` attribute.
|
||||
*/
|
||||
@@ -205,11 +197,7 @@ class AlignAs extends Attribute, @alignas {
|
||||
|
||||
/**
|
||||
* A GNU `format` attribute of the form `__attribute__((format(archetype, format-index, first-arg)))`
|
||||
* that declares a function to accept a `printf` style format string. For example the attribute
|
||||
* on the following declaration:
|
||||
* ```
|
||||
* int myPrintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
* ```
|
||||
* that declares a function to accept a `printf` style format string.
|
||||
*/
|
||||
class FormatAttribute extends GnuAttribute {
|
||||
FormatAttribute() { getName() = "format" }
|
||||
@@ -254,11 +242,7 @@ class FormatAttribute extends GnuAttribute {
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to an `Attribute`. For example the argument "dllimport" on the
|
||||
* attribute in the following code:
|
||||
* ```
|
||||
* __declspec(dllimport) void myFunction();
|
||||
* ```
|
||||
* An argument to an `Attribute`.
|
||||
*/
|
||||
class AttributeArgument extends Element, @attribute_arg {
|
||||
/**
|
||||
|
||||
@@ -274,7 +274,7 @@ class Type extends Locatable, @type {
|
||||
|
||||
/**
|
||||
* Gets this type with any typedefs resolved. For example, given
|
||||
* `typedef C T`, this would resolve `const T&` to `const C&`.
|
||||
* `typedef C T`, this would resolve `const T&` to `const C&`.
|
||||
* Note that this will only work if the resolved type actually appears
|
||||
* on its own elsewhere in the program.
|
||||
*/
|
||||
@@ -1544,9 +1544,9 @@ class FunctionPointerIshType extends DerivedType {
|
||||
/**
|
||||
* A C++ pointer to data member. See 15.5.
|
||||
* ```
|
||||
* class C { public: int m; };
|
||||
* class C { int m; };
|
||||
* int C::* p = &C::m; // pointer to data member m of class C
|
||||
* class C c;
|
||||
* class C *;
|
||||
* int val = c.*p; // access data member
|
||||
* ```
|
||||
*/
|
||||
|
||||
@@ -50,15 +50,7 @@ predicate primitiveVariadicFormatter(
|
||||
then formatParamIndex = f.getNumberOfParameters() - 3
|
||||
else formatParamIndex = f.getNumberOfParameters() - 2
|
||||
) and
|
||||
(
|
||||
if type = "" then outputParamIndex = -1 else outputParamIndex = 0 // Conveniently, these buffer parameters are all at index 0.
|
||||
) and
|
||||
not (
|
||||
// exclude functions with an implementation in the snapshot source
|
||||
// directory, as they may not be standard implementations.
|
||||
exists(f.getBlock()) and
|
||||
exists(f.getFile().getRelativePath())
|
||||
)
|
||||
if type = "" then outputParamIndex = -1 else outputParamIndex = 0 // Conveniently, these buffer parameters are all at index 0.
|
||||
}
|
||||
|
||||
private predicate callsVariadicFormatter(
|
||||
|
||||
@@ -34,10 +34,10 @@ class Scanf extends ScanfFunction {
|
||||
Scanf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
|
||||
hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
|
||||
hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
|
||||
hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...)
|
||||
hasName("scanf") or // scanf(format, args...)
|
||||
hasName("wscanf") or // wscanf(format, args...)
|
||||
hasName("_scanf_l") or // _scanf_l(format, locale, args...)
|
||||
hasName("_wscanf_l") // _wscanf_l(format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,10 +53,10 @@ class Fscanf extends ScanfFunction {
|
||||
Fscanf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
|
||||
hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
|
||||
hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
|
||||
hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
|
||||
hasName("fscanf") or // fscanf(src_stream, format, args...)
|
||||
hasName("fwscanf") or // fwscanf(src_stream, format, args...)
|
||||
hasName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
|
||||
hasName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,10 +72,10 @@ class Sscanf extends ScanfFunction {
|
||||
Sscanf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
|
||||
hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
|
||||
hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
|
||||
hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
|
||||
hasName("sscanf") or // sscanf(src_stream, format, args...)
|
||||
hasName("swscanf") or // swscanf(src, format, args...)
|
||||
hasName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
|
||||
hasName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,10 +91,8 @@ class Snscanf extends ScanfFunction {
|
||||
Snscanf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
|
||||
hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
|
||||
hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
|
||||
hasName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
hasName("_snwscanf") // _snwscanf(src, max_amount, format, args...)
|
||||
// note that the max_amount is not a limit on the output length, it's an input length
|
||||
// limit used with non null-terminated strings.
|
||||
)
|
||||
@@ -103,12 +101,6 @@ class Snscanf extends ScanfFunction {
|
||||
override int getInputParameterIndex() { result = 0 }
|
||||
|
||||
override int getFormatParameterIndex() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets the position at which the maximum number of characters in the
|
||||
* input string is specified.
|
||||
*/
|
||||
int getInputLengthParameterIndex() { result = 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -87,7 +87,7 @@ abstract class MutexType extends Type {
|
||||
private Function mustlockCandidate() {
|
||||
exists(string name | name = result.getName() |
|
||||
name = "lock" or
|
||||
name.matches("%mutex\\_lock")
|
||||
name.suffix(name.length() - 10) = "mutex_lock"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ private Function mustlockCandidate() {
|
||||
private Function trylockCandidate() {
|
||||
exists(string name | name = result.getName() |
|
||||
name = "try_lock" or
|
||||
name.matches("%mutex\\_trylock")
|
||||
name.suffix(name.length() - 13) = "mutex_trylock"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ private Function trylockCandidate() {
|
||||
private Function unlockCandidate() {
|
||||
exists(string name | name = result.getName() |
|
||||
name = "unlock" or
|
||||
name.matches("%mutex\\_unlock")
|
||||
name.suffix(name.length() - 12) = "mutex_unlock"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -131,22 +131,7 @@ private predicate lvalueToUpdate(Expr lvalue, Expr outer, ControlFlowNode node)
|
||||
exists(Call call | node = call |
|
||||
outer = call.getQualifier().getFullyConverted() and
|
||||
outer.getUnspecifiedType() instanceof Class and
|
||||
not (
|
||||
call.getTarget().hasSpecifier("const") and
|
||||
// Given the following program:
|
||||
// ```
|
||||
// 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`).
|
||||
call.getType().isDeeplyConstBelow()
|
||||
)
|
||||
not call.getTarget().hasSpecifier("const")
|
||||
)
|
||||
or
|
||||
assignmentTo(outer, node)
|
||||
@@ -185,11 +170,7 @@ private predicate pointerToUpdate(Expr pointer, Expr outer, ControlFlowNode node
|
||||
or
|
||||
outer = call.getQualifier().getFullyConverted() and
|
||||
outer.getUnspecifiedType() instanceof PointerType and
|
||||
not (
|
||||
call.getTarget().hasSpecifier("const") and
|
||||
// See the `lvalueToUpdate` case for an explanation of this conjunct.
|
||||
call.getType().isDeeplyConstBelow()
|
||||
)
|
||||
not call.getTarget().hasSpecifier("const")
|
||||
)
|
||||
or
|
||||
exists(PointerFieldAccess fa |
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -545,9 +545,6 @@ module TaintedWithPath {
|
||||
/** 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.
|
||||
@@ -581,9 +578,7 @@ module TaintedWithPath {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
|
||||
}
|
||||
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -3598,7 +3598,6 @@ private module FlowExploration {
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not clearsContent(node, ap.getHead()) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
@@ -3612,7 +3611,6 @@ private module FlowExploration {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not clearsContent(node, ap.getHead().getContent()) and
|
||||
if node instanceof CastingNode
|
||||
then compatibleTypes(getNodeType(node), ap.getType())
|
||||
else any()
|
||||
|
||||
@@ -716,15 +716,21 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
|
||||
iTo = call
|
||||
or
|
||||
exists(int index, WriteSideEffectInstruction outNode |
|
||||
modelOut.isParameterDerefOrQualifierObject(index) and
|
||||
modelOut.isParameterDeref(index) and
|
||||
iTo = outNode and
|
||||
outNode = getSideEffectFor(call, index)
|
||||
)
|
||||
or
|
||||
exists(WriteSideEffectInstruction outNode |
|
||||
modelOut.isQualifierObject() and
|
||||
iTo = outNode and
|
||||
outNode = getSideEffectFor(call, -1)
|
||||
)
|
||||
) and
|
||||
(
|
||||
exists(int index |
|
||||
modelIn.isParameterOrQualifierAddress(index) and
|
||||
opFrom = call.getArgumentOperand(index)
|
||||
modelIn.isParameter(index) and
|
||||
opFrom = call.getPositionalArgumentOperand(index)
|
||||
)
|
||||
or
|
||||
exists(int index, ReadSideEffectInstruction read |
|
||||
@@ -733,6 +739,9 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
|
||||
opFrom = read.getSideEffectOperand()
|
||||
)
|
||||
or
|
||||
modelIn.isQualifierAddress() and
|
||||
opFrom = call.getThisArgumentOperand()
|
||||
or
|
||||
exists(ReadSideEffectInstruction read |
|
||||
modelIn.isQualifierObject() and
|
||||
read = getSideEffectFor(call, -1) and
|
||||
|
||||
@@ -341,7 +341,7 @@ module IRTypeConsistency {
|
||||
query predicate multipleIRTypes(Language::LanguageType type, string message) {
|
||||
strictcount(type.getIRType()) > 1 and
|
||||
message =
|
||||
"`LanguageType` " + type + " has multiple `IRType`s: " +
|
||||
"`LanguageType` " + type.getAQlClass() + " has multiple `IRType`s: " +
|
||||
concat(type.getIRType().toString(), ", ")
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@ private import implementations.Strcat
|
||||
private import implementations.Strcpy
|
||||
private import implementations.Strdup
|
||||
private import implementations.Strftime
|
||||
private import implementations.Strtok
|
||||
private import implementations.Strset
|
||||
private import implementations.Strcrement
|
||||
private import implementations.Strnextc
|
||||
private import implementations.StdContainer
|
||||
private import implementations.StdPair
|
||||
private import implementations.StdMap
|
||||
@@ -27,9 +23,3 @@ private import implementations.StdString
|
||||
private import implementations.Swap
|
||||
private import implementations.GetDelim
|
||||
private import implementations.SmartPointer
|
||||
private import implementations.Sscanf
|
||||
private import implementations.Send
|
||||
private import implementations.Recv
|
||||
private import implementations.Accept
|
||||
private import implementations.Poll
|
||||
private import implementations.Select
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `accept` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The function `accept` and its assorted variants
|
||||
*/
|
||||
private class Accept extends ArrayFunction, AliasFunction, TaintFunction, SideEffectFunction {
|
||||
Accept() { this.hasGlobalName(["accept", "accept4", "WSAAccept"]) }
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
bufParam = 1 and countParam = 2
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 1 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { exists(this.getParameter(index)) }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(input.isParameter(0) or input.isParameterDeref(1)) and
|
||||
(output.isReturnValue() or output.isParameterDeref(1))
|
||||
}
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 1 and buffer = true and mustWrite = false
|
||||
or
|
||||
i = 2 and buffer = false and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = true
|
||||
or
|
||||
i = 1 and buffer = false
|
||||
}
|
||||
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
|
||||
|
||||
// NOTE: We implement thse two predicates as none because we can't model the low-level changes made to
|
||||
// the structure pointed to by the file-descriptor argument.
|
||||
override predicate hasOnlySpecificReadSideEffects() { none() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { none() }
|
||||
}
|
||||
@@ -15,7 +15,7 @@ private class MallocAllocationFunction extends AllocationFunction {
|
||||
|
||||
MallocAllocationFunction() {
|
||||
// --- C library allocation
|
||||
hasGlobalOrStdOrBslName("malloc") and // malloc(size)
|
||||
hasGlobalOrStdName("malloc") and // malloc(size)
|
||||
sizeArg = 0
|
||||
or
|
||||
hasGlobalName([
|
||||
@@ -104,7 +104,7 @@ private class CallocAllocationFunction extends AllocationFunction {
|
||||
|
||||
CallocAllocationFunction() {
|
||||
// --- C library allocation
|
||||
hasGlobalOrStdOrBslName("calloc") and // calloc(num, size)
|
||||
hasGlobalOrStdName("calloc") and // calloc(num, size)
|
||||
sizeArg = 1 and
|
||||
multArg = 0
|
||||
}
|
||||
@@ -124,7 +124,7 @@ private class ReallocAllocationFunction extends AllocationFunction {
|
||||
|
||||
ReallocAllocationFunction() {
|
||||
// --- C library allocation
|
||||
hasGlobalOrStdOrBslName("realloc") and // realloc(ptr, size)
|
||||
hasGlobalOrStdName("realloc") and // realloc(ptr, size)
|
||||
sizeArg = 1 and
|
||||
reallocArg = 0
|
||||
or
|
||||
|
||||
@@ -13,13 +13,9 @@ private class StandardDeallocationFunction extends DeallocationFunction {
|
||||
int freedArg;
|
||||
|
||||
StandardDeallocationFunction() {
|
||||
hasGlobalOrStdOrBslName([
|
||||
// --- C library allocation
|
||||
"free", "realloc"
|
||||
]) and
|
||||
freedArg = 0
|
||||
or
|
||||
hasGlobalName([
|
||||
// --- C library allocation
|
||||
"free", "realloc",
|
||||
// --- OpenSSL memory allocation
|
||||
"CRYPTO_free", "CRYPTO_secure_free"
|
||||
]) and
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
private class Fread extends AliasFunction, RemoteFlowSourceFunction {
|
||||
Fread() { this.hasGlobalOrStdOrBslName("fread") }
|
||||
private class Fread extends AliasFunction, RemoteFlowFunction {
|
||||
Fread() { this.hasGlobalName("fread") }
|
||||
|
||||
override predicate parameterNeverEscapes(int n) {
|
||||
n = 0 or
|
||||
|
||||
@@ -7,7 +7,7 @@ import semmle.code.cpp.models.interfaces.FlowSource
|
||||
* The standard functions `getdelim`, `getwdelim` and the glibc variant `__getdelim`.
|
||||
*/
|
||||
private class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction,
|
||||
RemoteFlowSourceFunction {
|
||||
RemoteFlowFunction {
|
||||
GetDelimFunction() { hasGlobalName(["getdelim", "getwdelim", "__getdelim"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) {
|
||||
|
||||
@@ -8,8 +8,8 @@ import semmle.code.cpp.models.interfaces.FlowSource
|
||||
/**
|
||||
* The POSIX function `getenv`.
|
||||
*/
|
||||
class Getenv extends LocalFlowSourceFunction {
|
||||
Getenv() { this.hasGlobalOrStdOrBslName("getenv") }
|
||||
class Getenv extends LocalFlowFunction {
|
||||
Getenv() { this.hasGlobalName("getenv") }
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
|
||||
@@ -14,12 +14,12 @@ import semmle.code.cpp.models.interfaces.FlowSource
|
||||
* The standard functions `gets` and `fgets`.
|
||||
*/
|
||||
private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||
SideEffectFunction, RemoteFlowSourceFunction {
|
||||
SideEffectFunction, RemoteFlowFunction {
|
||||
GetsFunction() {
|
||||
// gets(str)
|
||||
// fgets(str, num, stream)
|
||||
// fgetws(wstr, num, stream)
|
||||
hasGlobalOrStdOrBslName(["gets", "fgets", "fgetws"])
|
||||
hasGlobalOrStdName(["gets", "fgets", "fgetws"])
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -54,13 +54,13 @@ private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunctio
|
||||
}
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
not hasName("gets") and
|
||||
not hasGlobalOrStdName("gets") and
|
||||
bufParam = 0 and
|
||||
countParam = 1
|
||||
}
|
||||
|
||||
override predicate hasArrayWithUnknownSize(int bufParam) {
|
||||
hasName("gets") and
|
||||
hasGlobalOrStdName("gets") and
|
||||
bufParam = 0
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
/**
|
||||
* The standard function templates `std::move` and `std::forward`.
|
||||
*/
|
||||
private class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction,
|
||||
FunctionTemplateInstantiation {
|
||||
IdentityFunction() { this.hasQualifiedName("std", ["move", "forward"]) }
|
||||
private class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
|
||||
IdentityFunction() {
|
||||
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
|
||||
this.getNamespace().getName() = "std" and
|
||||
this.getName() = ["move", "forward"]
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
@@ -29,7 +32,5 @@ private class IdentityFunction extends DataFlowFunction, SideEffectFunction, Ali
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// These functions simply return the argument value.
|
||||
input.isParameter(0) and output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import semmle.code.cpp.models.interfaces.Iterator
|
||||
*/
|
||||
private class IteratorTraits extends Class {
|
||||
IteratorTraits() {
|
||||
this.hasQualifiedName(["std", "bsl"], "iterator_traits") and
|
||||
this.hasQualifiedName("std", "iterator_traits") and
|
||||
not this instanceof TemplateClass and
|
||||
exists(TypedefType t |
|
||||
this.getAMember() = t and
|
||||
@@ -26,14 +26,6 @@ private class IteratorTraits extends Class {
|
||||
Type getIteratorType() { result = this.getTemplateArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that is deduced to be an iterator because there is a corresponding
|
||||
* `std::iterator_traits` instantiation for it.
|
||||
*/
|
||||
private class IteratorByTraits extends Iterator {
|
||||
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type which has the typedefs expected for an iterator.
|
||||
*/
|
||||
@@ -44,7 +36,7 @@ private class IteratorByTypedefs extends Iterator, Class {
|
||||
this.getAMember().(TypedefType).hasName("pointer") and
|
||||
this.getAMember().(TypedefType).hasName("reference") and
|
||||
this.getAMember().(TypedefType).hasName("iterator_category") and
|
||||
not this.hasQualifiedName(["std", "bsl"], "iterator_traits")
|
||||
not this.hasQualifiedName("std", "iterator_traits")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +44,17 @@ private class IteratorByTypedefs extends Iterator, Class {
|
||||
* The `std::iterator` class.
|
||||
*/
|
||||
private class StdIterator extends Iterator, Class {
|
||||
StdIterator() { this.hasQualifiedName(["std", "bsl"], "iterator") }
|
||||
StdIterator() { this.hasQualifiedName("std", "iterator") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `FunctionInput` corresponding to an iterator parameter to
|
||||
* user-defined operator `op`, at `index`.
|
||||
* A type that is deduced to be an iterator because there is a corresponding
|
||||
* `std::iterator_traits` instantiation for it.
|
||||
*/
|
||||
private class IteratorByTraits extends Iterator {
|
||||
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
|
||||
}
|
||||
|
||||
private FunctionInput getIteratorArgumentInput(Operator op, int index) {
|
||||
exists(Type t |
|
||||
t =
|
||||
@@ -113,8 +109,6 @@ private class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +153,7 @@ private class IteratorSubOperator extends Operator, TaintFunction {
|
||||
private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -168,12 +162,6 @@ private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunctio
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the object referenced by the first parameter
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isParameterDeref(1) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
@@ -185,7 +173,8 @@ private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunctio
|
||||
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
IteratorPointerDereferenceMemberOperator() {
|
||||
this.getClassAndName("operator*") instanceof Iterator
|
||||
this.hasName("operator*") and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -202,7 +191,8 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
|
||||
*/
|
||||
private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
IteratorCrementMemberOperator() {
|
||||
this.getClassAndName(["operator++", "operator--"]) instanceof Iterator
|
||||
this.hasName(["operator++", "operator--"]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -211,9 +201,6 @@ private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunc
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -226,7 +213,10 @@ private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunc
|
||||
* A member `operator->` function for an iterator type.
|
||||
*/
|
||||
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
IteratorFieldMemberOperator() { this.getClassAndName("operator->") instanceof Iterator }
|
||||
IteratorFieldMemberOperator() {
|
||||
this.hasName("operator->") and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
@@ -239,7 +229,8 @@ private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
*/
|
||||
private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
|
||||
IteratorBinaryArithmeticMemberOperator() {
|
||||
this.getClassAndName(["operator+", "operator-"]) instanceof Iterator
|
||||
this.hasName(["operator+", "operator-"]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -254,24 +245,21 @@ private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, Tai
|
||||
private class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction,
|
||||
TaintFunction {
|
||||
IteratorAssignArithmeticMemberOperator() {
|
||||
this.getClassAndName(["operator+=", "operator-="]) instanceof Iterator
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the qualifier
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +268,10 @@ private class IteratorAssignArithmeticMemberOperator extends MemberFunction, Dat
|
||||
*/
|
||||
private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
IteratorArrayMemberOperator() { this.getClassAndName("operator[]") instanceof Iterator }
|
||||
IteratorArrayMemberOperator() {
|
||||
this.hasName("operator[]") and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
@@ -297,7 +288,8 @@ private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
|
||||
*/
|
||||
private class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
|
||||
IteratorAssignmentMemberOperator() {
|
||||
this.getClassAndName("operator=") instanceof Iterator and
|
||||
this.hasName("operator=") and
|
||||
this.getDeclaringType() instanceof Iterator and
|
||||
not this instanceof CopyAssignmentOperator and
|
||||
not this instanceof MoveAssignmentOperator
|
||||
}
|
||||
@@ -338,7 +330,7 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera
|
||||
*/
|
||||
private class InserterIteratorFunction extends GetIteratorFunction {
|
||||
InserterIteratorFunction() {
|
||||
this.hasQualifiedName(["std", "bsl"], ["front_inserter", "inserter", "back_inserter"])
|
||||
this.hasQualifiedName("std", ["front_inserter", "inserter", "back_inserter"])
|
||||
}
|
||||
|
||||
override predicate getsIterator(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
@@ -14,35 +13,32 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
* The standard functions `memcpy`, `memmove` and `bcopy`; and the gcc variant
|
||||
* `__builtin___memcpy_chk`.
|
||||
*/
|
||||
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction,
|
||||
AliasFunction {
|
||||
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
|
||||
MemcpyFunction() {
|
||||
// memcpy(dest, src, num)
|
||||
// memmove(dest, src, num)
|
||||
// memmove(dest, src, num, remaining)
|
||||
this.hasGlobalOrStdOrBslName(["memcpy", "memmove"])
|
||||
this.hasName(["memcpy", "memmove", "__builtin___memcpy_chk"])
|
||||
or
|
||||
// bcopy(src, dest, num)
|
||||
// mempcpy(dest, src, num)
|
||||
// memccpy(dest, src, c, n)
|
||||
this.hasGlobalName(["bcopy", mempcpy(), "memccpy", "__builtin___memcpy_chk"])
|
||||
this.hasGlobalOrStdName("bcopy")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the source buffer for the copy.
|
||||
*/
|
||||
int getParamSrc() { if this.hasGlobalName("bcopy") then result = 0 else result = 1 }
|
||||
int getParamSrc() { if this.hasGlobalOrStdName("bcopy") then result = 0 else result = 1 }
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the destination buffer for the
|
||||
* copy.
|
||||
*/
|
||||
int getParamDest() { if this.hasGlobalName("bcopy") then result = 1 else result = 0 }
|
||||
int getParamDest() { if this.hasGlobalOrStdName("bcopy") then result = 1 else result = 0 }
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the size of the copy (in bytes).
|
||||
*/
|
||||
int getParamSize() { if this.hasGlobalName("memccpy") then result = 3 else result = 2 }
|
||||
int getParamSize() { result = 2 }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = getParamSrc() }
|
||||
|
||||
@@ -72,10 +68,7 @@ private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffect
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = getParamDest() and
|
||||
buffer = true and
|
||||
// memccpy only writes until a given character `c` is found
|
||||
(if this.hasGlobalName("memccpy") then mustWrite = false else mustWrite = true)
|
||||
i = getParamDest() and buffer = true and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
@@ -89,21 +82,4 @@ private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffect
|
||||
i = getParamSrc()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
index = getParamSrc()
|
||||
or
|
||||
this.hasGlobalName("bcopy") and index = getParamDest()
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||
not this.hasGlobalName("bcopy") and index = getParamDest()
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) {
|
||||
not this.hasGlobalName(["bcopy", mempcpy(), "memccpy"]) and
|
||||
index = getParamDest()
|
||||
}
|
||||
}
|
||||
|
||||
private string mempcpy() { result = ["mempcpy", "wmempcpy"] }
|
||||
|
||||
@@ -15,11 +15,8 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
MemsetFunction() {
|
||||
this.hasGlobalOrStdOrBslName("memset")
|
||||
or
|
||||
this.hasGlobalOrStdName("wmemset")
|
||||
or
|
||||
this.hasGlobalName([bzero(), "__builtin_memset", "__builtin_memset_chk"])
|
||||
hasGlobalName(["memset", "wmemset", "bzero", "__builtin_memset", "__builtin_memset_chk"]) or
|
||||
hasQualifiedName("std", ["memset", "wmemset"])
|
||||
}
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
@@ -31,17 +28,17 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
bufParam = 0 and
|
||||
(if hasGlobalName(bzero()) then countParam = 1 else countParam = 2)
|
||||
(if hasGlobalName("bzero") then countParam = 1 else countParam = 2)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { hasGlobalName(bzero()) and index = 0 }
|
||||
override predicate parameterNeverEscapes(int index) { hasGlobalName("bzero") and index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||
not hasGlobalName(bzero()) and index = 0
|
||||
not hasGlobalName("bzero") and index = 0
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) {
|
||||
not hasGlobalName(bzero()) and index = 0
|
||||
not hasGlobalName("bzero") and index = 0
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
@@ -54,8 +51,6 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct
|
||||
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
|
||||
i = 0 and
|
||||
if hasGlobalName(bzero()) then result = 1 else result = 2
|
||||
if hasGlobalName("bzero") then result = 1 else result = 2
|
||||
}
|
||||
}
|
||||
|
||||
private string bzero() { result = ["bzero", "explicit_bzero"] }
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `poll` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The function `poll` and its assorted variants
|
||||
*/
|
||||
private class Poll extends ArrayFunction, AliasFunction, SideEffectFunction {
|
||||
Poll() { this.hasGlobalName(["poll", "ppoll", "WSAPoll"]) }
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
bufParam = 0 and countParam = 1
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { exists(this.getParameter(index)) }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = true and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = true
|
||||
or
|
||||
this.hasGlobalName("ppoll") and i = [2, 3] and buffer = false
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
@@ -15,7 +15,7 @@ private class Printf extends FormattingFunction, AliasFunction {
|
||||
Printf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName(["printf", "wprintf"]) or
|
||||
hasGlobalOrStdName(["printf", "wprintf"]) or
|
||||
hasGlobalName(["printf_s", "wprintf_s", "g_printf"])
|
||||
) and
|
||||
not exists(getDefinition().getFile().getRelativePath())
|
||||
@@ -23,7 +23,10 @@ private class Printf extends FormattingFunction, AliasFunction {
|
||||
|
||||
override int getFormatParameterIndex() { result = 0 }
|
||||
|
||||
deprecated override predicate isWideCharDefault() { hasName(["wprintf", "wprintf_s"]) }
|
||||
deprecated override predicate isWideCharDefault() {
|
||||
hasGlobalOrStdName("wprintf") or
|
||||
hasGlobalName("wprintf_s")
|
||||
}
|
||||
|
||||
override predicate isOutputGlobal() { any() }
|
||||
|
||||
@@ -41,7 +44,7 @@ private class Fprintf extends FormattingFunction {
|
||||
Fprintf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName(["fprintf", "fwprintf"]) or
|
||||
hasGlobalOrStdName(["fprintf", "fwprintf"]) or
|
||||
hasGlobalName("g_fprintf")
|
||||
) and
|
||||
not exists(getDefinition().getFile().getRelativePath())
|
||||
@@ -49,7 +52,7 @@ private class Fprintf extends FormattingFunction {
|
||||
|
||||
override int getFormatParameterIndex() { result = 1 }
|
||||
|
||||
deprecated override predicate isWideCharDefault() { hasName("fwprintf") }
|
||||
deprecated override predicate isWideCharDefault() { hasGlobalOrStdName("fwprintf") }
|
||||
|
||||
override int getOutputParameterIndex(boolean isStream) { result = 0 and isStream = true }
|
||||
}
|
||||
@@ -61,7 +64,7 @@ private class Sprintf extends FormattingFunction {
|
||||
Sprintf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName([
|
||||
hasGlobalOrStdName([
|
||||
"sprintf", // sprintf(dst, format, args...)
|
||||
"wsprintf" // wsprintf(dst, format, args...)
|
||||
])
|
||||
@@ -87,20 +90,22 @@ private class Sprintf extends FormattingFunction {
|
||||
}
|
||||
|
||||
override int getFormatParameterIndex() {
|
||||
hasName("g_strdup_printf") and result = 0
|
||||
hasGlobalName("g_strdup_printf") and result = 0
|
||||
or
|
||||
hasName("__builtin___sprintf_chk") and result = 3
|
||||
hasGlobalName("__builtin___sprintf_chk") and result = 3
|
||||
or
|
||||
not getName() = ["g_strdup_printf", "__builtin___sprintf_chk"] and
|
||||
result = 1
|
||||
}
|
||||
|
||||
override int getOutputParameterIndex(boolean isStream) {
|
||||
not hasName("g_strdup_printf") and result = 0 and isStream = false
|
||||
not hasGlobalName("g_strdup_printf") and result = 0 and isStream = false
|
||||
}
|
||||
|
||||
override int getFirstFormatArgumentIndex() {
|
||||
if hasName("__builtin___sprintf_chk") then result = 4 else result = getNumberOfParameters()
|
||||
if hasGlobalName("__builtin___sprintf_chk")
|
||||
then result = 4
|
||||
else result = getNumberOfParameters()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +116,7 @@ private class SnprintfImpl extends Snprintf {
|
||||
SnprintfImpl() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName([
|
||||
hasGlobalOrStdName([
|
||||
"snprintf", // C99 defines snprintf
|
||||
"swprintf" // The s version of wide-char printf is also always the n version
|
||||
])
|
||||
@@ -158,7 +163,10 @@ private class SnprintfImpl extends Snprintf {
|
||||
}
|
||||
|
||||
override predicate returnsFullFormatLength() {
|
||||
hasName(["snprintf", "g_snprintf", "__builtin___snprintf_chk", "snprintf_s"]) and
|
||||
(
|
||||
hasGlobalOrStdName("snprintf") or
|
||||
hasGlobalName(["g_snprintf", "__builtin___snprintf_chk", "snprintf_s"])
|
||||
) and
|
||||
not exists(getDefinition().getFile().getRelativePath())
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,14 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* A function that operates on strings and is pure. That is, its evaluation is
|
||||
* guaranteed to be side-effect free.
|
||||
*/
|
||||
/** Pure string functions. */
|
||||
private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction,
|
||||
SideEffectFunction {
|
||||
PureStrFunction() {
|
||||
hasGlobalOrStdOrBslName([
|
||||
atoi(), "strcasestr", "strchnul", "strchr", "strchrnul", "strstr", "strpbrk", "strrchr",
|
||||
"strspn", strtol(), strrev(), strcmp(), strlwr(), strupr()
|
||||
hasGlobalOrStdName([
|
||||
"atof", "atoi", "atol", "atoll", "strcasestr", "strchnul", "strchr", "strchrnul", "strstr",
|
||||
"strpbrk", "strcmp", "strcspn", "strncmp", "strrchr", "strspn", "strtod", "strtof",
|
||||
"strtol", "strtoll", "strtoq", "strtoul"
|
||||
])
|
||||
}
|
||||
|
||||
@@ -26,16 +24,11 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(ParameterIndex i |
|
||||
(
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
) and
|
||||
// Functions that end with _l also take a locale argument (always as the last argument),
|
||||
// and we don't want taint from those arguments.
|
||||
(not this.getName().matches("%\\_l") or exists(getParameter(i + 1)))
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
) and
|
||||
(
|
||||
output.isReturnValueDeref() and
|
||||
@@ -67,37 +60,10 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
}
|
||||
}
|
||||
|
||||
private string atoi() { result = ["atof", "atoi", "atol", "atoll"] }
|
||||
|
||||
private string strtol() { result = ["strtod", "strtof", "strtol", "strtoll", "strtoq", "strtoul"] }
|
||||
|
||||
private string strlwr() {
|
||||
result = ["_strlwr", "_wcslwr", "_mbslwr", "_strlwr_l", "_wcslwr_l", "_mbslwr_l"]
|
||||
}
|
||||
|
||||
private string strupr() {
|
||||
result = ["_strupr", "_wcsupr", "_mbsupr", "_strupr_l", "_wcsupr_l", "_mbsupr_l"]
|
||||
}
|
||||
|
||||
private string strrev() { result = ["_strrev", "_wcsrev", "_mbsrev", "_mbsrev_l"] }
|
||||
|
||||
private string strcmp() {
|
||||
// NOTE: `strcoll` doesn't satisfy _all_ the definitions of purity: its behavior depends on
|
||||
// `LC_COLLATE` (which is set by `setlocale`). Not sure this behavior worth including in the model, so
|
||||
// for now we interpret the function as being pure.
|
||||
result =
|
||||
[
|
||||
"strcmp", "strcspn", "strncmp", "strcoll", "strverscmp", "_mbsnbcmp", "_mbsnbcmp_l",
|
||||
"_stricmp"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* A function such as `strlen` that returns the length of the given string.
|
||||
*/
|
||||
/** String standard `strlen` function, and related functions for computing string lengths. */
|
||||
private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
|
||||
StrLenFunction() {
|
||||
hasGlobalOrStdOrBslName(["strlen", "strnlen", "wcslen"])
|
||||
hasGlobalOrStdName(["strlen", "strnlen", "wcslen"])
|
||||
or
|
||||
hasGlobalName(["_mbslen", "_mbslen_l", "_mbstrlen", "_mbstrlen_l"])
|
||||
}
|
||||
@@ -128,12 +94,9 @@ private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFun
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that is pure, that is, its evaluation is guaranteed to be
|
||||
* side-effect free. Excludes functions modeled by `PureStrFunction` and `PureMemFunction`.
|
||||
*/
|
||||
/** Pure functions. */
|
||||
private class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
PureFunction() { hasGlobalOrStdOrBslName(["abs", "labs"]) }
|
||||
PureFunction() { hasGlobalOrStdName(["abs", "labs"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(ParameterIndex i |
|
||||
@@ -148,18 +111,10 @@ private class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that operates on memory buffers and is pure. That is, its
|
||||
* evaluation is guaranteed to be side-effect free.
|
||||
*/
|
||||
/** Pure raw-memory functions. */
|
||||
private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunction,
|
||||
SideEffectFunction {
|
||||
PureMemFunction() {
|
||||
hasGlobalOrStdOrBslName([
|
||||
"memchr", "__builtin_memchr", "memrchr", "rawmemchr", "memcmp", "__builtin_memcmp", "memmem"
|
||||
]) or
|
||||
this.hasGlobalName("memfrob")
|
||||
}
|
||||
PureMemFunction() { hasGlobalOrStdName(["memchr", "memrchr", "rawmemchr", "memcmp", "memmem"]) }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) {
|
||||
getParameter(bufParam).getUnspecifiedType() instanceof PointerType
|
||||
@@ -167,15 +122,11 @@ private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(ParameterIndex i |
|
||||
(
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
) and
|
||||
// `memfrob` should not have taint from the size argument.
|
||||
(not this.hasGlobalName("memfrob") or i = 0)
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
) and
|
||||
(
|
||||
output.isReturnValueDeref() and
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `recv` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/** The function `recv` and its assorted variants */
|
||||
private class Recv extends AliasFunction, ArrayFunction, SideEffectFunction,
|
||||
RemoteFlowSourceFunction {
|
||||
Recv() {
|
||||
this.hasGlobalName([
|
||||
"recv", // recv(socket, dest, len, flags)
|
||||
"recvfrom", // recvfrom(socket, dest, len, flags, from, fromlen)
|
||||
"recvmsg", // recvmsg(socket, msg, flags)
|
||||
"read", // read(socket, dest, len)
|
||||
"pread", // pread(socket, dest, len, offset)
|
||||
"readv", // readv(socket, dest, len)
|
||||
"preadv", // readv(socket, dest, len, offset)
|
||||
"preadv2" // readv2(socket, dest, len, offset, flags)
|
||||
])
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
this.getParameter(index).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
not this.hasGlobalName("recvmsg") and
|
||||
bufParam = 1 and
|
||||
countParam = 2
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { this.hasGlobalName("recvfrom") and bufParam = 4 }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) {
|
||||
bufParam = 1
|
||||
or
|
||||
this.hasGlobalName("recvfrom") and bufParam = 4
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
this.hasGlobalName("recvfrom") and
|
||||
(
|
||||
i = 4 and buffer = true
|
||||
or
|
||||
i = 5 and buffer = false
|
||||
)
|
||||
or
|
||||
this.hasGlobalName("recvmsg") and
|
||||
i = 1 and
|
||||
buffer = true
|
||||
}
|
||||
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 1 and buffer = true and mustWrite = false
|
||||
or
|
||||
this.hasGlobalName("recvfrom") and
|
||||
(
|
||||
i = 4 and buffer = true and mustWrite = false
|
||||
or
|
||||
i = 5 and buffer = false and mustWrite = false
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
output.isParameterDeref(1)
|
||||
or
|
||||
this.hasGlobalName("recvfrom") and output.isParameterDeref([4, 5])
|
||||
) and
|
||||
description = "Buffer read by " + this.getName()
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `select` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The function `select` and its assorted variants
|
||||
*/
|
||||
private class Select extends ArrayFunction, AliasFunction, SideEffectFunction {
|
||||
Select() { this.hasGlobalName(["select", "pselect"]) }
|
||||
|
||||
override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = [1 .. 3] }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = [1 .. 3] }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = [1 .. 3] }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { exists(this.getParameter(index)) }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = [1 .. 3] and buffer = true and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = [1 .. 5] and buffer = true
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `send` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/** The function `send` and its assorted variants */
|
||||
private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, RemoteFlowSinkFunction {
|
||||
Send() {
|
||||
this.hasGlobalName([
|
||||
"send", // send(socket, buf, len, flags)
|
||||
"sendto", // sendto(socket, buf, len, flags, to, tolen)
|
||||
"sendmsg", // sendmsg(socket, msg, flags)
|
||||
"write", // write(socket, buf, len)
|
||||
"writev", // writev(socket, buf, len)
|
||||
"pwritev", // pwritev(socket, buf, len, offset)
|
||||
"pwritev2" // pwritev2(socket, buf, len, offset, flags)
|
||||
])
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
this.getParameter(index).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
not this.hasGlobalName("sendmsg") and
|
||||
bufParam = 1 and
|
||||
countParam = 2
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 1 and buffer = true
|
||||
or
|
||||
this.hasGlobalName("sendto") and i = 4 and buffer = false
|
||||
or
|
||||
this.hasGlobalName("sendmsg") and i = 1 and buffer = true
|
||||
}
|
||||
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
|
||||
|
||||
override predicate hasRemoteFlowSink(FunctionInput input, string description) {
|
||||
input.isParameterDeref(1) and description = "Buffer sent by " + this.getName()
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,14 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
* The `std::shared_ptr` and `std::unique_ptr` template classes.
|
||||
*/
|
||||
private class UniqueOrSharedPtr extends Class {
|
||||
UniqueOrSharedPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "unique_ptr"]) }
|
||||
UniqueOrSharedPtr() { this.hasQualifiedName("std", ["shared_ptr", "unique_ptr"]) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::make_shared` and `std::make_unique` template functions.
|
||||
*/
|
||||
private class MakeUniqueOrShared extends TaintFunction {
|
||||
MakeUniqueOrShared() { this.hasQualifiedName(["bsl", "std"], ["make_shared", "make_unique"]) }
|
||||
MakeUniqueOrShared() { this.hasQualifiedName("std", ["make_shared", "make_unique"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// Exclude the specializations of `std::make_shared` and `std::make_unique` that allocate arrays
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `sscanf`, `fscanf` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.commons.Scanf
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `sscanf`, `fscanf` and its assorted variants
|
||||
*/
|
||||
private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, SideEffectFunction {
|
||||
SscanfModel() { this instanceof Sscanf or this instanceof Fscanf or this instanceof Snscanf }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
bufParam = this.(ScanfFunction).getFormatParameterIndex()
|
||||
or
|
||||
not this instanceof Fscanf and
|
||||
bufParam = this.(ScanfFunction).getInputParameterIndex()
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { hasArrayWithNullTerminator(bufParam) }
|
||||
|
||||
private int getLengthParameterIndex() { result = this.(Snscanf).getInputLengthParameterIndex() }
|
||||
|
||||
private int getLocaleParameterIndex() {
|
||||
this.getName().matches("%\\_l") and
|
||||
(
|
||||
if exists(getLengthParameterIndex())
|
||||
then result = getLengthParameterIndex() + 2
|
||||
else result = 2
|
||||
)
|
||||
}
|
||||
|
||||
private int getArgsStartPosition() { result = this.getNumberOfParameters() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and
|
||||
output.isParameterDeref(any(int i | i >= getArgsStartPosition()))
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
index = [0 .. max(getACallToThisFunction().getNumberOfArguments())]
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i >= getArgsStartPosition() and
|
||||
buffer = true and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
buffer = true and
|
||||
i =
|
||||
[
|
||||
this.(ScanfFunction).getInputParameterIndex(),
|
||||
this.(ScanfFunction).getFormatParameterIndex(), getLocaleParameterIndex()
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,41 +5,6 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
* The `std::array` template class.
|
||||
*/
|
||||
private class Array extends Class {
|
||||
Array() { this.hasQualifiedName(["std", "bsl"], "array") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::deque` template class.
|
||||
*/
|
||||
private class Deque extends Class {
|
||||
Deque() { this.hasQualifiedName(["std", "bsl"], "deque") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::forward_list` template class.
|
||||
*/
|
||||
private class ForwardList extends Class {
|
||||
ForwardList() { this.hasQualifiedName(["std", "bsl"], "forward_list") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::list` template class.
|
||||
*/
|
||||
private class List extends Class {
|
||||
List() { this.hasQualifiedName(["std", "bsl"], "list") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::vector` template class.
|
||||
*/
|
||||
private class Vector extends Class {
|
||||
Vector() { this.hasQualifiedName(["std", "bsl"], "vector") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional model for standard container constructors that reference the
|
||||
* value type of the container (that is, the `T` in `std::vector<T>`). For
|
||||
@@ -50,10 +15,7 @@ private class Vector extends Class {
|
||||
*/
|
||||
private class StdSequenceContainerConstructor extends Constructor, TaintFunction {
|
||||
StdSequenceContainerConstructor() {
|
||||
this.getDeclaringType() instanceof Vector or
|
||||
this.getDeclaringType() instanceof Deque or
|
||||
this.getDeclaringType() instanceof List or
|
||||
this.getDeclaringType() instanceof ForwardList
|
||||
this.getDeclaringType().hasQualifiedName("std", ["vector", "deque", "list", "forward_list"])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,10 +50,7 @@ private class StdSequenceContainerConstructor extends Constructor, TaintFunction
|
||||
* The standard container function `data`.
|
||||
*/
|
||||
private class StdSequenceContainerData extends TaintFunction {
|
||||
StdSequenceContainerData() {
|
||||
this.getClassAndName("data") instanceof Array or
|
||||
this.getClassAndName("data") instanceof Vector
|
||||
}
|
||||
StdSequenceContainerData() { this.hasQualifiedName("std", ["array", "vector"], "data") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from container itself (qualifier) to return value
|
||||
@@ -110,10 +69,8 @@ private class StdSequenceContainerData extends TaintFunction {
|
||||
*/
|
||||
private class StdSequenceContainerPush extends TaintFunction {
|
||||
StdSequenceContainerPush() {
|
||||
this.getClassAndName("push_back") instanceof Vector or
|
||||
this.getClassAndName(["push_back", "push_front"]) instanceof Deque or
|
||||
this.getClassAndName("push_front") instanceof ForwardList or
|
||||
this.getClassAndName(["push_back", "push_front"]) instanceof List
|
||||
this.hasQualifiedName("std", ["vector", "deque", "list"], "push_back") or
|
||||
this.hasQualifiedName("std", ["deque", "list", "forward_list"], "push_front")
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -128,11 +85,8 @@ private class StdSequenceContainerPush extends TaintFunction {
|
||||
*/
|
||||
private class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
StdSequenceContainerFrontBack() {
|
||||
this.getClassAndName(["front", "back"]) instanceof Array or
|
||||
this.getClassAndName(["front", "back"]) instanceof Deque or
|
||||
this.getClassAndName("front") instanceof ForwardList or
|
||||
this.getClassAndName(["front", "back"]) instanceof List or
|
||||
this.getClassAndName(["front", "back"]) instanceof Vector
|
||||
this.hasQualifiedName("std", ["array", "vector", "deque", "list", "forward_list"], "front") or
|
||||
this.hasQualifiedName("std", ["array", "vector", "deque", "list"], "back")
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -147,10 +101,8 @@ private class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
*/
|
||||
private class StdSequenceContainerInsert extends TaintFunction {
|
||||
StdSequenceContainerInsert() {
|
||||
this.getClassAndName("insert") instanceof Deque or
|
||||
this.getClassAndName("insert") instanceof List or
|
||||
this.getClassAndName("insert") instanceof Vector or
|
||||
this.getClassAndName("insert_after") instanceof ForwardList
|
||||
this.hasQualifiedName("std", ["vector", "deque", "list"], "insert") or
|
||||
this.hasQualifiedName("std", "forward_list", "insert_after")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,10 +138,7 @@ private class StdSequenceContainerInsert extends TaintFunction {
|
||||
*/
|
||||
private class StdSequenceContainerAssign extends TaintFunction {
|
||||
StdSequenceContainerAssign() {
|
||||
this.getClassAndName("assign") instanceof Deque or
|
||||
this.getClassAndName("assign") instanceof ForwardList or
|
||||
this.getClassAndName("assign") instanceof List or
|
||||
this.getClassAndName("assign") instanceof Vector
|
||||
this.hasQualifiedName("std", ["vector", "deque", "list", "forward_list"], "assign")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,9 +170,7 @@ private class StdSequenceContainerAssign extends TaintFunction {
|
||||
*/
|
||||
private class StdSequenceContainerAt extends TaintFunction {
|
||||
StdSequenceContainerAt() {
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Array or
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Vector
|
||||
this.hasQualifiedName("std", ["vector", "array", "deque"], ["at", "operator[]"])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -241,12 +188,12 @@ private class StdSequenceContainerAt extends TaintFunction {
|
||||
* The standard vector `emplace` function.
|
||||
*/
|
||||
class StdVectorEmplace extends TaintFunction {
|
||||
StdVectorEmplace() { this.getClassAndName("emplace") instanceof Vector }
|
||||
StdVectorEmplace() { this.hasQualifiedName("std", "vector", "emplace") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter except the position iterator to qualifier and return value
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
input.isParameterDeref([1 .. getNumberOfParameters() - 1]) and
|
||||
input.isParameter([1 .. getNumberOfParameters() - 1]) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValue()
|
||||
@@ -258,12 +205,12 @@ class StdVectorEmplace extends TaintFunction {
|
||||
* The standard vector `emplace_back` function.
|
||||
*/
|
||||
class StdVectorEmplaceBack extends TaintFunction {
|
||||
StdVectorEmplaceBack() { this.getClassAndName("emplace_back") instanceof Vector }
|
||||
StdVectorEmplaceBack() { this.hasQualifiedName("std", "vector", "emplace_back") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter to qualifier
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
input.isParameterDeref([0 .. getNumberOfParameters() - 1]) and
|
||||
input.isParameter([0 .. getNumberOfParameters() - 1]) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,14 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
* The `std::map` and `std::unordered_map` template classes.
|
||||
*/
|
||||
private class MapOrUnorderedMap extends Class {
|
||||
MapOrUnorderedMap() { this.hasQualifiedName(["std", "bsl"], ["map", "unordered_map"]) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional model for map constructors using iterator inputs.
|
||||
*/
|
||||
private class StdMapConstructor extends Constructor, TaintFunction {
|
||||
StdMapConstructor() { this.getDeclaringType() instanceof MapOrUnorderedMap }
|
||||
StdMapConstructor() {
|
||||
this.hasQualifiedName("std", "map", "map") or
|
||||
this.hasQualifiedName("std", "unordered_map", "unordered_map")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
@@ -41,7 +37,7 @@ private class StdMapConstructor extends Constructor, TaintFunction {
|
||||
*/
|
||||
private class StdMapInsert extends TaintFunction {
|
||||
StdMapInsert() {
|
||||
this.getClassAndName(["insert", "insert_or_assign"]) instanceof MapOrUnorderedMap
|
||||
this.hasQualifiedName("std", ["map", "unordered_map"], ["insert", "insert_or_assign"])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -59,7 +55,9 @@ private class StdMapInsert extends TaintFunction {
|
||||
* The standard map `emplace` and `emplace_hint` functions.
|
||||
*/
|
||||
private class StdMapEmplace extends TaintFunction {
|
||||
StdMapEmplace() { this.getClassAndName(["emplace", "emplace_hint"]) instanceof MapOrUnorderedMap }
|
||||
StdMapEmplace() {
|
||||
this.hasQualifiedName("std", ["map", "unordered_map"], ["emplace", "emplace_hint"])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from the last parameter (which may be the value part used to
|
||||
@@ -81,7 +79,7 @@ private class StdMapEmplace extends TaintFunction {
|
||||
* The standard map `try_emplace` function.
|
||||
*/
|
||||
private class StdMapTryEmplace extends TaintFunction {
|
||||
StdMapTryEmplace() { this.getClassAndName("try_emplace") instanceof MapOrUnorderedMap }
|
||||
StdMapTryEmplace() { this.hasQualifiedName("std", ["map", "unordered_map"], "try_emplace") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter apart from the key to qualifier and return value
|
||||
@@ -108,7 +106,7 @@ private class StdMapTryEmplace extends TaintFunction {
|
||||
* The standard map `merge` function.
|
||||
*/
|
||||
private class StdMapMerge extends TaintFunction {
|
||||
StdMapMerge() { this.getClassAndName("merge") instanceof MapOrUnorderedMap }
|
||||
StdMapMerge() { this.hasQualifiedName("std", ["map", "unordered_map"], "merge") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// container1.merge(container2)
|
||||
@@ -121,7 +119,7 @@ private class StdMapMerge extends TaintFunction {
|
||||
* The standard map functions `at` and `operator[]`.
|
||||
*/
|
||||
private class StdMapAt extends TaintFunction {
|
||||
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
|
||||
StdMapAt() { this.hasQualifiedName("std", ["map", "unordered_map"], ["at", "operator[]"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
@@ -138,7 +136,7 @@ private class StdMapAt extends TaintFunction {
|
||||
* The standard map `find` function.
|
||||
*/
|
||||
private class StdMapFind extends TaintFunction {
|
||||
StdMapFind() { this.getClassAndName("find") instanceof MapOrUnorderedMap }
|
||||
StdMapFind() { this.hasQualifiedName("std", ["map", "unordered_map"], "find") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
@@ -150,7 +148,7 @@ private class StdMapFind extends TaintFunction {
|
||||
* The standard map `erase` function.
|
||||
*/
|
||||
private class StdMapErase extends TaintFunction {
|
||||
StdMapErase() { this.getClassAndName("erase") instanceof MapOrUnorderedMap }
|
||||
StdMapErase() { this.hasQualifiedName("std", ["map", "unordered_map"], "erase") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to iterator return value
|
||||
@@ -165,7 +163,8 @@ private class StdMapErase extends TaintFunction {
|
||||
*/
|
||||
private class StdMapEqualRange extends TaintFunction {
|
||||
StdMapEqualRange() {
|
||||
this.getClassAndName(["lower_bound", "upper_bound", "equal_range"]) instanceof MapOrUnorderedMap
|
||||
this.hasQualifiedName("std", ["map", "unordered_map"],
|
||||
["lower_bound", "upper_bound", "equal_range"])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -7,16 +7,10 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
/**
|
||||
* An instantiation of `std::pair<T1, T2>`.
|
||||
*/
|
||||
private class StdPair extends ClassTemplateInstantiation {
|
||||
StdPair() { this.hasQualifiedName(["std", "bsl"], "pair") }
|
||||
class StdPairClass extends ClassTemplateInstantiation {
|
||||
StdPairClass() { getTemplate().hasQualifiedName("std", "pair") }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: This is now called `StdPair` and is a private part of the
|
||||
* library implementation.
|
||||
*/
|
||||
deprecated class StdPairClass = StdPair;
|
||||
|
||||
/**
|
||||
* Any of the single-parameter constructors of `std::pair` that takes a reference to an
|
||||
* instantiation of `std::pair`. These constructors allow conversion between pair types when the
|
||||
@@ -24,9 +18,9 @@ deprecated class StdPairClass = StdPair;
|
||||
*/
|
||||
class StdPairCopyishConstructor extends Constructor, TaintFunction {
|
||||
StdPairCopyishConstructor() {
|
||||
this.getDeclaringType() instanceof StdPair and
|
||||
this.getDeclaringType() instanceof StdPairClass and
|
||||
this.getNumberOfParameters() = 1 and
|
||||
this.getParameter(0).getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdPair
|
||||
this.getParameter(0).getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdPairClass
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -44,7 +38,7 @@ class StdPairCopyishConstructor extends Constructor, TaintFunction {
|
||||
* Additional model for `std::pair` constructors.
|
||||
*/
|
||||
private class StdPairConstructor extends Constructor, TaintFunction {
|
||||
StdPairConstructor() { this.getDeclaringType() instanceof StdPair }
|
||||
StdPairConstructor() { this.hasQualifiedName("std", "pair", "pair") }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to
|
||||
|
||||
@@ -5,18 +5,14 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
* An instantiation of `std::set` or `std::unordered_set`.
|
||||
*/
|
||||
private class StdSet extends ClassTemplateInstantiation {
|
||||
StdSet() { this.hasQualifiedName(["std", "bsl"], ["set", "unordered_set"]) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional model for set constructors using iterator inputs.
|
||||
*/
|
||||
private class StdSetConstructor extends Constructor, TaintFunction {
|
||||
StdSetConstructor() { this.getDeclaringType() instanceof StdSet }
|
||||
StdSetConstructor() {
|
||||
this.hasQualifiedName("std", "set", "set") or
|
||||
this.hasQualifiedName("std", "unordered_set", "unordered_set")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
@@ -40,7 +36,7 @@ private class StdSetConstructor extends Constructor, TaintFunction {
|
||||
* The standard set `insert` and `insert_or_assign` functions.
|
||||
*/
|
||||
private class StdSetInsert extends TaintFunction {
|
||||
StdSetInsert() { this.getClassAndName("insert") instanceof StdSet }
|
||||
StdSetInsert() { this.hasQualifiedName("std", ["set", "unordered_set"], "insert") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from last parameter to qualifier and return value
|
||||
@@ -57,7 +53,9 @@ private class StdSetInsert extends TaintFunction {
|
||||
* The standard set `emplace` and `emplace_hint` functions.
|
||||
*/
|
||||
private class StdSetEmplace extends TaintFunction {
|
||||
StdSetEmplace() { this.getClassAndName(["emplace", "emplace_hint"]) instanceof StdSet }
|
||||
StdSetEmplace() {
|
||||
this.hasQualifiedName("std", ["set", "unordered_set"], ["emplace", "emplace_hint"])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter to qualifier and return value
|
||||
@@ -78,7 +76,7 @@ private class StdSetEmplace extends TaintFunction {
|
||||
* The standard set `merge` function.
|
||||
*/
|
||||
private class StdSetMerge extends TaintFunction {
|
||||
StdSetMerge() { this.getClassAndName("merge") instanceof StdSet }
|
||||
StdSetMerge() { this.hasQualifiedName("std", ["set", "unordered_set"], "merge") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// container1.merge(container2)
|
||||
@@ -91,7 +89,7 @@ private class StdSetMerge extends TaintFunction {
|
||||
* The standard set `find` function.
|
||||
*/
|
||||
private class StdSetFind extends TaintFunction {
|
||||
StdSetFind() { this.getClassAndName("find") instanceof StdSet }
|
||||
StdSetFind() { this.hasQualifiedName("std", ["set", "unordered_set"], "find") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
@@ -103,7 +101,7 @@ private class StdSetFind extends TaintFunction {
|
||||
* The standard set `erase` function.
|
||||
*/
|
||||
private class StdSetErase extends TaintFunction {
|
||||
StdSetErase() { this.getClassAndName("erase") instanceof StdSet }
|
||||
StdSetErase() { this.hasQualifiedName("std", ["set", "unordered_set"], "erase") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to iterator return value
|
||||
@@ -118,7 +116,8 @@ private class StdSetErase extends TaintFunction {
|
||||
*/
|
||||
private class StdSetEqualRange extends TaintFunction {
|
||||
StdSetEqualRange() {
|
||||
this.getClassAndName(["lower_bound", "upper_bound", "equal_range"]) instanceof StdSet
|
||||
this.hasQualifiedName("std", ["set", "unordered_set"],
|
||||
["lower_bound", "upper_bound", "equal_range"])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -9,10 +9,10 @@ import semmle.code.cpp.models.interfaces.Iterator
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
|
||||
/**
|
||||
* The `std::basic_string` template class instantiations.
|
||||
* The `std::basic_string` template class.
|
||||
*/
|
||||
private class StdBasicString extends ClassTemplateInstantiation {
|
||||
StdBasicString() { this.hasQualifiedName(["std", "bsl"], "basic_string") }
|
||||
private class StdBasicString extends TemplateClass {
|
||||
StdBasicString() { this.hasQualifiedName("std", "basic_string") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ private class StdBasicString extends ClassTemplateInstantiation {
|
||||
* ```
|
||||
*/
|
||||
private class StdStringConstructor extends Constructor, TaintFunction {
|
||||
StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString }
|
||||
StdStringConstructor() { this.getDeclaringType().hasQualifiedName("std", "basic_string") }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
@@ -69,7 +69,7 @@ private class StdStringConstructor extends Constructor, TaintFunction {
|
||||
* The `std::string` function `c_str`.
|
||||
*/
|
||||
private class StdStringCStr extends TaintFunction {
|
||||
StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString }
|
||||
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string itself (qualifier) to return value
|
||||
@@ -82,7 +82,7 @@ private class StdStringCStr extends TaintFunction {
|
||||
* The `std::string` function `data`.
|
||||
*/
|
||||
private class StdStringData extends TaintFunction {
|
||||
StdStringData() { this.getClassAndName("data") instanceof StdBasicString }
|
||||
StdStringData() { this.hasQualifiedName("std", "basic_string", "data") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string itself (qualifier) to return value
|
||||
@@ -100,7 +100,7 @@ private class StdStringData extends TaintFunction {
|
||||
* The `std::string` function `push_back`.
|
||||
*/
|
||||
private class StdStringPush extends TaintFunction {
|
||||
StdStringPush() { this.getClassAndName("push_back") instanceof StdBasicString }
|
||||
StdStringPush() { this.hasQualifiedName("std", "basic_string", "push_back") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to qualifier
|
||||
@@ -113,7 +113,7 @@ private class StdStringPush extends TaintFunction {
|
||||
* The `std::string` functions `front` and `back`.
|
||||
*/
|
||||
private class StdStringFrontBack extends TaintFunction {
|
||||
StdStringFrontBack() { this.getClassAndName(["front", "back"]) instanceof StdBasicString }
|
||||
StdStringFrontBack() { this.hasQualifiedName("std", "basic_string", ["front", "back"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from object to returned reference
|
||||
@@ -123,12 +123,12 @@ private class StdStringFrontBack extends TaintFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The (non-member) `std::string` function `operator+`.
|
||||
* The `std::string` function `operator+`.
|
||||
*/
|
||||
private class StdStringPlus extends TaintFunction {
|
||||
StdStringPlus() {
|
||||
this.hasQualifiedName(["std", "bsl"], "operator+") and
|
||||
this.getUnspecifiedType() instanceof StdBasicString
|
||||
this.hasQualifiedName("std", "operator+") and
|
||||
this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -148,7 +148,7 @@ private class StdStringPlus extends TaintFunction {
|
||||
*/
|
||||
private class StdStringAppend extends TaintFunction {
|
||||
StdStringAppend() {
|
||||
this.getClassAndName(["operator+=", "append", "insert", "replace"]) instanceof StdBasicString
|
||||
this.hasQualifiedName("std", "basic_string", ["operator+=", "append", "insert", "replace"])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,7 +190,7 @@ private class StdStringAppend extends TaintFunction {
|
||||
* The standard function `std::string.assign`.
|
||||
*/
|
||||
private class StdStringAssign extends TaintFunction {
|
||||
StdStringAssign() { this.getClassAndName("assign") instanceof StdBasicString }
|
||||
StdStringAssign() { this.hasQualifiedName("std", "basic_string", "assign") }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
@@ -230,7 +230,7 @@ private class StdStringAssign extends TaintFunction {
|
||||
* The standard function `std::string.copy`.
|
||||
*/
|
||||
private class StdStringCopy extends TaintFunction {
|
||||
StdStringCopy() { this.getClassAndName("copy") instanceof StdBasicString }
|
||||
StdStringCopy() { this.hasQualifiedName("std", "basic_string", "copy") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// copy(dest, num, pos)
|
||||
@@ -243,7 +243,7 @@ private class StdStringCopy extends TaintFunction {
|
||||
* The standard function `std::string.substr`.
|
||||
*/
|
||||
private class StdStringSubstr extends TaintFunction {
|
||||
StdStringSubstr() { this.getClassAndName("substr") instanceof StdBasicString }
|
||||
StdStringSubstr() { this.hasQualifiedName("std", "basic_string", "substr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// substr(pos, num)
|
||||
@@ -256,7 +256,7 @@ private class StdStringSubstr extends TaintFunction {
|
||||
* The `std::string` functions `at` and `operator[]`.
|
||||
*/
|
||||
private class StdStringAt extends TaintFunction {
|
||||
StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString }
|
||||
StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
@@ -270,25 +270,22 @@ private class StdStringAt extends TaintFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_istream` template class instantiations.
|
||||
* The `std::basic_istream` template class.
|
||||
*/
|
||||
private class StdBasicIStream extends ClassTemplateInstantiation {
|
||||
StdBasicIStream() { this.hasQualifiedName(["std", "bsl"], "basic_istream") }
|
||||
private class StdBasicIStream extends TemplateClass {
|
||||
StdBasicIStream() { this.hasQualifiedName("std", "basic_istream") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::istream` function `operator>>` (defined as a member function).
|
||||
*/
|
||||
private class StdIStreamIn extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamIn() { this.getClassAndName("operator>>") instanceof StdBasicIStream }
|
||||
StdIStreamIn() { this.hasQualifiedName("std", "basic_istream", "operator>>") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -307,17 +304,15 @@ private class StdIStreamIn extends DataFlowFunction, TaintFunction {
|
||||
*/
|
||||
private class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamInNonMember() {
|
||||
this.hasQualifiedName(["std", "bsl"], "operator>>") and
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdBasicIStream
|
||||
this.hasQualifiedName("std", "operator>>") and
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
any(StdBasicIStream s).getAnInstantiation()
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from first parameter to return value
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -336,7 +331,7 @@ private class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
|
||||
*/
|
||||
private class StdIStreamGet extends TaintFunction {
|
||||
StdIStreamGet() {
|
||||
this.getClassAndName(["get", "peek"]) instanceof StdBasicIStream and
|
||||
this.hasQualifiedName("std", "basic_istream", ["get", "peek"]) and
|
||||
this.getNumberOfParameters() = 0
|
||||
}
|
||||
|
||||
@@ -352,7 +347,7 @@ private class StdIStreamGet extends TaintFunction {
|
||||
*/
|
||||
private class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamRead() {
|
||||
this.getClassAndName(["get", "read"]) instanceof StdBasicIStream and
|
||||
this.hasQualifiedName("std", "basic_istream", ["get", "read"]) and
|
||||
this.getNumberOfParameters() > 0
|
||||
}
|
||||
|
||||
@@ -360,9 +355,6 @@ private class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -380,7 +372,7 @@ private class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
* The `std::istream` function `readsome`.
|
||||
*/
|
||||
private class StdIStreamReadSome extends TaintFunction {
|
||||
StdIStreamReadSome() { this.getClassAndName("readsome") instanceof StdBasicIStream }
|
||||
StdIStreamReadSome() { this.hasQualifiedName("std", "basic_istream", "readsome") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to first parameter
|
||||
@@ -393,15 +385,12 @@ private class StdIStreamReadSome extends TaintFunction {
|
||||
* The `std::istream` function `putback`.
|
||||
*/
|
||||
private class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamPutBack() { this.getClassAndName("putback") instanceof StdBasicIStream }
|
||||
StdIStreamPutBack() { this.hasQualifiedName("std", "basic_istream", "putback") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -429,15 +418,12 @@ private class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
|
||||
* The `std::istream` function `getline`.
|
||||
*/
|
||||
private class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamGetLine() { this.getClassAndName("getline") instanceof StdBasicIStream }
|
||||
StdIStreamGetLine() { this.hasQualifiedName("std", "basic_istream", "getline") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -455,15 +441,12 @@ private class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
|
||||
* The (non-member) function `std::getline`.
|
||||
*/
|
||||
private class StdGetLine extends DataFlowFunction, TaintFunction {
|
||||
StdGetLine() { this.hasQualifiedName(["std", "bsl"], "getline") }
|
||||
StdGetLine() { this.hasQualifiedName("std", "getline") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from first parameter to return value
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -478,10 +461,10 @@ private class StdGetLine extends DataFlowFunction, TaintFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_ostream` template class instantiations.
|
||||
* The `std::basic_ostream` template class.
|
||||
*/
|
||||
private class StdBasicOStream extends ClassTemplateInstantiation {
|
||||
StdBasicOStream() { this.hasQualifiedName(["std", "bsl"], "basic_ostream") }
|
||||
private class StdBasicOStream extends TemplateClass {
|
||||
StdBasicOStream() { this.hasQualifiedName("std", "basic_ostream") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,17 +472,12 @@ private class StdBasicOStream extends ClassTemplateInstantiation {
|
||||
* `put` and `write`.
|
||||
*/
|
||||
private class StdOStreamOut extends DataFlowFunction, TaintFunction {
|
||||
StdOStreamOut() {
|
||||
this.getClassAndName(["operator<<", "put", "write"]) instanceof StdBasicOStream
|
||||
}
|
||||
StdOStreamOut() { this.hasQualifiedName("std", "basic_ostream", ["operator<<", "put", "write"]) }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -528,17 +506,15 @@ private class StdOStreamOut extends DataFlowFunction, TaintFunction {
|
||||
*/
|
||||
private class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
|
||||
StdOStreamOutNonMember() {
|
||||
this.hasQualifiedName(["std", "bsl"], "operator<<") and
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdBasicOStream
|
||||
this.hasQualifiedName("std", "operator<<") and
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
any(StdBasicOStream s).getAnInstantiation()
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from first parameter to return value
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -556,19 +532,14 @@ private class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_stringstream` template class instantiations.
|
||||
*/
|
||||
private class StdBasicStringStream extends ClassTemplateInstantiation {
|
||||
StdBasicStringStream() { this.hasQualifiedName(["std", "bsl"], "basic_stringstream") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional model for `std::stringstream` constructors that take a string
|
||||
* input parameter.
|
||||
*/
|
||||
private class StdStringStreamConstructor extends Constructor, TaintFunction {
|
||||
StdStringStreamConstructor() { this.getDeclaringType() instanceof StdBasicStringStream }
|
||||
StdStringStreamConstructor() {
|
||||
this.getDeclaringType().hasQualifiedName("std", "basic_stringstream")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string.
|
||||
@@ -592,7 +563,7 @@ private class StdStringStreamConstructor extends Constructor, TaintFunction {
|
||||
* The `std::stringstream` function `str`.
|
||||
*/
|
||||
private class StdStringStreamStr extends TaintFunction {
|
||||
StdStringStreamStr() { this.getClassAndName("str") instanceof StdBasicStringStream }
|
||||
StdStringStreamStr() { this.hasQualifiedName("std", "basic_stringstream", "str") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to return value (if any)
|
||||
@@ -605,33 +576,21 @@ private class StdStringStreamStr extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_ios` template class instantiations.
|
||||
*/
|
||||
private class StdBasicIOS extends ClassTemplateInstantiation {
|
||||
StdBasicIOS() { this.hasQualifiedName(["std", "bsl"], "basic_ios") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `std::` stream function that does not require a model, except that it
|
||||
* returns a reference to `*this` and thus could be used in a chain.
|
||||
*/
|
||||
private class StdStreamFunction extends DataFlowFunction, TaintFunction {
|
||||
StdStreamFunction() {
|
||||
this.getClassAndName(["ignore", "unget", "seekg"]) instanceof StdBasicIStream
|
||||
or
|
||||
this.getClassAndName(["seekp", "flush"]) instanceof StdBasicOStream
|
||||
or
|
||||
this.getClassAndName("copyfmt") instanceof StdBasicIOS
|
||||
this.hasQualifiedName("std", "basic_istream", ["ignore", "unget", "seekg"]) or
|
||||
this.hasQualifiedName("std", "basic_ostream", ["seekp", "flush"]) or
|
||||
this.hasQualifiedName("std", "basic_ios", "copyfmt")
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -13,20 +13,16 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
*/
|
||||
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, SideEffectFunction {
|
||||
StrcatFunction() {
|
||||
this.hasGlobalOrStdOrBslName([
|
||||
getName() =
|
||||
[
|
||||
"strcat", // strcat(dst, src)
|
||||
"strncat", // strncat(dst, src, max_amount)
|
||||
"wcscat", // wcscat(dst, src)
|
||||
"wcsncat" // wcsncat(dst, src, max_amount)
|
||||
])
|
||||
or
|
||||
this.hasGlobalName([
|
||||
"_mbscat", // _mbscat(dst, src)
|
||||
"wcsncat", // wcsncat(dst, src, max_amount)
|
||||
"_mbsncat", // _mbsncat(dst, src, max_amount)
|
||||
"_mbsncat_l", // _mbsncat_l(dst, src, max_amount, locale)
|
||||
"_mbsnbcat", // _mbsnbcat(dest, src, count)
|
||||
"_mbsnbcat_l" // _mbsnbcat_l(dest, src, count, locale)
|
||||
])
|
||||
"_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale)
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +50,7 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid
|
||||
input.isParameter(2) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
getName() = ["_mbsncat_l", "_mbsnbcat_l"] and
|
||||
getName() = "_mbsncat_l" and
|
||||
input.isParameter(3) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
|
||||
@@ -13,36 +13,25 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
*/
|
||||
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction {
|
||||
StrcpyFunction() {
|
||||
this.hasGlobalOrStdOrBslName([
|
||||
getName() =
|
||||
[
|
||||
"strcpy", // strcpy(dst, src)
|
||||
"wcscpy", // wcscpy(dst, src)
|
||||
"strncpy", // strncpy(dst, src, max_amount)
|
||||
"wcsncpy", // wcsncpy(dst, src, max_amount)
|
||||
"strxfrm", // strxfrm(dest, src, max_amount)
|
||||
"wcsxfrm" // wcsxfrm(dest, src, max_amount)
|
||||
])
|
||||
or
|
||||
this.hasGlobalName([
|
||||
"_mbscpy", // _mbscpy(dst, src)
|
||||
"strncpy", // strncpy(dst, src, max_amount)
|
||||
"_strncpy_l", // _strncpy_l(dst, src, max_amount, locale)
|
||||
"wcsncpy", // wcsncpy(dst, src, max_amount)
|
||||
"_wcsncpy_l", // _wcsncpy_l(dst, src, max_amount, locale)
|
||||
"_mbsncpy", // _mbsncpy(dst, src, max_amount)
|
||||
"_mbsncpy_l", // _mbsncpy_l(dst, src, max_amount, locale)
|
||||
"_strxfrm_l", // _strxfrm_l(dest, src, max_amount, locale)
|
||||
"wcsxfrm_l", // _strxfrm_l(dest, src, max_amount, locale)
|
||||
"_mbsnbcpy", // _mbsnbcpy(dest, src, max_amount)
|
||||
"stpcpy", // stpcpy(dest, src)
|
||||
"stpncpy" // stpcpy(dest, src, max_amount)
|
||||
])
|
||||
"_mbsncpy_l"
|
||||
] // _mbsncpy_l(dst, src, max_amount, locale)
|
||||
or
|
||||
(
|
||||
this.hasGlobalOrStdName([
|
||||
"strcpy_s", // strcpy_s(dst, max_amount, src)
|
||||
"wcscpy_s" // wcscpy_s(dst, max_amount, src)
|
||||
])
|
||||
or
|
||||
this.hasGlobalName("_mbscpy_s") // _mbscpy_s(dst, max_amount, src)
|
||||
) and
|
||||
getName() =
|
||||
[
|
||||
"strcpy_s", // strcpy_s(dst, max_amount, src)
|
||||
"wcscpy_s", // wcscpy_s(dst, max_amount, src)
|
||||
"_mbscpy_s"
|
||||
] and // _mbscpy_s(dst, max_amount, src)
|
||||
// exclude the 2-parameter template versions
|
||||
// that find the size of a fixed size destination buffer.
|
||||
getNumberOfParameters() = 3
|
||||
@@ -51,7 +40,9 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
/**
|
||||
* Holds if this is one of the `strcpy_s` variants.
|
||||
*/
|
||||
private predicate isSVariant() { getName().matches("%\\_s") }
|
||||
private predicate isSVariant() {
|
||||
exists(string name | name = getName() | name.suffix(name.length() - 2) = "_s")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the maximum size of the copy (in characters).
|
||||
@@ -59,10 +50,10 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
int getParamSize() {
|
||||
if isSVariant()
|
||||
then result = 1
|
||||
else (
|
||||
getName().matches(["%ncpy%", "%nbcpy%", "%xfrm%"]) and
|
||||
result = 2
|
||||
)
|
||||
else
|
||||
if exists(getName().indexOf("ncpy"))
|
||||
then result = 2
|
||||
else none()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `_strinc`, `_strdec` and their variants.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The function `_strinc`, `_strdec` and their variants.
|
||||
*/
|
||||
private class Strcrement extends ArrayFunction, TaintFunction, SideEffectFunction {
|
||||
Strcrement() {
|
||||
this.hasGlobalName([
|
||||
"_strinc", // _strinc(source, locale)
|
||||
"_wcsinc", // _strinc(source, locale)
|
||||
"_mbsinc", // _strinc(source)
|
||||
"_mbsinc_l", // _strinc(source, locale)
|
||||
"_strdec", // _strdec(start, source)
|
||||
"_wcsdec", // _wcsdec(start, source)
|
||||
"_mbsdec", // _mbsdec(start, source)
|
||||
"_mbsdec_l" // _mbsdec_l(start, source, locale)
|
||||
])
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
// Match all parameters that are not locales.
|
||||
this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { hasArrayWithNullTerminator(bufParam) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(int index | hasArrayInput(index) |
|
||||
input.isParameter(index) and output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(index) and output.isReturnValueDeref()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
hasArrayInput(i) and buffer = true
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `strnextc` and various similar functions.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The function `strnextc` and its variants.
|
||||
*/
|
||||
private class Strnextc extends TaintFunction, ArrayFunction, AliasFunction, SideEffectFunction {
|
||||
Strnextc() { this.hasGlobalName(["_strnextc", "_wcsnextc", "_mbsnextc", "_mbsnextc_l"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = true
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `strset` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `strset` and its assorted variants
|
||||
*/
|
||||
private class StrsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
StrsetFunction() {
|
||||
hasGlobalName([
|
||||
"strset", "_strset", "_strset_l", "_wcsset", "_wcsset_l", "_mbsset", "_mbsset_l",
|
||||
"_mbsnbset", "_mbsnbset_l", "_strnset", "_strnset_l", "_wcsnset", "_wcsnset_l", "_mbsnset",
|
||||
"_mbsnset_l"
|
||||
])
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from the character that overrides the string
|
||||
input.isParameter(1) and
|
||||
(
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
output.isParameterDeref(0)
|
||||
)
|
||||
or
|
||||
// flow from the input string to the output string
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { none() }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = true and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = true
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `strtok` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* The standard function `strtok` and its assorted variants
|
||||
*/
|
||||
private class Strtok extends ArrayFunction, AliasFunction, TaintFunction, SideEffectFunction {
|
||||
Strtok() {
|
||||
this.hasGlobalOrStdOrBslName("strtok") or
|
||||
this.hasGlobalName(["strtok_r", "_strtok_l", "wcstok", "_wcstok_l", "_mbstok", "_mbstok_l"])
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = [0, 1] }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = [0, 1] }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { none() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { none() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = true and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = [0, 1] and buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `strtok` is a variant of `strtok` that takes a `char**` parameter instead of
|
||||
* a `char*` as the first parameter.
|
||||
*/
|
||||
private class Strsep extends ArrayFunction, AliasFunction, TaintFunction, SideEffectFunction {
|
||||
Strsep() { this.hasGlobalName("strsep") }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 1 }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = [0, 1] }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// NOTE: What we really want here is: (input.isParameterDerefDeref(0) or input.isParameterDeref(1))
|
||||
// as the first conjunct.
|
||||
input.isParameterDeref([0, 1]) and
|
||||
(output.isReturnValue() or output.isReturnValueDeref())
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = false and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = false
|
||||
or
|
||||
i = 1 and buffer = true
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import semmle.code.cpp.models.interfaces.Alias
|
||||
* ```
|
||||
*/
|
||||
private class Swap extends DataFlowFunction {
|
||||
Swap() { this.hasQualifiedName(["std", "bsl"], "swap") }
|
||||
Swap() { this.hasQualifiedName("std", "swap") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
|
||||
@@ -24,6 +24,5 @@ abstract class DataFlowFunction extends Function {
|
||||
* represented by `input` to the return value or buffer represented by
|
||||
* `output`
|
||||
*/
|
||||
pragma[nomagic]
|
||||
abstract predicate hasDataFlow(FunctionInput input, FunctionOutput output);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* Provides classes for modeling functions that return data from (or send data to) potentially untrusted
|
||||
* sources. To use this QL library, create a QL class extending `DataFlowFunction` with a
|
||||
* Provides a class for modeling functions that return data from potentially untrusted sources. To use
|
||||
* this QL library, create a QL class extending `DataFlowFunction` with a
|
||||
* characteristic predicate that selects the function or set of functions you
|
||||
* are modeling. Within that class, override the predicates provided by
|
||||
* `RemoteFlowSourceFunction` or `RemoteFlowSinkFunction` to match the flow within that function.
|
||||
* `RemoteFlowFunction` to match the flow within that function.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
@@ -13,42 +13,19 @@ import semmle.code.cpp.models.Models
|
||||
/**
|
||||
* A library function that returns data that may be read from a network connection.
|
||||
*/
|
||||
abstract class RemoteFlowSourceFunction extends Function {
|
||||
abstract class RemoteFlowFunction extends Function {
|
||||
/**
|
||||
* Holds if remote data described by `description` flows from `output` of a call to this function.
|
||||
*/
|
||||
abstract predicate hasRemoteFlowSource(FunctionOutput output, string description);
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RemoteFlowSourceFunction` instead.
|
||||
*
|
||||
* A library function that returns data that may be read from a network connection.
|
||||
*/
|
||||
deprecated class RemoteFlowFunction = RemoteFlowSourceFunction;
|
||||
|
||||
/**
|
||||
* A library function that returns data that is directly controlled by a user.
|
||||
*/
|
||||
abstract class LocalFlowSourceFunction extends Function {
|
||||
abstract class LocalFlowFunction extends Function {
|
||||
/**
|
||||
* Holds if data described by `description` flows from `output` of a call to this function.
|
||||
*/
|
||||
abstract predicate hasLocalFlowSource(FunctionOutput output, string description);
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `LocalFlowSourceFunction` instead.
|
||||
*
|
||||
* A library function that returns data that is directly controlled by a user.
|
||||
*/
|
||||
deprecated class LocalFlowFunction = LocalFlowSourceFunction;
|
||||
|
||||
/** A library function that sends data over a network connection. */
|
||||
abstract class RemoteFlowSinkFunction extends Function {
|
||||
/**
|
||||
* Holds if data described by `description` flows into `input` to a call to this function, and is then
|
||||
* send over a network connection.
|
||||
*/
|
||||
abstract predicate hasRemoteFlowSink(FunctionInput input, string description);
|
||||
}
|
||||
|
||||
@@ -108,16 +108,6 @@ class FunctionInput extends TFunctionInput {
|
||||
*/
|
||||
predicate isQualifierAddress() { none() }
|
||||
|
||||
/**
|
||||
* Holds if `i >= 0` and `isParameter(i)` holds for this value, or
|
||||
* if `i = -1` and `isQualifierAddress()` holds for this value.
|
||||
*/
|
||||
final predicate isParameterOrQualifierAddress(ParameterIndex i) {
|
||||
i >= 0 and this.isParameter(i)
|
||||
or
|
||||
i = -1 and this.isQualifierAddress()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is the input value pointed to by the return value of a
|
||||
* function, if the function returns a pointer, or the input value referred
|
||||
@@ -144,7 +134,7 @@ class FunctionInput extends TFunctionInput {
|
||||
predicate isReturnValueDeref() { none() }
|
||||
|
||||
/**
|
||||
* Holds if `i >= 0` and `isParameterDeref(i)` holds for this value, or
|
||||
* Holds if `i >= 0` and `isParameterDeref(i)` holds for this is value, or
|
||||
* if `i = -1` and `isQualifierObject()` holds for this value.
|
||||
*/
|
||||
final predicate isParameterDerefOrQualifierObject(ParameterIndex i) {
|
||||
|
||||
@@ -28,6 +28,5 @@ abstract class TaintFunction extends Function {
|
||||
* Holds if data passed into the argument, qualifier, or buffer represented by
|
||||
* `input` influences the return value or buffer represented by `output`
|
||||
*/
|
||||
pragma[nomagic]
|
||||
abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ private class RemoteReturnSource extends RemoteFlowSource {
|
||||
string sourceType;
|
||||
|
||||
RemoteReturnSource() {
|
||||
exists(RemoteFlowSourceFunction func, CallInstruction instr, FunctionOutput output |
|
||||
exists(RemoteFlowFunction func, CallInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getStaticCallTarget() = func and
|
||||
func.hasRemoteFlowSource(output, sourceType) and
|
||||
@@ -42,7 +42,7 @@ private class RemoteParameterSource extends RemoteFlowSource {
|
||||
string sourceType;
|
||||
|
||||
RemoteParameterSource() {
|
||||
exists(RemoteFlowSourceFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
|
||||
exists(RemoteFlowFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and
|
||||
func.hasRemoteFlowSource(output, sourceType) and
|
||||
@@ -57,7 +57,7 @@ private class LocalReturnSource extends LocalFlowSource {
|
||||
string sourceType;
|
||||
|
||||
LocalReturnSource() {
|
||||
exists(LocalFlowSourceFunction func, CallInstruction instr, FunctionOutput output |
|
||||
exists(LocalFlowFunction func, CallInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getStaticCallTarget() = func and
|
||||
func.hasLocalFlowSource(output, sourceType) and
|
||||
@@ -76,7 +76,7 @@ private class LocalParameterSource extends LocalFlowSource {
|
||||
string sourceType;
|
||||
|
||||
LocalParameterSource() {
|
||||
exists(LocalFlowSourceFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
|
||||
exists(LocalFlowFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and
|
||||
func.hasLocalFlowSource(output, sourceType) and
|
||||
@@ -98,31 +98,3 @@ private class ArgvSource extends LocalFlowSource {
|
||||
|
||||
override string getSourceType() { result = "a command-line argument" }
|
||||
}
|
||||
|
||||
/** A remote data flow sink. */
|
||||
abstract class RemoteFlowSink extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this flow sink. */
|
||||
abstract string getSinkType();
|
||||
}
|
||||
|
||||
private class RemoteParameterSink extends RemoteFlowSink {
|
||||
string sourceType;
|
||||
|
||||
RemoteParameterSink() {
|
||||
exists(RemoteFlowSinkFunction func, FunctionInput input, CallInstruction call, int index |
|
||||
func.hasRemoteFlowSink(input, sourceType) and call.getStaticCallTarget() = func
|
||||
|
|
||||
exists(ReadSideEffectInstruction read |
|
||||
call = read.getPrimaryInstruction() and
|
||||
read.getIndex() = index and
|
||||
this.asOperand() = read.getSideEffectOperand() and
|
||||
input.isParameterDerefOrQualifierObject(index)
|
||||
)
|
||||
or
|
||||
input.isParameterOrQualifierAddress(index) and
|
||||
this.asOperand() = call.getArgumentOperand(index)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSinkType() { result = sourceType }
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import semmle.code.cpp.exprs.Expr
|
||||
import semmle.code.cpp.commons.Environment
|
||||
import semmle.code.cpp.security.SecurityOptions
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* Extend this class to customize the security queries for
|
||||
@@ -60,14 +59,14 @@ class SecurityOptions extends string {
|
||||
or
|
||||
functionCall.getTarget().hasGlobalName(fname) and
|
||||
exists(functionCall.getArgument(arg)) and
|
||||
fname = "getaddrinfo" and
|
||||
arg = 3
|
||||
)
|
||||
or
|
||||
exists(RemoteFlowSourceFunction remote, FunctionOutput output |
|
||||
functionCall.getTarget() = remote and
|
||||
output.isParameterDerefOrQualifierObject(arg) and
|
||||
remote.hasRemoteFlowSource(output, _)
|
||||
(
|
||||
fname = ["read", "recv", "recvmsg"] and arg = 1
|
||||
or
|
||||
fname = "getaddrinfo" and arg = 3
|
||||
or
|
||||
fname = "recvfrom" and
|
||||
(arg = 1 or arg = 4 or arg = 5)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -82,12 +81,6 @@ class SecurityOptions extends string {
|
||||
userInputReturn(fname)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(RemoteFlowSourceFunction remote, FunctionOutput output |
|
||||
functionCall.getTarget() = remote and
|
||||
(output.isReturnValue() or output.isReturnValueDeref()) and
|
||||
remote.hasRemoteFlowSource(output, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -691,28 +691,8 @@ typedefbase(
|
||||
int type_id: @type ref
|
||||
);
|
||||
|
||||
/**
|
||||
* An instance of the C++11 `decltype` operator. For example:
|
||||
* ```
|
||||
* int a;
|
||||
* decltype(1+a) b;
|
||||
* ```
|
||||
* Here `expr` is `1+a`.
|
||||
*
|
||||
* Sometimes an additional pair of parentheses around the expression
|
||||
* would change the semantics of this decltype, e.g.
|
||||
* ```
|
||||
* struct A { double x; };
|
||||
* const A* a = new A();
|
||||
* decltype( a->x ); // type is double
|
||||
* decltype((a->x)); // type is const double&
|
||||
* ```
|
||||
* (Please consult the C++11 standard for more details).
|
||||
* `parentheses_would_change_meaning` is `true` iff that is the case.
|
||||
*/
|
||||
#keyset[id, expr]
|
||||
decltypes(
|
||||
int id: @decltype,
|
||||
unique int id: @decltype,
|
||||
int expr: @expr ref,
|
||||
int base_type: @type ref,
|
||||
boolean parentheses_would_change_meaning: boolean ref
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# C/C++ CodeQL tests
|
||||
|
||||
This document provides additional information about the C/C++ CodeQL tests located in `cpp/ql/test`. The principles under "Copying code", below, also apply to any other C/C++ code in this repository, such as examples linked from query `.qhelp` files in `cpp/ql/src`. For more general information about contributing to this repository, see [Contributing to CodeQL](/CONTRIBUTING.md).
|
||||
This document provides additional information about the C/C++ CodeQL Tests located in `cpp/ql/test`. See [Contributing to CodeQL](/CONTRIBUTING.md) for general information about contributing to this repository.
|
||||
|
||||
The tests can be run through Visual Studio Code. Advanced users may also use the `codeql test run` command.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user