Skip to content

Commit 6ae1ea7

Browse files
authored
Merge pull request #21185 from MathiasVP/mad-cpp-guards
C++: Support models-as-data barriers and barrier guards
2 parents 0b9691a + c11b464 commit 6ae1ea7

File tree

8 files changed

+317
-56
lines changed

8 files changed

+317
-56
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* Added modules `DataFlow::ParameterizedBarrierGuard` and `DataFlow::ParameterizedInstructionBarrierGuard`. These modules provide the same features as `DataFlow::BarrierGuard` and `DataFlow::InstructionBarrierGuard`, but allow for an additional parameter to support properly using them in dataflow configurations that uses flow states.

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

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595

9696
import cpp
9797
private import new.DataFlow
98+
private import semmle.code.cpp.controlflow.IRGuards
9899
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as Private
99100
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
100101
private import internal.FlowSummaryImpl
@@ -367,6 +368,8 @@ private predicate elementSpec(
367368
) {
368369
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
369370
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
371+
barrierModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) or
372+
barrierGuardModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _) or
370373
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _)
371374
}
372375

@@ -1028,6 +1031,84 @@ private module Cached {
10281031
isSinkNode(n, kind, model) and n.asNode() = node
10291032
)
10301033
}
1034+
1035+
private newtype TKindModelPair =
1036+
TMkPair(string kind, string model) { isBarrierGuardNode(_, _, kind, model) }
1037+
1038+
private GuardValue convertAcceptingValue(Public::AcceptingValue av) {
1039+
av.isTrue() and result.asBooleanValue() = true
1040+
or
1041+
av.isFalse() and result.asBooleanValue() = false
1042+
or
1043+
// NOTE: The below cases don't contribute anything currently since the
1044+
// callers immediately use `.asBooleanValue()` to convert the `GuardValue`
1045+
// to a boolean. Once we're willing to accept the breaking change of
1046+
// converting the barrier guard API to use `GuardValue`s instead `Boolean`s
1047+
// we can remove this restriction.
1048+
av.isNoException() and result.getDualValue().isThrowsException()
1049+
or
1050+
av.isZero() and result.asIntValue() = 0
1051+
or
1052+
av.isNotZero() and result.getDualValue().asIntValue() = 0
1053+
or
1054+
av.isNull() and result.isNullValue()
1055+
or
1056+
av.isNotNull() and result.isNonNullValue()
1057+
}
1058+
1059+
private predicate barrierGuardChecks(IRGuardCondition g, Expr e, boolean gv, TKindModelPair kmp) {
1060+
exists(
1061+
SourceSinkInterpretationInput::InterpretNode n, Public::AcceptingValue acceptingvalue,
1062+
string kind, string model
1063+
|
1064+
isBarrierGuardNode(n, acceptingvalue, kind, model) and
1065+
n.asNode().asExpr() = e and
1066+
kmp = TMkPair(kind, model) and
1067+
gv = convertAcceptingValue(acceptingvalue).asBooleanValue() and
1068+
n.asNode().(Private::ArgumentNode).getCall().asCallInstruction() = g
1069+
)
1070+
}
1071+
1072+
private newtype TKindModelPairIntPair =
1073+
MkKindModelPairIntPair(TKindModelPair pair, int indirectionIndex) {
1074+
indirectionIndex > 0 and
1075+
Private::nodeHasInstruction(_, _, indirectionIndex) and
1076+
exists(pair)
1077+
}
1078+
1079+
private predicate indirectBarrierGuardChecks(
1080+
IRGuardCondition g, Expr e, boolean gv, TKindModelPairIntPair kmp
1081+
) {
1082+
exists(
1083+
SourceSinkInterpretationInput::InterpretNode interpretNode,
1084+
Public::AcceptingValue acceptingvalue, string kind, string model, int indirectionIndex,
1085+
Private::ArgumentNode arg
1086+
|
1087+
isBarrierGuardNode(interpretNode, acceptingvalue, kind, model) and
1088+
arg = interpretNode.asNode() and
1089+
arg.asIndirectExpr(indirectionIndex) = e and
1090+
kmp = MkKindModelPairIntPair(TMkPair(kind, model), indirectionIndex) and
1091+
gv = convertAcceptingValue(acceptingvalue).asBooleanValue() and
1092+
arg.getCall().asCallInstruction() = g
1093+
)
1094+
}
1095+
1096+
/**
1097+
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
1098+
* model.
1099+
*/
1100+
cached
1101+
predicate barrierNode(DataFlow::Node node, string kind, string model) {
1102+
exists(SourceSinkInterpretationInput::InterpretNode n |
1103+
isBarrierNode(n, kind, model) and n.asNode() = node
1104+
)
1105+
or
1106+
DataFlow::ParameterizedBarrierGuard<TKindModelPair, barrierGuardChecks/4>::getABarrierNode(TMkPair(kind,
1107+
model)) = node
1108+
or
1109+
DataFlow::ParameterizedBarrierGuard<TKindModelPairIntPair, indirectBarrierGuardChecks/4>::getAnIndirectBarrierNode(MkKindModelPairIntPair(TMkPair(kind,
1110+
model), _)) = node
1111+
}
10311112
}
10321113

10331114
import Cached
@@ -1044,6 +1125,12 @@ predicate sourceNode(DataFlow::Node node, string kind) { sourceNode(node, kind,
10441125
*/
10451126
predicate sinkNode(DataFlow::Node node, string kind) { sinkNode(node, kind, _) }
10461127

1128+
/**
1129+
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
1130+
* model.
1131+
*/
1132+
predicate barrierNode(DataFlow::Node node, string kind) { barrierNode(node, kind, _) }
1133+
10471134
private predicate interpretSummary(
10481135
Function f, string input, string output, string kind, string provenance, string model
10491136
) {

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,16 +149,27 @@ module SourceSinkInterpretationInput implements
149149
}
150150

151151
predicate barrierElement(
152-
Element n, string output, string kind, Public::Provenance provenance, string model
152+
Element e, string output, string kind, Public::Provenance provenance, string model
153153
) {
154-
none()
154+
exists(
155+
string namespace, string type, boolean subtypes, string name, string signature, string ext
156+
|
157+
barrierModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance, model) and
158+
e = interpretElement(namespace, type, subtypes, name, signature, ext)
159+
)
155160
}
156161

157162
predicate barrierGuardElement(
158-
Element n, string input, Public::AcceptingValue acceptingvalue, string kind,
163+
Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
159164
Public::Provenance provenance, string model
160165
) {
161-
none()
166+
exists(
167+
string package, string type, boolean subtypes, string name, string signature, string ext
168+
|
169+
barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingvalue, kind,
170+
provenance, model) and
171+
e = interpretElement(package, type, subtypes, name, signature, ext)
172+
)
162173
}
163174

164175
private newtype TInterpretNode =

0 commit comments

Comments
 (0)