-
Notifications
You must be signed in to change notification settings - Fork 2k
Java: Convert all collection and array steps from taint flow to value flow. #5751
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
1001dd8
ffd52bb
3b6cef4
9e313d0
3f538e7
2f087e1
a40880a
43d1b0a
901996f
dbe352f
fc913e7
1c081ee
922b421
650c4f1
e86c534
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,5 @@ | ||
| lgtm,codescanning | ||
| * Data flow now tracks steps through collections and arrays more precisely. | ||
| That means that collection and array read steps are now matched up with | ||
| preceding store steps. This results in increased precision for all flow-based | ||
| queries, in particular most of the security queries. |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,10 +60,15 @@ private module Cached { | |
| localAdditionalTaintUpdateStep(src.asExpr(), | ||
| sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) | ||
| or | ||
| exists(Argument arg | | ||
| src.asExpr() = arg and | ||
| arg.isVararg() and | ||
| sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall() | ||
| exists(Content f | | ||
| readStep(src, f, sink) and | ||
| not sink.getTypeBound() instanceof PrimitiveType and | ||
| not sink.getTypeBound() instanceof BoxedType and | ||
| not sink.getTypeBound() instanceof NumberType | ||
| | | ||
| f instanceof ArrayContent or | ||
| f instanceof CollectionContent or | ||
| f instanceof MapValueContent | ||
| ) | ||
| or | ||
| FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false) | ||
|
|
@@ -93,6 +98,92 @@ private module Cached { | |
|
|
||
| import Cached | ||
|
|
||
| /** | ||
| * These configurations add a number of configuration-dependent additional taint | ||
| * steps to all taint configurations. For each sink or additional step provided | ||
| * by a given configuration the types are inspected to find those implicit | ||
| * collection or array read steps that might be required at the sink or step | ||
| * input. The corresponding store steps are then added as additional taint steps | ||
| * to provide backwards-compatible taint flow to such sinks and steps. | ||
| * | ||
| * This is a temporary measure until support is added for such sinks that | ||
| * require implicit read steps. | ||
| */ | ||
| private module StoreTaintSteps { | ||
| private import semmle.code.java.dataflow.TaintTracking | ||
| private import semmle.code.java.dataflow.TaintTracking2 | ||
|
|
||
| private class StoreTaintConfig extends TaintTracking::Configuration { | ||
| StoreTaintConfig() { this instanceof TaintTracking::Configuration or none() } | ||
|
Contributor
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. This is somewhat unusual, could you explain what's going on here? How does this not cause collisions with other implementations of
Contributor
Author
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. This is indeed unusual 😉 And yes, it collides with every other configuration. It's a hack to globally modify all other configurations with code that is itself configuration-dependent.
Contributor
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. Why use this vs implementing
Contributor
Author
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. No. The
Contributor
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. Why would this collection data flow only be applied to
Contributor
Author
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. There are only two copies
Contributor
Author
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. Besides, this is a temporary measure, since it is quite costly in terms of performance. |
||
|
|
||
| override predicate isSource(DataFlow::Node n) { none() } | ||
|
|
||
| override predicate isSink(DataFlow::Node n) { none() } | ||
|
|
||
| private predicate needsTaintStore(RefType container, Type elem, Content f) { | ||
| exists(DataFlow::Node arg | | ||
| (isSink(arg) or isAdditionalTaintStep(arg, _)) and | ||
| (arg.asExpr() instanceof Argument or arg instanceof ArgumentNode) and | ||
| arg.getType() = container | ||
| or | ||
| needsTaintStore(_, container, _) | ||
| | | ||
| container.(Array).getComponentType() = elem and | ||
| f instanceof ArrayContent | ||
| or | ||
| container.(CollectionType).getElementType() = elem and | ||
| f instanceof CollectionContent | ||
| or | ||
| container.(MapType).getValueType() = elem and | ||
| f instanceof MapValueContent | ||
| ) | ||
| } | ||
|
|
||
| override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
| exists(Content f, Type elem | | ||
| storeStep(node1, f, node2) and | ||
| needsTaintStore(_, elem, f) and | ||
| not exists(Type srctyp | srctyp = node1.getTypeBound() | not compatibleTypes(srctyp, elem)) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| private class StoreTaintConfig2 extends TaintTracking2::Configuration { | ||
| StoreTaintConfig2() { this instanceof TaintTracking2::Configuration or none() } | ||
|
|
||
| override predicate isSource(DataFlow::Node n) { none() } | ||
|
|
||
| override predicate isSink(DataFlow::Node n) { none() } | ||
|
|
||
| private predicate needsTaintStore(RefType container, Type elem, Content f) { | ||
| exists(DataFlow::Node arg | | ||
| (isSink(arg) or isAdditionalTaintStep(arg, _)) and | ||
| (arg.asExpr() instanceof Argument or arg instanceof ArgumentNode) and | ||
| arg.getType() = container | ||
| or | ||
| needsTaintStore(_, container, _) | ||
| | | ||
| container.(Array).getComponentType() = elem and | ||
| f instanceof ArrayContent | ||
| or | ||
| container.(CollectionType).getElementType() = elem and | ||
| f instanceof CollectionContent | ||
| or | ||
| container.(MapType).getValueType() = elem and | ||
| f instanceof MapValueContent | ||
| ) | ||
| } | ||
|
|
||
| override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
| exists(Content f, Type elem | | ||
| storeStep(node1, f, node2) and | ||
| needsTaintStore(_, elem, f) and | ||
| not exists(Type srctyp | srctyp = node1.getTypeBound() | not compatibleTypes(srctyp, elem)) | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Holds if taint can flow in one local step from `src` to `sink` excluding | ||
| * local data flow steps. That is, `src` and `sink` are likely to represent | ||
|
|
@@ -103,22 +194,8 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) { | |
| or | ||
| sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString | ||
| or | ||
| sink.(ArrayCreationExpr).getInit() = src | ||
| or | ||
| sink.(ArrayInit).getAnInit() = src | ||
| or | ||
| sink.(ArrayAccess).getArray() = src | ||
| or | ||
| sink.(LogicExpr).getAnOperand() = src | ||
| or | ||
| exists(EnhancedForStmt for, SsaExplicitUpdate v | | ||
| for.getExpr() = src and | ||
| v.getDefiningExpr() = for.getVariable() and | ||
| v.getAFirstUse() = sink | ||
| ) | ||
| or | ||
| containerReturnValueStep(src, sink) | ||
| or | ||
| constructorStep(src, sink) | ||
| or | ||
| qualifierToMethodStep(src, sink) | ||
|
|
@@ -141,12 +218,6 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) { | |
| * This is restricted to cases where the step updates the value of `sink`. | ||
| */ | ||
| private predicate localAdditionalTaintUpdateStep(Expr src, Expr sink) { | ||
| exists(Assignment assign | assign.getSource() = src | | ||
| sink = assign.getDest().(ArrayAccess).getArray() | ||
| ) | ||
| or | ||
| containerUpdateStep(src, sink) | ||
| or | ||
| qualifierToArgumentStep(src, sink) | ||
| or | ||
| argToArgStep(src, sink) | ||
|
|
||
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.
Document this for the benefit of external readers who ought to know this is a stop-gap / temporary measure (and what it intends to do).