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.LinkType.NONE; 20 import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; 21 import static com.google.errorprone.matchers.Matchers.allOf; 22 import static com.google.errorprone.matchers.Matchers.contains; 23 import static com.google.errorprone.matchers.Matchers.enclosingClass; 24 import static com.google.errorprone.matchers.Matchers.hasAnnotation; 25 import static com.google.errorprone.matchers.Matchers.instanceMethod; 26 import static com.google.errorprone.matchers.Matchers.isSameType; 27 import static com.google.errorprone.matchers.Matchers.methodInvocation; 28 import static com.google.errorprone.matchers.Matchers.not; 29 import static com.google.errorprone.matchers.Matchers.throwStatement; 30 import static com.google.errorprone.matchers.Matchers.variableType; 31 32 import com.google.auto.service.AutoService; 33 import com.google.errorprone.BugPattern; 34 import com.google.errorprone.VisitorState; 35 import com.google.errorprone.bugpatterns.BugChecker; 36 import com.google.errorprone.bugpatterns.BugChecker.TryTreeMatcher; 37 import com.google.errorprone.matchers.Description; 38 import com.google.errorprone.matchers.Matcher; 39 import com.google.errorprone.predicates.TypePredicate; 40 import com.sun.source.tree.CatchTree; 41 import com.sun.source.tree.ExpressionTree; 42 import com.sun.source.tree.StatementTree; 43 import com.sun.source.tree.Tree; 44 import com.sun.source.tree.TryTree; 45 import com.sun.source.tree.VariableTree; 46 import com.sun.tools.javac.code.Type; 47 48 import java.util.List; 49 50 /** 51 * Apps making calls into the system server may end up persisting internal state 52 * or making security decisions based on the perceived success or failure of a 53 * call, or any default values returned. For this reason, we want to strongly 54 * throw when there was trouble with the transaction. 55 * <p> 56 * The rethrowFromSystemServer() method is the best-practice way of doing this 57 * correctly, so that we don't clutter logs with misleading stack traces, and 58 * this checker verifies that best-practice is used. 59 */ 60 @AutoService(BugChecker.class) 61 @BugPattern( 62 name = "AndroidFrameworkRethrowFromSystem", 63 summary = "Verifies that system_server calls use rethrowFromSystemServer()", 64 linkType = NONE, 65 severity = WARNING) 66 public final class RethrowFromSystemChecker extends BugChecker implements TryTreeMatcher { 67 private static final Matcher<Tree> INSIDE_MANAGER = 68 enclosingClass(hasAnnotation("android.annotation.SystemService")); 69 70 // Purposefully exclude telephony Binder interfaces, since we know they 71 // always run under the separate AID_RADIO 72 private static final Matcher<ExpressionTree> SYSTEM_BINDER_CALL = methodInvocation(allOf( 73 instanceMethod().onDescendantOf("android.os.IInterface").withAnyName(), 74 not(instanceMethod().onClass(inPackage("com.android.internal.telephony"))), 75 not(instanceMethod().onClass(inPackage("com.android.internal.telecom"))))); 76 77 private static final Matcher<VariableTree> REMOTE_EXCEPTION = variableType( 78 isSameType("android.os.RemoteException")); 79 private static final Matcher<StatementTree> RETHROW_FROM_SYSTEM = throwStatement( 80 methodInvocation(instanceMethod().onExactClass("android.os.RemoteException") 81 .named("rethrowFromSystemServer"))); 82 83 @Override matchTry(TryTree tree, VisitorState state)84 public Description matchTry(TryTree tree, VisitorState state) { 85 if (INSIDE_MANAGER.matches(tree, state) 86 && contains(ExpressionTree.class, SYSTEM_BINDER_CALL) 87 .matches(tree.getBlock(), state)) { 88 for (CatchTree catchTree : tree.getCatches()) { 89 if (REMOTE_EXCEPTION.matches(catchTree.getParameter(), state)) { 90 final List<? extends StatementTree> statements = catchTree.getBlock() 91 .getStatements(); 92 if (statements.size() != 1 93 || !RETHROW_FROM_SYSTEM.matches(statements.get(0), state)) { 94 return buildDescription(catchTree) 95 .setMessage("Must contain single " 96 + "'throw e.rethrowFromSystemServer()' statement") 97 .build(); 98 } 99 } 100 } 101 } 102 return Description.NO_MATCH; 103 } 104 inPackage(final String filter)105 private static TypePredicate inPackage(final String filter) { 106 return new TypePredicate() { 107 @Override 108 public boolean apply(Type type, VisitorState state) { 109 return type.tsym.packge().fullname.toString().startsWith(filter); 110 } 111 }; 112 } 113 } 114