diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll index a0b1e4aac9c..7466f373ba0 100644 --- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll @@ -652,6 +652,48 @@ Element interpretElement( ) } +private predicate parseField(string c, FieldContent f) { + specSplit(_, c, _) and + exists(string fieldRegex, string package, string className, string fieldName | + fieldRegex = "^Field\\[(.*)\\.([^.]+)\\.([^.]+)\\]$" and + package = c.regexpCapture(fieldRegex, 1) and + className = c.regexpCapture(fieldRegex, 2) and + fieldName = c.regexpCapture(fieldRegex, 3) and + f.getField().hasQualifiedName(package, className, fieldName) + ) +} + +/** A string representing a synthetic instance field. */ +class SyntheticField extends string { + SyntheticField() { parseSynthField(_, this) } + + /** + * Gets the type of this field. The default type is `Object`, but this can be + * overridden. + */ + Type getType() { result instanceof TypeObject } +} + +private predicate parseSynthField(string c, string f) { + specSplit(_, c, _) and + c.regexpCapture("SyntheticField\\[([.a-zA-Z0-9]+)\\]", 1) = f +} + +/** Holds if the specification component parses as a `Content`. */ +predicate parseContent(string component, Content content) { + parseField(component, content) + or + parseSynthField(component, content.(SyntheticFieldContent).getField()) + or + component = "ArrayElement" and content instanceof ArrayContent + or + component = "Element" and content instanceof CollectionContent + or + component = "MapKey" and content instanceof MapKeyContent + or + component = "MapValue" and content instanceof MapValueContent +} + cached private module Cached { /** diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll index 1e405fbd070..230cd6e0da6 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -162,7 +162,8 @@ private newtype TContent = TArrayContent() or TCollectionContent() or TMapKeyContent() or - TMapValueContent() + TMapValueContent() or + TSyntheticFieldContent(SyntheticField s) /** * A description of the way data may be stored inside an object. Examples @@ -170,6 +171,9 @@ private newtype TContent = * of an array. */ class Content extends TContent { + /** Gets the type of the contained data for the purpose of type pruning. */ + abstract DataFlowType getType(); + /** Gets a textual representation of this element. */ abstract string toString(); @@ -193,6 +197,8 @@ class FieldContent extends Content, TFieldContent { InstanceField getField() { result = f } + override DataFlowType getType() { result = getErasedRepr(f.getType()) } + override string toString() { result = f.toString() } override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { @@ -202,24 +208,45 @@ class FieldContent extends Content, TFieldContent { /** A reference through an array. */ class ArrayContent extends Content, TArrayContent { + override DataFlowType getType() { result instanceof TypeObject } + override string toString() { result = "[]" } } /** A reference through the contents of some collection-like container. */ class CollectionContent extends Content, TCollectionContent { + override DataFlowType getType() { result instanceof TypeObject } + override string toString() { result = "" } } /** A reference through a map key. */ class MapKeyContent extends Content, TMapKeyContent { + override DataFlowType getType() { result instanceof TypeObject } + override string toString() { result = "" } } /** A reference through a map value. */ class MapValueContent extends Content, TMapValueContent { + override DataFlowType getType() { result instanceof TypeObject } + override string toString() { result = "" } } +/** A reference through a synthetic instance field. */ +class SyntheticFieldContent extends Content, TSyntheticFieldContent { + SyntheticField s; + + SyntheticFieldContent() { this = TSyntheticFieldContent(s) } + + SyntheticField getField() { result = s } + + override DataFlowType getType() { result = getErasedRepr(s.getType()) } + + override string toString() { result = s.toString() } +} + /** * A guard that validates some expression. * diff --git a/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll index 974982a9c29..39b6dcd983c 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll @@ -23,21 +23,7 @@ Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = getSum DataFlowCall summaryDataFlowCall(Node receiver) { none() } /** Gets the type of content `c`. */ -DataFlowType getContentType(Content c) { - result = getErasedRepr(c.(FieldContent).getField().getType()) - or - c instanceof CollectionContent and - result instanceof TypeObject - or - c instanceof ArrayContent and - result instanceof TypeObject - or - c instanceof MapKeyContent and - result instanceof TypeObject - or - c instanceof MapValueContent and - result instanceof TypeObject -} +DataFlowType getContentType(Content c) { result = c.getType() } /** Gets the return type of kind `rk` for callable `c`. */ DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { @@ -70,29 +56,10 @@ predicate summaryElement(DataFlowCallable c, string input, string output, string ) } -private FieldContent parseField(string c) { - External::specSplit(_, c, _) and - exists(string fieldRegex, string package, string className, string fieldName | - fieldRegex = "^Field\\[(.*)\\.([^.]+)\\.([^.]+)\\]$" and - package = c.regexpCapture(fieldRegex, 1) and - className = c.regexpCapture(fieldRegex, 2) and - fieldName = c.regexpCapture(fieldRegex, 3) and - result.getField().hasQualifiedName(package, className, fieldName) - ) -} - /** Gets the summary component for specification component `c`, if any. */ bindingset[c] SummaryComponent interpretComponentSpecific(string c) { - result = SummaryComponent::content(parseField(c)) - or - c = "ArrayElement" and result = SummaryComponent::content(any(ArrayContent c0)) - or - c = "Element" and result = SummaryComponent::content(any(CollectionContent c0)) - or - c = "MapKey" and result = SummaryComponent::content(any(MapKeyContent c0)) - or - c = "MapValue" and result = SummaryComponent::content(any(MapValueContent c0)) + exists(Content content | parseContent(c, content) and result = SummaryComponent::content(content)) } class SourceOrSinkElement = Top;