Skip to content

Commit b17d6d8

Browse files
committed
Ruby: Track types in data flow
1 parent 020a049 commit b17d6d8

File tree

22 files changed

+1347
-683
lines changed

22 files changed

+1347
-683
lines changed

ruby/ql/consistency-queries/DataFlowConsistency.ql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ private module Input implements InputSig<RubyDataFlow> {
4848
// arguments to all `yield` calls
4949
arg instanceof ArgumentNodes::BlockParameterArgumentNode
5050
}
51+
52+
predicate uniqueTypeExclude(Node n) {
53+
n =
54+
any(DataFlow::CallNode call |
55+
Private::isStandardNewCall(call.getExprNode(), _, _) and
56+
not call.getReceiver().asExpr().getExpr() instanceof ConstantReadAccess
57+
)
58+
}
5159
}
5260

5361
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
@@ -229,7 +229,7 @@ private predicate selfInModule(SelfVariable self, Module m) {
229229

230230
/** Holds if `self` belongs to method `method` inside module `m`. */
231231
pragma[nomagic]
232-
private predicate selfInMethod(SelfVariable self, MethodBase method, Module m) {
232+
predicate selfInMethod(SelfVariable self, MethodBase method, Module m) {
233233
exists(ModuleBase encl |
234234
method = self.getDeclaringScope() and
235235
encl = method.getEnclosingModule() and
@@ -239,64 +239,6 @@ private predicate selfInMethod(SelfVariable self, MethodBase method, Module m) {
239239
)
240240
}
241241

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

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

634486
newtype State = additional MkState(Module m, Boolean exact)
635487

636488
predicate start(DataFlow::Node start, State state) {
637489
exists(Module tp, boolean exact | state = MkState(tp, exact) |
638-
isInstance(start, tp, exact)
490+
TypeInference::hasType(start, tp, exact)
639491
or
640492
exists(Module m |
641493
(if m.isClass() then tp = TResolved("Class") else tp = TResolved("Module")) and
@@ -663,8 +515,8 @@ private module TrackInstanceInput implements CallGraphConstruction::InputSig {
663515
// We exclude steps into type checked variables. For those, we instead rely on the
664516
// type being checked against
665517
localFlowStep(nodeFrom, nodeTo, summary) and
666-
not hasAdjacentTypeCheckedReads(nodeTo) and
667-
not asModulePattern(nodeTo, _)
518+
not hasAdjacentTypeCheckedRead(nodeTo) and
519+
not TypeInference::asModulePattern(nodeTo.(SsaDefinitionExtNode).getDefinitionExt(), _)
668520
}
669521

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

0 commit comments

Comments
 (0)