-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New points-to and object model for Python. #1277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New points-to and object model for Python. #1277
Conversation
5278da6 to
a7029ab
Compare
…n functions. Move attribute lookup handling to objects.
… Tweak API to be a bit more backward-compatible.
taus-semmle
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First part of review.
taus-semmle
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finished review of ObjectInternal.qll.
…arifying a few comments.
taus-semmle
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This completes the review of everything in /objects (and also includes a bunch of comments in PointsTo.qll, as there was no easy way to separate these).
| /** Gets the `n`th item of this sequence, if one exists. */ | ||
| abstract ObjectInternal getItem(int n); | ||
|
|
||
| /** The boolean value of this object, this may be both |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and all other docstrings that are identical to the ones on the classes they extend would be better if removed altogether. As I understand it, the docstring on the base class will be shown instead.
| override string toString() { | ||
| this.length() = 0 and result = "()" | ||
| or | ||
| result = "(" + this.contents(0) + ")" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor comment, but should we show 1-element tuples as (foo,) the way Python does it? I know it's not the prettiest syntax, but it is unambiguous (and possibly more familiar to Python users).
|
|
||
| override predicate calleeAndOffset(Function scope, int paramOffset) { none() } | ||
|
|
||
| override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would we need to extend this in order to support e.g. sys.version_info.major, or is that handled through some other means?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
I've not seen any tests on sys.version_info.major, so I doubt it matters in practice.
We want to treat sys.version_info as an opaque object, to prevent premature pruning and
false positives when we are unsure of the actual version of Python that the code is expecting.
I've added a QL comment.
| reverse_step(this, Empty(), result) | ||
| } | ||
|
|
||
| boolean isSpecial() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should maybe be called containsSpecial or hasSpecial, and have an explanatory comment?
|
|
||
| /** Holds if this is a subclass of the `Iterable` abstract base class. */ | ||
| boolean isIterableSubclass() { | ||
| this = ObjectInternal::builtin("list") and result = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor point, but this could be written more succinctly by putting result = true in a conjunction outside the first three disjuncts. This way is probably better for readability, though.
| origin = CfgOrigin::unknown() | ||
| } | ||
|
|
||
| pragma [noinline] override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cls should be named instance in order to match up with the inherited docstring. (Alternatively this could have its own docstring.)
| exists(Module init | | ||
| init = this.getSourceModule() and | ||
| ( | ||
| not exists(EssaVariable var | var.getAUse() = init.getANormalExit() and var.getSourceVariable().getName() = name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line seems somewhat mysterious. Could you add an explanatory comment?
|
|
||
| /** Gets a call to this object */ | ||
| CallNode getACall() { | ||
| PointsToInternal::pointsTo(result.getFunction(), _, this, _) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks to me like this body could be replaced with simply result = this.getACall(_).
| * @classmethod | ||
| * def f(cls): pass | ||
| * ``` | ||
| * `this.lookup("f")` is equivent to `C.__dict__['f']`, which is the class-method |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
equivalent
taus-semmle
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with the review of the test files.
| @@ -1,3 +1,5 @@ | |||
| WARNING: Predicate points_to has been deprecated and may be removed in future (BooleanConstants.ql:8,5-24) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably not be in the .expected file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
qltest emits the warning so it has to be in the expected output.
We could update the test to use the new API, but I want to keep the diff on expected outputs small for this PR.
| @@ -0,0 +1,27 @@ | |||
| | 2 | bool False | | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be a false positive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is. Let me see if it is feasible to fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| @@ -1,3 +1,6 @@ | |||
| WARNING: Type CustomPointsToAttribute has been deprecated and may be removed in future (Extend.ql:26,35-58) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better if our tests did not rely on deprecated features (and emit warnings about this). Would rewriting these to use PointsToExtension be too time-consuming?
(If so, I'm happy to accept these warnings temporarily, given the time constraints.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still need to test the deprecated API, so we have to endure the warnings.
| @@ -1,2 +1 @@ | |||
| | expressions_test.py:51:4:51:11 | Compare | The result of this comparison with 'is' may differ between implementations of Python. | | |||
| | expressions_test.py:56:4:56:16 | Compare | The result of this comparison with 'is' may differ between implementations of Python. | | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks to (now) be a false negative, but it doesn't look terribly serious. Is this right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was arguably a false positive and is now correct.
The assumption being that if an object is declared locally then an is comparison is OK as it is testing for identity. E.g.
SENTINEL = object()
...
if x is SENTINEL:
...
5e64b9b to
71ae61c
Compare
taus-semmle
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finished review of PointsTo.qll. I believe this completes the reviewing.
| /** Helper for `scope_entry_value_transfer`. */ | ||
| private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { | ||
| exists(ImportTimeScope scope, ControlFlowNode class_def | | ||
| class_def = pred_var. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
base classes
b4eae7f to
b63d031
Compare
b63d031 to
8e2d6c4
Compare
New points-to and object model for Python.