Skip to content

Commit 8266714

Browse files
committed
Ruby: add hook for adding type-tracking steps
fixup docs fixup docs
1 parent 71c5358 commit 8266714

File tree

1 file changed

+116
-1
lines changed

1 file changed

+116
-1
lines changed

ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,11 @@ private predicate summarizedLocalStep(Node nodeFrom, Node nodeTo) {
105105
}
106106

107107
/** Holds if there is a level step from `nodeFrom` to `nodeTo`. */
108-
predicate levelStep(Node nodeFrom, Node nodeTo) { summarizedLocalStep(nodeFrom, nodeTo) }
108+
predicate levelStep(Node nodeFrom, Node nodeTo) {
109+
summarizedLocalStep(nodeFrom, nodeTo)
110+
or
111+
TypeTrackingStep::step(nodeFrom, nodeTo)
112+
}
109113

110114
pragma[noinline]
111115
private predicate argumentPositionMatch(
@@ -234,6 +238,8 @@ predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet conten
234238
not exists(pair.getKey().getConstantValue()) and
235239
contents.isAnyElement()
236240
)
241+
or
242+
TypeTrackingStep::storeStep(nodeFrom, nodeTo, contents)
237243
}
238244

239245
private predicate hashLiteralStore(DataFlow::CallNode hashCreation, DataFlow::Node argument) {
@@ -279,6 +285,8 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet content
279285
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
280286
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
281287
)
288+
or
289+
TypeTrackingStep::loadStep(nodeFrom, nodeTo, contents)
282290
}
283291

284292
/**
@@ -297,6 +305,8 @@ predicate basicLoadStoreStep(
297305
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
298306
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
299307
)
308+
or
309+
TypeTrackingStep::loadStoreStep(nodeFrom, nodeTo, loadContent, storeContent)
300310
}
301311

302312
predicate basicWithoutContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
@@ -310,6 +320,8 @@ predicate basicWithoutContentStep(Node nodeFrom, Node nodeTo, ContentFilter filt
310320
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
311321
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
312322
)
323+
or
324+
TypeTrackingStep::withoutContentStep(nodeFrom, nodeTo, filter)
313325
}
314326

315327
predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
@@ -331,6 +343,8 @@ predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter)
331343
nodeFrom.asExpr() = node.asExpr().(Cfg::CfgNodes::ExprNodes::UnaryOperationCfgNode).getOperand() and
332344
filter = MkElementFilter()
333345
)
346+
or
347+
TypeTrackingStep::withContentStep(nodeFrom, nodeTo, filter)
334348
}
335349

336350
/**
@@ -555,3 +569,104 @@ private DataFlow::Node evaluateSummaryComponentStackLocal(
555569
)
556570
)
557571
}
572+
573+
private newtype TUnit = MkUnit()
574+
575+
/**
576+
* A data flow edge that should be followed by type tracking.
577+
*
578+
* This type of edge does not affect the local data flow graph, and is not used by data-flow configurations.
579+
*
580+
* Note: For performance reasons, all subclasses of this class should be part
581+
* of the standard library, and their implementations may not depend on API graphs.
582+
* For query-specific steps, consider including the custom steps in the type-tracking predicate itself.
583+
*/
584+
class TypeTrackingStep extends TUnit {
585+
/** Gets the string `"unit"`. */
586+
string toString() { result = "unit" }
587+
588+
/**
589+
* Holds if type-tracking should step from `pred` to `succ`.
590+
*/
591+
predicate step(Node pred, Node succ) { none() }
592+
593+
/**
594+
* Holds if type-tracking should step from `pred` into the `content` of `succ`.
595+
*/
596+
predicate storeStep(Node pred, TypeTrackingNode succ, TypeTrackerContent content) { none() }
597+
598+
/**
599+
* Holds if type-tracking should step from the `content` of `pred` to `succ`.
600+
*/
601+
predicate loadStep(Node pred, Node succ, TypeTrackerContent content) { none() }
602+
603+
/**
604+
* Holds if type-tracking should step from the `loadContent` of `pred` to the `storeContent` in `succ`.
605+
*/
606+
predicate loadStoreStep(
607+
Node pred, TypeTrackingNode succ, TypeTrackerContent loadContent,
608+
TypeTrackerContent storeContent
609+
) {
610+
none()
611+
}
612+
613+
/**
614+
* Holds if type-tracking should step from `pred` to `succ` but block flow of `props` through here.
615+
*
616+
* This can be seen as taking a copy of the value in `pred` but without the properties in `props`.
617+
*/
618+
predicate withoutContentStep(Node pred, Node succ, ContentFilter filter) { none() }
619+
620+
/**
621+
* Holds if type-tracking should step from `pred` to `succ` but block flow of `props` through here.
622+
*
623+
* This can be seen as taking a copy of the value in `pred` but without the properties in `props`.
624+
*/
625+
predicate withContentStep(Node pred, Node succ, ContentFilter filter) { none() }
626+
}
627+
628+
/** Provides access to the steps contributed by subclasses of `SharedTypeTrackingStep`. */
629+
module TypeTrackingStep {
630+
/**
631+
* Holds if type-tracking should step from `pred` to `succ`.
632+
*/
633+
predicate step(Node pred, Node succ) { any(TypeTrackingStep st).step(pred, succ) }
634+
635+
/**
636+
* Holds if type-tracking should step from `pred` into the `content` of `succ`.
637+
*/
638+
predicate storeStep(Node pred, TypeTrackingNode succ, TypeTrackerContent content) {
639+
any(TypeTrackingStep st).storeStep(pred, succ, content)
640+
}
641+
642+
/**
643+
* Holds if type-tracking should step from the `content` of `pred` to `succ`.
644+
*/
645+
predicate loadStep(Node pred, Node succ, TypeTrackerContent content) {
646+
any(TypeTrackingStep st).loadStep(pred, succ, content)
647+
}
648+
649+
/**
650+
* Holds if type-tracking should step from the `loadContent` of `pred` to the `storeContent` in `succ`.
651+
*/
652+
predicate loadStoreStep(
653+
Node pred, TypeTrackingNode succ, TypeTrackerContent loadContent,
654+
TypeTrackerContent storeContent
655+
) {
656+
any(TypeTrackingStep st).loadStoreStep(pred, succ, loadContent, storeContent)
657+
}
658+
659+
/**
660+
* Holds if type-tracking should step from `pred` to `succ` but block flow of contents matched by `filter` through here.
661+
*/
662+
predicate withoutContentStep(Node pred, Node succ, ContentFilter filter) {
663+
any(TypeTrackingStep st).withoutContentStep(pred, succ, filter)
664+
}
665+
666+
/**
667+
* Holds if type-tracking should step from `pred` to `succ` if inside a content matched by `filter`.
668+
*/
669+
predicate withContentStep(Node pred, Node succ, ContentFilter filter) {
670+
any(TypeTrackingStep st).withContentStep(pred, succ, filter)
671+
}
672+
}

0 commit comments

Comments
 (0)