Skip to content

Commit 3c86aad

Browse files
authored
Merge pull request #14628 from hvitved/ruby/type-tracking-store-post-update
Ruby: Summarized type-tracking stores should target post-update nodes
2 parents 4ce1b68 + 0c5b528 commit 3c86aad

File tree

9 files changed

+135
-166
lines changed

9 files changed

+135
-166
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,12 @@ signature module Input {
7373
}
7474

7575
// Relating nodes to summaries
76-
/** Gets a dataflow node respresenting the argument of `call` indicated by `arg`. */
77-
Node argumentOf(Node call, SummaryComponent arg);
76+
/**
77+
* Gets a dataflow node respresenting the argument of `call` indicated by `arg`.
78+
*
79+
* Returns the post-update node of the argument when `isPostUpdate` is true.
80+
*/
81+
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate);
7882

7983
/** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */
8084
Node parameterOf(Node callable, SummaryComponent param);
@@ -221,14 +225,18 @@ module SummaryFlow<Input I> implements Output<I> {
221225

222226
/**
223227
* Gets a data flow `I::Node` corresponding an argument or return value of `call`,
224-
* as specified by `component`.
228+
* as specified by `component`. `isOutput` indicates whether the node represents
229+
* an output node or an input node.
225230
*/
226231
bindingset[call, component]
227-
private I::Node evaluateSummaryComponentLocal(I::Node call, I::SummaryComponent component) {
228-
result = I::argumentOf(call, component)
232+
private I::Node evaluateSummaryComponentLocal(
233+
I::Node call, I::SummaryComponent component, boolean isOutput
234+
) {
235+
result = I::argumentOf(call, component, isOutput)
229236
or
230237
component = I::return() and
231-
result = call
238+
result = call and
239+
isOutput = true
232240
}
233241

234242
/**
@@ -280,27 +288,40 @@ module SummaryFlow<Input I> implements Output<I> {
280288
*/
281289
pragma[nomagic]
282290
private I::Node evaluateSummaryComponentStackLocal(
283-
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack
291+
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack, boolean isOutput
284292
) {
285293
exists(I::SummaryComponent component |
286294
dependsOnSummaryComponentStackLeaf(callable, component) and
287295
stack = I::singleton(component) and
288296
call = I::callTo(callable) and
289-
result = evaluateSummaryComponentLocal(call, component)
297+
result = evaluateSummaryComponentLocal(call, component, isOutput)
290298
)
291299
or
292-
exists(I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail |
293-
prev = evaluateSummaryComponentStackLocal(callable, call, tail) and
300+
exists(
301+
I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail, boolean isOutput0
302+
|
303+
prev = evaluateSummaryComponentStackLocal(callable, call, tail, isOutput0) and
294304
dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head),
295305
pragma[only_bind_out](tail)) and
296306
stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
297307
|
298-
result = I::parameterOf(prev, head)
308+
// `Parameter[X]` is only allowed in the output of flow summaries (hence `isOutput = true`),
309+
// however the target of the parameter (e.g. `Argument[Y].Parameter[X]`) should be fetched
310+
// not from a post-update argument node (hence `isOutput0 = false`)
311+
result = I::parameterOf(prev, head) and
312+
isOutput0 = false and
313+
isOutput = true
299314
or
300-
result = I::returnOf(prev, head)
315+
// `ReturnValue` is only allowed in the input of flow summaries (hence `isOutput = false`),
316+
// and the target of the return value (e.g. `Argument[X].ReturnValue`) should be fetched not
317+
// from a post-update argument node (hence `isOutput0 = false`)
318+
result = I::returnOf(prev, head) and
319+
isOutput0 = false and
320+
isOutput = false
301321
or
302322
componentLevelStep(head) and
303-
result = prev
323+
result = prev and
324+
isOutput = isOutput0
304325
)
305326
}
306327

@@ -312,8 +333,8 @@ module SummaryFlow<Input I> implements Output<I> {
312333
|
313334
callable.propagatesFlow(input, output, true) and
314335
call = I::callTo(callable) and
315-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
316-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
336+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
337+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
317338
)
318339
}
319340

@@ -325,8 +346,8 @@ module SummaryFlow<Input I> implements Output<I> {
325346
hasLoadSummary(callable, content, pragma[only_bind_into](input),
326347
pragma[only_bind_into](output)) and
327348
call = I::callTo(callable) and
328-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
329-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
349+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
350+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
330351
)
331352
}
332353

@@ -338,8 +359,8 @@ module SummaryFlow<Input I> implements Output<I> {
338359
hasStoreSummary(callable, content, pragma[only_bind_into](input),
339360
pragma[only_bind_into](output)) and
340361
call = I::callTo(callable) and
341-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
342-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
362+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
363+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
343364
)
344365
}
345366

@@ -354,8 +375,8 @@ module SummaryFlow<Input I> implements Output<I> {
354375
hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input),
355376
pragma[only_bind_into](output)) and
356377
call = I::callTo(callable) and
357-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
358-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
378+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
379+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
359380
)
360381
}
361382

@@ -369,8 +390,8 @@ module SummaryFlow<Input I> implements Output<I> {
369390
hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input),
370391
pragma[only_bind_into](output)) and
371392
call = I::callTo(callable) and
372-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
373-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
393+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
394+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
374395
)
375396
}
376397

@@ -384,8 +405,8 @@ module SummaryFlow<Input I> implements Output<I> {
384405
hasWithContentSummary(callable, filter, pragma[only_bind_into](input),
385406
pragma[only_bind_into](output)) and
386407
call = I::callTo(callable) and
387-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
388-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
408+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
409+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
389410
)
390411
}
391412
}

python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,11 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
214214
predicate return = FlowSummary::SummaryComponent::return/0;
215215

216216
// Relating nodes to summaries
217-
Node argumentOf(Node call, SummaryComponent arg) {
217+
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
218218
exists(DataFlowDispatch::ParameterPosition pos |
219219
arg = FlowSummary::SummaryComponent::argument(pos) and
220-
argumentPositionMatch(call, result, pos)
220+
argumentPositionMatch(call, result, pos) and
221+
isPostUpdate = [false, true] // todo: implement when/if Python uses post-update nodes in type tracking
221222
)
222223
}
223224

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -596,11 +596,11 @@ private module Cached {
596596
entrySsaDefinition(n) and
597597
not LocalFlow::localFlowSsaParamInput(_, n)
598598
or
599-
TypeTrackerSpecific::storeStepIntoSourceNode(_, n, _)
599+
TypeTrackerSpecific::basicStoreStep(_, n, _)
600600
or
601-
TypeTrackerSpecific::readStepIntoSourceNode(_, n, _)
601+
TypeTrackerSpecific::basicLoadStep(_, n, _)
602602
or
603-
TypeTrackerSpecific::readStoreStepIntoSourceNode(_, n, _, _)
603+
TypeTrackerSpecific::basicLoadStoreStep(_, n, _, _)
604604
}
605605

606606
cached

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet conten
275275
* Holds if a store step `nodeFrom -> nodeTo` with `contents` exists, where the destination node
276276
* is a post-update node that should be treated as a local source node.
277277
*/
278-
predicate storeStepIntoSourceNode(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
278+
private predicate storeStepIntoSourceNode(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
279279
// TODO: support SetterMethodCall inside TuplePattern
280280
exists(ExprNodes::MethodCallCfgNode call |
281281
contents
@@ -311,7 +311,7 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet content
311311
* Holds if a read step `nodeFrom -> nodeTo` with `contents` exists, where the destination node
312312
* should be treated as a local source node.
313313
*/
314-
predicate readStepIntoSourceNode(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
314+
private predicate readStepIntoSourceNode(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
315315
DataFlowPrivate::readStepCommon(nodeFrom, contents, nodeTo)
316316
}
317317

@@ -330,7 +330,7 @@ predicate basicLoadStoreStep(
330330
* Holds if a read+store step `nodeFrom -> nodeTo` exists, where the destination node
331331
* should be treated as a local source node.
332332
*/
333-
predicate readStoreStepIntoSourceNode(
333+
private predicate readStoreStepIntoSourceNode(
334334
Node nodeFrom, Node nodeTo, DataFlow::ContentSet loadContent, DataFlow::ContentSet storeContent
335335
) {
336336
exists(DataFlowPrivate::SynthSplatParameterShiftNode shift |
@@ -441,10 +441,14 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
441441
class SummarizedCallable = FlowSummary::SummarizedCallable;
442442

443443
// Relating nodes to summaries
444-
Node argumentOf(Node call, SummaryComponent arg) {
445-
exists(DataFlowDispatch::ParameterPosition pos |
444+
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
445+
exists(DataFlowDispatch::ParameterPosition pos, DataFlowPrivate::ArgumentNode n |
446446
arg = SummaryComponent::argument(pos) and
447-
argumentPositionMatch(call.asExpr(), result, pos)
447+
argumentPositionMatch(call.asExpr(), n, pos)
448+
|
449+
isPostUpdate = false and result = n
450+
or
451+
isPostUpdate = true and result.(DataFlowPublic::PostUpdateNode).getPreUpdateNode() = n
448452
)
449453
}
450454

ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,12 @@ signature module Input {
7373
}
7474

7575
// Relating nodes to summaries
76-
/** Gets a dataflow node respresenting the argument of `call` indicated by `arg`. */
77-
Node argumentOf(Node call, SummaryComponent arg);
76+
/**
77+
* Gets a dataflow node respresenting the argument of `call` indicated by `arg`.
78+
*
79+
* Returns the post-update node of the argument when `isPostUpdate` is true.
80+
*/
81+
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate);
7882

7983
/** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */
8084
Node parameterOf(Node callable, SummaryComponent param);
@@ -221,14 +225,18 @@ module SummaryFlow<Input I> implements Output<I> {
221225

222226
/**
223227
* Gets a data flow `I::Node` corresponding an argument or return value of `call`,
224-
* as specified by `component`.
228+
* as specified by `component`. `isOutput` indicates whether the node represents
229+
* an output node or an input node.
225230
*/
226231
bindingset[call, component]
227-
private I::Node evaluateSummaryComponentLocal(I::Node call, I::SummaryComponent component) {
228-
result = I::argumentOf(call, component)
232+
private I::Node evaluateSummaryComponentLocal(
233+
I::Node call, I::SummaryComponent component, boolean isOutput
234+
) {
235+
result = I::argumentOf(call, component, isOutput)
229236
or
230237
component = I::return() and
231-
result = call
238+
result = call and
239+
isOutput = true
232240
}
233241

234242
/**
@@ -280,27 +288,40 @@ module SummaryFlow<Input I> implements Output<I> {
280288
*/
281289
pragma[nomagic]
282290
private I::Node evaluateSummaryComponentStackLocal(
283-
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack
291+
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack, boolean isOutput
284292
) {
285293
exists(I::SummaryComponent component |
286294
dependsOnSummaryComponentStackLeaf(callable, component) and
287295
stack = I::singleton(component) and
288296
call = I::callTo(callable) and
289-
result = evaluateSummaryComponentLocal(call, component)
297+
result = evaluateSummaryComponentLocal(call, component, isOutput)
290298
)
291299
or
292-
exists(I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail |
293-
prev = evaluateSummaryComponentStackLocal(callable, call, tail) and
300+
exists(
301+
I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail, boolean isOutput0
302+
|
303+
prev = evaluateSummaryComponentStackLocal(callable, call, tail, isOutput0) and
294304
dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head),
295305
pragma[only_bind_out](tail)) and
296306
stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
297307
|
298-
result = I::parameterOf(prev, head)
308+
// `Parameter[X]` is only allowed in the output of flow summaries (hence `isOutput = true`),
309+
// however the target of the parameter (e.g. `Argument[Y].Parameter[X]`) should be fetched
310+
// not from a post-update argument node (hence `isOutput0 = false`)
311+
result = I::parameterOf(prev, head) and
312+
isOutput0 = false and
313+
isOutput = true
299314
or
300-
result = I::returnOf(prev, head)
315+
// `ReturnValue` is only allowed in the input of flow summaries (hence `isOutput = false`),
316+
// and the target of the return value (e.g. `Argument[X].ReturnValue`) should be fetched not
317+
// from a post-update argument node (hence `isOutput0 = false`)
318+
result = I::returnOf(prev, head) and
319+
isOutput0 = false and
320+
isOutput = false
301321
or
302322
componentLevelStep(head) and
303-
result = prev
323+
result = prev and
324+
isOutput = isOutput0
304325
)
305326
}
306327

@@ -312,8 +333,8 @@ module SummaryFlow<Input I> implements Output<I> {
312333
|
313334
callable.propagatesFlow(input, output, true) and
314335
call = I::callTo(callable) and
315-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
316-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
336+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
337+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
317338
)
318339
}
319340

@@ -325,8 +346,8 @@ module SummaryFlow<Input I> implements Output<I> {
325346
hasLoadSummary(callable, content, pragma[only_bind_into](input),
326347
pragma[only_bind_into](output)) and
327348
call = I::callTo(callable) and
328-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
329-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
349+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
350+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
330351
)
331352
}
332353

@@ -338,8 +359,8 @@ module SummaryFlow<Input I> implements Output<I> {
338359
hasStoreSummary(callable, content, pragma[only_bind_into](input),
339360
pragma[only_bind_into](output)) and
340361
call = I::callTo(callable) and
341-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
342-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
362+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
363+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
343364
)
344365
}
345366

@@ -354,8 +375,8 @@ module SummaryFlow<Input I> implements Output<I> {
354375
hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input),
355376
pragma[only_bind_into](output)) and
356377
call = I::callTo(callable) and
357-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
358-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
378+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
379+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
359380
)
360381
}
361382

@@ -369,8 +390,8 @@ module SummaryFlow<Input I> implements Output<I> {
369390
hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input),
370391
pragma[only_bind_into](output)) and
371392
call = I::callTo(callable) and
372-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
373-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
393+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
394+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
374395
)
375396
}
376397

@@ -384,8 +405,8 @@ module SummaryFlow<Input I> implements Output<I> {
384405
hasWithContentSummary(callable, filter, pragma[only_bind_into](input),
385406
pragma[only_bind_into](output)) and
386407
call = I::callTo(callable) and
387-
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
388-
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
408+
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
409+
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
389410
)
390411
}
391412
}

0 commit comments

Comments
 (0)