Skip to content

Commit

Permalink
Python: Add argument passing meta-queries (++)
Browse files Browse the repository at this point in the history
and overall overview queries.

Relies on points-to dataflow copy
  • Loading branch information
RasmusWL committed Jan 25, 2023
1 parent bf611f8 commit 3e3416e
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 0 deletions.
66 changes: 66 additions & 0 deletions python/ql/src/meta/analysis-quality/ArgumentPassing.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/** Provides comparison of argument passing between type-tracking and points-to. */

import python

module PointsToArgumentPassing {
module DataFlow {
import semmle.python.dataflow.new.internal_pt.DataFlowImpl
}

// import semmle.python.dataflow.new.internal_pt.DataFlowPrivate as DFP
class PointsToArgumentPassingConfig extends DataFlow::Configuration {
PointsToArgumentPassingConfig() { this = "PointsToArgumentPassingConfig" }

override predicate isSource(DataFlow::Node node) {
node instanceof DataFlow::ArgumentNode and
exists(node.getLocation().getFile().getRelativePath())
}

override predicate isSink(DataFlow::Node node) {
node instanceof DataFlow::ParameterNode and
exists(node.getLocation().getFile().getRelativePath())
}

override predicate isBarrierIn(DataFlow::Node node) { this.isSource(node) }

override predicate isBarrierOut(DataFlow::Node node) { this.isSink(node) }
}

predicate argumentPassing(ControlFlowNode arg, Parameter param) {
exists(PointsToArgumentPassingConfig config |
config
.hasFlow(any(DataFlow::ArgumentNode node | node.asCfgNode() = arg),
any(DataFlow::ParameterNode node | node.getParameter() = param))
)
}
}

module TypeTrackingArgumentPassing {
import semmle.python.dataflow.new.DataFlow

class TypeTrackingArgumentPassingConfig extends DataFlow::Configuration {
TypeTrackingArgumentPassingConfig() { this = "TypeTrackingArgumentPassingConfig" }

override predicate isSource(DataFlow::Node node) {
node instanceof DataFlow::ArgumentNode and
exists(node.getLocation().getFile().getRelativePath())
}

override predicate isSink(DataFlow::Node node) {
node instanceof DataFlow::ParameterNode and
exists(node.getLocation().getFile().getRelativePath())
}

override predicate isBarrierIn(DataFlow::Node node) { this.isSource(node) }

override predicate isBarrierOut(DataFlow::Node node) { this.isSink(node) }
}

predicate argumentPassing(ControlFlowNode arg, Parameter param) {
exists(TypeTrackingArgumentPassingConfig config |
config
.hasFlow(any(DataFlow::ArgumentNode node | node.asCfgNode() = arg),
any(DataFlow::ParameterNode node | node.getParameter() = param))
)
}
}
15 changes: 15 additions & 0 deletions python/ql/src/meta/analysis-quality/TTArgumentPassing.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @name arg->param edge from using type-tracking
* @kind problem
* @problem.severity recommendation
* @id py/meta/arg-passing
* @tags meta
* @precision very-low
*/

import python
import ArgumentPassing

from ControlFlowNode arg, Parameter param
where TypeTrackingArgumentPassing::argumentPassing(arg, param)
select arg, "$@ flow to $@", arg, "Argument", param, param.toString()
17 changes: 17 additions & 0 deletions python/ql/src/meta/analysis-quality/TTArgumentPassingMissing.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @name Missing arg->param edge from using type-tracking instead of points-to
* @kind problem
* @problem.severity recommendation
* @id py/meta/arg-passing-missing
* @tags meta
* @precision very-low
*/

import python
import ArgumentPassing

from ControlFlowNode arg, Parameter param
where
PointsToArgumentPassing::argumentPassing(arg, param) and
not TypeTrackingArgumentPassing::argumentPassing(arg, param)
select arg, "MISSING: $@ flow to $@", arg, "Argument", param, param.toString()
17 changes: 17 additions & 0 deletions python/ql/src/meta/analysis-quality/TTArgumentPassingNew.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @name New arg->param edge from using type-tracking instead of points-to
* @kind problem
* @problem.severity recommendation
* @id py/meta/arg-passing-new
* @tags meta
* @precision very-low
*/

import python
import ArgumentPassing

from ControlFlowNode arg, Parameter param
where
not PointsToArgumentPassing::argumentPassing(arg, param) and
TypeTrackingArgumentPassing::argumentPassing(arg, param)
select arg, "NEW: $@ flow to $@", arg, "Argument", param, param.toString()
31 changes: 31 additions & 0 deletions python/ql/src/meta/analysis-quality/TTArgumentPassingOverview.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @name arg->param edge from using type-tracking vs. points-to
* @id py/meta/arg-passing-overview
*/

import python
import ArgumentPassing

from string tag, int c
where
tag = "SHARED" and
c =
count(ControlFlowNode arg, Parameter param |
PointsToArgumentPassing::argumentPassing(arg, param) and
TypeTrackingArgumentPassing::argumentPassing(arg, param)
)
or
tag = "NEW" and
c =
count(ControlFlowNode arg, Parameter param |
not PointsToArgumentPassing::argumentPassing(arg, param) and
TypeTrackingArgumentPassing::argumentPassing(arg, param)
)
or
tag = "MISSING" and
c =
count(ControlFlowNode arg, Parameter param |
PointsToArgumentPassing::argumentPassing(arg, param) and
not TypeTrackingArgumentPassing::argumentPassing(arg, param)
)
select tag, c
17 changes: 17 additions & 0 deletions python/ql/src/meta/analysis-quality/TTArgumentPassingShared.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @name Shared arg->param edge from using type-tracking instead of points-to
* @kind problem
* @problem.severity recommendation
* @id py/meta/arg-passing-shared
* @tags meta
* @precision very-low
*/

import python
import ArgumentPassing

from ControlFlowNode arg, Parameter param
where
PointsToArgumentPassing::argumentPassing(arg, param) and
TypeTrackingArgumentPassing::argumentPassing(arg, param)
select arg, "SHARED: $@ flow to $@", arg, "Argument", param, param.toString()
80 changes: 80 additions & 0 deletions python/ql/src/meta/analysis-quality/TTOverview.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @name Overview from using type-tracking instead of points-to, for both call-graph and
* argument passing
* @kind problem
* @problem.severity recommendation
* @id py/meta/type-tracking-overview
* @precision very-low
*/

import python
import CallGraphQuality
import ArgumentPassing

/** Helper predicate to select a single valid AST node, otherwise MRVA will not show results. */
Location firsExprLocation() {
result =
min(Expr expr, Location loc |
exists(loc.getFile().getRelativePath()) and
loc = expr.getLocation()
|
loc
order by
loc.getFile().getRelativePath(), loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(),
loc.getEndColumn()
)
}

from string part, string tag, int c
where
part = "call-graph" and
(
tag = "SHARED" and
c =
count(CallNode call, Target target |
target.isRelevant() and
call.(PointsToBasedCallGraph::ResolvableCall).getTarget() = target and
call.(TypeTrackingBasedCallGraph::ResolvableCall).getTarget() = target
)
or
tag = "NEW" and
c =
count(CallNode call, Target target |
target.isRelevant() and
not call.(PointsToBasedCallGraph::ResolvableCall).getTarget() = target and
call.(TypeTrackingBasedCallGraph::ResolvableCall).getTarget() = target
)
or
tag = "MISSING" and
c =
count(CallNode call, Target target |
target.isRelevant() and
call.(PointsToBasedCallGraph::ResolvableCall).getTarget() = target and
not call.(TypeTrackingBasedCallGraph::ResolvableCall).getTarget() = target
)
)
or
part = "argument-passing" and
(
tag = "SHARED" and
c =
count(ControlFlowNode arg, Parameter param |
PointsToArgumentPassing::argumentPassing(arg, param) and
TypeTrackingArgumentPassing::argumentPassing(arg, param)
)
or
tag = "NEW" and
c =
count(ControlFlowNode arg, Parameter param |
not PointsToArgumentPassing::argumentPassing(arg, param) and
TypeTrackingArgumentPassing::argumentPassing(arg, param)
)
or
tag = "MISSING" and
c =
count(ControlFlowNode arg, Parameter param |
PointsToArgumentPassing::argumentPassing(arg, param) and
not TypeTrackingArgumentPassing::argumentPassing(arg, param)
)
)
select firsExprLocation(), part + " | " + tag + " | " + c as msg order by msg desc
65 changes: 65 additions & 0 deletions python/ql/src/meta/analysis-quality/TTOverviewLocal.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// this version can be run locally, which will allow you to dig into shared/missing/new
// results without running a new query. (but does not work for MRVA).
import python
import CallGraphQuality
import ArgumentPassing

query predicate call_graph_shared(CallNode call, Target target) {
target.isRelevant() and
call.(PointsToBasedCallGraph::ResolvableCall).getTarget() = target and
call.(TypeTrackingBasedCallGraph::ResolvableCall).getTarget() = target
}

query predicate call_graph_new(CallNode call, Target target) {
target.isRelevant() and
not call.(PointsToBasedCallGraph::ResolvableCall).getTarget() = target and
call.(TypeTrackingBasedCallGraph::ResolvableCall).getTarget() = target
}

query predicate call_graph_missing(CallNode call, Target target) {
target.isRelevant() and
call.(PointsToBasedCallGraph::ResolvableCall).getTarget() = target and
not call.(TypeTrackingBasedCallGraph::ResolvableCall).getTarget() = target
}

query predicate argument_passing_shared(ControlFlowNode arg, Parameter param) {
PointsToArgumentPassing::argumentPassing(arg, param) and
TypeTrackingArgumentPassing::argumentPassing(arg, param)
}

query predicate argument_passing_new(ControlFlowNode arg, Parameter param) {
not PointsToArgumentPassing::argumentPassing(arg, param) and
TypeTrackingArgumentPassing::argumentPassing(arg, param)
}

query predicate argument_passing_missing(ControlFlowNode arg, Parameter param) {
PointsToArgumentPassing::argumentPassing(arg, param) and
not TypeTrackingArgumentPassing::argumentPassing(arg, param)
}

from string part, string tag, int c
where
part = "call-graph" and
(
tag = "SHARED" and
c = count(CallNode call, Target target | call_graph_shared(call, target))
or
tag = "NEW" and
c = count(CallNode call, Target target | call_graph_new(call, target))
or
tag = "MISSING" and
c = count(CallNode call, Target target | call_graph_missing(call, target))
)
or
part = "argument-passing" and
(
tag = "SHARED" and
c = count(ControlFlowNode arg, Parameter param | argument_passing_shared(arg, param))
or
tag = "NEW" and
c = count(ControlFlowNode arg, Parameter param | argument_passing_new(arg, param))
or
tag = "MISSING" and
c = count(ControlFlowNode arg, Parameter param | argument_passing_missing(arg, param))
)
select part + " | " + tag as msg, c order by msg desc

0 comments on commit 3e3416e

Please sign in to comment.