Skip to content

Commit 841e6a1

Browse files
committed
SSA: Lift barrier guards to logical operations
1 parent ee1a60d commit 841e6a1

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

shared/ssa/codeql/ssa/Ssa.qll

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,139 @@ module Make<LocationSig Location, InputSig<Location> Input> {
12201220

12211221
/** Holds if the `i`th node of basic block `bb` evaluates this guard. */
12221222
predicate hasCfgNode(BasicBlock bb, int i);
1223+
1224+
/** Gets the location of this guard. */
1225+
Location getLocation();
1226+
}
1227+
1228+
/**
1229+
* A logical operation guard.
1230+
*
1231+
* Guards are automatically lifted to logical operations, as dictacted
1232+
* by the `lift` predicate. For example, logical conjunctions can be
1233+
* lifted as follows:
1234+
*
1235+
* ```csharp
1236+
* // Example 1
1237+
* if (x == "safe" && x == "safe")
1238+
* {
1239+
* sink(x); // guarded
1240+
* }
1241+
* else
1242+
* {
1243+
* sink(x); // unguarded
1244+
* }
1245+
*
1246+
* // Example 2
1247+
* if (x == "safe" && x != "safe2")
1248+
* {
1249+
* sink(x); // guarded
1250+
* }
1251+
* else
1252+
* {
1253+
* sink(x); // unguarded
1254+
* }
1255+
*
1256+
* // Example 3
1257+
* if (x != "safe" && x == "safe2")
1258+
* {
1259+
* sink(x); // guarded
1260+
* }
1261+
* else
1262+
* {
1263+
* sink(x); // unguarded
1264+
* }
1265+
*
1266+
* // Example 4
1267+
* if (x != "safe1" && x != "safe2")
1268+
* {
1269+
* sink(x); // unguarded
1270+
* }
1271+
* else
1272+
* {
1273+
* sink(x); // guarded
1274+
* }
1275+
* ```
1276+
*
1277+
* Examples 1--3 should normally be handled via the control-flow graph (CFG)
1278+
* implementation, but they are included for completeness.
1279+
*
1280+
* Example 4, which does not come for free just from the CFG, requires that
1281+
* logical conjunctions be modeled post-order in the CFG, as they will
1282+
* otherwise not dominate their `else` branch.
1283+
*
1284+
* Similarly, logical disjunctions can be lifted as follows:
1285+
*
1286+
* ```csharp
1287+
* // Example 1
1288+
* if (x == "safe1" || x == "safe2")
1289+
* {
1290+
* sink(x); // guarded
1291+
* }
1292+
* else
1293+
* {
1294+
* sink(x); // unguarded
1295+
* }
1296+
*
1297+
* // Example 2
1298+
* if (x == "safe" || x != "safe2")
1299+
* {
1300+
* sink(x); // unguarded
1301+
* }
1302+
* else
1303+
* {
1304+
* sink(x); // guarded
1305+
* }
1306+
*
1307+
* // Example 3
1308+
* if (x != "safe" || x == "safe2")
1309+
* {
1310+
* sink(x); // unguarded
1311+
* }
1312+
* else
1313+
* {
1314+
* sink(x); // guarded
1315+
* }
1316+
*
1317+
* // Example 4
1318+
* if (x != "safe" || x != "safe")
1319+
* {
1320+
* sink(x); // unguarded
1321+
* }
1322+
* else
1323+
* {
1324+
* sink(x); // guarded
1325+
* }
1326+
* ```
1327+
*
1328+
* Again, Examples 1--3 should normally be handled via the control-flow graph
1329+
* (CFG) implementation, while Example 4 requires that logical disjunctions be
1330+
* modeled post-order in the CFG.
1331+
*/
1332+
class LogicalOperationGuard extends Guard {
1333+
/** Gets the `i`th operand of this logical operation. */
1334+
Guard getOperand(int i);
1335+
1336+
/**
1337+
* Holds if this logical operation guards `branch`, provided that the `i`th
1338+
* operand guards its `operandBranch`. The string `id` is used to encode
1339+
* the bit-pattern of the operands.
1340+
*
1341+
* Example: If this operand is a Boolean conjunction, then the following lift
1342+
* table should be used:
1343+
*
1344+
* | id | i | operandBranch | branch |
1345+
* |---------------|---|---------------|--------|
1346+
* | "false,false" | 0 | false | false |
1347+
* | "false,false" | 1 | false | false |
1348+
* | "false,true" | 0 | false | true |
1349+
* | "false,true" | 1 | true | true |
1350+
* | "true,false" | 0 | true | true |
1351+
* | "true,false" | 1 | false | true |
1352+
* | "true,true" | 0 | true | true |
1353+
* | "true,true" | 1 | true | true |
1354+
*/
1355+
predicate lift(string id, int i, boolean operandBranch, boolean branch);
12231356
}
12241357

12251358
/** Holds if `guard` controls block `bb` upon evaluating to `branch`. */
@@ -1681,6 +1814,26 @@ module Make<LocationSig Location, InputSig<Location> Input> {
16811814
DfInput::Guard g, Definition def, boolean branch, State state
16821815
) {
16831816
guardChecks(g, DfInput::getARead(def), branch, state)
1817+
or
1818+
exists(int last |
1819+
logicalOperationGuardChecksSsaDef(g, _, last, def, branch, state) and
1820+
last = strictcount(int i | exists(g.(DfInput::LogicalOperationGuard).getOperand(i))) - 1
1821+
)
1822+
}
1823+
1824+
pragma[nomagic]
1825+
private predicate logicalOperationGuardChecksSsaDef(
1826+
DfInput::LogicalOperationGuard op, string id, int i, Definition def, boolean branch,
1827+
State state
1828+
) {
1829+
exists(boolean operandBranch |
1830+
op.lift(id, i, operandBranch, branch) and
1831+
guardChecksSsaDef(op.getOperand(i), def, operandBranch, state)
1832+
|
1833+
i = 0
1834+
or
1835+
logicalOperationGuardChecksSsaDef(op, id, i - 1, def, branch, state)
1836+
)
16841837
}
16851838

16861839
/** Gets a node that is safely guarded by the given guard check. */

0 commit comments

Comments
 (0)