Skip to content

Commit 82c4c9a

Browse files
authored
Merge branch 'main' into feature/cpp-mmio-unsanitized-memcpy
2 parents 89071c5 + 7a367c9 commit 82c4c9a

48 files changed

Lines changed: 6007 additions & 4694 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Label external contributions
2+
3+
on:
4+
schedule:
5+
- cron: "7,22,37,52 * * * *"
6+
workflow_dispatch:
7+
8+
permissions: {}
9+
10+
concurrency:
11+
group: label-external-contributions
12+
cancel-in-progress: false
13+
14+
jobs:
15+
label:
16+
if: github.ref_name == github.event.repository.default_branch
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 10
19+
permissions:
20+
pull-requests: write
21+
22+
steps:
23+
- name: Label external contributions
24+
env:
25+
GH_TOKEN: ${{ github.token }}
26+
REPO: ${{ github.repository }}
27+
run: |
28+
set -euo pipefail
29+
30+
label="external-contribution"
31+
updated_cutoff=$(date -u -d "1 hour ago" "+%Y-%m-%dT%H:%M:%SZ")
32+
33+
while IFS= read -r pr_number; do
34+
if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]]; then
35+
echo "Skipping malformed pull request number."
36+
continue
37+
fi
38+
39+
pr_json=$(gh api "repos/$REPO/pulls/$pr_number")
40+
if ! jq -e \
41+
--arg repo "$REPO" \
42+
--arg label "$label" \
43+
'.state == "open" and
44+
.draft == false and
45+
.base.repo.full_name == $repo and
46+
(.head.repo.full_name | type == "string") and
47+
.head.repo.full_name != $repo and
48+
.user.type == "User" and
49+
.author_association != "MEMBER" and
50+
.author_association != "OWNER" and
51+
(any(.labels[]?; .name == $label) | not)' \
52+
>/dev/null <<<"$pr_json"; then
53+
continue
54+
fi
55+
56+
events=$(gh api --paginate \
57+
"repos/$REPO/issues/$pr_number/events?per_page=100" |
58+
jq -cs 'add')
59+
60+
if jq -e --arg label "$label" \
61+
'any(.[]; .event == "labeled" and .label.name == $label)' \
62+
>/dev/null <<<"$events"; then
63+
continue
64+
fi
65+
66+
jq -n --arg label "$label" '{labels: [$label]}' |
67+
gh api --method POST \
68+
"repos/$REPO/issues/$pr_number/labels" \
69+
--input - \
70+
>/dev/null
71+
echo "Labelled pull request #$pr_number."
72+
done < <(
73+
gh api --method GET --paginate "repos/$REPO/issues" \
74+
-f state=open \
75+
-f since="$updated_cutoff" \
76+
-f sort=updated \
77+
-f direction=desc \
78+
-f per_page=100 |
79+
jq -r '.[] | select(.pull_request != null) | .number'
80+
)

‎cpp/ql/lib/ext/empty.model.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ extensions:
2121
pack: codeql/cpp-all
2222
extensible: summaryModel
2323
data: []
24+
- addsTo:
25+
pack: codeql/cpp-all
26+
extensible: forwardsModel
27+
data: []

‎cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll‎

Lines changed: 187 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
* `namespace; type; subtypes; name; signature; ext; output; kind; provenance`
1616
* - BarrierGuards:
1717
* `namespace; type; subtypes; name; signature; ext; input; acceptingValue; kind; provenance`
18+
* - Forwards:
19+
* `namespace; type; subtypes; name; signature; ext; start; constructor; output; provenance`
1820
*
1921
* The interpretation of a row is similar to API-graphs with a left-to-right
2022
* reading.
@@ -108,13 +110,23 @@
108110
* - "manual": The model has been written by hand.
109111
* This information is used in a heuristic for dataflow analysis to determine, if a
110112
* model or source code should be used for determining flow.
113+
*
114+
* The "Forwards" relation allows modeling of function that perform C++11-style "perfect
115+
* forwarding" where a function receives a number of arguments and forwards those arguments
116+
* to a constructor of another type. For example, the row:
117+
* `"std"; "vector<T>"; "True"; "emplace"; ""; ""; "1"; T; Argument[-1].Element; manual`
118+
* says that `std::vector<T>::emplace(arg0, arg1, ..., argn)` forwards arguments
119+
* `arg1, ..., argn` to a constructor for `T`, and the result of `T(arg1, ..., argn)`
120+
* flows to `Argument[-1].Element` (see information about the semantics of the `output`
121+
* column further above).
111122
*/
112123

113124
import cpp
114125
private import new.DataFlow
115126
private import semmle.code.cpp.controlflow.IRGuards
116127
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes as Nodes
117128
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as Private
129+
private import semmle.code.cpp.ir.dataflow.internal.SsaImpl as SsaImpl
118130
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
119131
private import internal.FlowSummaryImpl
120132
private import internal.FlowSummaryImpl::Public
@@ -160,6 +172,20 @@ predicate summaryModel(
160172
)
161173
}
162174

175+
/**
176+
* Holds if a forward model exists for the given parameters.
177+
*/
178+
predicate forwardsModel(
179+
string namespace, string type, boolean subtypes, string name, string signature, string ext,
180+
string start, string constructor, string output, string provenance, string model
181+
) {
182+
exists(QlBuiltins::ExtensionId madId |
183+
Extensions::forwardsModel(namespace, type, subtypes, name, signature, ext, start, constructor,
184+
output, provenance, madId) and
185+
model = "MaD:" + madId.toString()
186+
)
187+
}
188+
163189
/** Provides a query predicate to check the data for validation errors. */
164190
module ModelValidation {
165191
private string getInvalidModelInput() {
@@ -186,6 +212,8 @@ module ModelValidation {
186212
sourceModel(_, _, _, _, _, _, output, _, _, _) and pred = "source"
187213
or
188214
summaryModel(_, _, _, _, _, _, _, output, _, _, _) and pred = "summary"
215+
or
216+
forwardsModel(_, _, _, _, _, _, _, _, output, _, _) and pred = "forwards"
189217
|
190218
invalidSpecComponent(output, part) and
191219
not part = "" and
@@ -259,7 +287,8 @@ private predicate elementSpec(
259287
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
260288
barrierModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
261289
barrierGuardModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _) or
262-
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _)
290+
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _) or
291+
forwardsModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _)
263292
}
264293

265294
/**
@@ -596,6 +625,14 @@ private string getAtIndex(string s, int i) {
596625
not (s = "" and i = 0)
597626
}
598627

628+
/** Gets the number of comma-separated arguments in `s`. */
629+
bindingset[s]
630+
private int getNumberOfArguments(string s) {
631+
s = "" and result = 0
632+
or
633+
s != "" and result = count(s.indexOf(",")) + 1
634+
}
635+
599636
/**
600637
* Normalizes `partiallyNormalizedSignature` by replacing the `remaining`
601638
* number of template arguments in `partiallyNormalizedSignature` with their
@@ -605,7 +642,7 @@ private string getSignatureWithoutClassTemplateNames(
605642
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
606643
) {
607644
elementSpecWithArguments0(_, _, _, partiallyNormalizedSignature, typeArgs, nameArgs) and
608-
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
645+
remaining = getNumberOfArguments(typeArgs) and
609646
result = partiallyNormalizedSignature
610647
or
611648
exists(string mid |
@@ -619,7 +656,7 @@ private string getSignatureWithoutClassTemplateNames(
619656
)
620657
or
621658
// Make sure `remaining` is properly bound
622-
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
659+
remaining = [0 .. getNumberOfArguments(typeArgs)] and
623660
not exists(getAtIndex(typeArgs, remaining)) and
624661
result = mid
625662
)
@@ -636,7 +673,7 @@ pragma[nomagic]
636673
private string getSignatureWithoutFunctionTemplateNames(
637674
string partiallyNormalizedSignature, string typeArgs, string nameArgs, int remaining
638675
) {
639-
remaining = count(partiallyNormalizedSignature.indexOf(",")) + 1 and
676+
remaining = getNumberOfArguments(nameArgs) and
640677
result =
641678
getSignatureWithoutClassTemplateNames(partiallyNormalizedSignature, typeArgs, nameArgs, 0)
642679
or
@@ -651,7 +688,7 @@ private string getSignatureWithoutFunctionTemplateNames(
651688
)
652689
or
653690
// Make sure `remaining` is properly bound
654-
remaining = [0 .. count(partiallyNormalizedSignature.indexOf(",")) + 1] and
691+
remaining = [0 .. getNumberOfArguments(nameArgs)] and
655692
not exists(getAtIndex(nameArgs, remaining)) and
656693
result = mid
657694
)
@@ -1046,6 +1083,148 @@ private module Cached {
10461083

10471084
import Cached
10481085

1086+
/** Gets the constructor type selected by `constructorType` in a forwarding model. */
1087+
private Type getForwardedConstructorType(
1088+
Function forwarder, string namespace, string type, boolean subtypes, string name,
1089+
string signature, string ext, string constructorType
1090+
) {
1091+
exists(int index |
1092+
forwardsModel(namespace, type, subtypes, name, signature, ext, _, constructorType, _, _, _) and
1093+
forwarder = interpretElement(namespace, type, subtypes, name, signature, ext)
1094+
|
1095+
exists(string typeArguments |
1096+
parseAngles(type, _, typeArguments, "") and
1097+
constructorType = getAtIndex(typeArguments, index) and
1098+
result = forwarder.getDeclaringType().getTemplateArgument(index)
1099+
)
1100+
or
1101+
exists(string nameArguments |
1102+
parseAngles(name, _, nameArguments, "") and
1103+
constructorType = getAtIndex(nameArguments, index) and
1104+
result = forwarder.getTemplateArgument(index)
1105+
)
1106+
)
1107+
}
1108+
1109+
/** Interprets a forwarding model, retaining its constructed type, output, and provenance. */
1110+
private predicate interpretForwardsModelType(
1111+
Function forwarder, Type constructedType, int start, string output, string provenance,
1112+
string model
1113+
) {
1114+
exists(
1115+
string namespace, string type, boolean subtypes, string name, string signature, string ext,
1116+
string startString, string constructorType
1117+
|
1118+
forwardsModel(namespace, type, subtypes, name, signature, ext, startString, constructorType,
1119+
output, provenance, model) and
1120+
forwarder = interpretElement(namespace, type, subtypes, name, signature, ext) and
1121+
start = startString.toInt()
1122+
|
1123+
// Either the row specifies forwarding to a type given by the type or
1124+
// function template, in which case we need to resolve that from the type
1125+
// or function name.
1126+
constructedType =
1127+
getForwardedConstructorType(forwarder, namespace, type, subtypes, name, signature, ext,
1128+
constructorType).getUnspecifiedType()
1129+
or
1130+
// Or the row specifies forwarding to a specific type.
1131+
not exists(
1132+
getForwardedConstructorType(forwarder, namespace, type, subtypes, name, signature, ext,
1133+
constructorType)
1134+
) and
1135+
classHasQualifiedName(constructedType, namespace, constructorType)
1136+
)
1137+
}
1138+
1139+
/**
1140+
* Holds if `forwarder` may forward its arguments starting at `start` to `constructor`. The
1141+
* actual constructor being forwarded to depends on the types of arguments from `start`
1142+
* at calls to `forwarder`.
1143+
*/
1144+
private predicate interpretForwardsModel(
1145+
Function forwarder, Constructor constructor, int start, string output, string provenance,
1146+
string model
1147+
) {
1148+
interpretForwardsModelType(forwarder, constructor.getDeclaringType(), start, output, provenance,
1149+
model)
1150+
}
1151+
1152+
/** Holds if `forwarder` forwards its arguments starting at `start` to `constructor`. */
1153+
predicate forwards(Function forwarder, Constructor constructor, int start) {
1154+
interpretForwardsModel(forwarder, constructor, start, _, _, _)
1155+
}
1156+
1157+
private int referenceIndirection(Type unspecified) {
1158+
if unspecified instanceof ReferenceType then result = 1 else result = 0
1159+
}
1160+
1161+
/** Gets `unspecified`, but with its outermost reference removed, if any. */
1162+
private Type stripReference(Type unspecified) {
1163+
result = unspecified.(ReferenceType).getBaseType().getUnspecifiedType()
1164+
or
1165+
not unspecified instanceof ReferenceType and
1166+
result = unspecified
1167+
}
1168+
1169+
/**
1170+
* In order to support flow summaries for functions that perform "perfect
1171+
* forwarding" we interpret a call such as:
1172+
* ```cpp
1173+
* struct Foo { Foo(int) };
1174+
* std::vector<Foo> v;
1175+
* v.emplace_back(42);
1176+
* ```
1177+
* as:
1178+
* ```cpp
1179+
* v.emplace_back(42, &Foo);
1180+
* ```
1181+
* and add two summaries:
1182+
* (1) One flow from `42` to the first argument of a call to `Foo`
1183+
* (2) One flow from the return value of `Foo` to the `this` argument of the call
1184+
* to `emplace_back` (with a sequence of output `Content`s).
1185+
*
1186+
* These two summaries are automatically generated when a forwarding model
1187+
* for `emplace_back` exists.
1188+
*/
1189+
private predicate interpretForwardingSummary(
1190+
Function forwarder, string input, string output, string provenance, string model
1191+
) {
1192+
exists(Constructor constructor, int start, string constructorOutput |
1193+
interpretForwardsModel(forwarder, constructor, start, constructorOutput, provenance, model)
1194+
|
1195+
// Generate the (1) summary
1196+
exists(int index, Parameter arg, Parameter p, int indirection |
1197+
arg = forwarder.getParameter(start + index) and
1198+
p = constructor.getParameter(index) and
1199+
indirection = [0 .. SsaImpl::getMaxIndirectionsForPRType(p.getUnspecifiedType())] and
1200+
input =
1201+
"Argument[" + repeatStars(indirection + referenceIndirection(arg.getUnspecifiedType())) +
1202+
(start + index) + "]" and
1203+
output =
1204+
"Argument[forward].Parameter[" +
1205+
repeatStars(indirection + referenceIndirection(p.getUnspecifiedType())) + index + "]"
1206+
)
1207+
or
1208+
// Generate the (2) summary
1209+
input = "Argument[forward].Parameter[-1]" and
1210+
output = constructorOutput
1211+
)
1212+
or
1213+
// Scalar types have no constructor to synthesize. In this case, directly
1214+
// preserve the value of the single forwarded argument at the modeled output.
1215+
exists(Type constructedType, int start, Parameter p, int indirection |
1216+
interpretForwardsModelType(forwarder, constructedType, start, output, provenance, model) and
1217+
not constructedType instanceof Class and
1218+
forwarder.getNumberOfParameters() = start + 1 and
1219+
p = forwarder.getParameter(start) and
1220+
stripReference(p.getUnspecifiedType()) = constructedType and
1221+
indirection = [0 .. SsaImpl::getMaxIndirectionsForPRType(constructedType)] and
1222+
input =
1223+
"Argument[" + repeatStars(indirection + referenceIndirection(p.getUnspecifiedType())) + start +
1224+
"]"
1225+
)
1226+
}
1227+
10491228
/**
10501229
* Holds if `node` is specified as a source with the given kind in a MaD flow
10511230
* model.
@@ -1074,6 +1253,9 @@ private predicate interpretSummary(
10741253
model) and
10751254
f = interpretElement(namespace, type, subtypes, name, signature, ext)
10761255
)
1256+
or
1257+
interpretForwardingSummary(f, input, output, provenance, model) and
1258+
kind = "value"
10771259
}
10781260

10791261
// adapter class for converting Mad summaries to `SummarizedCallable`s

‎cpp/ql/lib/semmle/code/cpp/dataflow/internal/ExternalFlowExtensions.qll‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ extensible predicate neutralModel(
5151
string namespace, string type, string name, string signature, string kind, string provenance
5252
);
5353

54+
/**
55+
* Holds if a constructor forwarding model exists for the given parameters.
56+
*/
57+
extensible predicate forwardsModel(
58+
string namespace, string type, boolean subtypes, string name, string signature, string ext,
59+
string start, string constructor, string output, string provenance, QlBuiltins::ExtensionId madId
60+
);
61+
5462
module Extensions implements SharedMaD::ExtensionsSig {
5563
import ExternalFlowExtensions
5664

‎cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
111111
pos = -1 and result = TIndirectionPosition(pos, indirection + 1)
112112
)
113113
)
114+
or
115+
argString = "forward" and
116+
result = TForwardPosition()
114117
}
115118

116119
bindingset[token]

0 commit comments

Comments
 (0)