Skip to content

Commit 021d941

Browse files
committed
Merge branch 'main' into use-range-analysis-in-buffer-write
2 parents a9b7fed + e0b121c commit 021d941

File tree

68 files changed

+2909
-2042
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+2909
-2042
lines changed

.github/actions/fetch-codeql/action.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ runs:
88
run: |
99
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
1010
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
11-
unzip -q codeql-linux64.zip
12-
echo "${{ github.workspace }}/codeql" >> $GITHUB_PATH
11+
unzip -q -d "${RUNNER_TEMP}" codeql-linux64.zip
12+
echo "${RUNNER_TEMP}/codeql" >> "${GITHUB_PATH}"
1313
env:
1414
GITHUB_TOKEN: ${{ github.token }}

.github/workflows/post-pr-comment.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Post pull-request comment
2+
on:
3+
workflow_run:
4+
workflows: ["Query help preview"]
5+
types:
6+
- completed
7+
8+
permissions:
9+
pull-requests: write
10+
11+
jobs:
12+
post_comment:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Download artifact
16+
run: gh run download "${WORKFLOW_RUN_ID}" --repo "${GITHUB_REPOSITORY}" --name "comment"
17+
env:
18+
GITHUB_TOKEN: ${{ github.token }}
19+
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
20+
- run: |
21+
PR="$(grep -o '^[0-9]\+$' pr.txt)"
22+
PR_HEAD_SHA="$(gh api "/repos/${GITHUB_REPOSITORY}/pulls/${PR}" --jq .head.sha)"
23+
# Check that the pull-request head SHA matches the head SHA of the workflow run
24+
if [ "${WORKFLOW_RUN_HEAD_SHA}" != "${PR_HEAD_SHA}" ]; then
25+
echo "PR head SHA ${PR_HEAD_SHA} does not match workflow_run event SHA ${WORKFLOW_RUN_HEAD_SHA}. Stopping." 1>&2
26+
exit 1
27+
fi
28+
gh pr comment "${PR}" --repo "${GITHUB_REPOSITORY}" -F comment.txt
29+
env:
30+
GITHUB_TOKEN: ${{ github.token }}
31+
WORKFLOW_RUN_HEAD_SHA: ${{ github.event.workflow_run.head_commit.id }}
Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,63 @@
11
name: Query help preview
22

3+
permissions:
4+
contents: read
5+
36
on:
47
pull_request:
58
branches:
69
- main
7-
- 'rc/*'
10+
- "rc/*"
811
paths:
912
- "ruby/**/*.qhelp"
1013

1114
jobs:
1215
qhelp:
1316
runs-on: ubuntu-latest
1417
steps:
18+
- run: echo "${{ github.event.number }}" > pr.txt
19+
- uses: actions/upload-artifact@v2
20+
with:
21+
name: comment
22+
path: pr.txt
23+
retention-days: 1
1524
- uses: actions/checkout@v2
1625
with:
1726
fetch-depth: 2
27+
persist-credentials: false
28+
- uses: ./.github/actions/fetch-codeql
1829
- name: Determine changed files
1930
id: changes
2031
run: |
21-
echo -n "::set-output name=qhelp_files::"
22-
(git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep .qhelp$ | grep -v .inc.qhelp;
23-
git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep .inc.qhelp$ | xargs -d '\n' -rn1 basename | xargs -d '\n' -rn1 git grep -l) |
24-
sort -u | xargs -d '\n' -n1 printf "'%s' "
25-
26-
- uses: ./.github/actions/fetch-codeql
32+
(git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.qhelp$' | grep -z -v '.inc.qhelp';
33+
git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.inc.qhelp$' | xargs --null -rn1 basename | xargs --null -rn1 git grep -z -l) |
34+
grep -z '.qhelp$' | grep -z -v '^-' | sort -z -u > "${RUNNER_TEMP}/paths.txt"
2735
2836
- name: QHelp preview
29-
if: ${{ steps.changes.outputs.qhelp_files }}
3037
run: |
31-
( echo "QHelp previews:";
32-
for path in ${{ steps.changes.outputs.qhelp_files }} ; do
38+
EXIT_CODE=0
39+
echo "QHelp previews:" > comment.txt
40+
while read -r -d $'\0' path; do
41+
if [ ! -f "${path}" ]; then
42+
exit 1
43+
fi
3344
echo "<details> <summary>${path}</summary>"
3445
echo
35-
codeql generate query-help --format=markdown ${path}
46+
codeql generate query-help --format=markdown -- "./${path}" 2> errors.txt || EXIT_CODE="$?"
47+
if [ -s errors.txt ]; then
48+
echo "# errors/warnings:"
49+
echo '```'
50+
cat errors.txt
51+
cat errors.txt 1>&2
52+
echo '```'
53+
fi
3654
echo "</details>"
37-
done) | gh pr comment "${{ github.event.pull_request.number }}" -F -
38-
env:
39-
GITHUB_TOKEN: ${{ github.token }}
55+
done < "${RUNNER_TEMP}/paths.txt" >> comment.txt
56+
exit "${EXIT_CODE}"
57+
58+
- if: always()
59+
uses: actions/upload-artifact@v2
60+
with:
61+
name: comment
62+
path: comment.txt
63+
retention-days: 1

.github/workflows/ruby-build.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ name: "Ruby: Build"
33
on:
44
push:
55
paths:
6-
- 'ruby/**'
6+
- "ruby/**"
7+
- .github/workflows/ruby-build.yml
78
branches:
89
- main
9-
- 'rc/*'
10+
- "rc/*"
1011
pull_request:
1112
paths:
12-
- 'ruby/**'
13+
- "ruby/**"
14+
- .github/workflows/ruby-build.yml
1315
branches:
1416
- main
15-
- 'rc/*'
17+
- "rc/*"
1618
workflow_dispatch:
1719
inputs:
1820
tag:

.github/workflows/ruby-dataset-measure.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ on:
44
push:
55
branches:
66
- main
7-
- 'rc/*'
7+
- "rc/*"
88
paths:
99
- ruby/ql/lib/ruby.dbscheme
10+
- .github/workflows/ruby-dataset-measure.yml
1011
pull_request:
1112
branches:
1213
- main
13-
- 'rc/*'
14+
- "rc/*"
1415
paths:
1516
- ruby/ql/lib/ruby.dbscheme
17+
- .github/workflows/ruby-dataset-measure.yml
1618
workflow_dispatch:
1719

1820
jobs:

.github/workflows/ruby-qltest.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ name: "Ruby: Run QL Tests"
33
on:
44
push:
55
paths:
6-
- 'ruby/**'
6+
- "ruby/**"
7+
- .github/workflows/ruby-qltest.yml
78
branches:
89
- main
9-
- 'rc/*'
10+
- "rc/*"
1011
pull_request:
1112
paths:
12-
- 'ruby/**'
13+
- "ruby/**"
14+
- .github/workflows/ruby-qltest.yml
1315
branches:
1416
- main
15-
- 'rc/*'
17+
- "rc/*"
1618

1719
env:
1820
CARGO_TERM_COLOR: always
@@ -44,5 +46,5 @@ jobs:
4446
run: |
4547
echo >empty.trap
4648
codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
47-
codeql dataset upgrade testdb --additional-packs ql/lib/upgrades
49+
codeql dataset upgrade testdb --additional-packs ql/lib
4850
diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme

cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ private import semmle.code.cpp.models.interfaces.ArrayFunction
33
private import semmle.code.cpp.models.implementations.Strcat
44
import semmle.code.cpp.dataflow.DataFlow
55

6-
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
7-
exists(StackVariable v0, Expr val |
8-
exprDefinition(v0, e, val) and
9-
val.getAChild*() = va and
10-
mayAddNullTerminator(e0, v0.getAnAccess())
6+
/**
7+
* Holds if the expression `e` assigns something including `va` to a
8+
* stack variable `v0`.
9+
*/
10+
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, StackVariable v0) {
11+
exists(Expr val |
12+
exprDefinition(v0, e, val) and // `e` is `v0 := val`
13+
val.getAChild*() = va
1114
)
1215
}
1316

@@ -25,8 +28,8 @@ private predicate controlFlowNodeSuccessorTransitive(ControlFlowNode n1, Control
2528
}
2629

2730
/**
28-
* Holds if the expression `e` may add a null terminator to the string in
29-
* variable `v`.
31+
* Holds if the expression `e` may add a null terminator to the string
32+
* accessed by `va`.
3033
*/
3134
predicate mayAddNullTerminator(Expr e, VariableAccess va) {
3235
// Assignment: dereferencing or array access
@@ -43,8 +46,9 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
4346
)
4447
or
4548
// Assignment to another stack variable
46-
exists(Expr e0 |
47-
mayAddNullTerminatorHelper(pragma[only_bind_into](e), va, pragma[only_bind_into](e0)) and
49+
exists(StackVariable v0, Expr e0 |
50+
mayAddNullTerminatorHelper(e, va, v0) and
51+
mayAddNullTerminator(pragma[only_bind_into](e0), pragma[only_bind_into](v0.getAnAccess())) and
4852
controlFlowNodeSuccessorTransitive(e, e0)
4953
)
5054
or

cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,24 @@ module TaintedWithPath {
474474
}
475475
}
476476

477+
/**
478+
* INTERNAL: Do not use.
479+
*/
480+
module Private {
481+
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
482+
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
483+
484+
/** Gets the element that `pathNode` wraps, if any. */
485+
Element getElementFromPathNode(PathNode pathNode) {
486+
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
487+
result = node.asExpr() or
488+
result = node.asParameter()
489+
)
490+
or
491+
result = pathNode.(EndpointPathNode).inner()
492+
}
493+
}
494+
477495
private class WrapPathNode extends PathNode, TWrapPathNode {
478496
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
479497

cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_path_to_sink/dispatch.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ using SinkFunction = void (*)(int);
44

55
void notSink(int notSinkParam);
66

7-
void callsSink(int sinkParam) {
8-
sink(sinkParam); // $ ast,ir=31:28 ast,ir=32:31 ast,ir=34:22 MISSING: ast,ir=28
7+
void callsSink(int sinkParam) { // $ ir-path=31:28 ir-path=32:31 ir-path=34:22
8+
sink(sinkParam); // $ ir-sink=31:28 ir-sink=32:31 ir-sink=34:22 ast=31:28 ast=32:31 ast=34:22 MISSING: ast,ir=28
99
}
1010

1111
struct {
@@ -25,13 +25,13 @@ void assignGlobals() {
2525
};
2626

2727
void testStruct() {
28-
globalStruct.sinkPtr(atoi(getenv("TAINTED"))); // $ MISSING: ast,ir
28+
globalStruct.sinkPtr(atoi(getenv("TAINTED"))); // $ ir MISSING: ast
2929
globalStruct.notSinkPtr(atoi(getenv("TAINTED"))); // clean
3030

31-
globalUnion.sinkPtr(atoi(getenv("TAINTED"))); // $ ast,ir
32-
globalUnion.notSinkPtr(atoi(getenv("TAINTED"))); // $ ast,ir
31+
globalUnion.sinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
32+
globalUnion.notSinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
3333

34-
globalSinkPtr(atoi(getenv("TAINTED"))); // $ ast,ir
34+
globalSinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
3535
}
3636

3737
class B {
@@ -48,19 +48,19 @@ class D2 : public D1 {
4848

4949
class D3 : public D2 {
5050
public:
51-
void f(const char* p) override {
52-
sink(p); // $ ast,ir=58:10 ast,ir=60:17 ast,ir=61:28 ast,ir=62:29 ast,ir=63:33 SPURIOUS: ast,ir=73:30
51+
void f(const char* p) override { // $ ir-path=58:10 ir-path=60:17 ir-path=61:28 ir-path=62:29 ir-path=63:33 ir-path=73:30
52+
sink(p); // $ ir-sink=58:10 ir-sink=60:17 ir-sink=61:28 ir-sink=62:29 ir-sink=63:33 ast=58:10 ast=60:17 ast=61:28 ast=62:29 ast=63:33 SPURIOUS: ast=73:30 ir-sink=73:30
5353
}
5454
};
5555

5656
void test_dynamic_cast() {
5757
B* b = new D3();
58-
b->f(getenv("VAR")); // $ ast,ir
58+
b->f(getenv("VAR")); // $ ast ir-path
5959

60-
((D2*)b)->f(getenv("VAR")); // $ ast,ir
61-
static_cast<D2*>(b)->f(getenv("VAR")); // $ ast,ir
62-
dynamic_cast<D2*>(b)->f(getenv("VAR")); // $ ast,ir
63-
reinterpret_cast<D2*>(b)->f(getenv("VAR")); // $ ast,ir
60+
((D2*)b)->f(getenv("VAR")); // $ ast ir-path
61+
static_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
62+
dynamic_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
63+
reinterpret_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
6464

6565
B* b2 = new D2();
6666
b2->f(getenv("VAR"));
@@ -70,5 +70,5 @@ void test_dynamic_cast() {
7070
dynamic_cast<D2*>(b2)->f(getenv("VAR"));
7171
reinterpret_cast<D2*>(b2)->f(getenv("VAR"));
7272

73-
dynamic_cast<D3*>(b2)->f(getenv("VAR")); // $ SPURIOUS: ast,ir
73+
dynamic_cast<D3*>(b2)->f(getenv("VAR")); // $ SPURIOUS: ast ir-path
7474
}

cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_path_to_sink/tainted.ql

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import cpp
77
import semmle.code.cpp.security.TaintTrackingImpl as ASTTaintTracking
88
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
99
import IRDefaultTaintTracking::TaintedWithPath as TaintedWithPath
10+
import TaintedWithPath::Private
1011
import TestUtilities.InlineExpectationsTest
1112

12-
predicate isSink(Element sink) {
13+
predicate isSinkArgument(Element sink) {
1314
exists(FunctionCall call |
1415
call.getTarget().getName() = "sink" and
1516
sink = call.getAnArgument()
@@ -19,31 +20,34 @@ predicate isSink(Element sink) {
1920
predicate astTaint(Expr source, Element sink) { ASTTaintTracking::tainted(source, sink) }
2021

2122
class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
22-
override predicate isSink(Element e) { any() }
23+
override predicate isSink(Element e) { isSinkArgument(e) }
2324
}
2425

25-
predicate irTaint(Expr source, Element sink) {
26-
TaintedWithPath::taintedWithPath(source, sink, _, _)
26+
predicate irTaint(Element source, Element sink, string tag) {
27+
exists(TaintedWithPath::PathNode sinkNode, TaintedWithPath::PathNode predNode |
28+
TaintedWithPath::taintedWithPath(source, _, _, sinkNode) and
29+
predNode = getAPredecessor*(sinkNode) and
30+
sink = getElementFromPathNode(predNode) and
31+
// Make sure the path is actually reachable from this predecessor.
32+
// Otherwise, we could pick `predNode` to be b when `source` is
33+
// `source1` in this dataflow graph:
34+
// source1 ---> a ---> c ---> sinkNode
35+
// ^
36+
// source2 ---> b --/
37+
source = getElementFromPathNode(getAPredecessor*(predNode)) and
38+
if sinkNode = predNode then tag = "ir-sink" else tag = "ir-path"
39+
)
2740
}
2841

2942
class IRDefaultTaintTrackingTest extends InlineExpectationsTest {
3043
IRDefaultTaintTrackingTest() { this = "IRDefaultTaintTrackingTest" }
3144

32-
override string getARelevantTag() { result = "ir" }
45+
override string getARelevantTag() { result = ["ir-path", "ir-sink"] }
3346

3447
override predicate hasActualResult(Location location, string element, string tag, string value) {
35-
exists(Expr source, Element tainted, int n |
36-
tag = "ir" and
37-
irTaint(source, tainted) and
38-
(
39-
isSink(tainted)
40-
or
41-
exists(Element sink |
42-
isSink(sink) and
43-
irTaint(tainted, sink)
44-
)
45-
) and
46-
n = strictcount(Expr otherSource | irTaint(otherSource, tainted)) and
48+
exists(Element source, Element tainted, int n |
49+
irTaint(source, tainted, tag) and
50+
n = strictcount(Element otherSource | irTaint(otherSource, tainted, _)) and
4751
(
4852
n = 1 and value = ""
4953
or
@@ -70,10 +74,10 @@ class ASTTaintTrackingTest extends InlineExpectationsTest {
7074
tag = "ast" and
7175
astTaint(source, tainted) and
7276
(
73-
isSink(tainted)
77+
isSinkArgument(tainted)
7478
or
7579
exists(Element sink |
76-
isSink(sink) and
80+
isSinkArgument(sink) and
7781
astTaint(tainted, sink)
7882
)
7983
) and

0 commit comments

Comments
 (0)