Compare commits

..

10 Commits

Author SHA1 Message Date
Ian Wright
a716d39370 repatch 2021-12-17 16:59:31 +00:00
Ian Wright
335b2466a9 patch again 2021-12-17 16:49:40 +00:00
Ian Wright
96ae9617ec post cherry-pick patch 2021-12-17 16:43:07 +00:00
Henry Mercer
82029663b2 JS: Push FeaturizationConfig context into more predicates 2021-12-17 16:03:15 +00:00
Henry Mercer
4cd15ba654 JS: Only featurize endpoints that are part of a flow path 2021-12-17 15:58:46 +00:00
Ian Wright
c17c10e450 Revert "JS: Push FeaturizationConfig context into more predicates"
This reverts commit a0f479d503.
2021-12-17 15:54:03 +00:00
Henry Mercer
a0f479d503 JS: Push FeaturizationConfig context into more predicates 2021-12-17 13:54:25 +00:00
Ian Wright
24a5e8a8e1 bump the release number 2021-12-17 13:12:05 +00:00
Henry Mercer
427cdf480a JS: Update featurization for absent features optimization
Absent features are now represented implicitly by the absence of a row
in the `tokenFeatures` relation, rather than explicitly by an empty
string. This leads to improved runtime performance. To enable this
implicit representation, we pass the set of supported token features to
the `scoreEndpoints` HOP. Requires CodeQL CLI v2.7.4.
2021-12-17 13:10:10 +00:00
Ian Wright
8e1f2645cb bump the release number 2021-12-17 13:10:10 +00:00
1323 changed files with 30336 additions and 41209 deletions

View File

@@ -4,12 +4,10 @@
"*/ql/lib/qlpack.yml",
"*/ql/test/qlpack.yml",
"*/ql/examples/qlpack.yml",
"*/upgrades/qlpack.yml",
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
"csharp/ql/campaigns/Solorigate/lib/qlpack.yml",
"csharp/ql/campaigns/Solorigate/src/qlpack.yml",
"csharp/ql/campaigns/Solorigate/test/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml",
"ruby/extractor-pack/codeql-extractor.yml",

3
.github/labeler.yml vendored
View File

@@ -26,6 +26,3 @@ documentation:
- "**/*.qhelp"
- "**/*.md"
- docs/**/*
"QL-for-QL":
- ql/**/*

View File

@@ -7,7 +7,6 @@ on:
- "*/ql/src/**/*.ql"
- "*/ql/src/**/*.qll"
- "!**/experimental/**"
- "!ql/**"
jobs:
check-change-note:

View File

@@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v2
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@esbena/ql
with:
languages: javascript # does not matter
- name: Get CodeQL version
@@ -56,46 +56,27 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Cache entire extractor
id: cache-extractor
uses: actions/cache@v2
with:
path: |
ql/target/release/ql-autobuilder
ql/target/release/ql-autobuilder.exe
ql/target/release/ql-extractor
ql/target/release/ql-extractor.exe
key: ${{ runner.os }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
- name: Cache cargo
if: steps.cache-extractor.outputs.cache-hit != 'true'
uses: actions/cache@v2
- uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
ql/target
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Check formatting
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd ql; cargo fmt --all -- --check
- name: Build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd ql; cargo build --verbose
- name: Run tests
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd ql; cargo test --verbose
- name: Release build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd ql; cargo build --release
- name: Generate dbscheme
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: ql/target/release/ql-generator --dbscheme ql/ql/src/ql.dbscheme --library ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v2
with:
name: extractor-ubuntu-latest
path: |
ql/target/release/ql-autobuilder
ql/target/release/ql-autobuilder.exe
ql/target/release/ql-extractor
ql/target/release/ql-extractor.exe
retention-days: 1
@@ -120,10 +101,6 @@ jobs:
unzip query-pack-zip/*.zip -d pack
cp -r ql/codeql-extractor.yml ql/tools ql/ql/src/ql.dbscheme.stats pack/
mkdir -p pack/tools/linux64
if [[ -f linux64/ql-autobuilder ]]; then
cp linux64/ql-autobuilder pack/tools/linux64/autobuilder
chmod +x pack/tools/linux64/autobuilder
fi
if [[ -f linux64/ql-extractor ]]; then
cp linux64/ql-extractor pack/tools/linux64/extractor
chmod +x pack/tools/linux64/extractor
@@ -135,16 +112,13 @@ jobs:
name: codeql-ql-pack
path: codeql-ql.zip
retention-days: 1
analyze:
analyze:
runs-on: ubuntu-latest
strategy:
matrix:
folder: [cpp, csharp, java, javascript, python, ql, ruby]
needs:
needs:
- package
steps:
steps:
- name: Download pack
uses: actions/download-artifact@v2
with:
@@ -166,27 +140,13 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v2
- name: Create CodeQL config file
run: |
echo "paths:" > ${CONF}
echo " - ${FOLDER}" >> ${CONF}
echo "paths-ignore:" >> ${CONF}
echo " - ql/ql/test" >> ${CONF}
echo "Config file: "
cat ${CONF}
env:
CONF: ./ql-for-ql-config.yml
FOLDER: ${{ matrix.folder }}
- name: Initialize CodeQL
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@esbena/ql
with:
languages: ql
db-location: ${{ runner.temp }}/db
config-file: ./ql-for-ql-config.yml
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@erik-krogh/ql
with:
category: "ql-for-ql-${{ matrix.folder }}"
uses: github/codeql-action/analyze@esbena/ql

View File

@@ -26,7 +26,7 @@ jobs:
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@esbena/ql
with:
languages: javascript # does not matter
- uses: actions/cache@v2

View File

@@ -4,11 +4,11 @@ on:
push:
branches: [main]
paths:
- "ql/**"
- ql/*
pull_request:
branches: [main]
paths:
- "ql/**"
- ql/*
env:
CARGO_TERM_COLOR: always
@@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v2
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@esbena/ql
with:
languages: javascript # does not matter
- uses: actions/cache@v2

View File

@@ -1,11 +1,11 @@
# CodeQL
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
## How do I learn CodeQL and run queries?
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
You can use the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension or the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com (Semmle Legacy product) to try out your queries on any open source project that's currently being analyzed.
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension to try out your queries on any open source project that's currently being analyzed.
## Contributing
@@ -13,7 +13,7 @@ We welcome contributions to our standard library and standard checks. Do you hav
## License
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com). The use of CodeQL on open source code is licensed under specific [Terms & Conditions](https://securitylab.github.com/tools/codeql/license/) UNLESS you have a commercial license in place. If you'd like to use CodeQL with a commercial codebase, please [contact us](https://github.com/enterprise/contact) for further help.
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com).
## Visual Studio Code integration

View File

@@ -1,51 +0,0 @@
# benjamin-buttons.md
This file describes the changes that have been applied to
the library to make it behave as if it was younger.
## TaintedPath.ql
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
Sinks added between 2018-08-02 and 2020-01-01 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+pathinjection
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+tainted-path
Sinks from the "graceful-fs" and "fs-extra" (added before the open-sourcing squash).
## Xss.ql
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
- recursive type tracking for `jQuery::dollar`, `DOM::domValueRef`.
## SqlInjection.ql
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-089
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
Sinks added between 2018-08-02 and 2020-01-01 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-089
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sql
TypeTracking in SQL.qll (added before the open-sourcing squash)
The model of `mssql` and `sequelize` (added before the open-sourcing squash)
## PseudoProperties
Pseudo-properties (`$name$`) used in type-tracking and global dataflow configurations have been disabled.
Found by searching for `"\$.*\$"`.

View File

@@ -452,15 +452,9 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll"
],
"CryptoAlgorithms Python/JS/Ruby": [
"CryptoAlgorithms Python/JS": [
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll",
"ruby/ql/lib/codeql/ruby/security/CryptoAlgorithms.qll"
],
"CryptoAlgorithmNames Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/internal/CryptoAlgorithmNames.qll",
"python/ql/lib/semmle/python/concepts/internal/CryptoAlgorithmNames.qll",
"ruby/ql/lib/codeql/ruby/security/internal/CryptoAlgorithmNames.qll"
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll"
],
"SensitiveDataHeuristics Python/JS": [
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",

View File

@@ -0,0 +1,4 @@
lgtm,codescanning
* The QL library `semmle.code.cpp.commons.Exclusions` now contains a predicate
`isFromSystemMacroDefinition` for identifying code that originates from a
macro outside the project being analyzed.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* A new query `cpp/non-https-url` has been added for C/C++. The query flags uses of `http` URLs that might be better replaced with `https`.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* A new query `cpp/certificate-not-checked` has been added for C/C++. The query flags unsafe use of OpenSSL and similar libraries.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* A new query `cpp/certificate-result-conflation` has been added for C/C++. The query flags unsafe use of OpenSSL and similar libraries.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* The "Cleartext transmission of sensitive information" (`cpp/cleartext-transmission`) query has been improved, reducing the number of false positive results when encryption is present.

View File

@@ -1,5 +1,3 @@
## 0.0.5
## 0.0.4
### New Features

View File

@@ -73,7 +73,7 @@ class Options extends string {
* __assume(0);
* ```
* (note that in this case if the hint is wrong and the expression is reached at
* runtime, the program's behavior is undefined)
* runtime, the program's behaviour is undefined)
*/
predicate exprExits(Expr e) {
e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or

View File

@@ -50,7 +50,7 @@ class CustomOptions extends Options {
* __assume(0);
* ```
* (note that in this case if the hint is wrong and the expression is reached at
* runtime, the program's behavior is undefined)
* runtime, the program's behaviour is undefined)
*/
override predicate exprExits(Expr e) { Options.super.exprExits(e) }

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.5
lastReleaseVersion: 0.0.4

View File

@@ -37,7 +37,7 @@ abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition {
* dependencies. Without this information, range analysis might work for
* simple cases but will go into infinite loops on complex code.
*
* For example, when modeling the definition by reference in a call to an
* For example, when modelling the definition by reference in a call to an
* overloaded `operator=`, written as `v = e`, the definition of `(this, v)`
* depends on `e`.
*/

View File

@@ -5,7 +5,7 @@
* `Instruction` level), and then using the array length analysis and the range
* analysis together to prove that some of these pointer dereferences are safe.
*
* The analysis is soundy, i.e. it is sound if no undefined behavior is present
* The analysis is soundy, i.e. it is sound if no undefined behaviour is present
* in the program.
* Furthermore, it crucially depends on the soundiness of the range analysis and
* the array length analysis.

View File

@@ -1,7 +1,8 @@
name: codeql/cpp-all
version: 0.0.6-dev
version: 0.0.5-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
library: true
upgrades: upgrades
dependencies:
codeql/cpp-upgrades: ^0.0.3

View File

@@ -153,11 +153,9 @@ library class SSAHelper extends int {
* Modern Compiler Implementation by Andrew Appel.
*/
private predicate frontier_phi_node(StackVariable v, BasicBlock b) {
exists(BasicBlock x |
dominanceFrontier(x, b) and ssa_defn_rec(pragma[only_bind_into](v), pragma[only_bind_into](x))
) and
exists(BasicBlock x | dominanceFrontier(x, b) and ssa_defn_rec(v, x)) and
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
live_at_start_of_bb(pragma[only_bind_into](v), b)
live_at_start_of_bb(v, b)
}
private predicate ssa_defn_rec(StackVariable v, BasicBlock b) {

View File

@@ -15,23 +15,11 @@ module Consistency {
class ConsistencyConfiguration extends TConsistencyConfiguration {
string toString() { none() }
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
predicate uniqueEnclosingCallableExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
predicate uniqueNodeLocationExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
predicate missingLocationExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
predicate postWithInFlowExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
predicate reverseReadExclude(Node n) { none() }
}
private class RelevantNode extends Node {
@@ -58,7 +46,6 @@ module Consistency {
n instanceof RelevantNode and
c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
msg = "Node should have one enclosing callable but has " + c + "."
)
}
@@ -79,7 +66,6 @@ module Consistency {
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
msg = "Node should have one location but has " + c + "."
)
}
@@ -90,8 +76,7 @@ module Consistency {
strictcount(Node n |
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
)
) and
msg = "Nodes without location: " + c
)
@@ -187,7 +172,6 @@ module Consistency {
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
msg = "Origin of readStep is missing a PostUpdateNode."
}

View File

@@ -118,7 +118,7 @@ class LambdaCapture extends Locatable, @lambdacapture {
* An identifier is captured by reference if:
* - It is explicitly captured by reference.
* - It is implicitly captured, and the lambda's default capture mode is by-reference.
* - The identifier is "this". [Said behavior is dictated by the C++11 standard, but it
* - The identifier is "this". [Said behaviour is dictated by the C++11 standard, but it
* is actually "*this" being captured rather than "this".]
*/
predicate isCapturedByReference() { lambda_capture(this, _, _, _, true, _, _) }

View File

@@ -63,7 +63,7 @@ private module VirtualDispatch {
this.flowsFrom(other, allowOtherFromArg)
|
// Call argument
exists(DataFlowCall call, Position i |
exists(DataFlowCall call, int i |
other
.(DataFlow::ParameterNode)
.isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
@@ -268,6 +268,16 @@ Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
)
}
/** A parameter position represented by an integer. */
class ParameterPosition extends int {
ParameterPosition() { any(ParameterNode p).isParameterOf(_, this) }
}
/** An argument position represented by an integer. */
class ArgumentPosition extends int {
ArgumentPosition() { any(ArgumentNode a).argumentOf(_, this) }
}
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }

View File

@@ -15,23 +15,11 @@ module Consistency {
class ConsistencyConfiguration extends TConsistencyConfiguration {
string toString() { none() }
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
predicate uniqueEnclosingCallableExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
predicate uniqueNodeLocationExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
predicate missingLocationExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
predicate postWithInFlowExclude(Node n) { none() }
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
predicate reverseReadExclude(Node n) { none() }
}
private class RelevantNode extends Node {
@@ -58,7 +46,6 @@ module Consistency {
n instanceof RelevantNode and
c = count(nodeGetEnclosingCallable(n)) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
msg = "Node should have one enclosing callable but has " + c + "."
)
}
@@ -79,7 +66,6 @@ module Consistency {
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
msg = "Node should have one location but has " + c + "."
)
}
@@ -90,8 +76,7 @@ module Consistency {
strictcount(Node n |
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
)
) and
msg = "Nodes without location: " + c
)
@@ -187,7 +172,6 @@ module Consistency {
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
msg = "Origin of readStep is missing a PostUpdateNode."
}

View File

@@ -27,7 +27,7 @@ abstract class ArgumentNode extends OperandNode {
* Holds if this argument occurs at the given position in the given call.
* The instance argument is considered to have index `-1`.
*/
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
abstract predicate argumentOf(DataFlowCall call, int pos);
/** Gets the call in which this node is an argument. */
DataFlowCall getCall() { this.argumentOf(result, _) }
@@ -42,9 +42,7 @@ private class PrimaryArgumentNode extends ArgumentNode {
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
op = call.getArgumentOperand(pos.(DirectPosition).getIndex())
}
override predicate argumentOf(DataFlowCall call, int pos) { op = call.getArgumentOperand(pos) }
override string toString() {
exists(Expr unconverted |
@@ -73,9 +71,9 @@ private class SideEffectArgumentNode extends ArgumentNode {
SideEffectArgumentNode() { op = read.getSideEffectOperand() }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
override predicate argumentOf(DataFlowCall call, int pos) {
read.getPrimaryInstruction() = call and
pos.(IndirectionPosition).getIndex() = read.getIndex()
pos = getArgumentPosOfSideEffect(read.getIndex())
}
override string toString() {
@@ -92,54 +90,6 @@ private class SideEffectArgumentNode extends ArgumentNode {
}
}
/** A parameter position represented by an integer. */
class ParameterPosition = Position;
/** An argument position represented by an integer. */
class ArgumentPosition = Position;
class Position extends TPosition {
abstract string toString();
}
class DirectPosition extends TDirectPosition {
int index;
DirectPosition() { this = TDirectPosition(index) }
string toString() {
index = -1 and
result = "this"
or
index != -1 and
result = index.toString()
}
int getIndex() { result = index }
}
class IndirectionPosition extends TIndirectionPosition {
int index;
IndirectionPosition() { this = TIndirectionPosition(index) }
string toString() {
index = -1 and
result = "this"
or
index != -1 and
result = index.toString()
}
int getIndex() { result = index }
}
newtype TPosition =
TDirectPosition(int index) { exists(any(CallInstruction c).getArgument(index)) } or
TIndirectionPosition(int index) {
exists(ReadSideEffectInstruction instr | instr.getIndex() = index)
}
private newtype TReturnKind =
TNormalReturnKind() or
TIndirectReturnKind(ParameterIndex index)

View File

@@ -490,6 +490,19 @@ class ExprNode extends InstructionNode {
override string toString() { result = this.asConvertedExpr().toString() }
}
/**
* INTERNAL: do not use. Translates a parameter/argument index into a negative
* number that denotes the index of its side effect (pointer indirection).
*/
bindingset[index]
int getArgumentPosOfSideEffect(int index) {
// -1 -> -2
// 0 -> -3
// 1 -> -4
// ...
result = -3 - index
}
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
@@ -512,7 +525,7 @@ class ParameterNode extends InstructionNode {
* implicit `this` parameter is considered to have position `-1`, and
* pointer-indirection parameters are at further negative positions.
*/
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
predicate isParameterOf(Function f, int pos) { none() } // overridden by subclasses
}
/** An explicit positional parameter, not including `this` or `...`. */
@@ -521,8 +534,8 @@ private class ExplicitParameterNode extends ParameterNode {
ExplicitParameterNode() { exists(instr.getParameter()) }
override predicate isParameterOf(Function f, ParameterPosition pos) {
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
override predicate isParameterOf(Function f, int pos) {
f.getParameter(pos) = instr.getParameter()
}
/** Gets the `Parameter` associated with this node. */
@@ -537,8 +550,8 @@ class ThisParameterNode extends ParameterNode {
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
override predicate isParameterOf(Function f, ParameterPosition pos) {
pos.(DirectPosition).getIndex() = -1 and instr.getEnclosingFunction() = f
override predicate isParameterOf(Function f, int pos) {
pos = -1 and instr.getEnclosingFunction() = f
}
override string toString() { result = "this" }
@@ -548,12 +561,12 @@ class ThisParameterNode extends ParameterNode {
class ParameterIndirectionNode extends ParameterNode {
override InitializeIndirectionInstruction instr;
override predicate isParameterOf(Function f, ParameterPosition pos) {
override predicate isParameterOf(Function f, int pos) {
exists(int index |
instr.getEnclosingFunction() = f and
instr.hasIndex(index)
|
pos.(IndirectionPosition).getIndex() = index
pos = getArgumentPosOfSideEffect(index)
)
}

View File

@@ -659,15 +659,4 @@ module Consistency {
not phiHasInputFromBlock(_, def, _) and
not uncertainWriteDefinitionInput(_, def)
}
query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) |
ssaDefReachesReadWithinBlock(v, def, bb, i) and
(bb != bbDef or i < iDef)
or
ssaDefReachesRead(v, def, bb, i) and
not ssaDefReachesReadWithinBlock(v, def, bb, i) and
not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _)
)
}
}

View File

@@ -51,6 +51,16 @@ private newtype TDefOrUse =
TExplicitUse(Operand op) { isExplicitUse(op) } or
TReturnParamIndirection(Operand op) { returnParameterIndirection(op, _) }
pragma[nomagic]
private int getRank(DefOrUse defOrUse, IRBlock block) {
defOrUse =
rank[result](int i, DefOrUse cand |
block.getInstruction(i) = toInstruction(cand)
|
cand order by i
)
}
private class DefOrUse extends TDefOrUse {
/** Gets the instruction associated with this definition, if any. */
Instruction asDef() { none() }
@@ -64,10 +74,9 @@ private class DefOrUse extends TDefOrUse {
/** Gets the block of this definition or use. */
abstract IRBlock getBlock();
/** Holds if this definition or use has index `index` in block `block`. */
final predicate hasIndexInBlock(IRBlock block, int index) {
block.getInstruction(index) = toInstruction(this)
}
/** Holds if this definition or use has rank `rank` in block `block`. */
cached
final predicate hasRankInBlock(IRBlock block, int rnk) { rnk = getRank(this, block) }
/** Gets the location of this element. */
abstract Cpp::Location getLocation();
@@ -304,8 +313,8 @@ cached
private module Cached {
private predicate defUseFlow(Node nodeFrom, Node nodeTo) {
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, DefOrUse defOrUse, Use use |
defOrUse.hasIndexInBlock(bb1, i1) and
use.hasIndexInBlock(bb2, i2) and
defOrUse.hasRankInBlock(bb1, i1) and
use.hasRankInBlock(bb2, i2) and
adjacentDefRead(_, bb1, i1, bb2, i2) and
nodeFrom.asInstruction() = toInstruction(defOrUse) and
flowOutOfAddressStep(use.getOperand(), nodeTo)
@@ -317,9 +326,9 @@ private module Cached {
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Def def, Use use |
nodeFrom.isTerminal() and
def.getInstruction() = nodeFrom.getStoreInstruction() and
def.hasIndexInBlock(bb1, i1) and
def.hasRankInBlock(bb1, i1) and
adjacentDefRead(_, bb1, i1, bb2, i2) and
use.hasIndexInBlock(bb2, i2) and
use.hasRankInBlock(bb2, i2) and
flowOutOfAddressStep(use.getOperand(), nodeTo)
)
or
@@ -350,8 +359,8 @@ private module Cached {
private predicate fromReadNode(ReadNode nodeFrom, Node nodeTo) {
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Use use1, Use use2 |
use1.hasIndexInBlock(bb1, i1) and
use2.hasIndexInBlock(bb2, i2) and
use1.hasRankInBlock(bb1, i1) and
use2.hasRankInBlock(bb2, i2) and
use1.getOperand().getDef() = nodeFrom.getInstruction() and
adjacentDefRead(_, bb1, i1, bb2, i2) and
flowOutOfAddressStep(use2.getOperand(), nodeTo)
@@ -362,7 +371,7 @@ private module Cached {
exists(PhiNode phi, Use use, IRBlock block, int rnk |
phi = nodeFrom.getPhiNode() and
adjacentDefRead(phi, _, _, block, rnk) and
use.hasIndexInBlock(block, rnk) and
use.hasRankInBlock(block, rnk) and
flowOutOfAddressStep(use.getOperand(), nodeTo)
)
}
@@ -370,7 +379,7 @@ private module Cached {
private predicate toPhiNode(Node nodeFrom, SsaPhiNode nodeTo) {
// Flow to phi nodes
exists(Def def, IRBlock block, int rnk |
def.hasIndexInBlock(block, rnk) and
def.hasRankInBlock(block, rnk) and
nodeTo.hasInputAtRankInBlock(block, rnk)
|
exists(StoreNodeInstr storeNode |
@@ -503,8 +512,8 @@ private module Cached {
|
store = def.getInstruction() and
store.getSourceValueOperand() = operand and
def.hasIndexInBlock(block1, rnk1) and
use.hasIndexInBlock(block2, rnk2) and
def.hasRankInBlock(block1, rnk1) and
use.hasRankInBlock(block2, rnk2) and
adjacentDefRead(_, block1, rnk1, block2, rnk2)
|
// The shared SSA library has determined that `use` is the next use of the operand
@@ -534,12 +543,12 @@ private module Cached {
not operand = getSourceAddressOperand(_) and
exists(Use use1, Use use2, IRBlock block1, int rnk1, IRBlock block2, int rnk2 |
use1.getOperand() = operand and
use1.hasIndexInBlock(block1, rnk1) and
use1.hasRankInBlock(block1, rnk1) and
// Don't flow to the next use if this use is part of a store operation that totally
// overrides a variable.
not explicitWrite(true, _, use1.getOperand().getDef()) and
adjacentDefRead(_, block1, rnk1, block2, rnk2) and
use2.hasIndexInBlock(block2, rnk2) and
use2.hasRankInBlock(block2, rnk2) and
flowOutOfAddressStep(use2.getOperand(), nodeTo)
)
or
@@ -611,7 +620,7 @@ import Cached
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
DataFlowImplCommon::forceCachingInSameStage() and
exists(Def def |
def.hasIndexInBlock(bb, i) and
def.hasRankInBlock(bb, i) and
v = def.getSourceVariable() and
(if def.isCertain() then certain = true else certain = false)
)
@@ -623,7 +632,7 @@ predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
*/
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
exists(Use use |
use.hasIndexInBlock(bb, i) and
use.hasRankInBlock(bb, i) and
v = use.getSourceVariable() and
certain = true
)

View File

@@ -266,20 +266,6 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
bitOffset = Ints::unknown()
}
pragma[nomagic]
private predicate initializeParameterInstructionHasVariable(
IRVariable var, InitializeParameterInstruction init
) {
init.getIRVariable() = var
}
private predicate instructionInitializesThisInFunction(
Language::Function f, InitializeParameterInstruction init
) {
initializeParameterInstructionHasVariable(any(IRThisVariable var), pragma[only_bind_into](init)) and
init.getEnclosingFunction() = f
}
private predicate isArgumentForParameter(
CallInstruction ci, Operand operand, InitializeParameterInstruction init
) {
@@ -289,7 +275,8 @@ private predicate isArgumentForParameter(
(
init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
instructionInitializesThisInFunction(f, init) and
init.getIRVariable() instanceof IRThisVariable and
unique( | | init.getEnclosingFunction()) = f and
operand instanceof ThisArgumentOperand
) and
not Language::isFunctionVirtual(f) and

View File

@@ -266,20 +266,6 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
bitOffset = Ints::unknown()
}
pragma[nomagic]
private predicate initializeParameterInstructionHasVariable(
IRVariable var, InitializeParameterInstruction init
) {
init.getIRVariable() = var
}
private predicate instructionInitializesThisInFunction(
Language::Function f, InitializeParameterInstruction init
) {
initializeParameterInstructionHasVariable(any(IRThisVariable var), pragma[only_bind_into](init)) and
init.getEnclosingFunction() = f
}
private predicate isArgumentForParameter(
CallInstruction ci, Operand operand, InitializeParameterInstruction init
) {
@@ -289,7 +275,8 @@ private predicate isArgumentForParameter(
(
init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
instructionInitializesThisInFunction(f, init) and
init.getIRVariable() instanceof IRThisVariable and
unique( | | init.getEnclosingFunction()) = f and
operand instanceof ThisArgumentOperand
) and
not Language::isFunctionVirtual(f) and

View File

@@ -9,7 +9,6 @@ import semmle.code.cpp.controlflow.Dominance
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
import semmle.code.cpp.controlflow.Guards
/**
* Holds if the value of `use` is guarded using `abs`.
@@ -17,16 +16,53 @@ import semmle.code.cpp.controlflow.Guards
predicate guardedAbs(Operation e, Expr use) {
exists(FunctionCall fc | fc.getTarget().getName() = ["abs", "labs", "llabs", "imaxabs"] |
fc.getArgument(0).getAChild*() = use and
exists(GuardCondition c | c.ensuresLt(fc, _, _, e.getBasicBlock(), true))
guardedLesser(e, fc)
)
}
/**
* Gets the position of `stmt` in basic block `block` (this is a thin layer
* over `BasicBlock.getNode`, intended to improve performance).
*/
pragma[noinline]
private int getStmtIndexInBlock(BasicBlock block, Stmt stmt) { block.getNode(result) = stmt }
pragma[inline]
private predicate stmtDominates(Stmt dominator, Stmt dominated) {
// In same block
exists(BasicBlock block, int dominatorIndex, int dominatedIndex |
dominatorIndex = getStmtIndexInBlock(block, dominator) and
dominatedIndex = getStmtIndexInBlock(block, dominated) and
dominatedIndex >= dominatorIndex
)
or
// In (possibly) different blocks
bbStrictlyDominates(dominator.getBasicBlock(), dominated.getBasicBlock())
}
/**
* Holds if the value of `use` is guarded to be less than something, and `e`
* is in code controlled by that guard (where the guard condition held).
*/
pragma[nomagic]
predicate guardedLesser(Operation e, Expr use) {
exists(GuardCondition c | c.ensuresLt(use, _, _, e.getBasicBlock(), true))
exists(IfStmt c, RelationalOperation guard |
use = guard.getLesserOperand().getAChild*() and
guard = c.getControllingExpr().getAChild*() and
stmtDominates(c.getThen(), e.getEnclosingStmt())
)
or
exists(Loop c, RelationalOperation guard |
use = guard.getLesserOperand().getAChild*() and
guard = c.getControllingExpr().getAChild*() and
stmtDominates(c.getStmt(), e.getEnclosingStmt())
)
or
exists(ConditionalExpr c, RelationalOperation guard |
use = guard.getLesserOperand().getAChild*() and
guard = c.getCondition().getAChild*() and
c.getThen().getAChild*() = e
)
or
guardedAbs(e, use)
}
@@ -35,8 +71,25 @@ predicate guardedLesser(Operation e, Expr use) {
* Holds if the value of `use` is guarded to be greater than something, and `e`
* is in code controlled by that guard (where the guard condition held).
*/
pragma[nomagic]
predicate guardedGreater(Operation e, Expr use) {
exists(GuardCondition c | c.ensuresLt(use, _, _, e.getBasicBlock(), false))
exists(IfStmt c, RelationalOperation guard |
use = guard.getGreaterOperand().getAChild*() and
guard = c.getControllingExpr().getAChild*() and
stmtDominates(c.getThen(), e.getEnclosingStmt())
)
or
exists(Loop c, RelationalOperation guard |
use = guard.getGreaterOperand().getAChild*() and
guard = c.getControllingExpr().getAChild*() and
stmtDominates(c.getStmt(), e.getEnclosingStmt())
)
or
exists(ConditionalExpr c, RelationalOperation guard |
use = guard.getGreaterOperand().getAChild*() and
guard = c.getCondition().getAChild*() and
c.getThen().getAChild*() = e
)
or
guardedAbs(e, use)
}

View File

@@ -1,10 +1,3 @@
## 0.0.5
### New Queries
* A new query `cpp/certificate-not-checked` has been added for C/C++. The query flags unsafe use of OpenSSL and similar libraries.
* A new query `cpp/certificate-result-conflation` has been added for C/C++. The query flags unsafe use of OpenSSL and similar libraries.
## 0.0.4
### New Queries

View File

@@ -26,8 +26,6 @@ where
// At least for C programs on Windows, BOOL is a common typedef for a type
// representing BoolType.
not bf.getType().hasName("BOOL") and
// GLib's gboolean is a typedef for a type representing BoolType.
not bf.getType().hasName("gboolean") and
// If this is true, then there cannot be unsigned sign extension or overflow.
not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and
not bf.isAnonymous() and

View File

@@ -3,7 +3,7 @@
"qhelp.dtd">
<qhelp>
<overview>
<p>The return value of a call to <code>snprintf</code> is the number of characters that <i>would have</i> been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behavior, for example:
<p>The return value of a call to <code>snprintf</code> is the number of characters that <i>would have</i> been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behaviour, for example:
</p>
</overview>

View File

@@ -5,7 +5,7 @@
* @kind path-problem
* @problem.severity warning
* @security-severity 8.6
* @precision high
* @precision medium
* @id cpp/uncontrolled-arithmetic
* @tags security
* external/cwe/cwe-190
@@ -82,11 +82,8 @@ predicate missingGuard(VariableAccess va, string effect) {
op.getUnspecifiedType().(IntegralType).isUnsigned() and
not op instanceof MulExpr
or
// overflow - only report signed integer overflow since unsigned overflow
// is well-defined.
op.getUnspecifiedType().(IntegralType).isSigned() and
missingGuardAgainstOverflow(op, va) and
effect = "overflow"
// overflow
missingGuardAgainstOverflow(op, va) and effect = "overflow"
)
}

View File

@@ -14,188 +14,105 @@
import cpp
import semmle.code.cpp.security.SensitiveExprs
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.models.interfaces.FlowSource
import DataFlow::PathGraph
/**
* A DataFlow node corresponding to a variable or function call that
* might contain or return a password or other sensitive information.
*/
class SensitiveNode extends DataFlow::Node {
SensitiveNode() {
this.asExpr() = any(SensitiveVariable sv).getInitializer().getExpr() or
this.asExpr().(VariableAccess).getTarget() =
any(SensitiveVariable sv).(GlobalOrNamespaceVariable) or
this.asUninitialized() instanceof SensitiveVariable or
this.asParameter() instanceof SensitiveVariable or
this.asExpr().(FunctionCall).getTarget() instanceof SensitiveFunction
}
}
/**
* A function that sends or receives data over a network.
*/
abstract class SendRecv extends Function {
/**
* Gets the expression for the socket or similar object used for sending or
* receiving data through the function call `call` (if any).
*/
abstract Expr getSocketExpr(Call call);
/**
* Gets the expression for the buffer to be sent from / received into through
* the function call `call`.
*/
abstract Expr getDataExpr(Call call);
}
/**
* A function that sends data over a network.
*/
class Send extends SendRecv instanceof RemoteFlowSinkFunction {
override Expr getSocketExpr(Call call) {
call.getTarget() = this and
exists(FunctionInput input, int arg |
super.hasSocketInput(input) and
input.isParameter(arg) and
result = call.getArgument(arg)
)
}
override Expr getDataExpr(Call call) {
call.getTarget() = this and
exists(FunctionInput input, int arg |
super.hasRemoteFlowSink(input, _) and
input.isParameterDeref(arg) and
result = call.getArgument(arg)
)
}
}
/**
* A function that receives data over a network.
*/
class Recv extends SendRecv instanceof RemoteFlowSourceFunction {
override Expr getSocketExpr(Call call) {
call.getTarget() = this and
exists(FunctionInput input, int arg |
super.hasSocketInput(input) and
input.isParameter(arg) and
result = call.getArgument(arg)
)
}
override Expr getDataExpr(Call call) {
call.getTarget() = this and
exists(FunctionOutput output, int arg |
super.hasRemoteFlowSource(output, _) and
output.isParameterDeref(arg) and
result = call.getArgument(arg)
)
}
}
/**
* A function call that sends or receives data over a network.
*
* note: function calls such as `write` may be writing to a network source
* or a file. We could attempt to determine which, and sort results into
* `cpp/cleartext-transmission` and perhaps `cpp/cleartext-storage-file`. In
* practice it usually isn't very important which query reports a result as
* long as its reported exactly once.
*
* We do exclude function calls that specify a constant socket, which is
* likely to mean standard input, standard output or a similar channel.
*/
abstract class NetworkSendRecv extends FunctionCall {
SendRecv target;
/**
* Gets the expression for the socket or similar object used for sending or
* receiving data (if any).
*/
abstract Expr getSocketExpr();
NetworkSendRecv() {
this.getTarget() = target and
// exclude calls based on the socket...
not exists(GVN g |
g = globalValueNumber(target.getSocketExpr(this)) and
(
// literal constant
globalValueNumber(any(Literal l)) = g
or
// variable (such as a global) initialized to a literal constant
exists(Variable v |
v.getInitializer().getExpr() instanceof Literal and
g = globalValueNumber(v.getAnAccess())
)
)
)
}
final Expr getDataExpr() { result = target.getDataExpr(this) }
/**
* Gets the expression for the buffer to be sent from / received into.
*/
abstract Expr getDataExpr();
}
/**
* A function call that sends data over a network.
*
* note: functions such as `write` may be writing to a network source or a file. We could attempt to determine which, and sort results into `cpp/cleartext-transmission` and perhaps `cpp/cleartext-storage-file`. In practice it usually isn't very important which query reports a result as long as its reported exactly once.
*/
class NetworkSend extends NetworkSendRecv {
override Send target;
RemoteFlowSinkFunction target;
NetworkSend() { target = this.getTarget() }
override Expr getSocketExpr() {
exists(FunctionInput input, int arg |
target.hasSocketInput(input) and
input.isParameter(arg) and
result = this.getArgument(arg)
)
}
override Expr getDataExpr() {
exists(FunctionInput input, int arg |
target.hasRemoteFlowSink(input, _) and
input.isParameterDeref(arg) and
result = this.getArgument(arg)
)
}
}
/**
* A function call that receives data over a network.
*/
class NetworkRecv extends NetworkSendRecv {
override Recv target;
}
RemoteFlowSourceFunction target;
/**
* An expression that is an argument or return value from an encryption or
* decryption call.
*/
class Encrypted extends Expr {
Encrypted() {
exists(FunctionCall fc |
fc.getTarget().getName().toLowerCase().regexpMatch(".*(crypt|encode|decode).*") and
(
this = fc or
this = fc.getAnArgument()
)
NetworkRecv() { target = this.getTarget() }
override Expr getSocketExpr() {
exists(FunctionInput input, int arg |
target.hasSocketInput(input) and
input.isParameter(arg) and
result = this.getArgument(arg)
)
}
override Expr getDataExpr() {
exists(FunctionOutput output, int arg |
target.hasRemoteFlowSource(output, _) and
output.isParameterDeref(arg) and
result = this.getArgument(arg)
)
}
}
/**
* Taint flow from a sensitive expression.
* Taint flow from a sensitive expression to a network operation with data
* tainted by that expression.
*/
class FromSensitiveConfiguration extends TaintTracking::Configuration {
FromSensitiveConfiguration() { this = "FromSensitiveConfiguration" }
class SensitiveSendRecvConfiguration extends TaintTracking::Configuration {
SensitiveSendRecvConfiguration() { this = "SensitiveSendRecvConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof SensitiveNode }
override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof SensitiveExpr }
override predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(NetworkSendRecv nsr).getDataExpr()
or
sink.asExpr() instanceof Encrypted
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
// flow through encryption functions to the return value (in case we can reach other sinks)
node2.asExpr().(Encrypted).(FunctionCall).getAnArgument() = node1.asExpr()
exists(NetworkSendRecv transmission |
sink.asExpr() = transmission.getDataExpr() and
// a zero socket descriptor is standard input, which is not interesting for this query.
not exists(Zero zero |
DataFlow::localFlow(DataFlow::exprNode(zero),
DataFlow::exprNode(transmission.getSocketExpr()))
)
)
}
}
from
FromSensitiveConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
NetworkSendRecv networkSendRecv, string msg
SensitiveSendRecvConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
NetworkSendRecv transmission, string msg
where
// flow from sensitive -> network data
config.hasFlowPath(source, sink) and
sink.getNode().asExpr() = networkSendRecv.getDataExpr() and
// no flow from sensitive -> evidence of encryption
not exists(DataFlow::Node encrypted |
config.hasFlow(source.getNode(), encrypted) and
encrypted.asExpr() instanceof Encrypted
) and
// construct result
if networkSendRecv instanceof NetworkSend
sink.getNode().asExpr() = transmission.getDataExpr() and
if transmission instanceof NetworkSend
then
msg =
"This operation transmits '" + sink.toString() +
@@ -204,4 +121,4 @@ where
msg =
"This operation receives into '" + sink.toString() +
"', which may put unencrypted sensitive data into $@"
select networkSendRecv, source, sink, msg, source, source.getNode().toString()
select transmission, source, sink, msg, source, source.getNode().asExpr().toString()

View File

@@ -89,83 +89,45 @@ predicate referenceTo(Expr source, Expr use) {
)
}
pragma[noinline]
predicate statCallWithPointer(Expr checkPath, Expr call, Expr e, Variable v) {
call = stat(checkPath, e) and
e.getAChild*().(VariableAccess).getTarget() = v
}
predicate checksPath(Expr check, Expr checkPath) {
// either:
// an access check
check = accessCheck(checkPath)
or
// a stat
check = stat(checkPath, _)
or
// access to a member variable on the stat buf
// (morally, this should be a use-use pair, but it seems unlikely
// that this variable will get reused in practice)
exists(Expr call, Expr e, Variable v |
statCallWithPointer(checkPath, call, e, v) and
check.(VariableAccess).getTarget() = v and
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
)
}
pragma[nomagic]
predicate checkPathControlsUse(Expr check, Expr checkPath, Expr use) {
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
guard.controls(use.getBasicBlock(), _)
) and
checksPath(pragma[only_bind_into](check), checkPath)
}
pragma[nomagic]
predicate fileNameOperationControlsUse(Expr check, Expr checkPath, Expr use) {
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
guard.controls(use.getBasicBlock(), _)
) and
pragma[only_bind_into](check) = filenameOperation(checkPath)
}
predicate checkUse(Expr check, Expr checkPath, FunctionCall use, Expr usePath) {
// `check` is part of a guard that controls `use`
checkPathControlsUse(check, checkPath, use) and
// `check` looks like a check on a filename
checksPath(check, checkPath) and
// `op` looks like an operation on a filename
use = filenameOperation(usePath)
or
// `check` is part of a guard that controls `use`
fileNameOperationControlsUse(check, checkPath, use) and
// another filename operation (null pointers can indicate errors)
check = filenameOperation(checkPath) and
// `op` looks like a sensitive operation on a filename
use = sensitiveFilenameOperation(usePath)
}
pragma[noinline]
predicate isCheckedPath(
Expr check, SsaDefinition def, StackVariable v, FunctionCall use, Expr usePath, Expr checkPath
) {
checkUse(check, checkPath, use, usePath) and
def.getAUse(v) = checkPath
}
pragma[noinline]
predicate isUsedPath(
Expr check, SsaDefinition def, StackVariable v, FunctionCall use, Expr usePath, Expr checkPath
) {
checkUse(check, checkPath, use, usePath) and
def.getAUse(v) = usePath
}
from Expr check, Expr checkPath, FunctionCall use, Expr usePath, SsaDefinition def, StackVariable v
from Expr check, Expr checkPath, FunctionCall use, Expr usePath
where
// `check` looks like a check on a filename
(
(
// either:
// an access check
check = accessCheck(checkPath)
or
// a stat
check = stat(checkPath, _)
or
// access to a member variable on the stat buf
// (morally, this should be a use-use pair, but it seems unlikely
// that this variable will get reused in practice)
exists(Expr call, Expr e, Variable v |
call = stat(checkPath, e) and
e.getAChild*().(VariableAccess).getTarget() = v and
check.(VariableAccess).getTarget() = v and
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
)
) and
// `op` looks like an operation on a filename
use = filenameOperation(usePath)
or
// another filename operation (null pointers can indicate errors)
check = filenameOperation(checkPath) and
// `op` looks like a sensitive operation on a filename
use = sensitiveFilenameOperation(usePath)
) and
// `checkPath` and `usePath` refer to the same SSA variable
isCheckedPath(check, def, v, use, usePath, checkPath) and
isUsedPath(check, def, v, use, usePath, checkPath)
exists(SsaDefinition def, StackVariable v |
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
) and
// the return value of `check` is used (possibly with one step of
// variable indirection) in a guard which controls `use`
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
guard.controls(use.(ControlFlowNode).getBasicBlock(), _)
)
select use,
"The $@ being operated upon was previously $@, but the underlying file may have been changed since then.",
usePath, "filename", check, "checked"

View File

@@ -1,5 +0,0 @@
---
category: minorAnalysis
---
* Added exception for GLib's gboolean to cpp/ambiguously-signed-bit-field.
This change reduces the number of false positives in the query.

View File

@@ -1,4 +0,0 @@
---
category: newQuery
---
* The "Uncontrolled data in arithmetic expression" (cpp/uncontrolled-arithmetic) query has been enhanced to reduce false positive results and its @precision increased to high.

View File

@@ -1,6 +0,0 @@
## 0.0.5
### New Queries
* A new query `cpp/certificate-not-checked` has been added for C/C++. The query flags unsafe use of OpenSSL and similar libraries.
* A new query `cpp/certificate-result-conflation` has been added for C/C++. The query flags unsafe use of OpenSSL and similar libraries.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.5
lastReleaseVersion: 0.0.4

View File

@@ -1,16 +0,0 @@
...
umask(0); // BAD
...
maskOut = S_IRWXG | S_IRWXO;
umask(maskOut); // GOOD
...
fchmod(fileno(fp), 0555 - maskOut); // BAD
...
fchmod(fileno(fp), 0555 & ~maskOut); // GOOD
...
umask(0666);
chmod(pathname, 0666); // BAD
...
umask(0022);
chmod(pathname, 0666); // GOOD
...

View File

@@ -1,23 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Finding for function calls that set file permissions that may have errors in use. Incorrect arithmetic for calculating the resolution mask, using the same mask in opposite functions, using a mask that is too wide.</p>
</overview>
<example>
<p>The following example demonstrates erroneous and fixed ways to use functions.</p>
<sample src="IncorrectPrivilegeAssignment.cpp" />
</example>
<references>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions">FIO06-C. Create files with appropriate access permissions</a>.
</li>
</references>
</qhelp>

View File

@@ -1,87 +0,0 @@
/**
* @name Find the wrong use of the umask function.
* @description Incorrectly evaluated argument to the umask function may have security implications.
* @kind problem
* @id cpp/wrong-use-of-the-umask
* @problem.severity warning
* @precision medium
* @tags correctness
* maintainability
* security
* external/cwe/cwe-266
* external/cwe/cwe-264
* external/cwe/cwe-200
* external/cwe/cwe-560
* external/cwe/cwe-687
*/
import cpp
import semmle.code.cpp.exprs.BitwiseOperation
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/**
* An expression that is either a `BinaryArithmeticOperation` or the result of one or more `BinaryBitwiseOperation`s on a `BinaryArithmeticOperation`. For example `1 | (2 + 3)`.
*/
class ContainsArithmetic extends Expr {
ContainsArithmetic() {
this instanceof BinaryArithmeticOperation
or
// recursive search into `Operation`s
this.(BinaryBitwiseOperation).getAnOperand() instanceof ContainsArithmetic
}
}
/** Holds for a function `f` that has an argument at index `apos` used to set file permissions. */
predicate numberArgumentModFunctions(Function f, int apos) {
f.hasGlobalOrStdName("umask") and apos = 0
or
f.hasGlobalOrStdName("fchmod") and apos = 1
or
f.hasGlobalOrStdName("chmod") and apos = 1
}
from FunctionCall fc, string msg, FunctionCall fcsnd
where
fc.getTarget().hasGlobalOrStdName("umask") and
fc.getArgument(0).getValue() = "0" and
not exists(FunctionCall fctmp |
fctmp.getTarget().hasGlobalOrStdName("umask") and
not fctmp.getArgument(0).getValue() = "0"
) and
exists(FunctionCall fctmp |
(
fctmp.getTarget().hasGlobalOrStdName("fopen") or
fctmp.getTarget().hasGlobalOrStdName("open")
) and
not fctmp.getArgument(1).getValue().matches("r%") and
fctmp.getNumberOfArguments() = 2 and
not fctmp.getArgument(0).getValue() = "/dev/null" and
fcsnd = fctmp
) and
not exists(FunctionCall fctmp |
fctmp.getTarget().hasGlobalOrStdName("chmod") or
fctmp.getTarget().hasGlobalOrStdName("fchmod")
) and
msg = "Using umask(0) may not be safe with call $@."
or
fc.getTarget().hasGlobalOrStdName("umask") and
exists(FunctionCall fctmp |
(
fctmp.getTarget().hasGlobalOrStdName("chmod") or
fctmp.getTarget().hasGlobalOrStdName("fchmod")
) and
(
globalValueNumber(fc.getArgument(0)) = globalValueNumber(fctmp.getArgument(1)) and
fc.getArgument(0).getValue() != "0"
) and
msg = "Not use equal argument in umask and $@ functions." and
fcsnd = fctmp
)
or
exists(ContainsArithmetic exptmp, int i |
numberArgumentModFunctions(fc.getTarget(), i) and
globalValueNumber(exptmp) = globalValueNumber(fc.getArgument(i)) and
msg = "Using arithmetic to compute the mask in $@ may not be safe." and
fcsnd = fc
)
select fc, msg, fcsnd, fcsnd.getTarget().getName()

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.0.6-dev
version: 0.0.5-dev
groups: cpp
dependencies:
codeql/cpp-all: "*"

View File

@@ -1,2 +0,0 @@
| test.cpp:9:3:9:7 | call to umask | Not use equal argument in umask and $@ functions. | test.cpp:13:3:13:7 | call to chmod | chmod |
| test.cpp:30:3:30:7 | call to chmod | Using arithmetic to compute the mask in $@ may not be safe. | test.cpp:30:3:30:7 | call to chmod | chmod |

View File

@@ -1 +0,0 @@
experimental/Security/CWE/CWE-266/IncorrectPrivilegeAssignment.ql

View File

@@ -1,49 +0,0 @@
typedef int FILE;
FILE *fopen(const char *filename, const char *mode);
int umask(int pmode);
int chmod(char * filename,int pmode);
int fclose(FILE *stream);
void funcTest1()
{
umask(0666); // BAD
FILE *fe;
fe = fopen("myFile.txt", "wt");
fclose(fe);
chmod("myFile.txt",0666);
}
void funcTest1g()
{
umask(0022);
FILE *fe;
fe = fopen("myFile.txt", "wt");
fclose(fe);
chmod("myFile.txt",0666); // GOOD
}
void funcTest2(int mode)
{
umask(mode);
FILE *fe;
fe = fopen("myFile.txt", "wt");
fclose(fe);
chmod("myFile.txt",0555-mode); // BAD
}
void funcTest2g(int mode)
{
umask(mode);
FILE *fe;
fe = fopen("myFile.txt", "wt");
fclose(fe);
chmod("myFile.txt",0555&~mode); // GOOD
}
int main(int argc, char *argv[])
{
funcTest1();
funcTest2(27);
funcTest1g();
funcTest2g(27);
return 0;
}

View File

@@ -35,6 +35,8 @@ edges
| test.cpp:190:10:190:13 | call to rand | test.cpp:205:7:205:7 | y |
| test.cpp:190:10:190:13 | call to rand | test.cpp:208:7:208:7 | y |
| test.cpp:215:11:215:14 | call to rand | test.cpp:219:8:219:8 | x |
| test.cpp:223:20:223:23 | call to rand | test.cpp:227:8:227:8 | x |
| test.cpp:223:20:223:25 | (unsigned int)... | test.cpp:227:8:227:8 | x |
nodes
| test.c:18:13:18:16 | call to rand | semmle.label | call to rand |
| test.c:21:17:21:17 | r | semmle.label | r |
@@ -90,6 +92,9 @@ nodes
| test.cpp:208:7:208:7 | y | semmle.label | y |
| test.cpp:215:11:215:14 | call to rand | semmle.label | call to rand |
| test.cpp:219:8:219:8 | x | semmle.label | x |
| test.cpp:223:20:223:23 | call to rand | semmle.label | call to rand |
| test.cpp:223:20:223:25 | (unsigned int)... | semmle.label | (unsigned int)... |
| test.cpp:227:8:227:8 | x | semmle.label | x |
subpaths
#select
| test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value |
@@ -120,3 +125,5 @@ subpaths
| test.cpp:205:7:205:7 | y | test.cpp:190:10:190:13 | call to rand | test.cpp:205:7:205:7 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:190:10:190:13 | call to rand | Uncontrolled value |
| test.cpp:208:7:208:7 | y | test.cpp:190:10:190:13 | call to rand | test.cpp:208:7:208:7 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:190:10:190:13 | call to rand | Uncontrolled value |
| test.cpp:219:8:219:8 | x | test.cpp:215:11:215:14 | call to rand | test.cpp:219:8:219:8 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:215:11:215:14 | call to rand | Uncontrolled value |
| test.cpp:227:8:227:8 | x | test.cpp:223:20:223:23 | call to rand | test.cpp:227:8:227:8 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:223:20:223:23 | call to rand | Uncontrolled value |
| test.cpp:227:8:227:8 | x | test.cpp:223:20:223:25 | (unsigned int)... | test.cpp:227:8:227:8 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:223:20:223:23 | call to rand | Uncontrolled value |

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