Skip to content

Commit 5ebce6e

Browse files
committed
Improve AsyncTask data flow support
Model the life-cycle described here: https://developer.android.com/reference/android/os/AsyncTask\#the-4-steps
1 parent ba2cee0 commit 5ebce6e

File tree

3 files changed

+101
-21
lines changed

3 files changed

+101
-21
lines changed

java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,103 @@ import java
44
private import semmle.code.java.dataflow.DataFlow
55
private import semmle.code.java.dataflow.FlowSteps
66

7+
/*
8+
* The following flow steps aim to model the life-cycle of `AsyncTask`s described here:
9+
* https://developer.android.com/reference/android/os/AsyncTask#the-4-steps
10+
*/
11+
712
/**
8-
* Models the value-preserving step from `asyncTask.execute(params)` to `AsyncTask::doInBackground(params)`.
13+
* A taint step from the vararg arguments of `AsyncTask::execute` and `AsyncTask::executeOnExecutor`
14+
* to the parameter of `AsyncTask::doInBackground`.
915
*/
10-
private class AsyncTaskAdditionalValueStep extends AdditionalValueStep {
16+
private class AsyncTaskExecuteAdditionalValueStep extends AdditionalTaintStep {
1117
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
1218
exists(ExecuteAsyncTaskMethodAccess ma, AsyncTaskRunInBackgroundMethod m |
13-
DataFlow::getInstanceArgument(ma).getType() = m.getDeclaringType() and
19+
DataFlow::getInstanceArgument(ma).getType() = m.getDeclaringType()
20+
|
1421
node1.asExpr() = ma.getParamsArgument() and
1522
node2.asParameter() = m.getParameter(0)
1623
)
1724
}
1825
}
1926

27+
/**
28+
* A value-preserving step from the return value of `AsyncTask::doInBackground`
29+
* to the parameter of `AsyncTask::onPostExecute`.
30+
*/
31+
private class AsyncTaskOnPostExecuteAdditionalValueStep extends AdditionalValueStep {
32+
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
33+
exists(
34+
AsyncTaskRunInBackgroundMethod runInBackground, AsyncTaskOnPostExecuteMethod onPostExecute
35+
|
36+
onPostExecute.getDeclaringType() = runInBackground.getDeclaringType()
37+
|
38+
node1.asExpr() = any(ReturnStmt r | r.getEnclosingCallable() = runInBackground).getResult() and
39+
node2.asParameter() = onPostExecute.getParameter(0)
40+
)
41+
}
42+
}
43+
44+
/**
45+
* A value-preserving step from field initializers in `AsyncTask`'s constructor or initializer method
46+
* to the instance parameter of `AsyncTask::runInBackground` and `AsyncTask::onPostExecute`.
47+
*/
48+
private class AsyncTaskFieldInitQualifierToInstanceParameterStep extends AdditionalValueStep {
49+
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
50+
exists(AsyncTaskInit init, Callable receiver |
51+
n1.(DataFlow::PostUpdateNode).getPreUpdateNode() =
52+
DataFlow::getFieldQualifier(any(FieldWrite f | f.getEnclosingCallable() = init)) and
53+
n2.(DataFlow::InstanceParameterNode).getCallable() = receiver and
54+
receiver.getDeclaringType() = init.getDeclaringType() and
55+
(
56+
receiver instanceof AsyncTaskRunInBackgroundMethod or
57+
receiver instanceof AsyncTaskOnPostExecuteMethod
58+
)
59+
)
60+
}
61+
}
62+
2063
/**
2164
* The Android class `android.os.AsyncTask`.
2265
*/
2366
private class AsyncTask extends RefType {
2467
AsyncTask() { this.hasQualifiedName("android.os", "AsyncTask") }
2568
}
2669

70+
/** The constructor or initializer method of the `android.os.AsyncTask` class. */
71+
private class AsyncTaskInit extends Callable {
72+
AsyncTaskInit() {
73+
this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and
74+
(this instanceof Constructor or this instanceof InitializerMethod)
75+
}
76+
}
77+
2778
/** A call to the `execute` or `executeOnExecutor` methods of the `android.os.AsyncTask` class. */
2879
private class ExecuteAsyncTaskMethodAccess extends MethodAccess {
2980
Argument paramsArgument;
3081

3182
ExecuteAsyncTaskMethodAccess() {
32-
exists(Method m |
33-
this.getMethod() = m and
34-
m.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask
35-
|
36-
m.getName() = "execute" and not m.isStatic() and paramsArgument = this.getArgument(0)
37-
or
38-
m.getName() = "executeOnExecutor" and paramsArgument = this.getArgument(1)
39-
)
83+
this.getMethod().hasName(["execute", "executeOnExecutor"]) and
84+
this.getMethod().getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof
85+
AsyncTask
4086
}
4187

4288
/** Returns the `params` argument of this call. */
43-
Argument getParamsArgument() { result = paramsArgument }
89+
Argument getParamsArgument() { result = this.getAnArgument() and result.isVararg() }
4490
}
4591

4692
/** The `doInBackground` method of the `android.os.AsyncTask` class. */
4793
private class AsyncTaskRunInBackgroundMethod extends Method {
4894
AsyncTaskRunInBackgroundMethod() {
4995
this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and
50-
this.getName() = "doInBackground"
96+
this.hasName("doInBackground")
97+
}
98+
}
99+
100+
/** The `onPostExecute` method of the `android.os.AsyncTask` class. */
101+
private class AsyncTaskOnPostExecuteMethod extends Method {
102+
AsyncTaskOnPostExecuteMethod() {
103+
this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and
104+
this.hasName("onPostExecute")
51105
}
52106
}

java/ql/test/library-tests/frameworks/android/asynctask/Test.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,54 @@ private static void sink(Object o) {}
1010

1111
public void test() {
1212
TestAsyncTask t = new TestAsyncTask();
13-
t.execute(source("execute"));
14-
t.executeOnExecutor(null, source("executeOnExecutor"));
13+
t.execute(source("execute"), null);
14+
t.executeOnExecutor(null, source("executeOnExecutor"), null);
1515
SafeAsyncTask t2 = new SafeAsyncTask();
1616
t2.execute("safe");
17+
TestConstructorTask t3 = new TestConstructorTask(source("constructor"), "safe");
18+
t3.execute(source("params"));
1719
}
1820

1921
private class TestAsyncTask extends AsyncTask<Object, Object, Object> {
2022
@Override
2123
protected Object doInBackground(Object... params) {
22-
sink(params); // $ hasValueFlow=execute hasValueFlow=executeOnExecutor
24+
sink(params[0]); // $ hasTaintFlow=execute hasTaintFlow=executeOnExecutor
25+
sink(params[1]); // $ SPURIOUS: hasTaintFlow=execute hasTaintFlow=executeOnExecutor
2326
return null;
2427
}
2528
}
2629

2730
private class SafeAsyncTask extends AsyncTask<Object, Object, Object> {
2831
@Override
2932
protected Object doInBackground(Object... params) {
30-
sink(params); // Safe
33+
sink(params[0]); // Safe
3134
return null;
3235
}
3336
}
37+
38+
class TestConstructorTask extends AsyncTask<Object, Object, Object> {
39+
private Object field;
40+
private Object safeField;
41+
42+
public TestConstructorTask(Object field, Object safeField) {
43+
this.field = field;
44+
this.safeField = safeField;
45+
}
46+
47+
@Override
48+
protected Object doInBackground(Object... params) {
49+
sink(params[0]); // $ hasTaintFlow=params
50+
sink(field); // $ hasValueFlow=constructor
51+
sink(safeField); // Safe
52+
return params[0];
53+
}
54+
55+
@Override
56+
protected void onPostExecute(Object param) {
57+
sink(param); // $ hasTaintFlow=params
58+
sink(field); // $ hasValueFlow=constructor
59+
sink(safeField); // Safe
60+
}
61+
62+
}
3463
}
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
import java
22
import TestUtilities.InlineFlowTest
3-
4-
class AsyncTaskTest extends InlineFlowTest {
5-
override TaintTracking::Configuration getTaintFlowConfig() { none() }
6-
}
3+
import semmle.code.java.dataflow.FlowSteps

0 commit comments

Comments
 (0)