Skip to content

Commit bd17a9e

Browse files
committed
Ruby: Lift barrier guards to logical operations
1 parent 4da7919 commit bd17a9e

File tree

4 files changed

+76
-34
lines changed

4 files changed

+76
-34
lines changed

ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,6 @@ private predicate stringConstCompare(CfgNodes::AstCfgNode guard, CfgNode testedN
3131
)
3232
or
3333
stringConstCaseCompare(guard, testedNode, branch)
34-
or
35-
exists(CfgNodes::ExprNodes::BinaryOperationCfgNode g |
36-
g = guard and
37-
stringConstCompareOr(guard, branch) and
38-
stringConstCompare(g.getLeftOperand(), testedNode, _)
39-
)
40-
}
41-
42-
/**
43-
* Holds if `guard` is an `or` expression whose operands are string comparison guards.
44-
* For example:
45-
*
46-
* ```rb
47-
* x == "foo" or x == "bar"
48-
* ```
49-
*/
50-
private predicate stringConstCompareOr(
51-
CfgNodes::ExprNodes::BinaryOperationCfgNode guard, boolean branch
52-
) {
53-
guard.getExpr() instanceof LogicalOrExpr and
54-
branch = true and
55-
forall(CfgNode innerGuard | innerGuard = guard.getAnOperand() |
56-
stringConstCompare(innerGuard, any(Ssa::Definition def).getARead(), branch)
57-
)
5834
}
5935

6036
/**

ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ class ParameterExt extends TParameterExt {
541541

542542
private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig {
543543
private import codeql.ruby.controlflow.internal.Guards as Guards
544+
private import codeql.util.Boolean
544545

545546
class Parameter = ParameterExt;
546547

@@ -560,6 +561,79 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
560561
predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) }
561562
}
562563

564+
abstract class LogicalOperationGuard extends Guard {
565+
abstract Guard getOperand(int i);
566+
567+
abstract predicate lift(string id, int i, boolean operandBranch, boolean branch);
568+
}
569+
570+
private class NotGuard extends LogicalOperationGuard,
571+
Cfg::CfgNodes::ExprNodes::UnaryOperationCfgNode
572+
{
573+
NotGuard() { this.getExpr() instanceof NotExpr }
574+
575+
override Guard getOperand(int i) { i = 0 and result = this.getOperand() }
576+
577+
override predicate lift(string id, int i, boolean operandBranch, boolean branch) {
578+
operandBranch instanceof Boolean and
579+
id = operandBranch.toString() and
580+
i = 0 and
581+
branch = operandBranch.booleanNot()
582+
}
583+
}
584+
585+
private class StmtSequenceGuard extends LogicalOperationGuard,
586+
Cfg::CfgNodes::ExprNodes::StmtSequenceCfgNode
587+
{
588+
override Guard getOperand(int i) { i = 0 and result = this.getLastStmt() }
589+
590+
override predicate lift(string id, int i, boolean operandBranch, boolean branch) {
591+
operandBranch instanceof Boolean and
592+
id = operandBranch.toString() and
593+
i = 0 and
594+
branch = operandBranch
595+
}
596+
}
597+
598+
abstract private class BinaryLogicalOperationGuard extends LogicalOperationGuard,
599+
Cfg::CfgNodes::ExprNodes::BinaryOperationCfgNode
600+
{
601+
final override Guard getOperand(int i) {
602+
i = 0 and result = this.getLeftOperand()
603+
or
604+
i = 1 and result = this.getRightOperand()
605+
}
606+
607+
abstract predicate lift(Boolean branchLeft, Boolean branchRight, boolean branch);
608+
609+
final override predicate lift(string id, int i, boolean operandBranch, boolean branch) {
610+
exists(Boolean branchLeft, Boolean branchRight |
611+
this.lift(branchLeft, branchRight, branch) and
612+
id = branchLeft + "," + branchRight
613+
|
614+
i = 0 and operandBranch = branchLeft
615+
or
616+
i = 1 and operandBranch = branchRight
617+
)
618+
}
619+
}
620+
621+
private class AndGuard extends BinaryLogicalOperationGuard {
622+
AndGuard() { this.getExpr() instanceof LogicalAndExpr }
623+
624+
override predicate lift(Boolean branchLeft, Boolean branchRight, boolean branch) {
625+
branch = branchLeft.booleanOr(branchRight) // yes, should not be `booleanAnd`
626+
}
627+
}
628+
629+
private class OrGuard extends BinaryLogicalOperationGuard {
630+
OrGuard() { this.getExpr() instanceof LogicalOrExpr }
631+
632+
override predicate lift(Boolean branchLeft, Boolean branchRight, boolean branch) {
633+
branch = branchLeft.booleanAnd(branchRight) // yes, should not be `booleanOr`
634+
}
635+
}
636+
563637
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
564638
predicate guardControlsBlock(Guard guard, SsaInput::BasicBlock bb, boolean branch) {
565639
Guards::guardControlsBlock(guard, bb, branch)

ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-flow.expected

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ edges
1010
| barrier_flow.rb:82:5:82:5 | x | barrier_flow.rb:93:14:93:14 | x | provenance | |
1111
| barrier_flow.rb:82:5:82:5 | x | barrier_flow.rb:99:14:99:14 | x | provenance | |
1212
| barrier_flow.rb:82:5:82:5 | x | barrier_flow.rb:103:14:103:14 | x | provenance | |
13-
| barrier_flow.rb:82:5:82:5 | x | barrier_flow.rb:105:14:105:14 | x | provenance | |
1413
| barrier_flow.rb:82:5:82:5 | x | barrier_flow.rb:109:14:109:14 | x | provenance | |
15-
| barrier_flow.rb:82:5:82:5 | x | barrier_flow.rb:111:14:111:14 | x | provenance | |
1614
| barrier_flow.rb:82:9:82:18 | call to source | barrier_flow.rb:82:5:82:5 | x | provenance | |
1715
| barrier_flow.rb:116:5:116:5 | x | barrier_flow.rb:121:14:121:14 | x | provenance | |
1816
| barrier_flow.rb:116:5:116:5 | x | barrier_flow.rb:125:14:125:14 | x | provenance | |
@@ -36,9 +34,7 @@ nodes
3634
| barrier_flow.rb:93:14:93:14 | x | semmle.label | x |
3735
| barrier_flow.rb:99:14:99:14 | x | semmle.label | x |
3836
| barrier_flow.rb:103:14:103:14 | x | semmle.label | x |
39-
| barrier_flow.rb:105:14:105:14 | x | semmle.label | x |
4037
| barrier_flow.rb:109:14:109:14 | x | semmle.label | x |
41-
| barrier_flow.rb:111:14:111:14 | x | semmle.label | x |
4238
| barrier_flow.rb:116:5:116:5 | x | semmle.label | x |
4339
| barrier_flow.rb:116:9:116:18 | call to source | semmle.label | call to source |
4440
| barrier_flow.rb:121:14:121:14 | x | semmle.label | x |
@@ -48,8 +44,6 @@ nodes
4844
| barrier_flow.rb:143:14:143:14 | x | semmle.label | x |
4945
subpaths
5046
testFailures
51-
| barrier_flow.rb:105:14:105:14 | x | Unexpected result: hasValueFlow=10 |
52-
| barrier_flow.rb:111:14:111:14 | x | Unexpected result: hasValueFlow=10 |
5347
#select
5448
| barrier_flow.rb:4:10:4:10 | x | barrier_flow.rb:2:9:2:17 | call to source | barrier_flow.rb:4:10:4:10 | x | $@ | barrier_flow.rb:2:9:2:17 | call to source | call to source |
5549
| barrier_flow.rb:11:14:11:14 | x | barrier_flow.rb:8:9:8:17 | call to source | barrier_flow.rb:11:14:11:14 | x | $@ | barrier_flow.rb:8:9:8:17 | call to source | call to source |
@@ -58,9 +52,7 @@ testFailures
5852
| barrier_flow.rb:93:14:93:14 | x | barrier_flow.rb:82:9:82:18 | call to source | barrier_flow.rb:93:14:93:14 | x | $@ | barrier_flow.rb:82:9:82:18 | call to source | call to source |
5953
| barrier_flow.rb:99:14:99:14 | x | barrier_flow.rb:82:9:82:18 | call to source | barrier_flow.rb:99:14:99:14 | x | $@ | barrier_flow.rb:82:9:82:18 | call to source | call to source |
6054
| barrier_flow.rb:103:14:103:14 | x | barrier_flow.rb:82:9:82:18 | call to source | barrier_flow.rb:103:14:103:14 | x | $@ | barrier_flow.rb:82:9:82:18 | call to source | call to source |
61-
| barrier_flow.rb:105:14:105:14 | x | barrier_flow.rb:82:9:82:18 | call to source | barrier_flow.rb:105:14:105:14 | x | $@ | barrier_flow.rb:82:9:82:18 | call to source | call to source |
6255
| barrier_flow.rb:109:14:109:14 | x | barrier_flow.rb:82:9:82:18 | call to source | barrier_flow.rb:109:14:109:14 | x | $@ | barrier_flow.rb:82:9:82:18 | call to source | call to source |
63-
| barrier_flow.rb:111:14:111:14 | x | barrier_flow.rb:82:9:82:18 | call to source | barrier_flow.rb:111:14:111:14 | x | $@ | barrier_flow.rb:82:9:82:18 | call to source | call to source |
6456
| barrier_flow.rb:121:14:121:14 | x | barrier_flow.rb:116:9:116:18 | call to source | barrier_flow.rb:121:14:121:14 | x | $@ | barrier_flow.rb:116:9:116:18 | call to source | call to source |
6557
| barrier_flow.rb:125:14:125:14 | x | barrier_flow.rb:116:9:116:18 | call to source | barrier_flow.rb:125:14:125:14 | x | $@ | barrier_flow.rb:116:9:116:18 | call to source | call to source |
6658
| barrier_flow.rb:131:14:131:14 | x | barrier_flow.rb:116:9:116:18 | call to source | barrier_flow.rb:131:14:131:14 | x | $@ | barrier_flow.rb:116:9:116:18 | call to source | call to source |

ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
testFailures
2-
| barrier_flow.rb:105:16:105:26 | # $ guarded | Missing result:guarded= |
3-
| barrier_flow.rb:111:16:111:26 | # $ guarded | Missing result:guarded= |
42
failures
53
newStyleBarrierGuards
64
| barrier-guards.rb:3:16:4:19 | [input] SSA phi read(foo) |
@@ -55,6 +53,8 @@ newStyleBarrierGuards
5553
| barrier_flow.rb:91:14:91:14 | x |
5654
| barrier_flow.rb:96:24:96:24 | x |
5755
| barrier_flow.rb:97:14:97:14 | x |
56+
| barrier_flow.rb:105:14:105:14 | x |
57+
| barrier_flow.rb:111:14:111:14 | x |
5858
| barrier_flow.rb:118:8:118:19 | [input] SSA phi read(x) |
5959
| barrier_flow.rb:118:24:118:35 | [input] SSA phi read(x) |
6060
| barrier_flow.rb:119:14:119:14 | x |

0 commit comments

Comments
 (0)