Compare commits

..

1 Commits

Author SHA1 Message Date
Erik Krogh Kristensen
73e6550fd0 update externs from closure-compiler 2021-01-13 13:54:22 +01:00
1595 changed files with 41186 additions and 153946 deletions

View File

@@ -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

View File

@@ -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

View 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
View File

@@ -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/

View File

@@ -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

View File

@@ -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).

View File

@@ -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"
]
}
}

View File

@@ -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.

View File

@@ -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).

View File

@@ -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

View File

@@ -1,3 +0,0 @@
char password[MAX_PASSWORD_LENGTH];
// read and verify password
memset(password, 0, MAX_PASSWORD_LENGTH);

View File

@@ -1,3 +0,0 @@
char password[MAX_PASSWORD_LENGTH];
// read and verify password
memset_s(password, MAX_PASSWORD_LENGTH, 0, MAX_PASSWORD_LENGTH);

View File

@@ -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>

View File

@@ -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."

View File

@@ -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(_, _)
)

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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."

View File

@@ -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"
]
)
}

View File

@@ -8,7 +8,6 @@
* @tags reliability
* security
* external/cwe/cwe-242
* external/cwe/cwe-676
*/
import cpp

View File

@@ -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)

View File

@@ -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>

View File

@@ -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"

View File

@@ -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."

View File

@@ -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"

View File

@@ -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);
// ..
}

View File

@@ -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 &amp;)</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>

View File

@@ -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"

View File

@@ -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

View File

@@ -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>

View File

@@ -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."

View File

@@ -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.

View File

@@ -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>

View File

@@ -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"

View File

@@ -50,5 +50,5 @@ class CStyleComment extends Comment {
* ```
*/
class CppStyleComment extends Comment {
CppStyleComment() { this.getContents().matches("//%") }
CppStyleComment() { this.getContents().prefix(2) = "//" }
}

View File

@@ -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

View File

@@ -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() + "\")" }

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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() }

View File

@@ -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 {
/**

View File

@@ -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&amp;` to `const C&amp;`.
* 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
* ```
*/

View File

@@ -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(

View File

@@ -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 }
}
/**

View File

@@ -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"
)
}

View File

@@ -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 |

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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) }
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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(), ", ")
}

View File

@@ -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

View File

@@ -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() }
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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) {
(

View File

@@ -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
}

View File

@@ -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()
}
}

View File

@@ -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) {

View File

@@ -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"] }

View File

@@ -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"] }

View File

@@ -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() }
}

View File

@@ -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())
}

View File

@@ -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

View File

@@ -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()
}
}

View File

@@ -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() }
}

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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()
]
}
}

View File

@@ -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()
}
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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

View File

@@ -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()
}
/**

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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 }
}

View File

@@ -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, _)
)
}
/**

View File

@@ -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

View File

@@ -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