mirror of
https://github.com/github/codeql.git
synced 2026-05-30 02:51:24 +02:00
Compare commits
304 Commits
query-help
...
revert-465
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d994c9b08 | ||
|
|
a94f244659 | ||
|
|
d3cded330e | ||
|
|
5e75a4109c | ||
|
|
7b4e890e7b | ||
|
|
8069e7b031 | ||
|
|
07c989deb1 | ||
|
|
5a808190d4 | ||
|
|
b11fc2f957 | ||
|
|
c751c516bf | ||
|
|
a48235e871 | ||
|
|
edb57c2da0 | ||
|
|
bc08e47a4e | ||
|
|
548f276e1f | ||
|
|
144e9e6271 | ||
|
|
ad4b2beafa | ||
|
|
301f49a9d9 | ||
|
|
2234d665ce | ||
|
|
9bb949a8b1 | ||
|
|
c077ca3fc9 | ||
|
|
f9e78085ac | ||
|
|
edb41655b4 | ||
|
|
e2419e8fed | ||
|
|
27aeb53f1e | ||
|
|
864fce43bd | ||
|
|
47ca4b0f3b | ||
|
|
55d47a70f4 | ||
|
|
252692e5c1 | ||
|
|
a4c060a4c6 | ||
|
|
cb91dc1308 | ||
|
|
f6c3c2bdcc | ||
|
|
b02ac7f523 | ||
|
|
5d80417854 | ||
|
|
18a757445d | ||
|
|
a931c59a28 | ||
|
|
89a4cff5f8 | ||
|
|
ba32459adf | ||
|
|
5f4ad3ad7d | ||
|
|
3bd6807681 | ||
|
|
f70072a2db | ||
|
|
d29a6ec4c6 | ||
|
|
6fecc38004 | ||
|
|
9b8d9771f8 | ||
|
|
c595baf1e3 | ||
|
|
bc71c72084 | ||
|
|
7730f5dfcf | ||
|
|
3bfb398516 | ||
|
|
acf6c218bc | ||
|
|
65dbb6e45f | ||
|
|
7eec988fb5 | ||
|
|
dcc048139e | ||
|
|
0b8403fc05 | ||
|
|
b4e45ad6cb | ||
|
|
4c3a26fea8 | ||
|
|
19883302af | ||
|
|
aa45920f31 | ||
|
|
34ffcb5677 | ||
|
|
6d9510591a | ||
|
|
81b03bf876 | ||
|
|
7791ec3084 | ||
|
|
7f13d4c356 | ||
|
|
8d024c7ff1 | ||
|
|
e68352bcde | ||
|
|
cafe55f5c7 | ||
|
|
5f0141953d | ||
|
|
48460e3e45 | ||
|
|
88643da01f | ||
|
|
36d9097d03 | ||
|
|
34896ae0d7 | ||
|
|
9b07782d19 | ||
|
|
395403789e | ||
|
|
71a8ac5183 | ||
|
|
9c30b82116 | ||
|
|
4be158b362 | ||
|
|
8184f76d1f | ||
|
|
648acc3bfc | ||
|
|
026abae323 | ||
|
|
aa4345ac76 | ||
|
|
215986bce5 | ||
|
|
05d156ba0f | ||
|
|
8c68463e76 | ||
|
|
3f04099c25 | ||
|
|
260a8d4afb | ||
|
|
2277242196 | ||
|
|
bc23809e1b | ||
|
|
7983b16e84 | ||
|
|
0450489022 | ||
|
|
9d21b226d2 | ||
|
|
14a03e2f54 | ||
|
|
08f8660b17 | ||
|
|
8bd14c5af6 | ||
|
|
f03429a4b8 | ||
|
|
9123f249ad | ||
|
|
33dab1717e | ||
|
|
39c5e0d487 | ||
|
|
38bb06a207 | ||
|
|
129deb0a00 | ||
|
|
cd77f14a75 | ||
|
|
7015a9cf53 | ||
|
|
2cf10a7658 | ||
|
|
f7f9beeefd | ||
|
|
02d5fbf46b | ||
|
|
9156163563 | ||
|
|
f0f5d44b33 | ||
|
|
17df059432 | ||
|
|
f3abaa406c | ||
|
|
234730419b | ||
|
|
7d38b2dd17 | ||
|
|
777100f25c | ||
|
|
c571e42cd5 | ||
|
|
a7644db762 | ||
|
|
61bbceb201 | ||
|
|
f173dc71c0 | ||
|
|
cc8d4b4c75 | ||
|
|
fddd353155 | ||
|
|
a2a4938f60 | ||
|
|
adc7bbfa4d | ||
|
|
f894cf2074 | ||
|
|
6ead6c6d38 | ||
|
|
0fa3cf7912 | ||
|
|
16429c8ca4 | ||
|
|
fd4f8c557c | ||
|
|
14aa6427ca | ||
|
|
3e836ef671 | ||
|
|
77afd5a617 | ||
|
|
c7efc91676 | ||
|
|
f3b5d7b830 | ||
|
|
416431a7c1 | ||
|
|
3342fac83e | ||
|
|
7536c49c6f | ||
|
|
52680cd1dc | ||
|
|
405f07720a | ||
|
|
b34df9ff33 | ||
|
|
1afd32c033 | ||
|
|
b478a51d4e | ||
|
|
eeece5937d | ||
|
|
056b0c2627 | ||
|
|
490bba5c9f | ||
|
|
7cbbf3bbf7 | ||
|
|
7e407d43d2 | ||
|
|
8ffcff0824 | ||
|
|
f737f34dcd | ||
|
|
2e6a3cd33b | ||
|
|
cd20163f6e | ||
|
|
6cc8e5acf1 | ||
|
|
072adaa279 | ||
|
|
39f134c1c1 | ||
|
|
a3b21ad43b | ||
|
|
02ec325380 | ||
|
|
866c98bbd9 | ||
|
|
2945eada9e | ||
|
|
08efd7fbd9 | ||
|
|
ebc6c49555 | ||
|
|
cc1d797cef | ||
|
|
5753a2d401 | ||
|
|
693626e460 | ||
|
|
4405547b04 | ||
|
|
cfe86bf53b | ||
|
|
f70f32e3ed | ||
|
|
a86679a377 | ||
|
|
429aa15b1b | ||
|
|
cb8c5e8cca | ||
|
|
58c31f0eca | ||
|
|
c4153a617e | ||
|
|
4746320f3c | ||
|
|
715f233360 | ||
|
|
decbb08340 | ||
|
|
959b8f39d2 | ||
|
|
8270bf5bb9 | ||
|
|
47fa2d31cc | ||
|
|
04fc1e44f2 | ||
|
|
8c9ea1632f | ||
|
|
b670522a9f | ||
|
|
09c5caa3bd | ||
|
|
55f2f86a26 | ||
|
|
f16591dffc | ||
|
|
ab715ec302 | ||
|
|
9a65962912 | ||
|
|
a92f7a4563 | ||
|
|
5c3de06b6d | ||
|
|
b9b6a35564 | ||
|
|
c4d7533701 | ||
|
|
97acf1fd87 | ||
|
|
52bbb326ca | ||
|
|
d93d3c8699 | ||
|
|
d1272d3a79 | ||
|
|
4cb25d8e18 | ||
|
|
dea16d4d62 | ||
|
|
eabc69b98e | ||
|
|
62767e7e0d | ||
|
|
5d2b85fcf5 | ||
|
|
7f0ad2d232 | ||
|
|
c37093f4bc | ||
|
|
3b8580efaf | ||
|
|
c3c29b8dd0 | ||
|
|
5c9b8f1cff | ||
|
|
5ad18eb748 | ||
|
|
55a38803cb | ||
|
|
f2259de5f1 | ||
|
|
71830abda0 | ||
|
|
057bb14eee | ||
|
|
a94826dc81 | ||
|
|
31d3e94cec | ||
|
|
74e05c111e | ||
|
|
27b4c67b9f | ||
|
|
8bef5f417e | ||
|
|
4a7f9100e4 | ||
|
|
27aab4062a | ||
|
|
088d5863fc | ||
|
|
10a9f7ba13 | ||
|
|
4be731d2ab | ||
|
|
80ee92ae97 | ||
|
|
020af1c88c | ||
|
|
4b8f338139 | ||
|
|
45a3024440 | ||
|
|
8262435d4b | ||
|
|
a4a47bf88d | ||
|
|
a49b99b18c | ||
|
|
affb11b0e3 | ||
|
|
2f4fcc2f5e | ||
|
|
52d6503fe0 | ||
|
|
0a6a22562b | ||
|
|
dfcb0ae7c2 | ||
|
|
402a320a55 | ||
|
|
4b25532b9f | ||
|
|
b249777bfb | ||
|
|
708fca4a2f | ||
|
|
eb6d6113d9 | ||
|
|
94deed39a2 | ||
|
|
202f7f07ec | ||
|
|
f8de94e906 | ||
|
|
2525cfd786 | ||
|
|
0804df42d1 | ||
|
|
5e1f36e7ff | ||
|
|
f417831a23 | ||
|
|
896dee5fb7 | ||
|
|
f2317aed55 | ||
|
|
dee1690748 | ||
|
|
3f150bb09b | ||
|
|
ce1082dc55 | ||
|
|
34531d559a | ||
|
|
12c6009895 | ||
|
|
3d9efa1dc2 | ||
|
|
a23b351201 | ||
|
|
ad0f99eb1b | ||
|
|
164065377f | ||
|
|
132d9814e6 | ||
|
|
8878248ff7 | ||
|
|
bab6d04139 | ||
|
|
28c7c42e34 | ||
|
|
c300ccf300 | ||
|
|
896d46469b | ||
|
|
00f067be84 | ||
|
|
50e90510ef | ||
|
|
4c27c3757c | ||
|
|
44e38ab6d2 | ||
|
|
e1ef41bd38 | ||
|
|
de0ca0aa74 | ||
|
|
5359e13421 | ||
|
|
30d8dce389 | ||
|
|
c3bc0d6c15 | ||
|
|
5b1858a514 | ||
|
|
14f1fa50f1 | ||
|
|
2c7a01952e | ||
|
|
c635166079 | ||
|
|
a330cae2f9 | ||
|
|
4bcf1f498b | ||
|
|
02cf49a773 | ||
|
|
24a47fbb0f | ||
|
|
3545edb92c | ||
|
|
c00587d2cb | ||
|
|
38de9b6433 | ||
|
|
3ef5d89e39 | ||
|
|
17a687b344 | ||
|
|
34fd0d89f5 | ||
|
|
ac514b1739 | ||
|
|
5f199e8b1a | ||
|
|
d038e9c658 | ||
|
|
a5e75f53ff | ||
|
|
0063cb140c | ||
|
|
2dd8b6ffef | ||
|
|
68fe03060d | ||
|
|
fa54ad1a5e | ||
|
|
a09ffd5cda | ||
|
|
4ede04f4d1 | ||
|
|
82252c0f1c | ||
|
|
62a8427d37 | ||
|
|
74a4f5887b | ||
|
|
e065466180 | ||
|
|
9db340c9ca | ||
|
|
16473fc2a4 | ||
|
|
804aaf36f0 | ||
|
|
64d680e2d3 | ||
|
|
321cf09bd8 | ||
|
|
d04f3df1cd | ||
|
|
c9f846e0d2 | ||
|
|
b5326b3937 | ||
|
|
7f54379a0c | ||
|
|
0e4d69709f | ||
|
|
f917cf826f | ||
|
|
7e8770d731 | ||
|
|
5dfe04a7a7 | ||
|
|
7d7b0eaa7b | ||
|
|
1a365d2098 |
59
.github/workflows/generate-query-help-docs.yml
vendored
59
.github/workflows/generate-query-help-docs.yml
vendored
@@ -1,59 +0,0 @@
|
||||
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:
|
||||
- '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@master
|
||||
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
|
||||
|
||||
@@ -358,6 +358,14 @@
|
||||
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"python/ql/test/TestUtilities/InlineExpectationsTest.qll"
|
||||
],
|
||||
"C++ ExternalAPIs": [
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"
|
||||
],
|
||||
"C++ SafeExternalAPIFunction": [
|
||||
"cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/SafeExternalAPIFunction.qll"
|
||||
],
|
||||
"XML": [
|
||||
"cpp/ql/src/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/XML.qll",
|
||||
|
||||
3
cpp/change-notes/2020-11-05-private-models.md
Normal file
3
cpp/change-notes/2020-11-05-private-models.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* Various classes in `semmle.code.cpp.models.implementations` have been made private. Users should not depend on library implementation details.
|
||||
* The `OperatorNewAllocationFunction`, `OperatorDeleteDeallocationFunction`, `Iterator` and `Snprintf` classes now have interfaces in `semmle.code.cpp.models.interfaces`.
|
||||
2
cpp/change-notes/2020-11-12-unsafe-use-of-this.md
Normal file
2
cpp/change-notes/2020-11-12-unsafe-use-of-this.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* A new query (`cpp/unsafe-use-of-this`) has been added. The query finds pure virtual function calls whose qualifier is an object under construction.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The queries `cpp/local-variable-hides-global-variable` and `cpp/missing-header-guard` now have severity `recommendation` instead of `warning`.
|
||||
@@ -9,6 +9,7 @@
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql: /Correctness/Dangerous Conversions
|
||||
+ 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
|
||||
# Consistent Use
|
||||
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
|
||||
+ semmlecode-cpp-queries/Likely Bugs/InconsistentCheckReturnNull.ql: /Correctness/Consistent Use
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Local variable hides global variable
|
||||
* @description A local variable or parameter that hides a global variable of the same name. This may be confusing. Consider renaming one of the variables.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @precision very-high
|
||||
* @id cpp/local-variable-hides-global-variable
|
||||
* @tags maintainability
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.models.implementations.Allocation
|
||||
import semmle.code.cpp.models.implementations.Deallocation
|
||||
|
||||
/**
|
||||
* Holds if `alloc` is a use of `malloc` or `new`. `kind` is
|
||||
|
||||
20
cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.cpp
Normal file
20
cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
class Base {
|
||||
private:
|
||||
// pure virtual member function used for initialization of derived classes.
|
||||
virtual void construct() = 0;
|
||||
public:
|
||||
Base() {
|
||||
// wrong: the virtual table of `Derived` has not been initialized yet. So this
|
||||
// call will resolve to `Base::construct`, which cannot be called as it is a pure
|
||||
// virtual function.
|
||||
construct();
|
||||
}
|
||||
};
|
||||
|
||||
class Derived : public Base {
|
||||
int field;
|
||||
|
||||
void construct() override {
|
||||
field = 1;
|
||||
}
|
||||
};
|
||||
30
cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.qhelp
Normal file
30
cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.qhelp
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds calls to pure virtual member functions in constructors and destructors. When executing the body of a constructor of class <code>T</code>, the virtual table of <code>T</code> refers to the virtual table of one of <code>T</code>'s base classes. This can produce unexpected behavior, including program abort that can lead to denial of service attacks. The same problem exists during destruction of an object.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Do not rely on virtual dispatch in constructors and destructors. Instead, each class should be responsible for acquiring and releasing its resources. If a base class needs to refer to a derived class during initialization, use the Dynamic Binding During Initialization idiom.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="UnsafeUseOfThis.cpp" />
|
||||
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>ISO C++ FAQ: <a href="https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors">When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked?</a>
|
||||
</li>
|
||||
<li>SEI CERT C++ Coding Standard <a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP50-CPP.+Do+not+invoke+virtual+functions+from+constructors+or+destructors">OOP50-CPP. Do not invoke virtual functions from constructors or destructors</a>
|
||||
</li>
|
||||
<li>ISO C++ FAQ: <a href="https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom">Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references></qhelp>
|
||||
212
cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
Normal file
212
cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
Normal file
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* @name Unsafe use of this in constructor
|
||||
* @description A call to a pure virtual function using a 'this'
|
||||
* pointer of an object that is under construction
|
||||
* may lead to undefined behavior.
|
||||
* @kind path-problem
|
||||
* @id cpp/unsafe-use-of-this
|
||||
* @problem.severity error
|
||||
* @precision very-high
|
||||
* @tags correctness
|
||||
* language-features
|
||||
* security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
// We don't actually use the global value numbering library in this query, but without it we end up
|
||||
// recomputing the IR.
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
bindingset[n, result]
|
||||
int unbind(int n) { result >= n and result <= n }
|
||||
|
||||
/** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */
|
||||
predicate parameterOf(Parameter p, Function f, int n) {
|
||||
not f.isVirtual() and f.getParameter(n) = p
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is the `n`'th argument to a call to the non-virtual function `f`, and
|
||||
* `init` is the corresponding initiazation instruction that receives the value of `instr` in `f`.
|
||||
*/
|
||||
predicate flowIntoParameter(
|
||||
CallInstruction call, Instruction instr, Function f, int n, InitializeParameterInstruction init
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
call.getPositionalArgument(n) = instr and
|
||||
f = call.getStaticCallTarget() and
|
||||
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
init.getParameter().getIndex() = unbind(n)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an argument to a call to the function `f`, and `init` is the
|
||||
* corresponding initialization instruction that receives the value of `instr` in `f`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate getPositionalArgumentInitParam(
|
||||
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
exists(int n |
|
||||
parameterOf(_, f, n) and
|
||||
flowIntoParameter(call, instr, f, unbind(n), init)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is the qualifier to a call to the non-virtual function `f`, and
|
||||
* `init` is the corresponding initiazation instruction that receives the value of
|
||||
* `instr` in `f`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate getThisArgumentInitParam(
|
||||
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
call.getStaticCallTarget() = f and
|
||||
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
call.getThisArgument() = instr and
|
||||
init.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
|
||||
/** Holds if `instr` is a `this` pointer used by the call instruction `call`. */
|
||||
predicate isSink(Instruction instr, CallInstruction call) {
|
||||
exists(PureVirtualFunction func |
|
||||
call.getStaticCallTarget() = func and
|
||||
call.getThisArgument() = instr and
|
||||
// Weed out implicit calls to destructors of a base class
|
||||
not func instanceof Destructor
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `init` initializes the `this` pointer in class `c`. */
|
||||
predicate isSource(InitializeParameterInstruction init, string msg, Class c) {
|
||||
(
|
||||
exists(Constructor func |
|
||||
not func instanceof CopyConstructor and
|
||||
not func instanceof MoveConstructor and
|
||||
func = init.getEnclosingFunction() and
|
||||
msg = "construction"
|
||||
)
|
||||
or
|
||||
init.getEnclosingFunction() instanceof Destructor and msg = "destruction"
|
||||
) and
|
||||
init.getIRVariable() instanceof IRThisVariable and
|
||||
init.getEnclosingFunction().getDeclaringType() = c
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` flows to a sink (which is a use of the value of `instr` as a `this` pointer).
|
||||
*/
|
||||
predicate flowsToSink(Instruction instr, Instruction sink) {
|
||||
flowsFromSource(instr) and
|
||||
(
|
||||
isSink(instr, _) and instr = sink
|
||||
or
|
||||
exists(Instruction mid |
|
||||
successor(instr, mid) and
|
||||
flowsToSink(mid, sink)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `instr` flows from a source. */
|
||||
predicate flowsFromSource(Instruction instr) {
|
||||
isSource(instr, _, _)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
successor(mid, instr) and
|
||||
flowsFromSource(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `f` is the enclosing non-virtual function of `init`. */
|
||||
predicate getEnclosingNonVirtualFunctionInitializeParameter(
|
||||
InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
init.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
/** Holds if `f` is the enclosing non-virtual function of `init`. */
|
||||
predicate getEnclosingNonVirtualFunctionInitializeIndirection(
|
||||
InitializeIndirectionInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
init.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an argument (or argument indirection) to a call, and
|
||||
* `succ` is the corresponding initialization instruction in the call target.
|
||||
*/
|
||||
predicate flowThroughCallable(Instruction instr, Instruction succ) {
|
||||
// Flow from an argument to a parameter
|
||||
exists(CallInstruction call, InitializeParameterInstruction init | init = succ |
|
||||
getPositionalArgumentInitParam(call, instr, init, call.getStaticCallTarget())
|
||||
or
|
||||
getThisArgumentInitParam(call, instr, init, call.getStaticCallTarget())
|
||||
)
|
||||
or
|
||||
// Flow from argument indirection to parameter indirection
|
||||
exists(
|
||||
CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init
|
||||
|
|
||||
init = succ and
|
||||
read.getPrimaryInstruction() = call and
|
||||
getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
||||
|
|
||||
exists(int n |
|
||||
read.getSideEffectOperand().getAnyDef() = instr and
|
||||
read.getIndex() = n and
|
||||
init.getParameter().getIndex() = unbind(n)
|
||||
)
|
||||
or
|
||||
call.getThisArgument() = instr and
|
||||
init.getIRVariable() instanceof IRThisVariable
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `instr` flows to `succ`. */
|
||||
predicate successor(Instruction instr, Instruction succ) {
|
||||
succ.(CopyInstruction).getSourceValue() = instr or
|
||||
succ.(CheckedConvertOrNullInstruction).getUnary() = instr or
|
||||
succ.(ChiInstruction).getTotal() = instr or
|
||||
succ.(ConvertInstruction).getUnary() = instr or
|
||||
succ.(InheritanceConversionInstruction).getUnary() = instr or
|
||||
flowThroughCallable(instr, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if:
|
||||
* - `source` is an initialization of a `this` pointer of type `sourceClass`, and
|
||||
* - `sink` is a use of the `this` pointer, and
|
||||
* - `call` invokes a pure virtual function using `sink` as the `this` pointer, and
|
||||
* - `msg` is a string describing whether `source` is from a constructor or destructor.
|
||||
*/
|
||||
predicate flows(
|
||||
Instruction source, string msg, Class sourceClass, Instruction sink, CallInstruction call
|
||||
) {
|
||||
isSource(source, msg, sourceClass) and
|
||||
flowsToSink(source, sink) and
|
||||
isSink(sink, call)
|
||||
}
|
||||
|
||||
query predicate edges(Instruction a, Instruction b) { successor(a, b) and flowsToSink(b, _) }
|
||||
|
||||
query predicate nodes(Instruction n, string key, string val) {
|
||||
flowsToSink(n, _) and
|
||||
key = "semmle.label" and
|
||||
val = n.toString()
|
||||
}
|
||||
|
||||
from Instruction source, Instruction sink, CallInstruction call, string msg, Class sourceClass
|
||||
where
|
||||
flows(source, msg, sourceClass, sink, call) and
|
||||
// Only raise an alert if there is no override of the pure virtual function in any base class.
|
||||
not exists(Class c | c = sourceClass.getABaseClass*() |
|
||||
c.getAMemberFunction().getAnOverriddenFunction() = call.getStaticCallTarget()
|
||||
)
|
||||
select call.getUnconvertedResultExpression(), source, sink,
|
||||
"Call to pure virtual function during " + msg
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
|
||||
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
|
||||
may be relevant for security analysis of this application.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not
|
||||
modeled as a taint step in the default taint library. External APIs may be from the C++ standard library,
|
||||
third party dependencies or from internal dependencies. The query will report the function name, along with
|
||||
either <code>[param x]</code>, where <code>x</code> indicates the position of the parameter receiving the
|
||||
untrusted data or <code>[qualifier]</code> indicating the untrusted data is used as the qualifier to the
|
||||
function call.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, no action is required.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
|
||||
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>If the query were to return the API <code>fputs [param 1]</code>
|
||||
then we should first consider whether this a security relevant sink. In this case, this is writing to a <code>FILE*</code>, so we should
|
||||
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
|
||||
|
||||
<p>If the query were to return the API <code>strcat [param 1]</code>, then this should be
|
||||
reviewed as a possible taint step, because tainted data would flow from the 1st argument to the 0th argument of the call.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Frequency counts for external APIs that are used with untrusted data
|
||||
* @description This reports the external APIs that are used with untrusted data, along with how
|
||||
* frequently the API is called, and how many unique sources of untrusted data flow
|
||||
* to it.
|
||||
* @id cpp/count-untrusted-data-external-api
|
||||
* @kind table
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import ExternalAPIs
|
||||
|
||||
from ExternalAPIUsedWithUntrustedData externalAPI
|
||||
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
|
||||
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
|
||||
numberOfUntrustedSources desc
|
||||
13
cpp/ql/src/Security/CWE/CWE-020/ExternalAPISinkExample.cpp
Normal file
13
cpp/ql/src/Security/CWE/CWE-020/ExternalAPISinkExample.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <cstdio>
|
||||
|
||||
void do_get(FILE* request, FILE* response) {
|
||||
char page[1024];
|
||||
fgets(page, 1024, request);
|
||||
|
||||
char buffer[1024];
|
||||
strcat(buffer, "The page \"");
|
||||
strcat(buffer, page);
|
||||
strcat(buffer, "\" was not found.");
|
||||
|
||||
fputs(buffer, response);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <cstdio>
|
||||
|
||||
void do_get(FILE* request, FILE* response) {
|
||||
char user_id[1024];
|
||||
fgets(user_id, 1024, request);
|
||||
|
||||
char buffer[1024];
|
||||
strcat(buffer, "SELECT * FROM user WHERE user_id='");
|
||||
strcat(buffer, user_id);
|
||||
strcat(buffer, "'");
|
||||
|
||||
// ...
|
||||
}
|
||||
50
cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll
Normal file
50
cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Definitions for reasoning about untrusted data used in APIs defined outside the
|
||||
* database.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
import ExternalAPIsSpecific
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
|
||||
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() {
|
||||
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TExternalAPI =
|
||||
TExternalAPIParameter(Function f, int index) {
|
||||
exists(UntrustedExternalAPIDataNode n |
|
||||
f = n.getExternalFunction() and
|
||||
index = n.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
||||
/** Gets a possibly untrusted use of this external API. */
|
||||
UntrustedExternalAPIDataNode getUntrustedDataNode() {
|
||||
this = TExternalAPIParameter(result.getExternalFunction(), result.getIndex())
|
||||
}
|
||||
|
||||
/** Gets the number of untrusted sources used with this external API. */
|
||||
int getNumberOfUntrustedSources() {
|
||||
result = strictcount(getUntrustedDataNode().getAnUntrustedSource())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Function f, int index, string indexString |
|
||||
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
||||
|
|
||||
this = TExternalAPIParameter(f, index) and
|
||||
result = f.toString() + " [" + indexString + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
56
cpp/ql/src/Security/CWE/CWE-020/ExternalAPIsSpecific.qll
Normal file
56
cpp/ql/src/Security/CWE/CWE-020/ExternalAPIsSpecific.qll
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Provides AST-specific definitions for use in the `ExternalAPI` library.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import SafeExternalAPIFunction
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class ExternalAPIDataNode extends DataFlow::Node {
|
||||
Call call;
|
||||
int i;
|
||||
|
||||
ExternalAPIDataNode() {
|
||||
// Argument to call to a function
|
||||
(
|
||||
this.asExpr() = call.getArgument(i)
|
||||
or
|
||||
i = -1 and this.asExpr() = call.getQualifier()
|
||||
) and
|
||||
exists(Function f |
|
||||
f = call.getTarget() and
|
||||
// Defined outside the source archive
|
||||
not f.hasDefinition() and
|
||||
// Not already modeled as a dataflow or taint step
|
||||
not f instanceof DataFlowFunction and
|
||||
not f instanceof TaintFunction and
|
||||
// Not a call to a known safe external API
|
||||
not f instanceof SafeExternalAPIFunction
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the called API `Function`. */
|
||||
Function getExternalFunction() { result = call.getTarget() }
|
||||
|
||||
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
|
||||
int getIndex() { result = i }
|
||||
|
||||
/** Gets the description of the function being called. */
|
||||
string getFunctionDescription() { result = getExternalFunction().toString() }
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
|
||||
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(RemoteFlowFunction remoteFlow |
|
||||
remoteFlow = source.asExpr().(Call).getTarget() and
|
||||
remoteFlow.hasRemoteFlowSource(_, _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
|
||||
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
|
||||
may be relevant for security analysis of this application.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not
|
||||
modeled as a taint step in the default taint library. External APIs may be from the C++ standard library,
|
||||
third party dependencies or from internal dependencies. The query will report the function name, along with
|
||||
either <code>[param x]</code>, where <code>x</code> indicates the position of the parameter receiving the
|
||||
untrusted data or <code>[qualifier]</code> indicating the untrusted data is used as the qualifier to the
|
||||
function call.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, no action is required.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
|
||||
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>If the query were to return the API <code>fputs [param 1]</code>
|
||||
then we should first consider whether this a security relevant sink. In this case, this is writing to a <code>FILE*</code>, so we should
|
||||
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
|
||||
|
||||
<p>If the query were to return the API <code>strcat [param 1]</code>, then this should be
|
||||
reviewed as a possible taint step, because tainted data would flow from the 1st argument to the 0th argument of the call.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Frequency counts for external APIs that are used with untrusted data
|
||||
* @description This reports the external APIs that are used with untrusted data, along with how
|
||||
* frequently the API is called, and how many unique sources of untrusted data flow
|
||||
* to it.
|
||||
* @id cpp/count-untrusted-data-external-api-ir
|
||||
* @kind table
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import ir.ExternalAPIs
|
||||
|
||||
from ExternalAPIUsedWithUntrustedData externalAPI
|
||||
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
|
||||
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
|
||||
numberOfUntrustedSources desc
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
external APIs that use untrusted data. The results are not filtered, so you can audit all examples.
|
||||
The query provides data for security reviews of the application and you can also use it to identify external APIs
|
||||
that should be modeled as either taint steps, or sinks for specific problems.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not modeled
|
||||
as a taint step in the default taint library. External APIs may be from the
|
||||
C++ standard library, third-party dependencies or from internal dependencies. The query reports uses of
|
||||
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
|
||||
that the result is a false positive because this data is sanitized.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
|
||||
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
|
||||
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this first example, input is read from <code>fgets</code> and then ultimately used in a call to the
|
||||
<code>fputs</code> external API:</p>
|
||||
|
||||
<sample src="ExternalAPISinkExample.cpp" />
|
||||
|
||||
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
|
||||
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
|
||||
some existing sanitization.</p>
|
||||
|
||||
<p>In this second example, again a request parameter is read from <code>fgets</code>.</p>
|
||||
|
||||
<sample src="ExternalAPITaintStepExample.cpp" />
|
||||
|
||||
<p>If the query reported the call to <code>strcat</code> on line 9, this would suggest that this external API is
|
||||
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
|
||||
re-run the query to determine what additional results might be found. In this example, it seems likely that <code>buffer</code>
|
||||
will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Untrusted data passed to external API
|
||||
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
|
||||
* @id cpp/untrusted-data-to-external-api-ir
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import ir.ExternalAPIs
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Call to " + sink.getNode().(ExternalAPIDataNode).getExternalFunction().toString() +
|
||||
" with untrusted data from $@.", source, source.getNode().(RemoteFlowSource).getSourceType()
|
||||
24
cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll
Normal file
24
cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Provides a class for modeling external functions that are "safe" from a security perspective.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* A `Function` that is considered a "safe" external API from a security perspective.
|
||||
*/
|
||||
abstract class SafeExternalAPIFunction extends Function { }
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalAPIFunction extends SafeExternalAPIFunction {
|
||||
DefaultSafeExternalAPIFunction() {
|
||||
// If a function does not write to any of its arguments, we consider it safe to
|
||||
// pass untrusted data to it. This means that string functions such as `strcmp`
|
||||
// and `strlen`, as well as memory functions such as `memcmp`, are considered safe.
|
||||
exists(SideEffectFunction model | model = this |
|
||||
model.hasOnlySpecificWriteSideEffects() and
|
||||
not model.hasSpecificWriteSideEffect(_, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
external APIs that use untrusted data. The results are not filtered, so you can audit all examples.
|
||||
The query provides data for security reviews of the application and you can also use it to identify external APIs
|
||||
that should be modeled as either taint steps, or sinks for specific problems.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not modeled
|
||||
as a taint step in the default taint library. External APIs may be from the
|
||||
C++ standard library, third-party dependencies or from internal dependencies. The query reports uses of
|
||||
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
|
||||
that the result is a false positive because this data is sanitized.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
|
||||
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
|
||||
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this first example, input is read from <code>fgets</code> and then ultimately used in a call to the
|
||||
<code>fputs</code> external API:</p>
|
||||
|
||||
<sample src="ExternalAPISinkExample.cpp" />
|
||||
|
||||
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
|
||||
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
|
||||
some existing sanitization.</p>
|
||||
|
||||
<p>In this second example, again a request parameter is read from <code>fgets</code>.</p>
|
||||
|
||||
<sample src="ExternalAPITaintStepExample.cpp" />
|
||||
|
||||
<p>If the query reported the call to <code>strcat</code> on line 9, this would suggest that this external API is
|
||||
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
|
||||
re-run the query to determine what additional results might be found. In this example, it seems likely that <code>buffer</code>
|
||||
will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Untrusted data passed to external API
|
||||
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
|
||||
* @id cpp/untrusted-data-to-external-api
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import ExternalAPIs
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Call to " + sink.getNode().(ExternalAPIDataNode).getExternalFunction().toString() +
|
||||
" with untrusted data from $@.", source, source.toString()
|
||||
50
cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll
Normal file
50
cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Definitions for reasoning about untrusted data used in APIs defined outside the
|
||||
* database.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
import ExternalAPIsSpecific
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
|
||||
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() {
|
||||
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TExternalAPI =
|
||||
TExternalAPIParameter(Function f, int index) {
|
||||
exists(UntrustedExternalAPIDataNode n |
|
||||
f = n.getExternalFunction() and
|
||||
index = n.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
||||
/** Gets a possibly untrusted use of this external API. */
|
||||
UntrustedExternalAPIDataNode getUntrustedDataNode() {
|
||||
this = TExternalAPIParameter(result.getExternalFunction(), result.getIndex())
|
||||
}
|
||||
|
||||
/** Gets the number of untrusted sources used with this external API. */
|
||||
int getNumberOfUntrustedSources() {
|
||||
result = strictcount(getUntrustedDataNode().getAnUntrustedSource())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Function f, int index, string indexString |
|
||||
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
||||
|
|
||||
this = TExternalAPIParameter(f, index) and
|
||||
result = f.toString() + " [" + indexString + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
51
cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIsSpecific.qll
Normal file
51
cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIsSpecific.qll
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Provides IR-specific definitions for use in the `ExternalAPI` library.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.security.FlowSources
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import SafeExternalAPIFunction
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class ExternalAPIDataNode extends DataFlow::Node {
|
||||
Call call;
|
||||
int i;
|
||||
|
||||
ExternalAPIDataNode() {
|
||||
// Argument to call to a function
|
||||
(
|
||||
this.asExpr() = call.getArgument(i)
|
||||
or
|
||||
i = -1 and this.asExpr() = call.getQualifier()
|
||||
) and
|
||||
exists(Function f |
|
||||
f = call.getTarget() and
|
||||
// Defined outside the source archive
|
||||
not f.hasDefinition() and
|
||||
// Not already modeled as a dataflow or taint step
|
||||
not f instanceof DataFlowFunction and
|
||||
not f instanceof TaintFunction and
|
||||
// Not a call to a known safe external API
|
||||
not f instanceof SafeExternalAPIFunction
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the called API `Function`. */
|
||||
Function getExternalFunction() { result = call.getTarget() }
|
||||
|
||||
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
|
||||
int getIndex() { result = i }
|
||||
|
||||
/** Gets the description of the function being called. */
|
||||
string getFunctionDescription() { result = getExternalFunction().toString() }
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
|
||||
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfigIR" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Provides a class for modeling external functions that are "safe" from a security perspective.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* A `Function` that is considered a "safe" external API from a security perspective.
|
||||
*/
|
||||
abstract class SafeExternalAPIFunction extends Function { }
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalAPIFunction extends SafeExternalAPIFunction {
|
||||
DefaultSafeExternalAPIFunction() {
|
||||
// If a function does not write to any of its arguments, we consider it safe to
|
||||
// pass untrusted data to it. This means that string functions such as `strcmp`
|
||||
// and `strlen`, as well as memory functions such as `memcmp`, are considered safe.
|
||||
exists(SideEffectFunction model | model = this |
|
||||
model.hasOnlySpecificWriteSideEffects() and
|
||||
not model.hasSpecificWriteSideEffect(_, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* the file from being included twice). This prevents errors and
|
||||
* inefficiencies caused by repeated inclusion.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id cpp/missing-header-guard
|
||||
* @tags efficiency
|
||||
|
||||
@@ -977,7 +977,12 @@ class ClassTemplateInstantiation extends Class {
|
||||
* specialization - see `FullClassTemplateSpecialization` and
|
||||
* `PartialClassTemplateSpecialization`).
|
||||
*/
|
||||
abstract class ClassTemplateSpecialization extends Class {
|
||||
class ClassTemplateSpecialization extends Class {
|
||||
ClassTemplateSpecialization() {
|
||||
isFullClassTemplateSpecialization(this) or
|
||||
isPartialClassTemplateSpecialization(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary template for the specialization, for example on
|
||||
* `S<T,int>`, the result is `S<T,U>`.
|
||||
@@ -997,6 +1002,16 @@ abstract class ClassTemplateSpecialization extends Class {
|
||||
override string getAPrimaryQlClass() { result = "ClassTemplateSpecialization" }
|
||||
}
|
||||
|
||||
private predicate isFullClassTemplateSpecialization(Class c) {
|
||||
// This class has template arguments, but none of them involves a template parameter.
|
||||
exists(c.getATemplateArgument()) and
|
||||
not exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
// This class does not have any instantiations.
|
||||
not exists(c.(TemplateClass).getAnInstantiation()) and
|
||||
// This class is not an instantiation of a class template.
|
||||
not c instanceof ClassTemplateInstantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* A full specialization of a class template. For example `MyTemplateClass<int>`
|
||||
* in the following code is a `FullClassTemplateSpecialization`:
|
||||
@@ -1013,19 +1028,31 @@ abstract class ClassTemplateSpecialization extends Class {
|
||||
* ```
|
||||
*/
|
||||
class FullClassTemplateSpecialization extends ClassTemplateSpecialization {
|
||||
FullClassTemplateSpecialization() {
|
||||
// This class has template arguments, but none of them involves a template parameter.
|
||||
exists(getATemplateArgument()) and
|
||||
not exists(Type ta | ta = getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
// This class does not have any instantiations.
|
||||
not exists(this.(TemplateClass).getAnInstantiation()) and
|
||||
// This class is not an instantiation of a class template.
|
||||
not this instanceof ClassTemplateInstantiation
|
||||
}
|
||||
FullClassTemplateSpecialization() { isFullClassTemplateSpecialization(this) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FullClassTemplateSpecialization" }
|
||||
}
|
||||
|
||||
private predicate isPartialClassTemplateSpecialization(Class c) {
|
||||
/*
|
||||
* (a) At least one of this class's template arguments involves a
|
||||
* template parameter in some respect, for example T, T*, etc.
|
||||
*
|
||||
* (b) It is not the case that the n template arguments of this class
|
||||
* are a set of n distinct template parameters.
|
||||
*
|
||||
* template <typename T,U> class X {}; // class template
|
||||
* template <typename T> class X<T,T> {}; // partial class template specialization
|
||||
* template <typename T> class X<T,int> {}; // partial class template specialization
|
||||
* template <typename T> class Y {}; // class template
|
||||
* template <typename T> class Y<T*> {}; // partial class template specialization
|
||||
*/
|
||||
|
||||
exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
count(TemplateParameter tp | tp = c.getATemplateArgument()) !=
|
||||
count(int i | exists(c.getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial specialization of a class template. For example `MyTemplateClass<int, T>`
|
||||
* in the following code is a `PartialClassTemplateSpecialization`:
|
||||
@@ -1042,25 +1069,7 @@ class FullClassTemplateSpecialization extends ClassTemplateSpecialization {
|
||||
* ```
|
||||
*/
|
||||
class PartialClassTemplateSpecialization extends ClassTemplateSpecialization {
|
||||
PartialClassTemplateSpecialization() {
|
||||
/*
|
||||
* (a) At least one of this class's template arguments involves a
|
||||
* template parameter in some respect, for example T, T*, etc.
|
||||
*
|
||||
* (b) It is not the case that the n template arguments of this class
|
||||
* are a set of n distinct template parameters.
|
||||
*
|
||||
* template <typename T,U> class X {}; // class template
|
||||
* template <typename T> class X<T,T> {}; // partial class template specialization
|
||||
* template <typename T> class X<T,int> {}; // partial class template specialization
|
||||
* template <typename T> class Y {}; // class template
|
||||
* template <typename T> class Y<T*> {}; // partial class template specialization
|
||||
*/
|
||||
|
||||
exists(Type ta | ta = getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
count(TemplateParameter tp | tp = getATemplateArgument()) !=
|
||||
count(int i | exists(getTemplateArgument(i)))
|
||||
}
|
||||
PartialClassTemplateSpecialization() { isPartialClassTemplateSpecialization(this) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PartialClassTemplateSpecialization" }
|
||||
}
|
||||
|
||||
@@ -65,11 +65,10 @@ class ElementBase extends @element {
|
||||
* which they belong; for example, `AddExpr` is a primary class, but
|
||||
* `BinaryOperation` is not.
|
||||
*
|
||||
* This predicate always has a result. If no primary class can be
|
||||
* determined, the result is `"???"`. If multiple primary classes match,
|
||||
* this predicate can have multiple results.
|
||||
* This predicate can have multiple results if multiple primary classes match.
|
||||
* For some elements, this predicate may not have a result.
|
||||
*/
|
||||
string getAPrimaryQlClass() { result = "???" }
|
||||
string getAPrimaryQlClass() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -205,12 +205,21 @@ class Constructor extends MemberFunction {
|
||||
/**
|
||||
* A function that defines an implicit conversion.
|
||||
*/
|
||||
abstract class ImplicitConversionFunction extends MemberFunction {
|
||||
class ImplicitConversionFunction extends MemberFunction {
|
||||
ImplicitConversionFunction() {
|
||||
// ConversionOperator
|
||||
functions(underlyingElement(this), _, 4)
|
||||
or
|
||||
// ConversionConstructor (deprecated)
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit")
|
||||
}
|
||||
|
||||
/** Gets the type this `ImplicitConversionFunction` takes as input. */
|
||||
abstract Type getSourceType();
|
||||
Type getSourceType() { none() } // overridden in subclasses
|
||||
|
||||
/** Gets the type this `ImplicitConversionFunction` converts to. */
|
||||
abstract Type getDestType();
|
||||
Type getDestType() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,7 +60,7 @@ private string getTemplateArgumentString(Declaration d, int i) {
|
||||
/**
|
||||
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
|
||||
*/
|
||||
abstract private class DumpDeclaration extends Declaration {
|
||||
private class DumpDeclaration extends Declaration {
|
||||
DumpDeclaration() { shouldPrintDeclaration(this) }
|
||||
|
||||
/**
|
||||
@@ -304,7 +304,7 @@ private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
|
||||
basePrefix = getBaseType().(DumpType).getDeclaratorPrefix() and
|
||||
if getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = basePrefix
|
||||
else result = basePrefix + " " + getSpecifierString().trim()
|
||||
else result = basePrefix + " " + getSpecifierString()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
|
||||
exists(string baseSuffix |
|
||||
baseSuffix = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and
|
||||
if getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = baseSuffix + " " + getSpecifierString().trim()
|
||||
then result = baseSuffix + " " + getSpecifierString()
|
||||
else result = baseSuffix
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@ private newtype TPrintASTNode =
|
||||
TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) {
|
||||
// We create a unique node for each pair of (stmt, entry), to avoid having one node with
|
||||
// multiple parents due to extractor bug CPP-413.
|
||||
stmt.getADeclarationEntry() = entry
|
||||
stmt.getADeclarationEntry() = entry and
|
||||
shouldPrintFunction(stmt.getEnclosingFunction())
|
||||
} or
|
||||
TParametersNode(Function func) { shouldPrintFunction(func) } or
|
||||
TConstructorInitializersNode(Constructor ctor) {
|
||||
@@ -234,11 +235,27 @@ class PrintASTNode extends TPrintASTNode {
|
||||
private Function getEnclosingFunction() { result = getParent*().(FunctionNode).getFunction() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that restricts the elements that we compute `qlClass` for.
|
||||
*/
|
||||
private class PrintableElement extends Element {
|
||||
PrintableElement() {
|
||||
exists(TASTNode(this))
|
||||
or
|
||||
exists(TDeclarationEntryNode(_, this))
|
||||
or
|
||||
this instanceof Type
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
string getAPrimaryQlClass0() { result = getAPrimaryQlClass() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the canonical QL class(es) for entity `el`
|
||||
*/
|
||||
private string qlClass(ElementBase el) {
|
||||
result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] "
|
||||
private string qlClass(PrintableElement el) {
|
||||
result = "[" + concat(el.getAPrimaryQlClass0(), ",") + "] "
|
||||
// Alternative implementation -- do not delete. It is useful for QL class discovery.
|
||||
//result = "["+ concat(el.getAQlClass(), ",") + "] "
|
||||
}
|
||||
|
||||
@@ -577,7 +577,9 @@ class BoolType extends IntegralType {
|
||||
* unsigned char e, f;
|
||||
* ```
|
||||
*/
|
||||
abstract class CharType extends IntegralType { }
|
||||
class CharType extends IntegralType {
|
||||
CharType() { builtintypes(underlyingElement(this), _, [5, 6, 7], _, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ `char` type (which is distinct from `signed char` and
|
||||
@@ -1304,14 +1306,16 @@ class SpecifiedType extends DerivedType {
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets all the specifiers of this type as a string in a fixed order (the order
|
||||
* only depends on the specifiers, not on the source program). This is intended
|
||||
* for debugging queries only and is an expensive operation.
|
||||
*/
|
||||
string getSpecifierString() { internalSpecString(this, result, 1) }
|
||||
string getSpecifierString() { result = concat(this.getASpecifier().getName(), " ") }
|
||||
|
||||
override string explain() {
|
||||
result = this.getSpecifierString() + "{" + this.getBaseType().explain() + "}"
|
||||
result = this.getSpecifierString() + " {" + this.getBaseType().explain() + "}"
|
||||
}
|
||||
|
||||
override predicate isDeeplyConst() {
|
||||
@@ -1710,28 +1714,6 @@ class AutoType extends TemplateParameter {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Internal implementation predicates
|
||||
//
|
||||
private predicate allSpecifiers(int i, string s) { s = rank[i](string t | specifiers(_, t) | t) }
|
||||
|
||||
private predicate internalSpecString(Type t, string res, int i) {
|
||||
(
|
||||
if allSpecifiers(i, t.getASpecifier().getName())
|
||||
then
|
||||
exists(string spec, string rest |
|
||||
allSpecifiers(i, spec) and
|
||||
res = spec + " " + rest and
|
||||
internalSpecString(t, rest, i + 1)
|
||||
)
|
||||
else (
|
||||
allSpecifiers(i, _) and internalSpecString(t, res, i + 1)
|
||||
)
|
||||
)
|
||||
or
|
||||
i = count(Specifier s) + 1 and res = ""
|
||||
}
|
||||
|
||||
private predicate suppressUnusedThis(Type t) { any() }
|
||||
|
||||
/** A source code location referring to a type */
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
|
||||
import semmle.files.FileSystem
|
||||
|
||||
private class TXMLLocatable =
|
||||
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
|
||||
|
||||
/** An XML element that has a location. */
|
||||
abstract class XMLLocatable extends @xmllocatable {
|
||||
class XMLLocatable extends @xmllocatable, TXMLLocatable {
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { xmllocations(this, result) }
|
||||
|
||||
@@ -33,7 +36,7 @@ abstract class XMLLocatable extends @xmllocatable {
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
string toString() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +54,7 @@ class XMLParent extends @xmlparent {
|
||||
* Gets a printable representation of this XML parent.
|
||||
* (Intended to be overridden in subclasses.)
|
||||
*/
|
||||
abstract string getName();
|
||||
string getName() { none() } // overridden in subclasses
|
||||
|
||||
/** Gets the file to which this XML parent belongs. */
|
||||
XMLFile getFile() { result = this or xmlElements(this, _, _, _, result) }
|
||||
|
||||
@@ -6,7 +6,6 @@ import semmle.code.cpp.Type
|
||||
import semmle.code.cpp.commons.CommonType
|
||||
import semmle.code.cpp.commons.StringAnalysis
|
||||
import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||
import semmle.code.cpp.models.implementations.Printf
|
||||
|
||||
class PrintfFormatAttribute extends FormatAttribute {
|
||||
PrintfFormatAttribute() { getArchetype() = ["printf", "__printf__"] }
|
||||
|
||||
@@ -212,14 +212,9 @@ private predicate addressMayEscapeAt(Expr e) {
|
||||
|
||||
private predicate addressMayEscapeMutablyAt(Expr e) {
|
||||
addressMayEscapeAt(e) and
|
||||
exists(Type t | t = e.getType().getUnderlyingType() |
|
||||
exists(PointerType pt |
|
||||
pt = t
|
||||
or
|
||||
pt = t.(SpecifiedType).getBaseType()
|
||||
|
|
||||
not pt.getBaseType().isConst()
|
||||
)
|
||||
exists(Type t | t = e.getType().stripTopLevelSpecifiers() |
|
||||
t instanceof PointerType and
|
||||
not t.(PointerType).getBaseType().isConst()
|
||||
or
|
||||
t instanceof ReferenceType and
|
||||
not t.(ReferenceType).getBaseType().isConst()
|
||||
@@ -227,6 +222,15 @@ private predicate addressMayEscapeMutablyAt(Expr e) {
|
||||
// If the address has been cast to an integral type, conservatively assume that it may eventually be cast back to a
|
||||
// pointer to non-const type.
|
||||
t instanceof IntegralType
|
||||
or
|
||||
// If we go through a temporary object step, we can take a reference to a temporary const pointer
|
||||
// object, where the pointer doesn't point to a const value
|
||||
exists(TemporaryObjectExpr temp, PointerType pt |
|
||||
temp.getConversion() = e.(ReferenceToExpr) and
|
||||
pt = temp.getType().stripTopLevelSpecifiers()
|
||||
|
|
||||
not pt.getBaseType().isConst()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -254,7 +258,7 @@ private predicate addressFromVariableAccess(VariableAccess va, Expr e) {
|
||||
// `e` could be a pointer that is converted to a reference as the final step,
|
||||
// meaning that we pass a value that is two dereferences away from referring
|
||||
// to `va`. This happens, for example, with `void std::vector::push_back(T&&
|
||||
// value);` when called as `v.push_back(&x)`, for a static variable `x`. It
|
||||
// value);` when called as `v.push_back(&x)`, for a variable `x`. It
|
||||
// can also happen when taking a reference to a const pointer to a
|
||||
// (potentially non-const) value.
|
||||
exists(Expr pointerValue |
|
||||
|
||||
@@ -64,7 +64,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
|
||||
* if the overall expression evaluates to `true`; for example on
|
||||
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
|
||||
*/
|
||||
abstract Expr getGreaterOperand();
|
||||
Expr getGreaterOperand() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the operand on the "lesser" (or "lesser-or-equal") side
|
||||
@@ -72,7 +72,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
|
||||
* if the overall expression evaluates to `true`; for example on
|
||||
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
|
||||
*/
|
||||
abstract Expr getLesserOperand();
|
||||
Expr getLesserOperand() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,6 @@ import semmle.code.cpp.Element
|
||||
private import semmle.code.cpp.Enclosing
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
private import semmle.code.cpp.internal.AddressConstantExpression
|
||||
private import semmle.code.cpp.models.implementations.Allocation
|
||||
|
||||
/**
|
||||
* A C/C++ expression.
|
||||
@@ -839,7 +838,7 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
|
||||
* For example, for `new int` the result is `int`.
|
||||
* For `new int[5]` the result is `int[5]`.
|
||||
*/
|
||||
abstract Type getAllocatedType();
|
||||
Type getAllocatedType() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the pointer `p` if this expression is of the form `new(p) T...`.
|
||||
|
||||
@@ -47,7 +47,17 @@ class LabelLiteral extends Literal {
|
||||
}
|
||||
|
||||
/** A character literal or a string literal. */
|
||||
abstract class TextLiteral extends Literal {
|
||||
class TextLiteral extends Literal {
|
||||
TextLiteral() {
|
||||
// String Literal
|
||||
// Note that `AggregateLiteral`s can also have an array type, but they derive from
|
||||
// @aggregateliteral rather than @literal.
|
||||
this.getType() instanceof ArrayType
|
||||
or
|
||||
// Char literal
|
||||
this.getValueText().regexpMatch("(?s)\\s*L?'.*")
|
||||
}
|
||||
|
||||
/** Gets a hex escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */
|
||||
string getAHexEscapeSequence(int occurrence, int offset) {
|
||||
result = getValueText().regexpFind("(?<!\\\\)\\\\x[0-9a-fA-F]+", occurrence, offset)
|
||||
|
||||
@@ -39,7 +39,7 @@ class BinaryLogicalOperation extends BinaryOperation, @bin_log_op_expr {
|
||||
* is true, `x` and `y` must also be true, so `impliesValue(x, true, true)` and
|
||||
* `impliesValue(y, true, true)` hold.
|
||||
*/
|
||||
abstract predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue);
|
||||
predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,7 +59,7 @@ class Namespace extends @namespace {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Declaration extends @declaration {
|
||||
class Declaration extends @declaration {
|
||||
string toString() { result = "QualifiedName Declaration" }
|
||||
|
||||
/** Gets the name of this declaration. */
|
||||
|
||||
@@ -28,11 +28,7 @@ private class PrimaryArgumentNode extends ArgumentNode {
|
||||
|
||||
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, int pos) {
|
||||
op = call.getPositionalArgumentOperand(pos)
|
||||
or
|
||||
op = call.getThisArgumentOperand() and pos = -1
|
||||
}
|
||||
override predicate argumentOf(DataFlowCall call, int pos) { op = call.getArgumentOperand(pos) }
|
||||
|
||||
override string toString() {
|
||||
result = "Argument " + op.(PositionalArgumentOperand).getIndex()
|
||||
@@ -110,10 +106,10 @@ class ReturnIndirectionNode extends ReturnNode {
|
||||
override ReturnIndirectionInstruction primary;
|
||||
|
||||
override ReturnKind getKind() {
|
||||
result = TIndirectReturnKind(-1) and
|
||||
primary.isThisIndirection()
|
||||
or
|
||||
result = TIndirectReturnKind(primary.getParameter().getIndex())
|
||||
exists(int index |
|
||||
primary.hasIndex(index) and
|
||||
result = TIndirectReturnKind(index)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,13 +496,6 @@ class DataFlowType = IRType;
|
||||
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends CallInstruction {
|
||||
/**
|
||||
* Gets the nth argument for this call.
|
||||
*
|
||||
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
|
||||
*/
|
||||
Node getArgument(int n) { result.asInstruction() = this.getPositionalArgument(n) }
|
||||
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
|
||||
@@ -266,10 +266,8 @@ class ParameterIndirectionNode extends ParameterNode {
|
||||
|
||||
override predicate isParameterOf(Function f, int pos) {
|
||||
exists(int index |
|
||||
f.getParameter(index) = instr.getParameter()
|
||||
or
|
||||
index = -1 and
|
||||
instr.getIRVariable().(IRThisVariable).getEnclosingFunction() = f
|
||||
instr.getEnclosingFunction() = f and
|
||||
instr.hasIndex(index)
|
||||
|
|
||||
pos = getArgumentPosOfSideEffect(index)
|
||||
)
|
||||
@@ -476,16 +474,8 @@ class DefinitionByReferenceNode extends InstructionNode {
|
||||
instr
|
||||
.getPrimaryInstruction()
|
||||
.(CallInstruction)
|
||||
.getPositionalArgument(instr.getIndex())
|
||||
.getArgument(instr.getIndex())
|
||||
.getUnconvertedResultExpression()
|
||||
or
|
||||
result =
|
||||
instr
|
||||
.getPrimaryInstruction()
|
||||
.(CallInstruction)
|
||||
.getThisArgument()
|
||||
.getUnconvertedResultExpression() and
|
||||
instr.getIndex() = -1
|
||||
}
|
||||
|
||||
/** Gets the parameter through which this value is assigned. */
|
||||
|
||||
@@ -163,6 +163,46 @@ class IRBlock extends IRBlockBase {
|
||||
not strictlyDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block immediately post-dominates `block`.
|
||||
*
|
||||
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
|
||||
* block `B` is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block strictly post-dominates `block`.
|
||||
*
|
||||
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
|
||||
* and `B` are not the same block.
|
||||
*/
|
||||
final predicate strictlyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates+(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is a post-dominator of `block`.
|
||||
*
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
*
|
||||
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postPominanceFrontier() {
|
||||
postDominates(result.getASuccessor()) and
|
||||
not strictlyPostDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
@@ -280,3 +320,12 @@ private module Cached {
|
||||
}
|
||||
|
||||
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
|
||||
|
||||
private predicate blockFunctionExit(IRBlock exit) {
|
||||
exit.getLastInstruction() instanceof ExitFunctionInstruction
|
||||
}
|
||||
|
||||
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
|
||||
|
||||
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
|
||||
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)
|
||||
|
||||
@@ -582,6 +582,17 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the parameter with index `index`, or
|
||||
* if `index` is `-1` and this instruction initializes `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,6 +616,18 @@ class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the memory pointed to by the parameter with
|
||||
* index `index`, or if `index` is `-1` and this instruction initializes the memory
|
||||
* pointed to by `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -779,6 +802,17 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for the parameter with index `index`, or
|
||||
* if this instruction is the return indirection for `this` and `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.isThisIndirection()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1591,6 +1625,22 @@ class CallInstruction extends Instruction {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ArgumentOperand getArgumentOperand(int index) {
|
||||
index >= 0 and result = getPositionalArgumentOperand(index)
|
||||
or
|
||||
index = -1 and result = getThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
|
||||
@@ -562,6 +562,9 @@ private Overlap getVariableMemoryLocationOverlap(
|
||||
use.getEndBitOffset())
|
||||
}
|
||||
|
||||
bindingset[result, b]
|
||||
private boolean unbindBool(boolean b) { result != b.booleanNot() }
|
||||
|
||||
MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
exists(MemoryAccessKind kind, boolean isMayAccess |
|
||||
kind = instr.getResultMemoryAccess() and
|
||||
@@ -574,7 +577,8 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset, isMayAccess) and
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset,
|
||||
unbindBool(isMayAccess))
|
||||
)
|
||||
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
@@ -582,7 +586,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
kind instanceof EntireAllocationMemoryAccess and
|
||||
result =
|
||||
TEntireAllocationMemoryLocation(getAddressOperandAllocation(instr.getResultAddressOperand()),
|
||||
isMayAccess)
|
||||
unbindBool(isMayAccess))
|
||||
or
|
||||
kind instanceof EscapedMemoryAccess and
|
||||
result = TAllAliasedMemory(instr.getEnclosingIRFunction(), isMayAccess)
|
||||
|
||||
@@ -403,16 +403,27 @@ private import PhiInsertion
|
||||
* instruction for the virtual variable as a whole.
|
||||
*/
|
||||
private module PhiInsertion {
|
||||
/**
|
||||
* Holds if `phiBlock` is a block in the dominance frontier of a block that has a definition of the
|
||||
* memory location `defLocation`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate dominanceFrontierOfDefinition(
|
||||
Alias::MemoryLocation defLocation, OldBlock phiBlock
|
||||
) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
definitionHasDefinitionInBlock(defLocation, defBlock)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction needs to be inserted for location `defLocation` at the beginning of block `phiBlock`.
|
||||
*/
|
||||
predicate definitionHasPhiNode(Alias::MemoryLocation defLocation, OldBlock phiBlock) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
definitionHasDefinitionInBlock(defLocation, defBlock) and
|
||||
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
|
||||
definitionLiveOnEntryToBlock(defLocation, phiBlock)
|
||||
)
|
||||
dominanceFrontierOfDefinition(defLocation, phiBlock) and
|
||||
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
|
||||
definitionLiveOnEntryToBlock(defLocation, phiBlock)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -163,6 +163,46 @@ class IRBlock extends IRBlockBase {
|
||||
not strictlyDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block immediately post-dominates `block`.
|
||||
*
|
||||
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
|
||||
* block `B` is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block strictly post-dominates `block`.
|
||||
*
|
||||
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
|
||||
* and `B` are not the same block.
|
||||
*/
|
||||
final predicate strictlyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates+(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is a post-dominator of `block`.
|
||||
*
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
*
|
||||
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postPominanceFrontier() {
|
||||
postDominates(result.getASuccessor()) and
|
||||
not strictlyPostDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
@@ -280,3 +320,12 @@ private module Cached {
|
||||
}
|
||||
|
||||
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
|
||||
|
||||
private predicate blockFunctionExit(IRBlock exit) {
|
||||
exit.getLastInstruction() instanceof ExitFunctionInstruction
|
||||
}
|
||||
|
||||
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
|
||||
|
||||
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
|
||||
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)
|
||||
|
||||
@@ -582,6 +582,17 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the parameter with index `index`, or
|
||||
* if `index` is `-1` and this instruction initializes `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,6 +616,18 @@ class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the memory pointed to by the parameter with
|
||||
* index `index`, or if `index` is `-1` and this instruction initializes the memory
|
||||
* pointed to by `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -779,6 +802,17 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for the parameter with index `index`, or
|
||||
* if this instruction is the return indirection for `this` and `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.isThisIndirection()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1591,6 +1625,22 @@ class CallInstruction extends Instruction {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ArgumentOperand getArgumentOperand(int index) {
|
||||
index >= 0 and result = getPositionalArgumentOperand(index)
|
||||
or
|
||||
index = -1 and result = getThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
|
||||
@@ -163,6 +163,46 @@ class IRBlock extends IRBlockBase {
|
||||
not strictlyDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block immediately post-dominates `block`.
|
||||
*
|
||||
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
|
||||
* block `B` is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block strictly post-dominates `block`.
|
||||
*
|
||||
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
|
||||
* and `B` are not the same block.
|
||||
*/
|
||||
final predicate strictlyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates+(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is a post-dominator of `block`.
|
||||
*
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
*
|
||||
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postPominanceFrontier() {
|
||||
postDominates(result.getASuccessor()) and
|
||||
not strictlyPostDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
@@ -280,3 +320,12 @@ private module Cached {
|
||||
}
|
||||
|
||||
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
|
||||
|
||||
private predicate blockFunctionExit(IRBlock exit) {
|
||||
exit.getLastInstruction() instanceof ExitFunctionInstruction
|
||||
}
|
||||
|
||||
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
|
||||
|
||||
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
|
||||
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)
|
||||
|
||||
@@ -582,6 +582,17 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the parameter with index `index`, or
|
||||
* if `index` is `-1` and this instruction initializes `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,6 +616,18 @@ class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the memory pointed to by the parameter with
|
||||
* index `index`, or if `index` is `-1` and this instruction initializes the memory
|
||||
* pointed to by `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -779,6 +802,17 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for the parameter with index `index`, or
|
||||
* if this instruction is the return indirection for `this` and `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.isThisIndirection()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1591,6 +1625,22 @@ class CallInstruction extends Instruction {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ArgumentOperand getArgumentOperand(int index) {
|
||||
index >= 0 and result = getPositionalArgumentOperand(index)
|
||||
or
|
||||
index = -1 and result = getThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
|
||||
@@ -403,16 +403,27 @@ private import PhiInsertion
|
||||
* instruction for the virtual variable as a whole.
|
||||
*/
|
||||
private module PhiInsertion {
|
||||
/**
|
||||
* Holds if `phiBlock` is a block in the dominance frontier of a block that has a definition of the
|
||||
* memory location `defLocation`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate dominanceFrontierOfDefinition(
|
||||
Alias::MemoryLocation defLocation, OldBlock phiBlock
|
||||
) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
definitionHasDefinitionInBlock(defLocation, defBlock)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction needs to be inserted for location `defLocation` at the beginning of block `phiBlock`.
|
||||
*/
|
||||
predicate definitionHasPhiNode(Alias::MemoryLocation defLocation, OldBlock phiBlock) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
definitionHasDefinitionInBlock(defLocation, defBlock) and
|
||||
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
|
||||
definitionLiveOnEntryToBlock(defLocation, phiBlock)
|
||||
)
|
||||
dominanceFrontierOfDefinition(defLocation, phiBlock) and
|
||||
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
|
||||
definitionLiveOnEntryToBlock(defLocation, phiBlock)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import implementations.Allocation
|
||||
private import implementations.Deallocation
|
||||
private import implementations.Fread
|
||||
private import implementations.Getenv
|
||||
private import implementations.Gets
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
|
||||
@@ -10,7 +10,7 @@ import semmle.code.cpp.models.interfaces.Allocation
|
||||
* An allocation function (such as `malloc`) that has an argument for the size
|
||||
* in bytes.
|
||||
*/
|
||||
class MallocAllocationFunction extends AllocationFunction {
|
||||
private class MallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
|
||||
MallocAllocationFunction() {
|
||||
@@ -112,7 +112,7 @@ class MallocAllocationFunction extends AllocationFunction {
|
||||
* An allocation function (such as `alloca`) that does not require a
|
||||
* corresponding free (and has an argument for the size in bytes).
|
||||
*/
|
||||
class AllocaAllocationFunction extends AllocationFunction {
|
||||
private class AllocaAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
|
||||
AllocaAllocationFunction() {
|
||||
@@ -137,7 +137,7 @@ class AllocaAllocationFunction extends AllocationFunction {
|
||||
* An allocation function (such as `calloc`) that has an argument for the size
|
||||
* and another argument for the size of those units (in bytes).
|
||||
*/
|
||||
class CallocAllocationFunction extends AllocationFunction {
|
||||
private class CallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
int multArg;
|
||||
|
||||
@@ -158,7 +158,7 @@ class CallocAllocationFunction extends AllocationFunction {
|
||||
* An allocation function (such as `realloc`) that has an argument for the size
|
||||
* in bytes, and an argument for an existing pointer that is to be reallocated.
|
||||
*/
|
||||
class ReallocAllocationFunction extends AllocationFunction {
|
||||
private class ReallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
int reallocArg;
|
||||
|
||||
@@ -197,7 +197,7 @@ class ReallocAllocationFunction extends AllocationFunction {
|
||||
* A miscellaneous allocation function that has no explicit argument for
|
||||
* the size of the allocation.
|
||||
*/
|
||||
class SizelessAllocationFunction extends AllocationFunction {
|
||||
private class SizelessAllocationFunction extends AllocationFunction {
|
||||
SizelessAllocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
@@ -236,40 +236,6 @@ class SizelessAllocationFunction extends AllocationFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator new` or `operator new[]` function that may be associated with `new` or
|
||||
* `new[]` expressions. Note that `new` and `new[]` are not function calls, but these
|
||||
* functions may also be called directly.
|
||||
*/
|
||||
class OperatorNewAllocationFunction extends AllocationFunction {
|
||||
OperatorNewAllocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
// operator new(bytes, ...)
|
||||
name = "operator new"
|
||||
or
|
||||
// operator new[](bytes, ...)
|
||||
name = "operator new[]"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getSizeArg() { result = 0 }
|
||||
|
||||
override predicate requiresDealloc() { not exists(getPlacementArgument()) }
|
||||
|
||||
/**
|
||||
* Gets the position of the placement pointer if this is a placement
|
||||
* `operator new` function.
|
||||
*/
|
||||
int getPlacementArgument() {
|
||||
getNumberOfParameters() = 2 and
|
||||
getParameter(1).getType() instanceof VoidPointerType and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sizeExpr` is an expression consisting of a subexpression
|
||||
* `lengthExpr` multiplied by a constant `sizeof` that is the result of a
|
||||
@@ -302,7 +268,7 @@ private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof
|
||||
/**
|
||||
* An allocation expression that is a function call, such as call to `malloc`.
|
||||
*/
|
||||
class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
private class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
AllocationFunction target;
|
||||
|
||||
CallAllocationExpr() {
|
||||
@@ -353,7 +319,7 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
/**
|
||||
* An allocation expression that is a `new` expression.
|
||||
*/
|
||||
class NewAllocationExpr extends AllocationExpr, NewExpr {
|
||||
private class NewAllocationExpr extends AllocationExpr, NewExpr {
|
||||
NewAllocationExpr() { this instanceof NewExpr }
|
||||
|
||||
override int getSizeBytes() { result = getAllocatedType().getSize() }
|
||||
@@ -366,7 +332,7 @@ class NewAllocationExpr extends AllocationExpr, NewExpr {
|
||||
/**
|
||||
* An allocation expression that is a `new []` expression.
|
||||
*/
|
||||
class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
|
||||
private class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
|
||||
NewArrayAllocationExpr() { this instanceof NewArrayExpr }
|
||||
|
||||
override Expr getSizeExpr() {
|
||||
|
||||
@@ -9,7 +9,7 @@ import semmle.code.cpp.models.interfaces.Deallocation
|
||||
/**
|
||||
* A deallocation function such as `free`.
|
||||
*/
|
||||
class StandardDeallocationFunction extends DeallocationFunction {
|
||||
private class StandardDeallocationFunction extends DeallocationFunction {
|
||||
int freedArg;
|
||||
|
||||
StandardDeallocationFunction() {
|
||||
@@ -89,32 +89,10 @@ class StandardDeallocationFunction extends DeallocationFunction {
|
||||
override int getFreedArg() { result = freedArg }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator delete` or `operator delete[]` function that may be associated
|
||||
* with `delete` or `delete[]` expressions. Note that `delete` and `delete[]`
|
||||
* are not function calls, but these functions may also be called directly.
|
||||
*/
|
||||
class OperatorDeleteDeallocationFunction extends DeallocationFunction {
|
||||
OperatorDeleteDeallocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
// operator delete(pointer, ...)
|
||||
name = "operator delete"
|
||||
or
|
||||
// operator delete[](pointer, ...)
|
||||
name = "operator delete[]"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getFreedArg() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* An deallocation expression that is a function call, such as call to `free`.
|
||||
*/
|
||||
class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
|
||||
private class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
|
||||
DeallocationFunction target;
|
||||
|
||||
CallDeallocationExpr() { target = getTarget() }
|
||||
@@ -125,7 +103,7 @@ class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
|
||||
/**
|
||||
* An deallocation expression that is a `delete` expression.
|
||||
*/
|
||||
class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
|
||||
private class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
|
||||
DeleteDeallocationExpr() { this instanceof DeleteExpr }
|
||||
|
||||
override Expr getFreedExpr() { result = getExpr() }
|
||||
@@ -134,7 +112,7 @@ class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
|
||||
/**
|
||||
* An deallocation expression that is a `delete []` expression.
|
||||
*/
|
||||
class DeleteArrayDeallocationExpr extends DeallocationExpr, DeleteArrayExpr {
|
||||
private class DeleteArrayDeallocationExpr extends DeallocationExpr, DeleteArrayExpr {
|
||||
DeleteArrayDeallocationExpr() { this instanceof DeleteArrayExpr }
|
||||
|
||||
override Expr getFreedExpr() { result = getExpr() }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
class Fread extends AliasFunction, RemoteFlowFunction {
|
||||
private class Fread extends AliasFunction, RemoteFlowFunction {
|
||||
Fread() { this.hasGlobalName("fread") }
|
||||
|
||||
override predicate parameterNeverEscapes(int n) {
|
||||
|
||||
@@ -6,7 +6,8 @@ import semmle.code.cpp.models.interfaces.FlowSource
|
||||
/**
|
||||
* The standard functions `getdelim`, `getwdelim` and the glibc variant `__getdelim`.
|
||||
*/
|
||||
class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction, RemoteFlowFunction {
|
||||
private class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction,
|
||||
RemoteFlowFunction {
|
||||
GetDelimFunction() { hasGlobalName(["getdelim", "getwdelim", "__getdelim"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) {
|
||||
|
||||
21
cpp/ql/src/semmle/code/cpp/models/implementations/Getenv.qll
Normal file
21
cpp/ql/src/semmle/code/cpp/models/implementations/Getenv.qll
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Provides an implementation class modeling the POSIX function `getenv`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The POSIX function `getenv`.
|
||||
*/
|
||||
class Getenv extends LocalFlowFunction {
|
||||
Getenv() { this.hasGlobalName("getenv") }
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
output.isReturnValueDeref() or
|
||||
output.isReturnValue()
|
||||
) and
|
||||
description = "an environment variable"
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.FlowSource
|
||||
/**
|
||||
* The standard functions `gets` and `fgets`.
|
||||
*/
|
||||
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||
private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||
SideEffectFunction, RemoteFlowFunction {
|
||||
GetsFunction() {
|
||||
// gets(str)
|
||||
|
||||
@@ -6,7 +6,7 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
/**
|
||||
* The standard function templates `std::move` and `std::forward`.
|
||||
*/
|
||||
class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
|
||||
private class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
|
||||
IdentityFunction() {
|
||||
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
|
||||
this.getNamespace().getName() = "std" and
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
|
||||
class InetNtoa extends TaintFunction {
|
||||
private class InetNtoa extends TaintFunction {
|
||||
InetNtoa() { hasGlobalName("inet_ntoa") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -10,7 +11,7 @@ class InetNtoa extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
class InetAton extends TaintFunction, ArrayFunction {
|
||||
private class InetAton extends TaintFunction, ArrayFunction {
|
||||
InetAton() { hasGlobalName("inet_aton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -30,7 +31,7 @@ class InetAton extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
}
|
||||
|
||||
class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
InetAddr() { hasGlobalName("inet_addr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -49,7 +50,7 @@ class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
}
|
||||
|
||||
class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
InetNetwork() { hasGlobalName("inet_network") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -62,7 +63,7 @@ class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
class InetMakeaddr extends TaintFunction {
|
||||
private class InetMakeaddr extends TaintFunction {
|
||||
InetMakeaddr() { hasGlobalName("inet_makeaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -74,7 +75,7 @@ class InetMakeaddr extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
class InetLnaof extends TaintFunction {
|
||||
private class InetLnaof extends TaintFunction {
|
||||
InetLnaof() { hasGlobalName("inet_lnaof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -83,7 +84,7 @@ class InetLnaof extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
class InetNetof extends TaintFunction {
|
||||
private class InetNetof extends TaintFunction {
|
||||
InetNetof() { hasGlobalName("inet_netof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -92,7 +93,7 @@ class InetNetof extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
class InetPton extends TaintFunction, ArrayFunction {
|
||||
private class InetPton extends TaintFunction, ArrayFunction {
|
||||
InetPton() { hasGlobalName("inet_pton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -112,7 +113,7 @@ class InetPton extends TaintFunction, ArrayFunction {
|
||||
override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = 2 }
|
||||
}
|
||||
|
||||
class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
Gethostbyname() { hasGlobalName("gethostbyname") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -125,7 +126,7 @@ class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
private class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
Gethostbyaddr() { hasGlobalName("gethostbyaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.Iterator
|
||||
/**
|
||||
* An instantiation of the `std::iterator_traits` template.
|
||||
*/
|
||||
class IteratorTraits extends Class {
|
||||
private class IteratorTraits extends Class {
|
||||
IteratorTraits() {
|
||||
this.hasQualifiedName("std", "iterator_traits") and
|
||||
not this instanceof TemplateClass and
|
||||
@@ -29,7 +29,7 @@ class IteratorTraits extends Class {
|
||||
/**
|
||||
* A type which has the typedefs expected for an iterator.
|
||||
*/
|
||||
class IteratorByTypedefs extends Class {
|
||||
private class IteratorByTypedefs extends Iterator, Class {
|
||||
IteratorByTypedefs() {
|
||||
this.getAMember().(TypedefType).hasName("difference_type") and
|
||||
this.getAMember().(TypedefType).hasName("value_type") and
|
||||
@@ -43,19 +43,16 @@ class IteratorByTypedefs extends Class {
|
||||
/**
|
||||
* The `std::iterator` class.
|
||||
*/
|
||||
class StdIterator extends Class {
|
||||
private class StdIterator extends Iterator, Class {
|
||||
StdIterator() { this.hasQualifiedName("std", "iterator") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type which can be used as an iterator
|
||||
* A type that is deduced to be an iterator because there is a corresponding
|
||||
* `std::iterator_traits` instantiation for it.
|
||||
*/
|
||||
class Iterator extends Type {
|
||||
Iterator() {
|
||||
this instanceof IteratorByTypedefs or
|
||||
exists(IteratorTraits it | it.getIteratorType() = this) or
|
||||
this instanceof StdIterator
|
||||
}
|
||||
private class IteratorByTraits extends Iterator {
|
||||
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
|
||||
}
|
||||
|
||||
private FunctionInput getIteratorArgumentInput(Operator op, int index) {
|
||||
@@ -81,7 +78,8 @@ private FunctionInput getIteratorArgumentInput(Operator op, int index) {
|
||||
/**
|
||||
* A non-member prefix `operator*` function for an iterator type.
|
||||
*/
|
||||
class IteratorPointerDereferenceOperator extends Operator, TaintFunction, IteratorReferenceFunction {
|
||||
private class IteratorPointerDereferenceOperator extends Operator, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorPointerDereferenceOperator() {
|
||||
@@ -101,7 +99,7 @@ class IteratorPointerDereferenceOperator extends Operator, TaintFunction, Iterat
|
||||
/**
|
||||
* A non-member `operator++` or `operator--` function for an iterator type.
|
||||
*/
|
||||
class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
private class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorCrementOperator() {
|
||||
@@ -118,7 +116,7 @@ class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
/**
|
||||
* A non-member `operator+` function for an iterator type.
|
||||
*/
|
||||
class IteratorAddOperator extends Operator, TaintFunction {
|
||||
private class IteratorAddOperator extends Operator, TaintFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorAddOperator() {
|
||||
@@ -135,7 +133,7 @@ class IteratorAddOperator extends Operator, TaintFunction {
|
||||
/**
|
||||
* A non-member `operator-` function that takes a pointer difference type as its second argument.
|
||||
*/
|
||||
class IteratorSubOperator extends Operator, TaintFunction {
|
||||
private class IteratorSubOperator extends Operator, TaintFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorSubOperator() {
|
||||
@@ -153,7 +151,7 @@ class IteratorSubOperator extends Operator, TaintFunction {
|
||||
/**
|
||||
* A non-member `operator+=` or `operator-=` function for an iterator type.
|
||||
*/
|
||||
class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
|
||||
private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -192,7 +190,7 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
|
||||
/**
|
||||
* An `operator++` or `operator--` member function for an iterator type.
|
||||
*/
|
||||
class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
IteratorCrementMemberOperator() {
|
||||
this.hasName(["operator++", "operator--"]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -215,7 +213,7 @@ class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, Ta
|
||||
/**
|
||||
* A member `operator->` function for an iterator type.
|
||||
*/
|
||||
class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
IteratorFieldMemberOperator() {
|
||||
this.hasName("operator->") and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -230,7 +228,7 @@ class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
/**
|
||||
* An `operator+` or `operator-` member function of an iterator class.
|
||||
*/
|
||||
class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
|
||||
private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
|
||||
IteratorBinaryArithmeticMemberOperator() {
|
||||
this.hasName(["operator+", "operator-"]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -245,7 +243,8 @@ class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFuncti
|
||||
/**
|
||||
* An `operator+=` or `operator-=` member function of an iterator class.
|
||||
*/
|
||||
class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
private class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction,
|
||||
TaintFunction {
|
||||
IteratorAssignArithmeticMemberOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -268,7 +267,8 @@ class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFun
|
||||
/**
|
||||
* An `operator[]` member function of an iterator class.
|
||||
*/
|
||||
class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, IteratorReferenceFunction {
|
||||
private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
IteratorArrayMemberOperator() {
|
||||
this.hasName("operator[]") and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -287,7 +287,7 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, Iterato
|
||||
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
|
||||
* `operator*` and use their own `operator=` to assign to the container.
|
||||
*/
|
||||
class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
|
||||
private class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
|
||||
IteratorAssignmentMemberOperator() {
|
||||
this.hasName("operator=") and
|
||||
this.getDeclaringType() instanceof Iterator and
|
||||
@@ -305,7 +305,7 @@ class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
|
||||
* A `begin` or `end` member function, or a related member function, that
|
||||
* returns an iterator.
|
||||
*/
|
||||
class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
|
||||
private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
|
||||
BeginOrEndFunction() {
|
||||
this
|
||||
.hasName([
|
||||
@@ -330,7 +330,7 @@ class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunct
|
||||
* The `std::front_inserter`, `std::inserter`, and `std::back_inserter`
|
||||
* functions.
|
||||
*/
|
||||
class InserterIteratorFunction extends GetIteratorFunction {
|
||||
private class InserterIteratorFunction extends GetIteratorFunction {
|
||||
InserterIteratorFunction() {
|
||||
this.hasQualifiedName("std", ["front_inserter", "inserter", "back_inserter"])
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
* it does correspond with the constructors we are confident taint should flow
|
||||
* through.
|
||||
*/
|
||||
class ConversionConstructorModel extends Constructor, TaintFunction {
|
||||
private class ConversionConstructorModel extends Constructor, TaintFunction {
|
||||
ConversionConstructorModel() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit")
|
||||
@@ -32,7 +32,7 @@ class ConversionConstructorModel extends Constructor, TaintFunction {
|
||||
/**
|
||||
* Model for C++ copy constructors.
|
||||
*/
|
||||
class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
|
||||
private class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// data flow from the first constructor argument to the returned object
|
||||
input.isParameterDeref(0) and
|
||||
@@ -47,7 +47,7 @@ class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
|
||||
/**
|
||||
* Model for C++ move constructors.
|
||||
*/
|
||||
class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
|
||||
private class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// data flow from the first constructor argument to the returned object
|
||||
input.isParameterDeref(0) and
|
||||
@@ -62,7 +62,7 @@ class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
|
||||
/**
|
||||
* Model for C++ copy assignment operators.
|
||||
*/
|
||||
class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction {
|
||||
private class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from argument to self
|
||||
input.isParameterDeref(0) and
|
||||
@@ -78,7 +78,7 @@ class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction
|
||||
/**
|
||||
* Model for C++ move assignment operators.
|
||||
*/
|
||||
class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction {
|
||||
private class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from argument to self
|
||||
input.isParameterDeref(0) and
|
||||
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
* The standard functions `memcpy`, `memmove` and `bcopy`; and the gcc variant
|
||||
* `__builtin___memcpy_chk`.
|
||||
*/
|
||||
class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
|
||||
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
|
||||
MemcpyFunction() {
|
||||
// memcpy(dest, src, num)
|
||||
// memmove(dest, src, num)
|
||||
|
||||
@@ -12,7 +12,8 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
/**
|
||||
* The standard function `memset` and its assorted variants
|
||||
*/
|
||||
class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction, SideEffectFunction {
|
||||
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
MemsetFunction() {
|
||||
hasGlobalName("memset") or
|
||||
hasGlobalName("wmemset") or
|
||||
|
||||
@@ -62,7 +62,7 @@ class Fprintf extends FormattingFunction {
|
||||
/**
|
||||
* The standard function `sprintf` and its Microsoft and glib variants.
|
||||
*/
|
||||
class Sprintf extends FormattingFunction {
|
||||
private class Sprintf extends FormattingFunction {
|
||||
Sprintf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
@@ -119,11 +119,10 @@ class Sprintf extends FormattingFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard functions `snprintf` and `swprintf`, and their
|
||||
* Microsoft and glib variants.
|
||||
* Implements `Snprintf`.
|
||||
*/
|
||||
class Snprintf extends FormattingFunction {
|
||||
Snprintf() {
|
||||
private class SnprintfImpl extends Snprintf {
|
||||
SnprintfImpl() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdName("snprintf") or // C99 defines snprintf
|
||||
@@ -180,12 +179,7 @@ class Snprintf extends FormattingFunction {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function returns the length of the formatted string
|
||||
* that would have been output, regardless of the amount of space
|
||||
* in the buffer.
|
||||
*/
|
||||
predicate returnsFullFormatLength() {
|
||||
override predicate returnsFullFormatLength() {
|
||||
(
|
||||
hasGlobalOrStdName("snprintf") or
|
||||
hasGlobalName("g_snprintf") or
|
||||
@@ -201,7 +195,7 @@ class Snprintf extends FormattingFunction {
|
||||
/**
|
||||
* The Microsoft `StringCchPrintf` function and variants.
|
||||
*/
|
||||
class StringCchPrintf extends FormattingFunction {
|
||||
private class StringCchPrintf extends FormattingFunction {
|
||||
StringCchPrintf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
|
||||
@@ -3,7 +3,9 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideEffectFunction {
|
||||
/** Pure string functions. */
|
||||
private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction,
|
||||
SideEffectFunction {
|
||||
PureStrFunction() {
|
||||
hasGlobalOrStdName([
|
||||
"atof", "atoi", "atol", "atoll", "strcasestr", "strchnul", "strchr", "strchrnul", "strstr",
|
||||
@@ -58,7 +60,8 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
||||
}
|
||||
}
|
||||
|
||||
class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
|
||||
/** String standard `strlen` function, and related functions for computing string lengths. */
|
||||
private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
|
||||
StrLenFunction() {
|
||||
hasGlobalOrStdName(["strlen", "strnlen", "wcslen"])
|
||||
or
|
||||
@@ -91,7 +94,8 @@ class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
|
||||
}
|
||||
}
|
||||
|
||||
class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
/** Pure functions. */
|
||||
private class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
PureFunction() { hasGlobalOrStdName(["abs", "labs"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -106,3 +110,50 @@ class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
}
|
||||
|
||||
/** Pure raw-memory functions. */
|
||||
private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunction,
|
||||
SideEffectFunction {
|
||||
PureMemFunction() { hasGlobalOrStdName(["memchr", "memrchr", "rawmemchr", "memcmp", "memmem"]) }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) {
|
||||
getParameter(bufParam).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
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
|
||||
(
|
||||
output.isReturnValueDeref() and
|
||||
getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
output.isReturnValue()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int i) {
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType and
|
||||
not parameterEscapesOnlyViaReturn(i)
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int i) {
|
||||
i = 0 and
|
||||
getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int i) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType and
|
||||
buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
/**
|
||||
* The `std::shared_ptr` and `std::unique_ptr` template classes.
|
||||
*/
|
||||
class UniqueOrSharedPtr extends Class {
|
||||
private class UniqueOrSharedPtr extends Class {
|
||||
UniqueOrSharedPtr() { this.hasQualifiedName("std", ["shared_ptr", "unique_ptr"]) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::make_shared` and `std::make_unique` template functions.
|
||||
*/
|
||||
class MakeUniqueOrShared extends TaintFunction {
|
||||
private class MakeUniqueOrShared extends TaintFunction {
|
||||
MakeUniqueOrShared() { this.hasQualifiedName("std", ["make_shared", "make_unique"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -37,7 +37,7 @@ class MakeUniqueOrShared extends TaintFunction {
|
||||
/**
|
||||
* A prefix `operator*` member function for a `shared_ptr` or `unique_ptr` type.
|
||||
*/
|
||||
class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction {
|
||||
private class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction {
|
||||
UniqueOrSharedDereferenceMemberOperator() {
|
||||
this.hasName("operator*") and
|
||||
this.getDeclaringType() instanceof UniqueOrSharedPtr
|
||||
@@ -52,7 +52,7 @@ class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunct
|
||||
/**
|
||||
* The `std::shared_ptr` or `std::unique_ptr` function `get`.
|
||||
*/
|
||||
class UniqueOrSharedGet extends TaintFunction {
|
||||
private class UniqueOrSharedGet extends TaintFunction {
|
||||
UniqueOrSharedGet() {
|
||||
this.hasName("get") and
|
||||
this.getDeclaringType() instanceof UniqueOrSharedPtr
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.implementations.Iterator
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
* Additional model for standard container constructors that reference the
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.models.implementations.Iterator
|
||||
* std::vector<std::string> v(100, potentially_tainted_string);
|
||||
* ```
|
||||
*/
|
||||
class StdSequenceContainerConstructor extends Constructor, TaintFunction {
|
||||
private class StdSequenceContainerConstructor extends Constructor, TaintFunction {
|
||||
StdSequenceContainerConstructor() {
|
||||
this.getDeclaringType().hasQualifiedName("std", ["vector", "deque", "list", "forward_list"])
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class StdSequenceContainerConstructor extends Constructor, TaintFunction {
|
||||
/**
|
||||
* The standard container function `data`.
|
||||
*/
|
||||
class StdSequenceContainerData extends TaintFunction {
|
||||
private class StdSequenceContainerData extends TaintFunction {
|
||||
StdSequenceContainerData() { this.hasQualifiedName("std", ["array", "vector"], "data") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -67,7 +67,7 @@ class StdSequenceContainerData extends TaintFunction {
|
||||
/**
|
||||
* The standard container functions `push_back` and `push_front`.
|
||||
*/
|
||||
class StdSequenceContainerPush extends TaintFunction {
|
||||
private class StdSequenceContainerPush extends TaintFunction {
|
||||
StdSequenceContainerPush() {
|
||||
this.hasQualifiedName("std", "vector", "push_back") or
|
||||
this.hasQualifiedName("std", "deque", ["push_back", "push_front"]) or
|
||||
@@ -85,7 +85,7 @@ class StdSequenceContainerPush extends TaintFunction {
|
||||
/**
|
||||
* The standard container functions `front` and `back`.
|
||||
*/
|
||||
class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
private class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
StdSequenceContainerFrontBack() {
|
||||
this.hasQualifiedName("std", "array", ["front", "back"]) or
|
||||
this.hasQualifiedName("std", "vector", ["front", "back"]) or
|
||||
@@ -104,7 +104,7 @@ class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
/**
|
||||
* The standard container functions `insert` and `insert_after`.
|
||||
*/
|
||||
class StdSequenceContainerInsert extends TaintFunction {
|
||||
private class StdSequenceContainerInsert extends TaintFunction {
|
||||
StdSequenceContainerInsert() {
|
||||
this.hasQualifiedName("std", ["vector", "deque", "list"], "insert") or
|
||||
this.hasQualifiedName("std", ["forward_list"], "insert_after")
|
||||
@@ -141,7 +141,7 @@ class StdSequenceContainerInsert extends TaintFunction {
|
||||
/**
|
||||
* The standard container function `assign`.
|
||||
*/
|
||||
class StdSequenceContainerAssign extends TaintFunction {
|
||||
private class StdSequenceContainerAssign extends TaintFunction {
|
||||
StdSequenceContainerAssign() {
|
||||
this.hasQualifiedName("std", ["vector", "deque", "list", "forward_list"], "assign")
|
||||
}
|
||||
@@ -173,7 +173,7 @@ class StdSequenceContainerAssign extends TaintFunction {
|
||||
/**
|
||||
* The standard container `swap` functions.
|
||||
*/
|
||||
class StdSequenceContainerSwap extends TaintFunction {
|
||||
private class StdSequenceContainerSwap extends TaintFunction {
|
||||
StdSequenceContainerSwap() {
|
||||
this.hasQualifiedName("std", ["array", "vector", "deque", "list", "forward_list"], "swap")
|
||||
}
|
||||
@@ -191,7 +191,7 @@ class StdSequenceContainerSwap extends TaintFunction {
|
||||
/**
|
||||
* The standard container functions `at` and `operator[]`.
|
||||
*/
|
||||
class StdSequenceContainerAt extends TaintFunction {
|
||||
private class StdSequenceContainerAt extends TaintFunction {
|
||||
StdSequenceContainerAt() {
|
||||
this.hasQualifiedName("std", ["vector", "array", "deque"], ["at", "operator[]"])
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.implementations.Iterator
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
* Additional model for map constructors using iterator inputs.
|
||||
*/
|
||||
class StdMapConstructor extends Constructor, TaintFunction {
|
||||
private class StdMapConstructor extends Constructor, TaintFunction {
|
||||
StdMapConstructor() {
|
||||
this.hasQualifiedName("std", "map", "map") or
|
||||
this.hasQualifiedName("std", "unordered_map", "unordered_map")
|
||||
@@ -35,7 +35,7 @@ class StdMapConstructor extends Constructor, TaintFunction {
|
||||
/**
|
||||
* The standard map `insert` and `insert_or_assign` functions.
|
||||
*/
|
||||
class StdMapInsert extends TaintFunction {
|
||||
private class StdMapInsert extends TaintFunction {
|
||||
StdMapInsert() {
|
||||
this.hasQualifiedName("std", ["map", "unordered_map"], ["insert", "insert_or_assign"])
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class StdMapInsert extends TaintFunction {
|
||||
/**
|
||||
* The standard map `emplace` and `emplace_hint` functions.
|
||||
*/
|
||||
class StdMapEmplace extends TaintFunction {
|
||||
private class StdMapEmplace extends TaintFunction {
|
||||
StdMapEmplace() {
|
||||
this.hasQualifiedName("std", ["map", "unordered_map"], ["emplace", "emplace_hint"])
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class StdMapEmplace extends TaintFunction {
|
||||
/**
|
||||
* The standard map `try_emplace` function.
|
||||
*/
|
||||
class StdMapTryEmplace extends TaintFunction {
|
||||
private class StdMapTryEmplace extends TaintFunction {
|
||||
StdMapTryEmplace() { this.hasQualifiedName("std", ["map", "unordered_map"], "try_emplace") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -105,7 +105,7 @@ class StdMapTryEmplace extends TaintFunction {
|
||||
/**
|
||||
* The standard map `swap` function.
|
||||
*/
|
||||
class StdMapSwap extends TaintFunction {
|
||||
private class StdMapSwap extends TaintFunction {
|
||||
StdMapSwap() { this.hasQualifiedName("std", ["map", "unordered_map"], "swap") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -121,7 +121,7 @@ class StdMapSwap extends TaintFunction {
|
||||
/**
|
||||
* The standard map `merge` function.
|
||||
*/
|
||||
class StdMapMerge extends TaintFunction {
|
||||
private class StdMapMerge extends TaintFunction {
|
||||
StdMapMerge() { this.hasQualifiedName("std", ["map", "unordered_map"], "merge") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -134,7 +134,7 @@ class StdMapMerge extends TaintFunction {
|
||||
/**
|
||||
* The standard map functions `at` and `operator[]`.
|
||||
*/
|
||||
class StdMapAt extends TaintFunction {
|
||||
private class StdMapAt extends TaintFunction {
|
||||
StdMapAt() { this.hasQualifiedName("std", ["map", "unordered_map"], ["at", "operator[]"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -151,7 +151,7 @@ class StdMapAt extends TaintFunction {
|
||||
/**
|
||||
* The standard map `find` function.
|
||||
*/
|
||||
class StdMapFind extends TaintFunction {
|
||||
private class StdMapFind extends TaintFunction {
|
||||
StdMapFind() { this.hasQualifiedName("std", ["map", "unordered_map"], "find") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -163,7 +163,7 @@ class StdMapFind extends TaintFunction {
|
||||
/**
|
||||
* The standard map `erase` function.
|
||||
*/
|
||||
class StdMapErase extends TaintFunction {
|
||||
private class StdMapErase extends TaintFunction {
|
||||
StdMapErase() { this.hasQualifiedName("std", ["map", "unordered_map"], "erase") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -177,7 +177,7 @@ class StdMapErase extends TaintFunction {
|
||||
/**
|
||||
* The standard map `lower_bound`, `upper_bound` and `equal_range` functions.
|
||||
*/
|
||||
class StdMapEqualRange extends TaintFunction {
|
||||
private class StdMapEqualRange extends TaintFunction {
|
||||
StdMapEqualRange() {
|
||||
this
|
||||
.hasQualifiedName("std", ["map", "unordered_map"],
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.implementations.Iterator
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
* Additional model for set constructors using iterator inputs.
|
||||
*/
|
||||
class StdSetConstructor extends Constructor, TaintFunction {
|
||||
private class StdSetConstructor extends Constructor, TaintFunction {
|
||||
StdSetConstructor() {
|
||||
this.hasQualifiedName("std", "set", "set") or
|
||||
this.hasQualifiedName("std", "unordered_set", "unordered_set")
|
||||
@@ -35,7 +35,7 @@ class StdSetConstructor extends Constructor, TaintFunction {
|
||||
/**
|
||||
* The standard set `insert` and `insert_or_assign` functions.
|
||||
*/
|
||||
class StdSetInsert extends TaintFunction {
|
||||
private class StdSetInsert extends TaintFunction {
|
||||
StdSetInsert() { this.hasQualifiedName("std", ["set", "unordered_set"], "insert") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -52,7 +52,7 @@ class StdSetInsert extends TaintFunction {
|
||||
/**
|
||||
* The standard set `emplace` and `emplace_hint` functions.
|
||||
*/
|
||||
class StdSetEmplace extends TaintFunction {
|
||||
private class StdSetEmplace extends TaintFunction {
|
||||
StdSetEmplace() {
|
||||
this.hasQualifiedName("std", ["set", "unordered_set"], ["emplace", "emplace_hint"])
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class StdSetEmplace extends TaintFunction {
|
||||
/**
|
||||
* The standard set `swap` functions.
|
||||
*/
|
||||
class StdSetSwap extends TaintFunction {
|
||||
private class StdSetSwap extends TaintFunction {
|
||||
StdSetSwap() { this.hasQualifiedName("std", ["set", "unordered_set"], "swap") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -91,7 +91,7 @@ class StdSetSwap extends TaintFunction {
|
||||
/**
|
||||
* The standard set `merge` function.
|
||||
*/
|
||||
class StdSetMerge extends TaintFunction {
|
||||
private class StdSetMerge extends TaintFunction {
|
||||
StdSetMerge() { this.hasQualifiedName("std", ["set", "unordered_set"], "merge") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -104,7 +104,7 @@ class StdSetMerge extends TaintFunction {
|
||||
/**
|
||||
* The standard set `find` function.
|
||||
*/
|
||||
class StdSetFind extends TaintFunction {
|
||||
private class StdSetFind extends TaintFunction {
|
||||
StdSetFind() { this.hasQualifiedName("std", ["set", "unordered_set"], "find") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -116,7 +116,7 @@ class StdSetFind extends TaintFunction {
|
||||
/**
|
||||
* The standard set `erase` function.
|
||||
*/
|
||||
class StdSetErase extends TaintFunction {
|
||||
private class StdSetErase extends TaintFunction {
|
||||
StdSetErase() { this.hasQualifiedName("std", ["set", "unordered_set"], "erase") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -130,7 +130,7 @@ class StdSetErase extends TaintFunction {
|
||||
/**
|
||||
* The standard set `lower_bound`, `upper_bound` and `equal_range` functions.
|
||||
*/
|
||||
class StdSetEqualRange extends TaintFunction {
|
||||
private class StdSetEqualRange extends TaintFunction {
|
||||
StdSetEqualRange() {
|
||||
this
|
||||
.hasQualifiedName("std", ["set", "unordered_set"],
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.implementations.Iterator
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
|
||||
/**
|
||||
* The `std::basic_string` template class.
|
||||
*/
|
||||
class StdBasicString extends TemplateClass {
|
||||
private class StdBasicString extends TemplateClass {
|
||||
StdBasicString() { this.hasQualifiedName("std", "basic_string") }
|
||||
}
|
||||
|
||||
@@ -22,7 +23,7 @@ class StdBasicString extends TemplateClass {
|
||||
* std::string b(a.begin(), a.end());
|
||||
* ```
|
||||
*/
|
||||
class StdStringConstructor extends Constructor, TaintFunction {
|
||||
private class StdStringConstructor extends Constructor, TaintFunction {
|
||||
StdStringConstructor() { this.getDeclaringType().hasQualifiedName("std", "basic_string") }
|
||||
|
||||
/**
|
||||
@@ -67,7 +68,7 @@ class StdStringConstructor extends Constructor, TaintFunction {
|
||||
/**
|
||||
* The `std::string` function `c_str`.
|
||||
*/
|
||||
class StdStringCStr extends TaintFunction {
|
||||
private class StdStringCStr extends TaintFunction {
|
||||
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -80,7 +81,7 @@ class StdStringCStr extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` function `data`.
|
||||
*/
|
||||
class StdStringData extends TaintFunction {
|
||||
private class StdStringData extends TaintFunction {
|
||||
StdStringData() { this.hasQualifiedName("std", "basic_string", "data") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -98,7 +99,7 @@ class StdStringData extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` function `push_back`.
|
||||
*/
|
||||
class StdStringPush extends TaintFunction {
|
||||
private class StdStringPush extends TaintFunction {
|
||||
StdStringPush() { this.hasQualifiedName("std", "basic_string", "push_back") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -111,7 +112,7 @@ class StdStringPush extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` functions `front` and `back`.
|
||||
*/
|
||||
class StdStringFrontBack extends TaintFunction {
|
||||
private class StdStringFrontBack extends TaintFunction {
|
||||
StdStringFrontBack() { this.hasQualifiedName("std", "basic_string", ["front", "back"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -124,7 +125,7 @@ class StdStringFrontBack extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` function `operator+`.
|
||||
*/
|
||||
class StdStringPlus extends TaintFunction {
|
||||
private class StdStringPlus extends TaintFunction {
|
||||
StdStringPlus() {
|
||||
this.hasQualifiedName("std", "operator+") and
|
||||
this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation()
|
||||
@@ -145,7 +146,7 @@ class StdStringPlus extends TaintFunction {
|
||||
* `replace`. All of these functions combine the existing string
|
||||
* with a new string (or character) from one of the arguments.
|
||||
*/
|
||||
class StdStringAppend extends TaintFunction {
|
||||
private class StdStringAppend extends TaintFunction {
|
||||
StdStringAppend() {
|
||||
this.hasQualifiedName("std", "basic_string", ["operator+=", "append", "insert", "replace"])
|
||||
}
|
||||
@@ -188,7 +189,7 @@ class StdStringAppend extends TaintFunction {
|
||||
/**
|
||||
* The standard function `std::string.assign`.
|
||||
*/
|
||||
class StdStringAssign extends TaintFunction {
|
||||
private class StdStringAssign extends TaintFunction {
|
||||
StdStringAssign() { this.hasQualifiedName("std", "basic_string", "assign") }
|
||||
|
||||
/**
|
||||
@@ -228,7 +229,7 @@ class StdStringAssign extends TaintFunction {
|
||||
/**
|
||||
* The standard function `std::string.copy`.
|
||||
*/
|
||||
class StdStringCopy extends TaintFunction {
|
||||
private class StdStringCopy extends TaintFunction {
|
||||
StdStringCopy() { this.hasQualifiedName("std", "basic_string", "copy") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -241,7 +242,7 @@ class StdStringCopy extends TaintFunction {
|
||||
/**
|
||||
* The standard function `std::string.substr`.
|
||||
*/
|
||||
class StdStringSubstr extends TaintFunction {
|
||||
private class StdStringSubstr extends TaintFunction {
|
||||
StdStringSubstr() { this.hasQualifiedName("std", "basic_string", "substr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -254,7 +255,7 @@ class StdStringSubstr extends TaintFunction {
|
||||
/**
|
||||
* The standard functions `std::string.swap` and `std::stringstream::swap`.
|
||||
*/
|
||||
class StdStringSwap extends TaintFunction {
|
||||
private class StdStringSwap extends TaintFunction {
|
||||
StdStringSwap() {
|
||||
this.hasQualifiedName("std", "basic_string", "swap") or
|
||||
this.hasQualifiedName("std", "basic_stringstream", "swap")
|
||||
@@ -273,7 +274,7 @@ class StdStringSwap extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` functions `at` and `operator[]`.
|
||||
*/
|
||||
class StdStringAt extends TaintFunction {
|
||||
private class StdStringAt extends TaintFunction {
|
||||
StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -290,14 +291,14 @@ class StdStringAt extends TaintFunction {
|
||||
/**
|
||||
* The `std::basic_istream` template class.
|
||||
*/
|
||||
class StdBasicIStream extends TemplateClass {
|
||||
private class StdBasicIStream extends TemplateClass {
|
||||
StdBasicIStream() { this.hasQualifiedName("std", "basic_istream") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::istream` function `operator>>` (defined as a member function).
|
||||
*/
|
||||
class StdIStreamIn extends DataFlowFunction, TaintFunction {
|
||||
private class StdIStreamIn extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamIn() { this.hasQualifiedName("std", "basic_istream", "operator>>") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -320,7 +321,7 @@ class StdIStreamIn extends DataFlowFunction, TaintFunction {
|
||||
/**
|
||||
* The `std::istream` function `operator>>` (defined as a non-member function).
|
||||
*/
|
||||
class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
|
||||
private class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamInNonMember() {
|
||||
this.hasQualifiedName("std", "operator>>") and
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
@@ -347,7 +348,7 @@ class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
|
||||
/**
|
||||
* The `std::istream` functions `get` (without parameters) and `peek`.
|
||||
*/
|
||||
class StdIStreamGet extends TaintFunction {
|
||||
private class StdIStreamGet extends TaintFunction {
|
||||
StdIStreamGet() {
|
||||
this.hasQualifiedName("std", "basic_istream", ["get", "peek"]) and
|
||||
this.getNumberOfParameters() = 0
|
||||
@@ -363,7 +364,7 @@ class StdIStreamGet extends TaintFunction {
|
||||
/**
|
||||
* The `std::istream` functions `get` (with parameters) and `read`.
|
||||
*/
|
||||
class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
private class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamRead() {
|
||||
this.hasQualifiedName("std", "basic_istream", ["get", "read"]) and
|
||||
this.getNumberOfParameters() > 0
|
||||
@@ -389,7 +390,7 @@ class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
/**
|
||||
* The `std::istream` function `readsome`.
|
||||
*/
|
||||
class StdIStreamReadSome extends TaintFunction {
|
||||
private class StdIStreamReadSome extends TaintFunction {
|
||||
StdIStreamReadSome() { this.hasQualifiedName("std", "basic_istream", "readsome") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -402,7 +403,7 @@ class StdIStreamReadSome extends TaintFunction {
|
||||
/**
|
||||
* The `std::istream` function `putback`.
|
||||
*/
|
||||
class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
|
||||
private class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamPutBack() { this.hasQualifiedName("std", "basic_istream", "putback") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -435,7 +436,7 @@ class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
|
||||
/**
|
||||
* The `std::istream` function `getline`.
|
||||
*/
|
||||
class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
|
||||
private class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamGetLine() { this.hasQualifiedName("std", "basic_istream", "getline") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -458,7 +459,7 @@ class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
|
||||
/**
|
||||
* The (non-member) function `std::getline`.
|
||||
*/
|
||||
class StdGetLine extends DataFlowFunction, TaintFunction {
|
||||
private class StdGetLine extends DataFlowFunction, TaintFunction {
|
||||
StdGetLine() { this.hasQualifiedName("std", "getline") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -481,7 +482,7 @@ class StdGetLine extends DataFlowFunction, TaintFunction {
|
||||
/**
|
||||
* The `std::basic_ostream` template class.
|
||||
*/
|
||||
class StdBasicOStream extends TemplateClass {
|
||||
private class StdBasicOStream extends TemplateClass {
|
||||
StdBasicOStream() { this.hasQualifiedName("std", "basic_ostream") }
|
||||
}
|
||||
|
||||
@@ -489,7 +490,7 @@ class StdBasicOStream extends TemplateClass {
|
||||
* The `std::ostream` functions `operator<<` (defined as a member function),
|
||||
* `put` and `write`.
|
||||
*/
|
||||
class StdOStreamOut extends DataFlowFunction, TaintFunction {
|
||||
private class StdOStreamOut extends DataFlowFunction, TaintFunction {
|
||||
StdOStreamOut() { this.hasQualifiedName("std", "basic_ostream", ["operator<<", "put", "write"]) }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -522,7 +523,7 @@ class StdOStreamOut extends DataFlowFunction, TaintFunction {
|
||||
/**
|
||||
* The `std::ostream` function `operator<<` (defined as a non-member function).
|
||||
*/
|
||||
class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
|
||||
private class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
|
||||
StdOStreamOutNonMember() {
|
||||
this.hasQualifiedName("std", "operator<<") and
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
@@ -554,7 +555,7 @@ class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
|
||||
* Additional model for `std::stringstream` constructors that take a string
|
||||
* input parameter.
|
||||
*/
|
||||
class StdStringStreamConstructor extends Constructor, TaintFunction {
|
||||
private class StdStringStreamConstructor extends Constructor, TaintFunction {
|
||||
StdStringStreamConstructor() {
|
||||
this.getDeclaringType().hasQualifiedName("std", "basic_stringstream")
|
||||
}
|
||||
@@ -580,7 +581,7 @@ class StdStringStreamConstructor extends Constructor, TaintFunction {
|
||||
/**
|
||||
* The `std::stringstream` function `str`.
|
||||
*/
|
||||
class StdStringStreamStr extends TaintFunction {
|
||||
private class StdStringStreamStr extends TaintFunction {
|
||||
StdStringStreamStr() { this.hasQualifiedName("std", "basic_stringstream", "str") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -598,7 +599,7 @@ class StdStringStreamStr extends TaintFunction {
|
||||
* 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.
|
||||
*/
|
||||
class StdStreamFunction extends DataFlowFunction, TaintFunction {
|
||||
private class StdStreamFunction extends DataFlowFunction, TaintFunction {
|
||||
StdStreamFunction() {
|
||||
this.hasQualifiedName("std", "basic_istream", ["ignore", "unget", "seekg"]) or
|
||||
this.hasQualifiedName("std", "basic_ostream", ["seekp", "flush"]) or
|
||||
|
||||
@@ -23,15 +23,15 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
"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)
|
||||
]
|
||||
"_mbsncpy_l"
|
||||
] // _mbsncpy_l(dst, src, max_amount, locale)
|
||||
or
|
||||
getName() =
|
||||
[
|
||||
"strcpy_s", // strcpy_s(dst, max_amount, src)
|
||||
"wcscpy_s", // wcscpy_s(dst, max_amount, src)
|
||||
"_mbscpy_s" // _mbscpy_s(dst, max_amount, src)
|
||||
] and
|
||||
"_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
|
||||
|
||||
@@ -11,7 +11,7 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
/**
|
||||
* A `strdup` style allocation function.
|
||||
*/
|
||||
class StrdupFunction extends AllocationFunction, ArrayFunction, DataFlowFunction {
|
||||
private class StrdupFunction extends AllocationFunction, ArrayFunction, DataFlowFunction {
|
||||
StrdupFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
@@ -47,7 +47,7 @@ class StrdupFunction extends AllocationFunction, ArrayFunction, DataFlowFunction
|
||||
/**
|
||||
* A `strndup` style allocation function.
|
||||
*/
|
||||
class StrndupFunction extends AllocationFunction, ArrayFunction, DataFlowFunction {
|
||||
private class StrndupFunction extends AllocationFunction, ArrayFunction, DataFlowFunction {
|
||||
StrndupFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
|
||||
class Strftime extends TaintFunction, ArrayFunction {
|
||||
private class Strftime extends TaintFunction, ArrayFunction {
|
||||
Strftime() { hasGlobalName("strftime") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
/**
|
||||
* The standard function `swap`.
|
||||
*/
|
||||
class Swap extends DataFlowFunction {
|
||||
private class Swap extends DataFlowFunction {
|
||||
Swap() { this.hasQualifiedName("std", "swap") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -85,3 +85,37 @@ abstract class AllocationExpr extends Expr {
|
||||
*/
|
||||
predicate requiresDealloc() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator new` or `operator new[]` function that may be associated with
|
||||
* `new` or `new[]` expressions. Note that `new` and `new[]` are not function
|
||||
* calls, but these functions may also be called directly.
|
||||
*/
|
||||
class OperatorNewAllocationFunction extends AllocationFunction {
|
||||
OperatorNewAllocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
// operator new(bytes, ...)
|
||||
name = "operator new"
|
||||
or
|
||||
// operator new[](bytes, ...)
|
||||
name = "operator new[]"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getSizeArg() { result = 0 }
|
||||
|
||||
override predicate requiresDealloc() { not exists(getPlacementArgument()) }
|
||||
|
||||
/**
|
||||
* Gets the position of the placement pointer if this is a placement
|
||||
* `operator new` function.
|
||||
*/
|
||||
int getPlacementArgument() {
|
||||
getNumberOfParameters() = 2 and
|
||||
getParameter(1).getType() instanceof VoidPointerType and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,3 +30,25 @@ abstract class DeallocationExpr extends Expr {
|
||||
*/
|
||||
Expr getFreedExpr() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator delete` or `operator delete[]` function that may be associated
|
||||
* with `delete` or `delete[]` expressions. Note that `delete` and `delete[]`
|
||||
* are not function calls, but these functions may also be called directly.
|
||||
*/
|
||||
class OperatorDeleteDeallocationFunction extends DeallocationFunction {
|
||||
OperatorDeleteDeallocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
// operator delete(pointer, ...)
|
||||
name = "operator delete"
|
||||
or
|
||||
// operator delete[](pointer, ...)
|
||||
name = "operator delete[]"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getFreedArg() { result = 0 }
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import FunctionInputsAndOutputs
|
||||
import semmle.code.cpp.models.Models
|
||||
|
||||
/**
|
||||
* A library function which returns data read from a network connection.
|
||||
* A library function that returns data that may be read from a network connection.
|
||||
*/
|
||||
abstract class RemoteFlowFunction extends Function {
|
||||
/**
|
||||
@@ -19,3 +19,13 @@ abstract class RemoteFlowFunction extends Function {
|
||||
*/
|
||||
abstract predicate hasRemoteFlowSource(FunctionOutput output, string description);
|
||||
}
|
||||
|
||||
/**
|
||||
* A library function that returns data that is directly controlled by a user.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -165,3 +165,16 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard functions `snprintf` and `swprintf`, and their
|
||||
* Microsoft and glib variants.
|
||||
*/
|
||||
abstract class Snprintf extends FormattingFunction {
|
||||
/**
|
||||
* Holds if this function returns the length of the formatted string
|
||||
* that would have been output, regardless of the amount of space
|
||||
* in the buffer.
|
||||
*/
|
||||
predicate returnsFullFormatLength() { none() }
|
||||
}
|
||||
|
||||
@@ -132,6 +132,16 @@ class FunctionInput extends TFunctionInput {
|
||||
* part of itself, or one of its other inputs.
|
||||
*/
|
||||
predicate isReturnValueDeref() { none() }
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
i >= 0 and this.isParameterDeref(i)
|
||||
or
|
||||
i = -1 and this.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,6 +380,16 @@ class FunctionOutput extends TFunctionOutput {
|
||||
* DEPRECATED: Use `isReturnValueDeref()` instead.
|
||||
*/
|
||||
deprecated final predicate isOutReturnPointer() { isReturnValueDeref() }
|
||||
|
||||
/**
|
||||
* Holds if `i >= 0` and `isParameterDeref(i)` holds for this is the value, or
|
||||
* if `i = -1` and `isQualifierObject()` holds for this value.
|
||||
*/
|
||||
final predicate isParameterDerefOrQualifierObject(ParameterIndex i) {
|
||||
i >= 0 and this.isParameterDeref(i)
|
||||
or
|
||||
i = -1 and this.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,3 +26,8 @@ abstract class GetIteratorFunction extends Function {
|
||||
*/
|
||||
abstract predicate getsIterator(FunctionInput input, FunctionOutput output);
|
||||
}
|
||||
|
||||
/**
|
||||
* A type which can be used as an iterator.
|
||||
*/
|
||||
abstract class Iterator extends Type { }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
/**
|
||||
* A function for running a command using a command interpreter.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.implementations.Printf
|
||||
|
||||
/**
|
||||
* A function call that writes to a file.
|
||||
|
||||
@@ -7,38 +7,94 @@ import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/** A data flow source of remote user input. */
|
||||
abstract class RemoteFlowSource extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this remote flow source. */
|
||||
/** A data flow source of user input, whether local or remote. */
|
||||
abstract class FlowSource extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this flow source. */
|
||||
abstract string getSourceType();
|
||||
}
|
||||
|
||||
private class TaintedReturnSource extends RemoteFlowSource {
|
||||
/** A data flow source of remote user input. */
|
||||
abstract class RemoteFlowSource extends FlowSource { }
|
||||
|
||||
/** A data flow source of local user input. */
|
||||
abstract class LocalFlowSource extends FlowSource { }
|
||||
|
||||
private class RemoteReturnSource extends RemoteFlowSource {
|
||||
string sourceType;
|
||||
|
||||
TaintedReturnSource() {
|
||||
RemoteReturnSource() {
|
||||
exists(RemoteFlowFunction func, CallInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getStaticCallTarget() = func and
|
||||
func.hasRemoteFlowSource(output, sourceType) and
|
||||
output.isReturnValue()
|
||||
(
|
||||
output.isReturnValue()
|
||||
or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
private class TaintedParameterSource extends RemoteFlowSource {
|
||||
private class RemoteParameterSource extends RemoteFlowSource {
|
||||
string sourceType;
|
||||
|
||||
TaintedParameterSource() {
|
||||
RemoteParameterSource() {
|
||||
exists(RemoteFlowFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and
|
||||
func.hasRemoteFlowSource(output, sourceType) and
|
||||
output.isParameterDeref(instr.getIndex())
|
||||
output.isParameterDerefOrQualifierObject(instr.getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
private class LocalReturnSource extends LocalFlowSource {
|
||||
string sourceType;
|
||||
|
||||
LocalReturnSource() {
|
||||
exists(LocalFlowFunction func, CallInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getStaticCallTarget() = func and
|
||||
func.hasLocalFlowSource(output, sourceType) and
|
||||
(
|
||||
output.isReturnValue()
|
||||
or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
private class LocalParameterSource extends LocalFlowSource {
|
||||
string sourceType;
|
||||
|
||||
LocalParameterSource() {
|
||||
exists(LocalFlowFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
|
||||
asInstruction() = instr and
|
||||
instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and
|
||||
func.hasLocalFlowSource(output, sourceType) and
|
||||
output.isParameterDerefOrQualifierObject(instr.getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
private class ArgvSource extends LocalFlowSource {
|
||||
ArgvSource() {
|
||||
exists(Parameter argv |
|
||||
argv.hasName("argv") and
|
||||
argv.getFunction().hasGlobalName("main") and
|
||||
this.asExpr() = argv.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "a command-line argument" }
|
||||
}
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
/**
|
||||
* Provides classes for working with UML diagrams generated by MagicDraw.
|
||||
*
|
||||
* Diagrams need to be saved in the `.mdxml` or `.xml` format.
|
||||
*
|
||||
* See [the MagicDraw website](http://www.nomagic.com/products/magicdraw.html)
|
||||
* for more information.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* An element of the UML diagram.
|
||||
*
|
||||
* Includes any XML element that is in a document whose root has namespace
|
||||
* `http://schema.omg.org/spec/XMI/2.1`.
|
||||
*/
|
||||
class UMLElement extends XMLElement {
|
||||
UMLElement() {
|
||||
this.getFile().getARootElement().getNamespace().getURI() = "http://schema.omg.org/spec/XMI/2.1"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a UML element that refers to this element, that is, which has an
|
||||
* `idref` attribute whose value is the same as the value of this
|
||||
* element's `id` attribute.
|
||||
*/
|
||||
UMLElement getUMLReference() { result.getAttributeValue("idref") = this.getAttributeValue("id") }
|
||||
|
||||
/**
|
||||
* Gets the name of a stereotype that applies to this element.
|
||||
*/
|
||||
string getAStereotype() {
|
||||
exists(UMLElement stereotype |
|
||||
stereotype.getName() = "Stereotype" and
|
||||
stereotype.getAttribute("name").getValue() = result and
|
||||
stereotype.getAChild().getAChild() = this.getUMLReference()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of a constraint that applies to this element.
|
||||
*/
|
||||
string getAConstraint() {
|
||||
exists(UMLElement constraint, UMLElement constrained |
|
||||
constraint.getName() = "Constraint" and
|
||||
constrained.getName() = "Constraint.constrainedElement" and
|
||||
constraint.getAChild() = constrained and
|
||||
constrained.getAChild() = this.getUMLReference() and
|
||||
constraint.getAttribute("name").getValue() = result
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this element, that is, the value of its `name` attribute.
|
||||
*/
|
||||
string getUMLName() { result = this.getAttribute("name").getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A UML element representing a type.
|
||||
*
|
||||
* In other words, a `packagedElement` whose `type` attribute has value
|
||||
* `uml:Class`, `uml:Interface` or `uml:PrimitiveType`.
|
||||
*/
|
||||
class UMLType extends UMLElement {
|
||||
UMLType() {
|
||||
this.getName() = "packagedElement" and
|
||||
this.getAttribute("type").getValue() = ["uml:Class", "uml:Interface", "uml:PrimitiveType"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the package that contains this type.
|
||||
*/
|
||||
UMLPackage getUMLPackage() { result.getAClass() = this }
|
||||
|
||||
/**
|
||||
* Gets a property directly contained in this type.
|
||||
*/
|
||||
UMLProperty getUMLProperty() { this.getAChild() = result }
|
||||
|
||||
/**
|
||||
* Gets an operation directly contained in this type.
|
||||
*/
|
||||
UMLOperation getUMLOperation() { this.getAChild() = result }
|
||||
|
||||
/**
|
||||
* Holds if this is an enum type, that is, if `enum` is one of its
|
||||
* stereotypes.
|
||||
*/
|
||||
predicate isEnum() { this.getAStereotype() = "enum" }
|
||||
|
||||
/**
|
||||
* Gets the C class, struct or union type corresponding to this UML type.
|
||||
*/
|
||||
Class getCType() { result.getQualifiedName() = this.getUMLQualifiedName() }
|
||||
|
||||
/**
|
||||
* Gets the qualified name of this type. If this type is in a package
|
||||
* then this is `package.name`; otherwise it is just `name`.
|
||||
*/
|
||||
string getUMLQualifiedName() {
|
||||
if exists(this.getUMLPackage())
|
||||
then result = this.getUMLPackage().getUMLQualifiedName() + "." + this.getUMLName()
|
||||
else result = this.getUMLName()
|
||||
}
|
||||
|
||||
override string toString() { result = this.getUMLName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A UML type representing a class, that is, whose `type` attribute has
|
||||
* value `uml:Class`.
|
||||
*/
|
||||
class UMLClass extends UMLType {
|
||||
UMLClass() { this.getAttribute("type").getValue() = "uml:Class" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A UML type representing an interface, that is, whose `type` attribute
|
||||
* has value `uml:Interface`.
|
||||
*/
|
||||
class UMLInterface extends UMLElement {
|
||||
UMLInterface() { this.getAttribute("type").getValue() = "uml:Interface" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A UML element representing a property of a UML type.
|
||||
*
|
||||
* In other words, an `ownedAttribute` directly contained in a `UMLType`
|
||||
* whose `type` attribute has value `uml:Property`.
|
||||
*/
|
||||
class UMLProperty extends UMLElement {
|
||||
UMLProperty() {
|
||||
this.getName() = "ownedAttribute" and
|
||||
this.getAttribute("type").getValue() = "uml:Property" and
|
||||
this.getParent() instanceof UMLType
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type that contains this property.
|
||||
*/
|
||||
UMLType getUMLType() { result.getUMLProperty() = this }
|
||||
|
||||
/**
|
||||
* Holds if this property represents an enum constant, that is, if it
|
||||
* has a stereotype with name `enum+constant`.
|
||||
*/
|
||||
predicate isEnumConstant() { this.getAStereotype() = "enum+constant" }
|
||||
|
||||
/**
|
||||
* Gets the C field corresponding to this property, if any.
|
||||
*/
|
||||
Field getCField() {
|
||||
result.hasName(this.getUMLName()) and
|
||||
result.getDeclaringType() = this.getUMLType().getCType()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
if this.isEnumConstant()
|
||||
then result = "- <<enum constant>> " + this.getUMLName()
|
||||
else result = "- " + this.getUMLName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A UML element representing an operation of a UML type.
|
||||
*
|
||||
* In other words, an `ownedAttribute` directly contained in a `UMLType`
|
||||
* whose `type` attribute has value `uml:Operation`.
|
||||
*/
|
||||
class UMLOperation extends UMLElement {
|
||||
UMLOperation() {
|
||||
this.getName() = "ownedOperation" and
|
||||
this.getAttribute("type").getValue() = "uml:Operation" and
|
||||
this.getParent() instanceof UMLType
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type that contains this operation.
|
||||
*/
|
||||
UMLType getUMLType() { result.getUMLOperation() = this }
|
||||
|
||||
/**
|
||||
* Gets the C function corresponding to this operation, if any.
|
||||
*/
|
||||
Function getCFunction() {
|
||||
result.hasName(this.getUMLName()) and
|
||||
result.getDeclaringType() = this.getUMLType().getCType()
|
||||
}
|
||||
|
||||
override string toString() { result = "+ " + this.getUMLName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A UML property that has an association.
|
||||
*/
|
||||
class UMLAssociation extends UMLProperty {
|
||||
UMLAssociation() { this.hasAttribute("association") }
|
||||
|
||||
/**
|
||||
* Gets the property that this property is associated with.
|
||||
*/
|
||||
UMLAssociation getConverse() {
|
||||
this.getAttribute("association").getValue() = result.getAttribute("association").getValue() and
|
||||
this != result
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this property.
|
||||
*/
|
||||
string getLabel() { result = this.getAttribute("name").getValue() }
|
||||
|
||||
/**
|
||||
* Gets the C field corresponding to this property, if any.
|
||||
*/
|
||||
override Field getCField() {
|
||||
result.hasName(this.getLabel()) and
|
||||
result.getDeclaringType() = this.getSource().getCType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class that this association is contained in.
|
||||
*/
|
||||
UMLClass getSource() { result = this.getParent() }
|
||||
|
||||
/**
|
||||
* Gets the class that this association is associated with.
|
||||
*/
|
||||
UMLClass getDest() { result = this.getConverse().getParent() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A UML element representing inheritance.
|
||||
*
|
||||
* In other words, an `interfaceRealization` whose `type` attribute has
|
||||
* value `uml:InterfaceRealization`.
|
||||
*/
|
||||
class UMLInheritance extends UMLElement {
|
||||
UMLInheritance() {
|
||||
this.getName() = "interfaceRealization" and
|
||||
this.getAttributeValue("type") = "uml:InterfaceRealization"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type that is being inherited from.
|
||||
*/
|
||||
UMLType getUMLSupplier() {
|
||||
exists(UMLElement e |
|
||||
e.getName() = "supplier" and
|
||||
result.getUMLReference() = e and
|
||||
e = this.getAChild()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type that is inheriting.
|
||||
*/
|
||||
UMLType getUMLClient() {
|
||||
exists(UMLElement e |
|
||||
e.getName() = "client" and
|
||||
result.getUMLReference() = e and
|
||||
e = this.getAChild()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = this.getUMLClient().getUMLName() + " implements " + this.getUMLSupplier().getUMLName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A UML element representing a package.
|
||||
*/
|
||||
class UMLPackage extends UMLElement {
|
||||
UMLPackage() { this.getName() = "Package" }
|
||||
|
||||
/**
|
||||
* Gets a class in this package.
|
||||
*/
|
||||
UMLClass getAClass() { result.getAChild().getAChild() = this.getUMLReference() }
|
||||
|
||||
/**
|
||||
* Gets the parent package of this package, if any.
|
||||
*/
|
||||
UMLPackage parentPackage() { result.getAChild().getAChild() = this }
|
||||
|
||||
/**
|
||||
* Gets the qualified name of this package. If this package is in a
|
||||
* parent package then this is `parent.package`; otherwise it is just
|
||||
* `package`.
|
||||
*/
|
||||
string getUMLQualifiedName() {
|
||||
if exists(this.parentPackage())
|
||||
then result = this.parentPackage().getUMLQualifiedName() + "." + this.getUMLName()
|
||||
else result = this.getUMLName()
|
||||
}
|
||||
|
||||
override string toString() { result = this.getUMLQualifiedName() }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
int x = int();
|
||||
float y = float();
|
||||
double z = double();
|
||||
|
||||
/* This produces a getValueText() of 0 for R() in line 9, which is debatable. */
|
||||
struct R {};
|
||||
struct S {
|
||||
S() : S(R()) { }
|
||||
S(R) { }
|
||||
};
|
||||
S s;
|
||||
@@ -1,4 +0,0 @@
|
||||
| constants.cpp:2:9:2:13 | 0 | int() |
|
||||
| constants.cpp:3:11:3:17 | 0.0 | float() |
|
||||
| constants.cpp:4:12:4:19 | 0.0 | double() |
|
||||
| constants.cpp:9:11:9:13 | 0 | 0 |
|
||||
@@ -1,4 +0,0 @@
|
||||
import cpp
|
||||
|
||||
from Expr e
|
||||
select e, e.getValueText()
|
||||
@@ -1,2 +0,0 @@
|
||||
| duplicate_functions.cpp:0:0:0:0 | duplicate_functions.cpp | 42 |
|
||||
| file://:0:0:0:0 | | 0 |
|
||||
@@ -1 +0,0 @@
|
||||
Metrics/Files/FLinesOfDuplicatedCode.ql
|
||||
@@ -1,47 +0,0 @@
|
||||
int x;
|
||||
|
||||
void Void1()
|
||||
{
|
||||
x = 0;
|
||||
x = 1;
|
||||
x = 2;
|
||||
x = 3;
|
||||
x = 4;
|
||||
x = 5;
|
||||
x = 6;
|
||||
}
|
||||
|
||||
void Void2()
|
||||
{
|
||||
x = 0;
|
||||
x = 1;
|
||||
x = 2;
|
||||
x = 3;
|
||||
x = 4;
|
||||
x = 5;
|
||||
x = 6;
|
||||
}
|
||||
|
||||
int Int1()
|
||||
{
|
||||
x = 0;
|
||||
++x;
|
||||
++x;
|
||||
--x;
|
||||
--x;
|
||||
++x;
|
||||
--x;
|
||||
return x;
|
||||
}
|
||||
|
||||
int Int2()
|
||||
{
|
||||
x = 0;
|
||||
++x;
|
||||
++x;
|
||||
--x;
|
||||
--x;
|
||||
++x;
|
||||
--x;
|
||||
return x;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
| duplicate_functions.cpp:3:6:3:10 | definition of Void1 | Function Void1 is duplicated at $@. | duplicate_functions.cpp:14:6:14:10 | definition of Void2 | duplicate_functions.cpp:14 |
|
||||
| duplicate_functions.cpp:14:6:14:10 | definition of Void2 | Function Void2 is duplicated at $@. | duplicate_functions.cpp:3:6:3:10 | definition of Void1 | duplicate_functions.cpp:3 |
|
||||
| duplicate_functions.cpp:25:5:25:8 | definition of Int1 | Function Int1 is duplicated at $@. | duplicate_functions.cpp:37:5:37:8 | definition of Int2 | duplicate_functions.cpp:37 |
|
||||
| duplicate_functions.cpp:37:5:37:8 | definition of Int2 | Function Int2 is duplicated at $@. | duplicate_functions.cpp:25:5:25:8 | definition of Int1 | duplicate_functions.cpp:25 |
|
||||
@@ -1 +0,0 @@
|
||||
external/DuplicateFunction.ql
|
||||
@@ -1,2 +0,0 @@
|
||||
| test.c:0:0:0:0 | test.c | 10 |
|
||||
| file://:0:0:0:0 | | 0 |
|
||||
@@ -1 +0,0 @@
|
||||
Metrics/Files/FLinesOfDuplicatedCode.ql
|
||||
@@ -1 +0,0 @@
|
||||
| 2 |
|
||||
@@ -1,5 +0,0 @@
|
||||
import cpp
|
||||
|
||||
// Provided tokenisation succeeded, we ought to have some
|
||||
// @duplication rows.
|
||||
select count(@duplication d)
|
||||
@@ -1,18 +0,0 @@
|
||||
|
||||
#define foo "/*bar"
|
||||
#define bar aaa \ bbb
|
||||
|
||||
int i;
|
||||
|
||||
void f(void) {
|
||||
i = 1;
|
||||
i = 2;
|
||||
i = 3;
|
||||
}
|
||||
|
||||
void g(void) {
|
||||
i = 1;
|
||||
i = 2;
|
||||
i = 3;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user