-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Java: Add data-flow consistency checks. #2921
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
Changes from all commits
ce70b86
a09e479
8e2b56c
67d386b
e97c72c
a9d76cb
85d6b7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
/** | ||
* Provides consistency queries for checking invariants in the language-specific | ||
* data-flow classes and predicates. | ||
*/ | ||
|
||
private import DataFlowImplSpecific::Private | ||
private import DataFlowImplSpecific::Public | ||
private import tainttracking1.TaintTrackingParameter::Private | ||
private import tainttracking1.TaintTrackingParameter::Public | ||
|
||
module Consistency { | ||
private class RelevantNode extends Node { | ||
RelevantNode() { | ||
this instanceof ArgumentNode or | ||
this instanceof ParameterNode or | ||
this instanceof ReturnNode or | ||
this = getAnOutNode(_, _) or | ||
simpleLocalFlowStep(this, _) or | ||
simpleLocalFlowStep(_, this) or | ||
jumpStep(this, _) or | ||
jumpStep(_, this) or | ||
storeStep(this, _, _) or | ||
storeStep(_, _, this) or | ||
readStep(this, _, _) or | ||
readStep(_, _, this) or | ||
defaultAdditionalTaintStep(this, _) or | ||
defaultAdditionalTaintStep(_, this) | ||
} | ||
} | ||
|
||
query predicate uniqueEnclosingCallable(Node n, string msg) { | ||
exists(int c | | ||
n instanceof RelevantNode and | ||
c = count(n.getEnclosingCallable()) and | ||
c != 1 and | ||
msg = "Node should have one enclosing callable but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate uniqueTypeBound(Node n, string msg) { | ||
exists(int c | | ||
n instanceof RelevantNode and | ||
c = count(n.getTypeBound()) and | ||
c != 1 and | ||
msg = "Node should have one type bound but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate uniqueTypeRepr(Node n, string msg) { | ||
exists(int c | | ||
n instanceof RelevantNode and | ||
c = count(getErasedRepr(n.getTypeBound())) and | ||
c != 1 and | ||
msg = "Node should have one type representation but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate uniqueNodeLocation(Node n, string msg) { | ||
exists(int c | | ||
c = | ||
count(string filepath, int startline, int startcolumn, int endline, int endcolumn | | ||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) | ||
) and | ||
c != 1 and | ||
msg = "Node should have one location but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate missingLocation(string msg) { | ||
exists(int c | | ||
c = | ||
strictcount(Node n | | ||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | | ||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) | ||
) | ||
) and | ||
msg = "Nodes without location: " + c | ||
) | ||
} | ||
|
||
query predicate uniqueNodeToString(Node n, string msg) { | ||
exists(int c | | ||
c = count(n.toString()) and | ||
c != 1 and | ||
msg = "Node should have one toString but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate missingToString(string msg) { | ||
exists(int c | | ||
c = strictcount(Node n | not exists(n.toString())) and | ||
msg = "Nodes without toString: " + c | ||
) | ||
} | ||
|
||
query predicate parameterCallable(ParameterNode p, string msg) { | ||
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and | ||
msg = "Callable mismatch for parameter." | ||
} | ||
|
||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) { | ||
simpleLocalFlowStep(n1, n2) and | ||
n1.getEnclosingCallable() != n2.getEnclosingCallable() and | ||
msg = "Local flow step does not preserve enclosing callable." | ||
} | ||
|
||
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } | ||
|
||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) { | ||
t = typeRepr() and | ||
not compatibleTypes(t, t) and | ||
msg = "Type compatibility predicate is not reflexive." | ||
} | ||
|
||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) { | ||
isUnreachableInCall(n, call) and | ||
exists(DataFlowCallable c | | ||
c = n.getEnclosingCallable() and | ||
not viableCallable(call) = c | ||
) and | ||
msg = "Call context for isUnreachableInCall is inconsistent with call graph." | ||
} | ||
|
||
query predicate localCallNodes(DataFlowCall call, Node n, string msg) { | ||
( | ||
n = getAnOutNode(call, _) and | ||
msg = "OutNode and call does not share enclosing callable." | ||
or | ||
n.(ArgumentNode).argumentOf(call, _) and | ||
msg = "ArgumentNode and call does not share enclosing callable." | ||
) and | ||
n.getEnclosingCallable() != call.getEnclosingCallable() | ||
} | ||
|
||
query predicate postIsNotPre(PostUpdateNode n, string msg) { | ||
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you comment briefly on what happens if this condition is violated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Flow that exits a method call through a side-effect on an argument may re-enter the method. E.g. in the following you'll get wrong flow from
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. |
||
} | ||
|
||
query predicate postHasUniquePre(PostUpdateNode n, string msg) { | ||
exists(int c | | ||
c = count(n.getPreUpdateNode()) and | ||
c != 1 and | ||
msg = "PostUpdateNode should have one pre-update node but has " + c + "." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One example is that a store to a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I was mostly asking about the case of multiple pre-update nodes. Glad to hear it's not known to mess things up massively. |
||
) | ||
} | ||
|
||
query predicate uniquePostUpdate(Node n, string msg) { | ||
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and | ||
msg = "Node has multiple PostUpdateNodes." | ||
} | ||
|
||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) { | ||
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and | ||
msg = "PostUpdateNode does not share callable with its pre-update node." | ||
} | ||
|
||
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) } | ||
|
||
query predicate reverseRead(Node n, string msg) { | ||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and | ||
msg = "Origin of readStep is missing a PostUpdateNode." | ||
} | ||
|
||
query predicate storeIsPostUpdate(Node n, string msg) { | ||
storeStep(_, _, n) and | ||
not n instanceof PostUpdateNode and | ||
msg = "Store targets should be PostUpdateNodes." | ||
} | ||
|
||
query predicate argHasPostUpdate(ArgumentNode n, string msg) { | ||
not hasPost(n) and | ||
not isImmutableOrUnobservable(n) and | ||
msg = "ArgumentNode is missing PostUpdateNode." | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
/** | ||
* Provides consistency queries for checking invariants in the language-specific | ||
* data-flow classes and predicates. | ||
*/ | ||
|
||
private import DataFlowImplSpecific::Private | ||
private import DataFlowImplSpecific::Public | ||
private import tainttracking1.TaintTrackingParameter::Private | ||
private import tainttracking1.TaintTrackingParameter::Public | ||
|
||
module Consistency { | ||
private class RelevantNode extends Node { | ||
RelevantNode() { | ||
this instanceof ArgumentNode or | ||
this instanceof ParameterNode or | ||
this instanceof ReturnNode or | ||
this = getAnOutNode(_, _) or | ||
simpleLocalFlowStep(this, _) or | ||
simpleLocalFlowStep(_, this) or | ||
jumpStep(this, _) or | ||
jumpStep(_, this) or | ||
storeStep(this, _, _) or | ||
storeStep(_, _, this) or | ||
readStep(this, _, _) or | ||
readStep(_, _, this) or | ||
defaultAdditionalTaintStep(this, _) or | ||
defaultAdditionalTaintStep(_, this) | ||
} | ||
} | ||
|
||
query predicate uniqueEnclosingCallable(Node n, string msg) { | ||
exists(int c | | ||
n instanceof RelevantNode and | ||
c = count(n.getEnclosingCallable()) and | ||
c != 1 and | ||
msg = "Node should have one enclosing callable but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate uniqueTypeBound(Node n, string msg) { | ||
exists(int c | | ||
n instanceof RelevantNode and | ||
c = count(n.getTypeBound()) and | ||
c != 1 and | ||
msg = "Node should have one type bound but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate uniqueTypeRepr(Node n, string msg) { | ||
exists(int c | | ||
n instanceof RelevantNode and | ||
c = count(getErasedRepr(n.getTypeBound())) and | ||
c != 1 and | ||
msg = "Node should have one type representation but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate uniqueNodeLocation(Node n, string msg) { | ||
exists(int c | | ||
c = | ||
count(string filepath, int startline, int startcolumn, int endline, int endcolumn | | ||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) | ||
) and | ||
c != 1 and | ||
msg = "Node should have one location but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate missingLocation(string msg) { | ||
exists(int c | | ||
c = | ||
strictcount(Node n | | ||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | | ||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) | ||
) | ||
) and | ||
msg = "Nodes without location: " + c | ||
) | ||
} | ||
|
||
query predicate uniqueNodeToString(Node n, string msg) { | ||
exists(int c | | ||
c = count(n.toString()) and | ||
c != 1 and | ||
msg = "Node should have one toString but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate missingToString(string msg) { | ||
exists(int c | | ||
c = strictcount(Node n | not exists(n.toString())) and | ||
msg = "Nodes without toString: " + c | ||
) | ||
} | ||
|
||
query predicate parameterCallable(ParameterNode p, string msg) { | ||
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and | ||
msg = "Callable mismatch for parameter." | ||
} | ||
|
||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) { | ||
simpleLocalFlowStep(n1, n2) and | ||
n1.getEnclosingCallable() != n2.getEnclosingCallable() and | ||
msg = "Local flow step does not preserve enclosing callable." | ||
} | ||
|
||
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } | ||
|
||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) { | ||
t = typeRepr() and | ||
not compatibleTypes(t, t) and | ||
msg = "Type compatibility predicate is not reflexive." | ||
} | ||
|
||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) { | ||
isUnreachableInCall(n, call) and | ||
exists(DataFlowCallable c | | ||
c = n.getEnclosingCallable() and | ||
not viableCallable(call) = c | ||
) and | ||
msg = "Call context for isUnreachableInCall is inconsistent with call graph." | ||
} | ||
|
||
query predicate localCallNodes(DataFlowCall call, Node n, string msg) { | ||
( | ||
n = getAnOutNode(call, _) and | ||
msg = "OutNode and call does not share enclosing callable." | ||
or | ||
n.(ArgumentNode).argumentOf(call, _) and | ||
msg = "ArgumentNode and call does not share enclosing callable." | ||
) and | ||
n.getEnclosingCallable() != call.getEnclosingCallable() | ||
} | ||
|
||
query predicate postIsNotPre(PostUpdateNode n, string msg) { | ||
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node." | ||
} | ||
|
||
query predicate postHasUniquePre(PostUpdateNode n, string msg) { | ||
exists(int c | | ||
c = count(n.getPreUpdateNode()) and | ||
c != 1 and | ||
msg = "PostUpdateNode should have one pre-update node but has " + c + "." | ||
) | ||
} | ||
|
||
query predicate uniquePostUpdate(Node n, string msg) { | ||
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and | ||
msg = "Node has multiple PostUpdateNodes." | ||
} | ||
|
||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) { | ||
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and | ||
msg = "PostUpdateNode does not share callable with its pre-update node." | ||
} | ||
|
||
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) } | ||
|
||
query predicate reverseRead(Node n, string msg) { | ||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and | ||
msg = "Origin of readStep is missing a PostUpdateNode." | ||
} | ||
|
||
query predicate storeIsPostUpdate(Node n, string msg) { | ||
storeStep(_, _, n) and | ||
not n instanceof PostUpdateNode and | ||
msg = "Store targets should be PostUpdateNodes." | ||
} | ||
|
||
query predicate argHasPostUpdate(ArgumentNode n, string msg) { | ||
not hasPost(n) and | ||
not isImmutableOrUnobservable(n) and | ||
msg = "ArgumentNode is missing PostUpdateNode." | ||
} | ||
} |
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.
You could use
getErasedNodeTypeBound
here instead...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.
And below.
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 requires an additional import of
DataFlowImplCommon
, so perhaps just leave as-is?