From 662e17ff85cc75305fc849c0c02df9e36ea5cca3 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 15 Mar 2021 15:09:03 +0100 Subject: [PATCH 1/2] Java: Bugfix dispatch to lambda in call context. --- .../dataflow/internal/DataFlowDispatch.qll | 14 +++-- .../dataflow/lambda/Executor.java | 63 +++++++++++++++++++ .../dataflow/lambda/Processor.java | 9 +++ .../dataflow/lambda/StringProcessor.java | 30 +++++++++ .../dataflow/lambda/flow.expected | 8 +++ .../library-tests/dataflow/lambda/flow.ql | 25 ++++++++ 6 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 java/ql/test/library-tests/dataflow/lambda/Executor.java create mode 100644 java/ql/test/library-tests/dataflow/lambda/Processor.java create mode 100644 java/ql/test/library-tests/dataflow/lambda/StringProcessor.java create mode 100644 java/ql/test/library-tests/dataflow/lambda/flow.expected create mode 100644 java/ql/test/library-tests/dataflow/lambda/flow.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 ba99bffbf5f..223af728fe4 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowDispatch.qll @@ -41,6 +41,12 @@ private module DispatchImpl { ) } + private RefType getPreciseType(Expr e) { + result = e.(FunctionalExpr).getConstructedType() + or + not e instanceof FunctionalExpr and result = e.getType() + } + /** * Holds if the `i`th argument of `ctx` has type `t` and `ctx` is a * relevant call context. @@ -55,7 +61,7 @@ private module DispatchImpl { ctx.getArgument(i) = arg | src = variableTrack(arg) and - srctype = src.getType() and + srctype = getPreciseType(src) and if src instanceof ClassInstanceExpr then exact = true else exact = false ) or @@ -67,11 +73,9 @@ private module DispatchImpl { if ctx instanceof ClassInstanceExpr then exact = true else exact = false ) | - exists(TypeVariable v | v = srctype | - t = v.getUpperBoundType+() and not t instanceof TypeVariable - ) + t = srctype.(BoundedType).getAnUltimateUpperBoundType() or - t = srctype and not srctype instanceof TypeVariable + t = srctype and not srctype instanceof BoundedType ) } diff --git a/java/ql/test/library-tests/dataflow/lambda/Executor.java b/java/ql/test/library-tests/dataflow/lambda/Executor.java new file mode 100644 index 00000000000..93f5e04221d --- /dev/null +++ b/java/ql/test/library-tests/dataflow/lambda/Executor.java @@ -0,0 +1,63 @@ +import java.lang.Runtime; +import java.util.function.Function; + +public class Executor { + + private static final Processor processor = new Processor(); + + private static String source() { return "taint"; } + + public static void main(String[] args) { + exec1(source()); + exec2(source()); + exec3(source()); + exec4(source()); + exec5(source()); + } + + private static void exec1(String command){ + command = process(s->s.toUpperCase(),command); + exec(command); + } + + private static void exec2(String command){ + command = process(s->"Taint stops here.",command); + exec(command); + } + + private static void exec3(String command){ + command = processor.process(s->s.toUpperCase(),command); + exec(command); + } + + private static void exec4(String command){ + command = processor.process(s->"Taint stops here.",command); + exec_b(command); + } + + private static void exec5(String command){ + command = processor.process(s->s.toUpperCase(),command); + exec_b(command); + } + + public static String process(Function fun, String command){ + return processor.process(fun, command); + } + + private static void exec(String command){ + command = process(s->s.trim(),command); + try { + Runtime.getRuntime().exec(command); + } + catch(Exception e) {} + } + + private static void exec_b(String command){ + command = processor.process(s->s.trim(),command); + try { + Runtime.getRuntime().exec(command); + } + catch(Exception e) {} + } +} + diff --git a/java/ql/test/library-tests/dataflow/lambda/Processor.java b/java/ql/test/library-tests/dataflow/lambda/Processor.java new file mode 100644 index 00000000000..c632519041c --- /dev/null +++ b/java/ql/test/library-tests/dataflow/lambda/Processor.java @@ -0,0 +1,9 @@ +import java.util.function.Function; + +public class Processor { + + public R process(Function function, T arg) { + return function.apply(arg); + } +} + diff --git a/java/ql/test/library-tests/dataflow/lambda/StringProcessor.java b/java/ql/test/library-tests/dataflow/lambda/StringProcessor.java new file mode 100644 index 00000000000..abe92ce5ca7 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/lambda/StringProcessor.java @@ -0,0 +1,30 @@ +import java.util.function.Function; + +public class StringProcessor { + + private static final Processor processor = new Processor(); + + public static void main(String[] args) { + String command = args[0]; + lambdaExec(command); + } + + public static void lambdaExec(String command){ + processor.process(s->exec(s), command); + } + + public static String lambdaUnrelated(String command){ + return processor.process(s->s+"not related to anything", command); + } + + public static String exec(String command){ + try { + command = processor.process(s->s.trim(), command); + Runtime.getRuntime().exec(command); + return "Executed: "+command; + } catch(Exception e) { + return null; + } + } +} + diff --git a/java/ql/test/library-tests/dataflow/lambda/flow.expected b/java/ql/test/library-tests/dataflow/lambda/flow.expected new file mode 100644 index 00000000000..3b467de204b --- /dev/null +++ b/java/ql/test/library-tests/dataflow/lambda/flow.expected @@ -0,0 +1,8 @@ +| Executor.java:11:15:11:22 | source(...) | Executor.java:50:39:50:45 | command | +| Executor.java:11:15:11:22 | source(...) | StringProcessor.java:23:39:23:45 | command | +| Executor.java:12:15:12:22 | source(...) | Executor.java:50:39:50:45 | command | +| Executor.java:12:15:12:22 | source(...) | StringProcessor.java:23:39:23:45 | command | +| Executor.java:13:15:13:22 | source(...) | Executor.java:50:39:50:45 | command | +| Executor.java:13:15:13:22 | source(...) | StringProcessor.java:23:39:23:45 | command | +| Executor.java:15:15:15:22 | source(...) | Executor.java:58:39:58:45 | command | +| StringProcessor.java:8:26:8:29 | args | StringProcessor.java:23:39:23:45 | command | diff --git a/java/ql/test/library-tests/dataflow/lambda/flow.ql b/java/ql/test/library-tests/dataflow/lambda/flow.ql new file mode 100644 index 00000000000..61bc7a33307 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/lambda/flow.ql @@ -0,0 +1,25 @@ +import java +import semmle.code.java.dataflow.TaintTracking + +class Conf extends TaintTracking::Configuration { + Conf() { this = "qltest lambda" } + + override predicate isSource(DataFlow::Node src) { + src.asExpr().(VarAccess).getVariable().hasName("args") + or + src.asExpr().(MethodAccess).getMethod().hasName("source") + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr().(Argument).getCall() = + any(MethodAccess ma | + ma.getMethod().hasName("exec") and + ma.getQualifier().(MethodAccess).getMethod().hasName("getRuntime") + ) + } +} + +from DataFlow::Node src, DataFlow::Node sink, Conf c +where c.hasFlow(src, sink) +select src, sink + From d1f30d91645e203f739605ef5daeee288b47c539 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 15 Mar 2021 15:28:04 +0100 Subject: [PATCH 2/2] Java: Autoformat. --- java/ql/test/library-tests/dataflow/lambda/flow.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/java/ql/test/library-tests/dataflow/lambda/flow.ql b/java/ql/test/library-tests/dataflow/lambda/flow.ql index 61bc7a33307..dc8d1c7d4b0 100644 --- a/java/ql/test/library-tests/dataflow/lambda/flow.ql +++ b/java/ql/test/library-tests/dataflow/lambda/flow.ql @@ -22,4 +22,3 @@ class Conf extends TaintTracking::Configuration { from DataFlow::Node src, DataFlow::Node sink, Conf c where c.hasFlow(src, sink) select src, sink -