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