From beab320557e1bfd80d135e3a54eb1b027e5af6b9 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 24 Apr 2020 14:17:47 +0200 Subject: [PATCH] Java: Add more qldoc. --- java/ql/src/semmle/code/java/Collections.qll | 6 ++++++ java/ql/src/semmle/code/java/Completion.qll | 8 +++++++- java/ql/src/semmle/code/java/ControlFlowGraph.qll | 1 + java/ql/src/semmle/code/java/Expr.qll | 1 + java/ql/src/semmle/code/java/Maps.qll | 8 ++++++++ java/ql/src/semmle/code/java/Reflection.qll | 3 +++ java/ql/src/semmle/code/java/StringFormat.qll | 3 +++ java/ql/src/semmle/code/java/Type.qll | 2 ++ java/ql/src/semmle/code/java/controlflow/Guards.qll | 5 +++++ java/ql/src/semmle/code/java/dataflow/Bound.qll | 12 ++++++++++++ .../ql/src/semmle/code/java/dataflow/FlowSources.qll | 9 ++++++++- java/ql/src/semmle/code/java/dataflow/Nullness.qll | 4 ++-- .../src/semmle/code/java/dataflow/RangeAnalysis.qll | 7 +++++++ java/ql/src/semmle/code/java/dataflow/RangeUtils.qll | 1 + java/ql/src/semmle/code/java/dataflow/SSA.qll | 6 +++++- java/ql/src/semmle/code/java/dataflow/TypeFlow.qll | 4 ++-- .../src/semmle/code/java/dispatch/DispatchFlow.qll | 8 ++++++++ .../semmle/code/java/dispatch/VirtualDispatch.qll | 6 ++++++ .../semmle/code/java/dispatch/WrappedInvocation.qll | 5 +++++ .../src/semmle/code/java/frameworks/Assertions.qll | 3 ++- .../src/semmle/code/java/frameworks/Networking.qll | 7 ++++++- 21 files changed, 100 insertions(+), 9 deletions(-) diff --git a/java/ql/src/semmle/code/java/Collections.qll b/java/ql/src/semmle/code/java/Collections.qll index b0cf771778d..9569dfc4c2c 100644 --- a/java/ql/src/semmle/code/java/Collections.qll +++ b/java/ql/src/semmle/code/java/Collections.qll @@ -1,3 +1,8 @@ +/** + * Provides classes and predicates for reasoning about instances of + * `java.util.Collection` and their methods. + */ + import java /** @@ -77,6 +82,7 @@ class CollectionMutator extends CollectionMethod { class CollectionMutation extends MethodAccess { CollectionMutation() { this.getMethod() instanceof CollectionMutator } + /** Holds if the result of this call is not immediately discarded. */ predicate resultIsChecked() { not this.getParent() instanceof ExprStmt } } diff --git a/java/ql/src/semmle/code/java/Completion.qll b/java/ql/src/semmle/code/java/Completion.qll index c8b4f966a3b..6ccdb16df72 100644 --- a/java/ql/src/semmle/code/java/Completion.qll +++ b/java/ql/src/semmle/code/java/Completion.qll @@ -68,21 +68,27 @@ newtype Completion = */ ThrowCompletion(ThrowableType tt) +/** A completion that is either a `NormalCompletion` or a `BooleanCompletion`. */ class NormalOrBooleanCompletion extends Completion { NormalOrBooleanCompletion() { this instanceof NormalCompletion or this instanceof BooleanCompletion } + /** Gets a textual representation of this completion. */ string toString() { result = "completion" } } +/** Gets the completion `ContinueCompletion(NoLabel())`. */ ContinueCompletion anonymousContinueCompletion() { result = ContinueCompletion(NoLabel()) } +/** Gets the completion `ContinueCompletion(JustLabel(l))`. */ ContinueCompletion labelledContinueCompletion(Label l) { result = ContinueCompletion(JustLabel(l)) } +/** Gets the completion `BreakCompletion(NoLabel())`. */ BreakCompletion anonymousBreakCompletion() { result = BreakCompletion(NoLabel()) } +/** Gets the completion `BreakCompletion(JustLabel(l))`. */ BreakCompletion labelledBreakCompletion(Label l) { result = BreakCompletion(JustLabel(l)) } -/** Gets the completion `booleanCompletion(value, value)`. */ +/** Gets the completion `BooleanCompletion(value, value)`. */ Completion basicBooleanCompletion(boolean value) { result = BooleanCompletion(value, value) } diff --git a/java/ql/src/semmle/code/java/ControlFlowGraph.qll b/java/ql/src/semmle/code/java/ControlFlowGraph.qll index 13e6524352a..820f49487b7 100644 --- a/java/ql/src/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/src/semmle/code/java/ControlFlowGraph.qll @@ -113,6 +113,7 @@ class ControlFlowNode extends Top, @exprparent { result = succ(this, NormalCompletion()) } + /** Gets the basic block that contains this node. */ BasicBlock getBasicBlock() { result.getANode() = this } } diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll index 73475f6cc70..dba1baf6148 100755 --- a/java/ql/src/semmle/code/java/Expr.qll +++ b/java/ql/src/semmle/code/java/Expr.qll @@ -844,6 +844,7 @@ class EqualityTest extends BinaryExpr { this instanceof NEExpr } + /** Gets a boolean indicating whether this is `==` (true) or `!=` (false). */ boolean polarity() { result = true and this instanceof EQExpr or diff --git a/java/ql/src/semmle/code/java/Maps.qll b/java/ql/src/semmle/code/java/Maps.qll index 6a681e60683..c86cb0ef47a 100644 --- a/java/ql/src/semmle/code/java/Maps.qll +++ b/java/ql/src/semmle/code/java/Maps.qll @@ -1,3 +1,8 @@ +/** + * Provides classes and predicates for reasoning about instances of + * `java.util.Map` and their methods. + */ + import java import Collections @@ -47,6 +52,7 @@ class MapSizeMethod extends MapMethod { class MapMutation extends MethodAccess { MapMutation() { this.getMethod() instanceof MapMutator } + /** Holds if the result of this call is not immediately discarded. */ predicate resultIsChecked() { not this.getParent() instanceof ExprStmt } } @@ -72,7 +78,9 @@ class FreshMap extends ClassInstanceExpr { class MapPutCall extends MethodAccess { MapPutCall() { getCallee().(MapMethod).hasName("put") } + /** Gets the key argument of this call. */ Expr getKey() { result = getArgument(0) } + /** Gets the value argument of this call. */ Expr getValue() { result = getArgument(1) } } diff --git a/java/ql/src/semmle/code/java/Reflection.qll b/java/ql/src/semmle/code/java/Reflection.qll index 3b0189e17ae..5a46fcb8be2 100644 --- a/java/ql/src/semmle/code/java/Reflection.qll +++ b/java/ql/src/semmle/code/java/Reflection.qll @@ -7,12 +7,14 @@ import JDKAnnotations import Serializability import semmle.code.java.dataflow.DefUse +/** Holds if `f` is a field that may be read by reflection. */ predicate reflectivelyRead(Field f) { f instanceof SerializableField or f.getAnAnnotation() instanceof ReflectiveAccessAnnotation or referencedInXmlFile(f) } +/** Holds if `f` is a field that may be written by reflection. */ predicate reflectivelyWritten(Field f) { f instanceof DeserializableField or f.getAnAnnotation() instanceof ReflectiveAccessAnnotation or @@ -360,6 +362,7 @@ class ReflectiveFieldAccess extends ClassMethodAccess { this.getCallee().hasName("getDeclaredField") } + /** Gets the field accessed by this call. */ Field inferAccessedField() { ( if this.getCallee().hasName("getDeclaredField") diff --git a/java/ql/src/semmle/code/java/StringFormat.qll b/java/ql/src/semmle/code/java/StringFormat.qll index 410d83818cc..fb2383b8e13 100644 --- a/java/ql/src/semmle/code/java/StringFormat.qll +++ b/java/ql/src/semmle/code/java/StringFormat.qll @@ -80,6 +80,7 @@ private newtype TFmtSyntax = /** A syntax for format strings. */ class FmtSyntax extends TFmtSyntax { + /** Gets a textual representation of this format string syntax. */ string toString() { result = "printf (%) syntax" and this = TFmtPrintf() or @@ -130,6 +131,7 @@ class FormattingCall extends Call { formatWrapper(this.getCallee(), result, _) } + /** Gets the format string syntax used by this call. */ FmtSyntax getSyntax() { this.getCallee() instanceof StringFormatMethod and result = TFmtPrintf() or @@ -146,6 +148,7 @@ class FormattingCall extends Call { ) } + /** Holds if this uses the "logger ({})" format syntax and the last argument is a `Throwable`. */ predicate hasTrailingThrowableArgument() { getSyntax() = TFmtLogger() and getLastArg().getType().(RefType).getASourceSupertype*() instanceof TypeThrowable diff --git a/java/ql/src/semmle/code/java/Type.qll b/java/ql/src/semmle/code/java/Type.qll index 35f35d44928..4f29ea94bd7 100755 --- a/java/ql/src/semmle/code/java/Type.qll +++ b/java/ql/src/semmle/code/java/Type.qll @@ -637,10 +637,12 @@ class IntersectionType extends RefType, @class { private RefType superInterface() { implInterface(this, result) } + /** Gets a textual representation of this type that includes all the intersected types. */ string getLongName() { result = superType().toString() + concat(" & " + superInterface().toString()) } + /** Gets the first bound of this intersection type. */ RefType getFirstBound() { extendsReftype(this, result) } } diff --git a/java/ql/src/semmle/code/java/controlflow/Guards.qll b/java/ql/src/semmle/code/java/controlflow/Guards.qll index 82cb1d997ae..0d18ac24e51 100644 --- a/java/ql/src/semmle/code/java/controlflow/Guards.qll +++ b/java/ql/src/semmle/code/java/controlflow/Guards.qll @@ -1,3 +1,8 @@ +/** + * Provides classes and predicates for reasoning about guards and the control + * flow elements controlled by those guards. + */ + import java private import semmle.code.java.controlflow.Dominance private import semmle.code.java.controlflow.internal.GuardsLogic diff --git a/java/ql/src/semmle/code/java/dataflow/Bound.qll b/java/ql/src/semmle/code/java/dataflow/Bound.qll index 1742e1dc4fe..4a89f8cfeba 100644 --- a/java/ql/src/semmle/code/java/dataflow/Bound.qll +++ b/java/ql/src/semmle/code/java/dataflow/Bound.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for representing abstract bounds for use in, for example, range analysis. + */ + import java private import SSA private import RangeUtils @@ -14,6 +18,7 @@ private newtype TBound = * A bound that may be inferred for an expression plus/minus an integer delta. */ abstract class Bound extends TBound { + /** Gets a textual representation of this bound. */ abstract string toString(); /** Gets an expression that equals this bound plus `delta`. */ @@ -22,6 +27,13 @@ abstract class Bound extends TBound { /** Gets an expression that equals this bound. */ Expr getExpr() { result = getExpr(0) } + /** + * Holds if this element is at the specified location. + * The location spans column `sc` of line `sl` to + * column `ec` of line `el` in file `path`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index e8872e1bdfb..65fc40f8f1c 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -152,9 +152,13 @@ deprecated class RemoteUserInput extends UserInput { RemoteUserInput() { this instanceof RemoteFlowSource } } -/** Input that may be controlled by a local user. */ +/** A node with input that may be controlled by a local user. */ abstract class LocalUserInput extends UserInput { } +/** + * A node with input from the local environment, such as files, standard in, + * environment variables, and main method parameters. + */ class EnvInput extends LocalUserInput { EnvInput() { // Parameters to a main method. @@ -180,6 +184,7 @@ class EnvInput extends LocalUserInput { } } +/** A node with input from a database. */ class DatabaseInput extends LocalUserInput { DatabaseInput() { this.asExpr().(MethodAccess).getMethod() instanceof ResultSetGetStringMethod } } @@ -222,10 +227,12 @@ private class EnvTaintedMethod extends Method { } } +/** The type `java.net.InetAddress`. */ class TypeInetAddr extends RefType { TypeInetAddr() { this.getQualifiedName() = "java.net.InetAddress" } } +/** A reverse DNS method. */ class ReverseDNSMethod extends Method { ReverseDNSMethod() { this.getDeclaringType() instanceof TypeInetAddr and diff --git a/java/ql/src/semmle/code/java/dataflow/Nullness.qll b/java/ql/src/semmle/code/java/dataflow/Nullness.qll index 644790bea6b..1679ce6b9b9 100644 --- a/java/ql/src/semmle/code/java/dataflow/Nullness.qll +++ b/java/ql/src/semmle/code/java/dataflow/Nullness.qll @@ -579,7 +579,7 @@ private predicate varMaybeNullInBlock_corrCond( * - int: A means a specific integer value and B means any other value. */ -newtype TrackVarKind = +private newtype TrackVarKind = TrackVarKindNull() or TrackVarKindBool() or TrackVarKindEnum() or @@ -701,7 +701,7 @@ private predicate isReset( } /** The abstract value of the tracked variable. */ -newtype TrackedValue = +private newtype TrackedValue = TrackedValueA() or TrackedValueB() or TrackedValueUnknown() diff --git a/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll b/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll index 435b976fde2..607ce04da80 100644 --- a/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll +++ b/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll @@ -264,14 +264,21 @@ private newtype TReason = * without going through a bounding condition. */ abstract class Reason extends TReason { + /** Gets a textual representation of this reason. */ abstract string toString(); } +/** + * A reason for an inferred bound that indicates that the bound is inferred + * without going through a bounding condition. + */ class NoReason extends Reason, TNoReason { override string toString() { result = "NoReason" } } +/** A reason for an inferred bound pointing to a condition. */ class CondReason extends Reason, TCondReason { + /** Gets the condition that is the reason for the bound. */ Guard getCond() { this = TCondReason(result) } override string toString() { result = getCond().toString() } diff --git a/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll b/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll index 22e363d9e3c..e7da9891b47 100644 --- a/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll +++ b/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll @@ -142,6 +142,7 @@ class SsaReadPosition extends TSsaReadPosition { /** Holds if `v` is read at this position. */ abstract predicate hasReadOfVar(SsaVariable v); + /** Gets a textual representation of this SSA read position. */ abstract string toString(); } diff --git a/java/ql/src/semmle/code/java/dataflow/SSA.qll b/java/ql/src/semmle/code/java/dataflow/SSA.qll index 561fdb35c5e..e6d92bb4d60 100644 --- a/java/ql/src/semmle/code/java/dataflow/SSA.qll +++ b/java/ql/src/semmle/code/java/dataflow/SSA.qll @@ -89,6 +89,7 @@ class SsaSourceVariable extends TSsaSourceVariable { this = TQualifiedField(result, _, _) } + /** Gets a textual representation of this `SsaSourceVariable`. */ string toString() { exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) | if c = v.getCallable() @@ -112,6 +113,7 @@ class SsaSourceVariable extends TSsaSourceVariable { ) } + /** Gets the source location for this element. */ Location getLocation() { exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation()) or @@ -935,8 +937,10 @@ class SsaVariable extends TSsaVariable { this = TSsaUntracked(_, result) } + /** Gets a textual representation of this SSA variable. */ string toString() { none() } + /** Gets the source location for this element. */ Location getLocation() { result = getCFGNode().getLocation() } /** Gets the `BasicBlock` in which this SSA variable is defined. */ @@ -1113,7 +1117,7 @@ class SsaPhiNode extends SsaVariable, TSsaPhiNode { } } -library class RefTypeCastExpr extends CastExpr { +private class RefTypeCastExpr extends CastExpr { RefTypeCastExpr() { this.getType() instanceof RefType } } diff --git a/java/ql/src/semmle/code/java/dataflow/TypeFlow.qll b/java/ql/src/semmle/code/java/dataflow/TypeFlow.qll index 24cf918a9b7..b5cea656a38 100644 --- a/java/ql/src/semmle/code/java/dataflow/TypeFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/TypeFlow.qll @@ -365,12 +365,12 @@ private predicate typeFlow(TypeFlowNode n, RefType t) { } pragma[nomagic] -predicate erasedTypeBound(RefType t) { +private predicate erasedTypeBound(RefType t) { exists(RefType t0 | typeFlow(_, t0) and t = t0.getErasure()) } pragma[nomagic] -predicate typeBound(RefType t) { typeFlow(_, t) } +private predicate typeBound(RefType t) { typeFlow(_, t) } /** * Holds if we have a bound for `n` that is better than `t`, taking only erased diff --git a/java/ql/src/semmle/code/java/dispatch/DispatchFlow.qll b/java/ql/src/semmle/code/java/dispatch/DispatchFlow.qll index 0b2b871e129..0ef6da8b150 100644 --- a/java/ql/src/semmle/code/java/dispatch/DispatchFlow.qll +++ b/java/ql/src/semmle/code/java/dispatch/DispatchFlow.qll @@ -1,3 +1,11 @@ +/** + * INTERNAL: This is part of the virtual dispatch computation. + * + * Provides a strengthening of the virtual dispatch relation using a dedicated + * data flow check for lambdas, anonymous classes, and other sufficiently + * private classes where all object instantiations are accounted for. + */ + import java private import VirtualDispatch private import semmle.code.java.dataflow.internal.BaseSSA diff --git a/java/ql/src/semmle/code/java/dispatch/VirtualDispatch.qll b/java/ql/src/semmle/code/java/dispatch/VirtualDispatch.qll index e291604ab06..bb6884ced06 100644 --- a/java/ql/src/semmle/code/java/dispatch/VirtualDispatch.qll +++ b/java/ql/src/semmle/code/java/dispatch/VirtualDispatch.qll @@ -1,3 +1,8 @@ +/** + * Provides predicates for reasoning about runtime call targets through virtual + * dispatch. + */ + import java import semmle.code.java.dataflow.TypeFlow private import DispatchFlow as DispatchFlow @@ -27,6 +32,7 @@ Callable exactCallable(Call c) { private predicate implCount(MethodAccess m, int c) { strictcount(viableImpl(m)) = c } +/** Gets a viable implementation of the target of the given `Call`. */ Callable viableCallable(Call c) { result = viableImpl(c) or diff --git a/java/ql/src/semmle/code/java/dispatch/WrappedInvocation.qll b/java/ql/src/semmle/code/java/dispatch/WrappedInvocation.qll index 6c57fced300..cbff880ca96 100644 --- a/java/ql/src/semmle/code/java/dispatch/WrappedInvocation.qll +++ b/java/ql/src/semmle/code/java/dispatch/WrappedInvocation.qll @@ -1,3 +1,8 @@ +/** + * Provides classes and predicates for reasoning about calls that may invoke one + * of their arguments. + */ + import java import VirtualDispatch diff --git a/java/ql/src/semmle/code/java/frameworks/Assertions.qll b/java/ql/src/semmle/code/java/frameworks/Assertions.qll index 810af5ca41e..3c3d090b993 100644 --- a/java/ql/src/semmle/code/java/frameworks/Assertions.qll +++ b/java/ql/src/semmle/code/java/frameworks/Assertions.qll @@ -7,7 +7,7 @@ import java -newtype AssertKind = +private newtype AssertKind = AssertKindTrue() or AssertKindFalse() or AssertKindNotNull() or @@ -50,6 +50,7 @@ private predicate assertionMethod(Method m, AssertKind kind) { ) } +/** An assertion method. */ class AssertionMethod extends Method { AssertionMethod() { assertionMethod(this, _) } diff --git a/java/ql/src/semmle/code/java/frameworks/Networking.qll b/java/ql/src/semmle/code/java/frameworks/Networking.qll index 92575995636..70cad9f54ff 100644 --- a/java/ql/src/semmle/code/java/frameworks/Networking.qll +++ b/java/ql/src/semmle/code/java/frameworks/Networking.qll @@ -1,21 +1,25 @@ -/* +/** * Definitions related to `java.net.*`. */ import semmle.code.java.Type +/** The type `java.net.URLConnection`. */ class TypeUrlConnection extends RefType { TypeUrlConnection() { hasQualifiedName("java.net", "URLConnection") } } +/** The type `java.net.Socket`. */ class TypeSocket extends RefType { TypeSocket() { hasQualifiedName("java.net", "Socket") } } +/** The type `java.net.URL`. */ class TypeUrl extends RefType { TypeUrl() { hasQualifiedName("java.net", "URL") } } +/** The method `java.net.URLConnection::getInputStream`. */ class URLConnectionGetInputStreamMethod extends Method { URLConnectionGetInputStreamMethod() { getDeclaringType() instanceof TypeUrlConnection and @@ -24,6 +28,7 @@ class URLConnectionGetInputStreamMethod extends Method { } } +/** The method `java.net.Socket::getInputStream`. */ class SocketGetInputStreamMethod extends Method { SocketGetInputStreamMethod() { getDeclaringType() instanceof TypeSocket and