• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.errorprone.bugpatterns.android;
18 
19 import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
20 import static com.google.errorprone.matchers.Matchers.allOf;
21 import static com.google.errorprone.matchers.Matchers.contains;
22 import static com.google.errorprone.matchers.Matchers.enclosingClass;
23 import static com.google.errorprone.matchers.Matchers.hasAnnotation;
24 import static com.google.errorprone.matchers.Matchers.instanceMethod;
25 import static com.google.errorprone.matchers.Matchers.isSameType;
26 import static com.google.errorprone.matchers.Matchers.methodInvocation;
27 import static com.google.errorprone.matchers.Matchers.not;
28 import static com.google.errorprone.matchers.Matchers.throwStatement;
29 import static com.google.errorprone.matchers.Matchers.variableType;
30 
31 import com.google.auto.service.AutoService;
32 import com.google.errorprone.BugPattern;
33 import com.google.errorprone.VisitorState;
34 import com.google.errorprone.bugpatterns.BugChecker;
35 import com.google.errorprone.bugpatterns.BugChecker.TryTreeMatcher;
36 import com.google.errorprone.matchers.Description;
37 import com.google.errorprone.matchers.Matcher;
38 import com.google.errorprone.predicates.TypePredicate;
39 import com.sun.source.tree.CatchTree;
40 import com.sun.source.tree.ExpressionTree;
41 import com.sun.source.tree.StatementTree;
42 import com.sun.source.tree.Tree;
43 import com.sun.source.tree.TryTree;
44 import com.sun.source.tree.VariableTree;
45 import com.sun.tools.javac.code.Type;
46 
47 import java.util.List;
48 
49 /**
50  * Apps making calls into the system server may end up persisting internal state
51  * or making security decisions based on the perceived success or failure of a
52  * call, or any default values returned. For this reason, we want to strongly
53  * throw when there was trouble with the transaction.
54  * <p>
55  * The rethrowFromSystemServer() method is the best-practice way of doing this
56  * correctly, so that we don't clutter logs with misleading stack traces, and
57  * this checker verifies that best-practice is used.
58  */
59 @AutoService(BugChecker.class)
60 @BugPattern(
61     name = "AndroidFrameworkRethrowFromSystem",
62     summary = "Verifies that system_server calls use rethrowFromSystemServer()",
63     severity = WARNING)
64 public final class RethrowFromSystemChecker extends BugChecker implements TryTreeMatcher {
65     private static final Matcher<Tree> INSIDE_MANAGER =
66             enclosingClass(hasAnnotation("android.annotation.SystemService"));
67 
68     // Purposefully exclude telephony Binder interfaces, since we know they
69     // always run under the separate AID_RADIO
70     private static final Matcher<ExpressionTree> SYSTEM_BINDER_CALL = methodInvocation(allOf(
71             instanceMethod().onDescendantOf("android.os.IInterface").withAnyName(),
72             not(instanceMethod().onClass(inPackage("com.android.internal.telephony"))),
73             not(instanceMethod().onClass(inPackage("com.android.internal.telecom")))));
74 
75     private static final Matcher<VariableTree> REMOTE_EXCEPTION = variableType(
76             isSameType("android.os.RemoteException"));
77     private static final Matcher<StatementTree> RETHROW_FROM_SYSTEM = throwStatement(
78             methodInvocation(instanceMethod().onExactClass("android.os.RemoteException")
79                     .named("rethrowFromSystemServer")));
80 
81     @Override
matchTry(TryTree tree, VisitorState state)82     public Description matchTry(TryTree tree, VisitorState state) {
83         if (INSIDE_MANAGER.matches(tree, state)
84                 && contains(ExpressionTree.class, SYSTEM_BINDER_CALL)
85                         .matches(tree.getBlock(), state)) {
86             for (CatchTree catchTree : tree.getCatches()) {
87                 if (REMOTE_EXCEPTION.matches(catchTree.getParameter(), state)) {
88                     final List<? extends StatementTree> statements = catchTree.getBlock()
89                             .getStatements();
90                     if (statements.size() != 1
91                             || !RETHROW_FROM_SYSTEM.matches(statements.get(0), state)) {
92                         return buildDescription(catchTree)
93                                 .setMessage("Must contain single "
94                                         + "'throw e.rethrowFromSystemServer()' statement")
95                                 .build();
96                     }
97                 }
98             }
99         }
100         return Description.NO_MATCH;
101     }
102 
inPackage(final String filter)103     private static TypePredicate inPackage(final String filter) {
104         return new TypePredicate() {
105             @Override
106             public boolean apply(Type type, VisitorState state) {
107                 return type.tsym.packge().fullname.toString().startsWith(filter);
108             }
109         };
110     }
111 }
112