|
| 1 | +import codeql.typetracking.TypeTracking as Shared |
| 2 | +import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl |
| 3 | +private import powershell |
| 4 | +private import semmle.code.powershell.controlflow.Cfg as Cfg |
| 5 | +private import Cfg::CfgNodes |
| 6 | +private import codeql.typetracking.internal.SummaryTypeTracker as SummaryTypeTracker |
| 7 | +private import semmle.code.powershell.dataflow.DataFlow |
| 8 | +private import semmle.code.powershell.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon |
| 9 | +private import semmle.code.powershell.dataflow.internal.DataFlowPublic as DataFlowPublic |
| 10 | +private import semmle.code.powershell.dataflow.internal.DataFlowPrivate as DataFlowPrivate |
| 11 | +private import semmle.code.powershell.dataflow.internal.DataFlowDispatch as DataFlowDispatch |
| 12 | +private import codeql.util.Unit |
| 13 | + |
| 14 | +pragma[noinline] |
| 15 | +private predicate argumentPositionMatch( |
| 16 | + DataFlowDispatch::DataFlowCall call, DataFlowPrivate::ArgumentNode arg, |
| 17 | + DataFlowDispatch::ParameterPosition ppos |
| 18 | +) { |
| 19 | + exists(DataFlowDispatch::ArgumentPosition apos | |
| 20 | + arg.argumentOf(call, apos) and |
| 21 | + DataFlowDispatch::parameterMatch(ppos, apos) |
| 22 | + ) |
| 23 | +} |
| 24 | + |
| 25 | +pragma[noinline] |
| 26 | +private predicate viableParam( |
| 27 | + DataFlowDispatch::DataFlowCall call, DataFlowPrivate::ParameterNodeImpl p, |
| 28 | + DataFlowDispatch::ParameterPosition ppos |
| 29 | +) { |
| 30 | + exists(DataFlowDispatch::DataFlowCallable callable | |
| 31 | + DataFlowDispatch::getTarget(call) = callable.asCfgScope() |
| 32 | + | |
| 33 | + p.isParameterOf(callable, ppos) |
| 34 | + ) |
| 35 | +} |
| 36 | + |
| 37 | +/** Holds if there is flow from `arg` to `p` via the call `call`. */ |
| 38 | +pragma[nomagic] |
| 39 | +predicate callStep( |
| 40 | + DataFlowDispatch::DataFlowCall call, DataFlow::Node arg, DataFlowPrivate::ParameterNodeImpl p |
| 41 | +) { |
| 42 | + exists(DataFlowDispatch::ParameterPosition pos | |
| 43 | + argumentPositionMatch(call, arg, pos) and |
| 44 | + viableParam(call, p, pos) |
| 45 | + ) |
| 46 | +} |
| 47 | + |
| 48 | +private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input { |
| 49 | + class Node = DataFlow::Node; |
| 50 | + |
| 51 | + class Content = DataFlowPublic::ContentSet; |
| 52 | + |
| 53 | + class ContentFilter = TypeTrackingInput::ContentFilter; |
| 54 | + |
| 55 | + ContentFilter getFilterFromWithoutContentStep(Content content) { |
| 56 | + none() // TODO |
| 57 | + } |
| 58 | + |
| 59 | + ContentFilter getFilterFromWithContentStep(Content content) { |
| 60 | + none() // TODO |
| 61 | + } |
| 62 | + |
| 63 | + // Summaries and their stacks |
| 64 | + class SummaryComponent extends Unit { |
| 65 | + SummaryComponent() { none() } |
| 66 | + } |
| 67 | + |
| 68 | + class SummaryComponentStack extends Unit { |
| 69 | + SummaryComponent head() { none() } |
| 70 | + } |
| 71 | + |
| 72 | + SummaryComponentStack singleton(SummaryComponent component) { none() } |
| 73 | + |
| 74 | + SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail) { none() } |
| 75 | + |
| 76 | + SummaryComponent return() { none() } |
| 77 | + |
| 78 | + SummaryComponent content(Content contents) { none() } |
| 79 | + |
| 80 | + SummaryComponent withoutContent(Content contents) { none() } |
| 81 | + |
| 82 | + SummaryComponent withContent(Content contents) { none() } |
| 83 | + |
| 84 | + class SummarizedCallable extends Unit { |
| 85 | + SummarizedCallable() { none() } |
| 86 | + |
| 87 | + predicate propagatesFlow( |
| 88 | + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue |
| 89 | + ) { |
| 90 | + none() |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) { none() } |
| 95 | + |
| 96 | + Node parameterOf(Node callable, SummaryComponent param) { none() } |
| 97 | + |
| 98 | + Node returnOf(Node callable, SummaryComponent return) { none() } |
| 99 | + |
| 100 | + Node callTo(SummarizedCallable callable) { none() } |
| 101 | +} |
| 102 | + |
| 103 | +private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>; |
| 104 | + |
| 105 | +private newtype TContentFilter = MkElementFilter() |
| 106 | + |
| 107 | +module TypeTrackingInput implements Shared::TypeTrackingInput { |
| 108 | + class Node = DataFlowPublic::Node; |
| 109 | + |
| 110 | + class LocalSourceNode = DataFlowPublic::LocalSourceNode; |
| 111 | + |
| 112 | + class Content = DataFlowPublic::ContentSet; |
| 113 | + |
| 114 | + /** |
| 115 | + * A label to use for `WithContent` and `WithoutContent` steps, restricting |
| 116 | + * which `ContentSet` may pass through. |
| 117 | + */ |
| 118 | + class ContentFilter extends TContentFilter { |
| 119 | + /** Gets a string representation of this content filter. */ |
| 120 | + string toString() { this = MkElementFilter() and result = "elements" } |
| 121 | + |
| 122 | + /** Gets the content of a type-tracker that matches this filter. */ |
| 123 | + Content getAMatchingContent() { |
| 124 | + none() // TODO |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + /** |
| 129 | + * Holds if a value stored with `storeContents` can be read back with `loadContents`. |
| 130 | + */ |
| 131 | + pragma[inline] |
| 132 | + predicate compatibleContents(Content storeContents, Content loadContents) { |
| 133 | + storeContents.getAStoreContent() = loadContents.getAReadContent() |
| 134 | + } |
| 135 | + |
| 136 | + /** Holds if there is a simple local flow step from `nodeFrom` to `nodeTo` */ |
| 137 | + predicate simpleLocalSmallStep = DataFlowPrivate::localFlowStepTypeTracker/2; |
| 138 | + |
| 139 | + /** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */ |
| 140 | + pragma[nomagic] |
| 141 | + predicate levelStepNoCall(Node nodeFrom, LocalSourceNode nodeTo) { |
| 142 | + TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo) |
| 143 | + } |
| 144 | + |
| 145 | + /** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */ |
| 146 | + pragma[nomagic] |
| 147 | + predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) { none() } |
| 148 | + |
| 149 | + /** |
| 150 | + * Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call. |
| 151 | + * |
| 152 | + * Flow into summarized library methods is not included, as that will lead to negative |
| 153 | + * recursion (or, at best, terrible performance), since identifying calls to library |
| 154 | + * methods is done using API graphs (which uses type tracking). |
| 155 | + */ |
| 156 | + predicate callStep(Node nodeFrom, LocalSourceNode nodeTo) { callStep(_, nodeFrom, nodeTo) } |
| 157 | + |
| 158 | + /** |
| 159 | + * Holds if `nodeFrom` steps to `nodeTo` by being returned from a call. |
| 160 | + */ |
| 161 | + predicate returnStep(Node nodeFrom, LocalSourceNode nodeTo) { |
| 162 | + exists(CallCfgNode call | |
| 163 | + nodeFrom instanceof DataFlowPrivate::ReturnNode and |
| 164 | + nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() = |
| 165 | + DataFlowDispatch::getTarget(DataFlowDispatch::TNormalCall(call)) and |
| 166 | + nodeTo.asExpr().getAstNode() = call.getAstNode() |
| 167 | + ) |
| 168 | + } |
| 169 | + |
| 170 | + /** |
| 171 | + * Holds if `nodeFrom` is being written to the `contents` of the object |
| 172 | + * in `nodeTo`. |
| 173 | + * |
| 174 | + * Note that the choice of `nodeTo` does not have to make sense |
| 175 | + * "chronologically". All we care about is whether the `contents` of |
| 176 | + * `nodeTo` can have a specific type, and the assumption is that if a specific |
| 177 | + * type appears here, then any access of that particular content can yield |
| 178 | + * something of that particular type. |
| 179 | + */ |
| 180 | + predicate storeStep(Node nodeFrom, Node nodeTo, Content contents) { |
| 181 | + DataFlowPrivate::storeStep(nodeFrom, contents, nodeTo) |
| 182 | + or |
| 183 | + TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents) |
| 184 | + } |
| 185 | + |
| 186 | + /** |
| 187 | + * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`. |
| 188 | + */ |
| 189 | + predicate loadStep(Node nodeFrom, LocalSourceNode nodeTo, Content contents) { |
| 190 | + DataFlowPrivate::readStep(nodeFrom, contents, nodeTo) |
| 191 | + or |
| 192 | + TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents) |
| 193 | + } |
| 194 | + |
| 195 | + /** |
| 196 | + * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. |
| 197 | + */ |
| 198 | + predicate loadStoreStep(Node nodeFrom, Node nodeTo, Content loadContent, Content storeContent) { |
| 199 | + TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent) |
| 200 | + } |
| 201 | + |
| 202 | + /** |
| 203 | + * Same as `withContentStep`, but `nodeTo` has type `Node` instead of `LocalSourceNode`, |
| 204 | + * which allows for it by used in the definition of `LocalSourceNode`. |
| 205 | + */ |
| 206 | + additional predicate withContentStepImpl(Node nodeFrom, Node nodeTo, ContentFilter filter) { |
| 207 | + TypeTrackerSummaryFlow::basicWithContentStep(nodeFrom, nodeTo, filter) |
| 208 | + } |
| 209 | + |
| 210 | + /** |
| 211 | + * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a |
| 212 | + * content matched by `filter`. |
| 213 | + */ |
| 214 | + predicate withContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter filter) { |
| 215 | + withContentStepImpl(nodeFrom, nodeTo, filter) |
| 216 | + } |
| 217 | + |
| 218 | + /** |
| 219 | + * Same as `withoutContentStep`, but `nodeTo` has type `Node` instead of `LocalSourceNode`, |
| 220 | + * which allows for it by used in the definition of `LocalSourceNode`. |
| 221 | + */ |
| 222 | + additional predicate withoutContentStepImpl(Node nodeFrom, Node nodeTo, ContentFilter filter) { |
| 223 | + TypeTrackerSummaryFlow::basicWithoutContentStep(nodeFrom, nodeTo, filter) |
| 224 | + } |
| 225 | + |
| 226 | + /** |
| 227 | + * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block |
| 228 | + * flow of contents matched by `filter` through here. |
| 229 | + */ |
| 230 | + predicate withoutContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter filter) { |
| 231 | + withoutContentStepImpl(nodeFrom, nodeTo, filter) |
| 232 | + } |
| 233 | + |
| 234 | + /** |
| 235 | + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. |
| 236 | + */ |
| 237 | + predicate jumpStep(Node nodeFrom, LocalSourceNode nodeTo) { |
| 238 | + DataFlowPrivate::jumpStep(nodeFrom, nodeTo) |
| 239 | + } |
| 240 | + |
| 241 | + predicate hasFeatureBacktrackStoreTarget() { none() } |
| 242 | +} |
| 243 | + |
| 244 | +import SharedImpl::TypeTracking<TypeTrackingInput> |
0 commit comments