Python: Add type tracker and step summary implementation.#3961
Python: Add type tracker and step summary implementation.#3961calumgrant merged 13 commits intogithub:mainfrom
Conversation
yoff
left a comment
There was a problem hiding this comment.
Got curious, looks like a good start :-)
| module StepSummary { | ||
| cached | ||
| predicate step(Node pred, Node succ, StepSummary summary) { | ||
| exists(Node mid | simpleLocalFlowStep(pred, mid) and smallstep(mid, succ, summary)) |
There was a problem hiding this comment.
This looks a little odd, are you requiring a local step in between all other steps (even local ones)?
Nodes to which we track type tracking flow from the source (any identifier named `tracked`) are indicated with a `$tracked` tag, and `$tracked=attr_name` if the attribute is for the specified attribute of the given node. For nodes that do have flow from `tracked`, I indicate this in one of two ways: - If it's expected due to the design of type tracking, I omit the `$tracked tag. - If it's flow that _ought_ to be there, I indicate it as a false negative: `$f-:tracked` Currently, only an instance of global flow is in the latter category.
It is subsumed by `tracked.ql` anyway.
RasmusWL
left a comment
There was a problem hiding this comment.
Overall looks good and well documented 👍
From my understanding, the current state only supports attribute access for a single depth. So x.foo.bar = tracked wouldn't work right? (if you could add a test case showing this, that would be super sweet)
General observation:
Most of the (new) dataflow library uses predicate someStep(Node nodeFrom, Node nodeTo). I'm not specifically confused by this code using pred and succ as the names, but I thought it might be cool to be consistent across the new library code we are writing? 😊
| * | ||
| * A description of a step on an inter-procedural data flow path. | ||
| */ | ||
| class StepSummary extends TStepSummary { |
There was a problem hiding this comment.
| class StepSummary extends TStepSummary { | |
| private class StepSummary extends TStepSummary { |
Not sure whether this class needs to be used by other ql files, but marking it as private would be an easy way to ensure it's only used internally 😉
There was a problem hiding this comment.
In the JS libs, these files are in an internal directory, and generally I don't see any reason to expose them, so yeah, private makes a lot of sense here.
| } | ||
|
|
||
| /** | ||
| * A utility class that is equivalent to `boolean` but does not require type joining. |
There was a problem hiding this comment.
Can you expand the comment to explain what the problem is? I don't quite understand it at least 😐
There was a problem hiding this comment.
This comes straight from the JavaScript libraries, so I'm not 100% what the intent is, but I think the issue is that if you write
private newtype TTypeTracker = MkTypeTracker(boolean hasCall, OptionalAttributeName prop)(note lowercase boolean) the compiler will complain about hasCall not being sufficiently bound. This is because boolean is treated the same way as, say, int or string for the purposes of checking binding, even though boolean only has two possible values. By encapsulating it in a utility class, we avoid having to specify hasCall = true or hasCall = false in the above definition. (Which, in isolation, is probably not a large gain, but I imagine it could be useful if we start using booleans elsewhere.)
There was a problem hiding this comment.
right, I see. Thanks. If you could add some of that nice explanation into the comment in the code that could be really good! 💪
python/ql/test/experimental/dataflow/typetracking/attribute_tests.py
Outdated
Show resolved
Hide resolved
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
…thon-add-typetracker
| /** | ||
| * A utility class that is equivalent to `boolean` but does not require type joining. | ||
| */ | ||
| private class Boolean extends boolean { |
There was a problem hiding this comment.
Could put this into a util file.
There was a problem hiding this comment.
Sure. I was going to wait until we get another place where Boolean is useful, and then find a good home for it then. 🙂 (In the JS libs, it's just unceremoniously plopped in the middle of FlowSteps.qll.)
|
|
||
| /** Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call. */ | ||
| predicate callStep(ArgumentNode nodeFrom, ParameterNode nodeTo) { | ||
| // TODO: Support special methods? |
There was a problem hiding this comment.
Has this been resolved?
There was a problem hiding this comment.
Not to the extent that type tracking through special method invocations currently works correctly, but we're also still in the process of making sure special methods are supported in the rest of the dataflow framework. Once this has been done, we can start adding type tracking support for this as well.
WIP (Can be merged for further development, but the tests don't currently yield the correct results.)This represents a first stab at using the
TypeTrackerinterface for tracking custom types. It reuses the underlying implementation of local flow steps that is defined inDataFlowPrivate. (This should probably be split out into a more public part, but for now we're just importing it directly.)There are a few issues that still need to be fixed before this can be used fully:Type tracking into and out of attributes.When we've tracked some custom type toxand we dofoo.bar = x, we want to continue tracking the fact thatfoois an object whosebarproperty has the type associated withx. Likewise, a read offoo.barnow results in a node that tracks this same custom type. These steps are handled bybasicStoreStepandbasicLoadStepwhich are currently not implemented.Missing flowAlthough it appears that we are correctly resolving calls and returns, in the given test file we do not manage to track the custom type into theuse_trackedfunction correctly. It's not clear to me where this flow is going missing.Spurious flowLooking at theStepSummary::stepfunction, we see flow from e.g. the SSA variablexthat is assigned inbaz, to the exit node for the functionbazitself. This does not seem entirely right to me (and I think the issue here is insimpleLocalFlowStep-- @yoff may want to take a look at this case).