Skip to content

Commit b30f2d7

Browse files
committed
Ruby: Track types in data flow
1 parent 6f45de1 commit b30f2d7

File tree

29 files changed

+2084
-1311
lines changed

29 files changed

+2084
-1311
lines changed

ruby/ql/consistency-queries/DataFlowConsistency.ql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ private module Input implements InputSig<RubyDataFlow> {
4444
n.getASplit() instanceof Split::ConditionalCompletionSplit
4545
)
4646
}
47+
48+
predicate uniqueTypeExclude(Node n) {
49+
n =
50+
any(DataFlow::CallNode call |
51+
Private::isStandardNewCall(call.getExprNode(), _, _) and
52+
not call.getReceiver().asExpr().getExpr() instanceof ConstantReadAccess
53+
)
54+
}
4755
}
4856

4957
import MakeConsistency<RubyDataFlow, RubyTaintTracking, Input>

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

Lines changed: 7 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ private predicate selfInModule(SelfVariable self, Module m) {
231231

232232
/** Holds if `self` belongs to method `method` inside module `m`. */
233233
pragma[nomagic]
234-
private predicate selfInMethod(SelfVariable self, MethodBase method, Module m) {
234+
predicate selfInMethod(SelfVariable self, MethodBase method, Module m) {
235235
exists(ModuleBase encl |
236236
method = self.getDeclaringScope() and
237237
encl = method.getEnclosingModule() and
@@ -241,64 +241,6 @@ private predicate selfInMethod(SelfVariable self, MethodBase method, Module m) {
241241
)
242242
}
243243

244-
/** Holds if `self` belongs to the top-level. */
245-
pragma[nomagic]
246-
private predicate selfInToplevel(SelfVariable self, Module m) {
247-
self.getDeclaringScope() instanceof Toplevel and
248-
m = TResolved("Object")
249-
}
250-
251-
/**
252-
* Holds if SSA definition `def` belongs to a variable introduced via pattern
253-
* matching on type `m`. For example, in
254-
*
255-
* ```rb
256-
* case object
257-
* in C => c then c.foo
258-
* end
259-
* ```
260-
*
261-
* the SSA definition for `c` is introduced by matching on `C`.
262-
*/
263-
private predicate asModulePattern(SsaDefinitionExtNode def, Module m) {
264-
exists(AsPattern ap |
265-
m = resolveConstantReadAccess(ap.getPattern()) and
266-
def.getDefinitionExt().(Ssa::WriteDefinition).getWriteAccess().getAstNode() =
267-
ap.getVariableAccess()
268-
)
269-
}
270-
271-
/**
272-
* Holds if `read1` and `read2` are adjacent reads of SSA definition `def`,
273-
* and `read2` is checked to have type `m`. For example, in
274-
*
275-
* ```rb
276-
* case object
277-
* when C then object.foo
278-
* end
279-
* ```
280-
*
281-
* the two reads of `object` are adjacent, and the second is checked to have type `C`.
282-
*/
283-
private predicate hasAdjacentTypeCheckedReads(
284-
Ssa::Definition def, CfgNodes::ExprCfgNode read1, CfgNodes::ExprCfgNode read2, Module m
285-
) {
286-
exists(
287-
CfgNodes::ExprCfgNode pattern, ConditionBlock cb, CfgNodes::ExprNodes::CaseExprCfgNode case
288-
|
289-
m = resolveConstantReadAccess(pattern.getExpr()) and
290-
cb.getLastNode() = pattern and
291-
cb.controls(read2.getBasicBlock(),
292-
any(SuccessorTypes::MatchingSuccessor match | match.getValue() = true)) and
293-
def.hasAdjacentReads(read1, read2) and
294-
case.getValue() = read1
295-
|
296-
pattern = case.getBranch(_).(CfgNodes::ExprNodes::WhenClauseCfgNode).getPattern(_)
297-
or
298-
pattern = case.getBranch(_).(CfgNodes::ExprNodes::InClauseCfgNode).getPattern()
299-
)
300-
}
301-
302244
/** Holds if `new` is a user-defined `self.new` method. */
303245
predicate isUserDefinedNew(SingletonMethod new) {
304246
exists(Expr object | singletonMethod(new, "new", object) |
@@ -509,7 +451,7 @@ private predicate hasUserDefinedNew(Module m) {
509451
* `self.new` on `m`.
510452
*/
511453
pragma[nomagic]
512-
private predicate isStandardNewCall(RelevantCall new, Module m, boolean exact) {
454+
predicate isStandardNewCall(RelevantCall new, Module m, boolean exact) {
513455
exists(DataFlow::LocalSourceNode sourceNode |
514456
flowsToMethodCallReceiver(new, sourceNode, "new") and
515457
// `m` should not have a user-defined `self.new` method
@@ -539,105 +481,15 @@ private predicate localFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo,
539481

540482
private module TrackInstanceInput implements CallGraphConstruction::InputSig {
541483
pragma[nomagic]
542-
private predicate isInstanceNoCall(DataFlow::Node n, Module tp, boolean exact) {
543-
n.asExpr().getExpr() instanceof NilLiteral and
544-
tp = TResolved("NilClass") and
545-
exact = true
546-
or
547-
n.asExpr().getExpr().(BooleanLiteral).isFalse() and
548-
tp = TResolved("FalseClass") and
549-
exact = true
550-
or
551-
n.asExpr().getExpr().(BooleanLiteral).isTrue() and
552-
tp = TResolved("TrueClass") and
553-
exact = true
554-
or
555-
n.asExpr().getExpr() instanceof IntegerLiteral and
556-
tp = TResolved("Integer") and
557-
exact = true
558-
or
559-
n.asExpr().getExpr() instanceof FloatLiteral and
560-
tp = TResolved("Float") and
561-
exact = true
562-
or
563-
n.asExpr().getExpr() instanceof RationalLiteral and
564-
tp = TResolved("Rational") and
565-
exact = true
566-
or
567-
n.asExpr().getExpr() instanceof ComplexLiteral and
568-
tp = TResolved("Complex") and
569-
exact = true
570-
or
571-
n.asExpr().getExpr() instanceof StringlikeLiteral and
572-
tp = TResolved("String") and
573-
exact = true
574-
or
575-
n.asExpr() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode and
576-
tp = TResolved("Array") and
577-
exact = true
578-
or
579-
n.asExpr() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode and
580-
tp = TResolved("Hash") and
581-
exact = true
582-
or
583-
n.asExpr().getExpr() instanceof MethodBase and
584-
tp = TResolved("Symbol") and
585-
exact = true
586-
or
587-
n.asParameter() instanceof BlockParameter and
588-
tp = TResolved("Proc") and
589-
exact = true
590-
or
591-
n.asExpr().getExpr() instanceof Lambda and
592-
tp = TResolved("Proc") and
593-
exact = true
594-
or
595-
// `self` reference in method or top-level (but not in module or singleton method,
596-
// where instance methods cannot be called; only singleton methods)
597-
n =
598-
any(SelfLocalSourceNode self |
599-
exists(MethodBase m |
600-
selfInMethod(self.getVariable(), m, tp) and
601-
not m instanceof SingletonMethod and
602-
if m.getEnclosingModule() instanceof Toplevel then exact = true else exact = false
603-
)
604-
or
605-
selfInToplevel(self.getVariable(), tp) and
606-
exact = true
607-
)
608-
or
609-
// `in C => c then c.foo`
610-
asModulePattern(n, tp) and
611-
exact = false
612-
or
613-
// `case object when C then object.foo`
614-
hasAdjacentTypeCheckedReads(_, _, n.asExpr(), tp) and
615-
exact = false
616-
}
617-
618-
pragma[nomagic]
619-
private predicate isInstanceCall(DataFlow::Node n, Module tp, boolean exact) {
620-
isStandardNewCall(n.asExpr(), tp, exact)
621-
}
622-
623-
/** Holds if `n` is an instance of type `tp`. */
624-
pragma[inline]
625-
private predicate isInstance(DataFlow::Node n, Module tp, boolean exact) {
626-
isInstanceNoCall(n, tp, exact)
627-
or
628-
isInstanceCall(n, tp, exact)
629-
}
630-
631-
pragma[nomagic]
632-
private predicate hasAdjacentTypeCheckedReads(DataFlow::Node node) {
633-
hasAdjacentTypeCheckedReads(_, _, node.asExpr(), _)
484+
private predicate hasAdjacentTypeCheckedRead(DataFlow::Node node) {
485+
TypeInference::hasAdjacentTypeCheckedRead(node.asExpr(), _)
634486
}
635487

636488
newtype State = additional MkState(Module m, Boolean exact)
637489

638490
predicate start(DataFlow::Node start, State state) {
639491
exists(Module tp, boolean exact | state = MkState(tp, exact) |
640-
isInstance(start, tp, exact)
492+
TypeInference::hasType(start, tp, exact)
641493
or
642494
exists(Module m |
643495
(if m.isClass() then tp = TResolved("Class") else tp = TResolved("Module")) and
@@ -665,8 +517,8 @@ private module TrackInstanceInput implements CallGraphConstruction::InputSig {
665517
// We exclude steps into type checked variables. For those, we instead rely on the
666518
// type being checked against
667519
localFlowStep(nodeFrom, nodeTo, summary) and
668-
not hasAdjacentTypeCheckedReads(nodeTo) and
669-
not asModulePattern(nodeTo, _)
520+
not hasAdjacentTypeCheckedRead(nodeTo) and
521+
not TypeInference::asModulePattern(nodeTo.(SsaDefinitionExtNode).getDefinitionExt(), _)
670522
}
671523

672524
predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {

0 commit comments

Comments
 (0)