From bcad18f490fc6bb4086d0d444366db256793deb3 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 25 Jun 2020 12:49:39 +0200 Subject: [PATCH] Java: Use the instance argument type in call contexts. --- .../dataflow/internal/DataFlowDispatch.qll | 45 +++++++++---- .../library-tests/dataflow/callctx/A.java | 63 +++++++++++++++++++ .../dataflow/callctx/test.expected | 15 +++++ .../library-tests/dataflow/callctx/test.ql | 15 +++++ 4 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 java/ql/test/library-tests/dataflow/callctx/A.java create mode 100644 java/ql/test/library-tests/dataflow/callctx/test.expected create mode 100644 java/ql/test/library-tests/dataflow/callctx/test.ql diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll index 97b833524921..ba99bffbf5f4 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll @@ -1,5 +1,7 @@ private import java private import DataFlowPrivate +private import DataFlowUtil +private import semmle.code.java.dataflow.InstanceAccess import semmle.code.java.dispatch.VirtualDispatch private module DispatchImpl { @@ -17,6 +19,13 @@ private module DispatchImpl { not p.isVarargs() and c = ma.getEnclosingCallable() ) + or + exists(OwnInstanceAccess ia | + 2 <= strictcount(viableImpl(ma)) and + (ia.isExplicit(ma.getQualifier()) or ia.isImplicitMethodQualifier(ma)) and + i = -1 and + c = ma.getEnclosingCallable() + ) } /** @@ -37,18 +46,32 @@ private module DispatchImpl { * relevant call context. */ private predicate contextArgHasType(Call ctx, int i, RefType t, boolean exact) { - exists(Expr arg, Expr src | - relevantContext(ctx, i) and - ctx.getArgument(i) = arg and - src = variableTrack(arg) and - exists(RefType srctype | srctype = src.getType() | - exists(TypeVariable v | v = srctype | - t = v.getUpperBoundType+() and not t instanceof TypeVariable - ) + relevantContext(ctx, i) and + exists(RefType srctype | + exists(Expr arg, Expr src | + i = -1 and + ctx.getQualifier() = arg or - t = srctype and not srctype instanceof TypeVariable - ) and - if src instanceof ClassInstanceExpr then exact = true else exact = false + ctx.getArgument(i) = arg + | + src = variableTrack(arg) and + srctype = src.getType() and + if src instanceof ClassInstanceExpr then exact = true else exact = false + ) + or + exists(Node arg | + i = -1 and + not exists(ctx.getQualifier()) and + getInstanceArgument(ctx) = arg and + arg.getTypeBound() = srctype and + if ctx instanceof ClassInstanceExpr then exact = true else exact = false + ) + | + exists(TypeVariable v | v = srctype | + t = v.getUpperBoundType+() and not t instanceof TypeVariable + ) + or + t = srctype and not srctype instanceof TypeVariable ) } diff --git a/java/ql/test/library-tests/dataflow/callctx/A.java b/java/ql/test/library-tests/dataflow/callctx/A.java new file mode 100644 index 000000000000..3bb6382b356b --- /dev/null +++ b/java/ql/test/library-tests/dataflow/callctx/A.java @@ -0,0 +1,63 @@ +public class A { + static void sink(Object x) { } + + static Object source() { return null; } + + static class C1 { + C1() { } + + C1(Object x) { + foo(x); + } + + void wrapFoo1(Object x) { + foo(x); + } + + void wrapFoo2(Object x) { + this.foo(x); + } + + void foo(Object x) { + Object c1 = x; + sink(c1); + } + } + + static class C2 extends C1 { + C2() { } + + C2(Object x) { + super(x); + } + + void foo(Object x) { + Object c2 = x; + sink(c2); + } + + void callWrapFoo2() { + wrapFoo2(source()); + } + } + + static void wrapFoo3(C1 c1, Object x) { + c1.foo(x); + } + + void test(C1 c) { + c.wrapFoo1(source()); + c.wrapFoo2(source()); + wrapFoo3(c, source()); + + new C1(source()); + new C1().wrapFoo1(source()); + new C1().wrapFoo2(source()); + wrapFoo3(new C1(), source()); + + new C2(source()); + new C2().wrapFoo1(source()); + new C2().wrapFoo2(source()); + wrapFoo3(new C2(), source()); + } +} diff --git a/java/ql/test/library-tests/dataflow/callctx/test.expected b/java/ql/test/library-tests/dataflow/callctx/test.expected new file mode 100644 index 000000000000..d633eae89cee --- /dev/null +++ b/java/ql/test/library-tests/dataflow/callctx/test.expected @@ -0,0 +1,15 @@ +| A.java:40:16:40:23 | source(...) | A.java:36:12:36:13 | c2 | +| A.java:49:16:49:23 | source(...) | A.java:23:12:23:13 | c1 | +| A.java:49:16:49:23 | source(...) | A.java:36:12:36:13 | c2 | +| A.java:50:16:50:23 | source(...) | A.java:23:12:23:13 | c1 | +| A.java:50:16:50:23 | source(...) | A.java:36:12:36:13 | c2 | +| A.java:51:17:51:24 | source(...) | A.java:23:12:23:13 | c1 | +| A.java:51:17:51:24 | source(...) | A.java:36:12:36:13 | c2 | +| A.java:53:12:53:19 | source(...) | A.java:23:12:23:13 | c1 | +| A.java:54:23:54:30 | source(...) | A.java:23:12:23:13 | c1 | +| A.java:55:23:55:30 | source(...) | A.java:23:12:23:13 | c1 | +| A.java:56:24:56:31 | source(...) | A.java:23:12:23:13 | c1 | +| A.java:58:12:58:19 | source(...) | A.java:36:12:36:13 | c2 | +| A.java:59:23:59:30 | source(...) | A.java:36:12:36:13 | c2 | +| A.java:60:23:60:30 | source(...) | A.java:36:12:36:13 | c2 | +| A.java:61:24:61:31 | source(...) | A.java:36:12:36:13 | c2 | diff --git a/java/ql/test/library-tests/dataflow/callctx/test.ql b/java/ql/test/library-tests/dataflow/callctx/test.ql new file mode 100644 index 000000000000..36ba2f3d17b1 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/callctx/test.ql @@ -0,0 +1,15 @@ +import java +import semmle.code.java.dataflow.DataFlow +import DataFlow + +class Conf extends Configuration { + Conf() { this = "qqconf" } + + override predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("source") } + + override predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") } +} + +from Node src, Node sink, Conf c +where c.hasFlow(src, sink) +select src, sink