summaryrefslogtreecommitdiff
path: root/errorprone/java
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2020-10-06 03:41:30 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2020-10-06 03:41:30 +0000
commita6a8d02b62bb71764eaeaeef6c889462450a000b (patch)
treed1d3d9ecfbe8292be2bb15c4517aec606667e83b /errorprone/java
parentaf14f91177d7785f8f5b3c132702fd0a66697f46 (diff)
parent749789d24047f546a18392f83a9438bc737de490 (diff)
Merge changes from topic "oct5b"
* changes: Detector to suggest more efficient collections. Detector for Binder.clearCallingIdentity() bugs.
Diffstat (limited to 'errorprone/java')
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java108
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java98
2 files changed, 206 insertions, 0 deletions
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
new file mode 100644
index 000000000000..68477edf97d1
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.contains;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.TryTree;
+import com.sun.source.tree.VariableTree;
+
+import java.util.List;
+
+import javax.lang.model.element.Modifier;
+
+/**
+ * Binder maintains thread-local identity information about any remote caller,
+ * which can be temporarily cleared while performing operations that need to be
+ * handled as the current process. However, it's important to restore the
+ * original remote calling identity after carefully scoping this work inside a
+ * try/finally block, to avoid obscure security vulnerabilities.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkBinderIdentity",
+ summary = "Verifies that Binder.clearCallingIdentity() is always restored",
+ severity = WARNING)
+public final class BinderIdentityChecker extends BugChecker implements MethodInvocationTreeMatcher {
+ private static final Matcher<ExpressionTree> CLEAR_CALL = methodInvocation(staticMethod()
+ .onClass("android.os.Binder").withSignature("clearCallingIdentity()"));
+ private static final Matcher<ExpressionTree> RESTORE_CALL = methodInvocation(staticMethod()
+ .onClass("android.os.Binder").withSignature("restoreCallingIdentity(long)"));
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (CLEAR_CALL.matches(tree, state)) {
+ // First, make sure we're recording the token for later
+ final VariableTree token = state.findEnclosing(VariableTree.class);
+ if (token == null || !token.getModifiers().getFlags().contains(Modifier.FINAL)) {
+ return buildDescription(tree)
+ .setMessage("Must store Binder.clearCallingIdentity() token as final"
+ + " variable to support safe restore")
+ .build();
+ }
+
+ // Next, verify the very next block is try-finally; any other calls
+ // between the clearing and try risk throwing an exception without
+ // doing a safe restore
+ final Tree next = nextStatement(token, state);
+ if (next == null || next.getKind() != Kind.TRY) {
+ return buildDescription(tree)
+ .setMessage("Must immediately define a try-finally block after"
+ + " Binder.clearCallingIdentity() to support safe restore")
+ .build();
+ }
+
+ // Finally, verify that we restore inside the finally block
+ final TryTree tryTree = (TryTree) next;
+ final BlockTree finallyTree = tryTree.getFinallyBlock();
+ if (finallyTree == null
+ || !contains(ExpressionTree.class, RESTORE_CALL).matches(finallyTree, state)) {
+ return buildDescription(tree)
+ .setMessage("Must call Binder.restoreCallingIdentity() in finally"
+ + " block to support safe restore")
+ .build();
+ }
+ }
+ return Description.NO_MATCH;
+ }
+
+ private static Tree nextStatement(Tree tree, VisitorState state) {
+ final BlockTree block = state.findEnclosing(BlockTree.class);
+ if (block == null) return null;
+ final List<? extends StatementTree> siblings = block.getStatements();
+ if (siblings == null) return null;
+ final int index = siblings.indexOf(tree);
+ if (index == -1 || index + 1 >= siblings.size()) return null;
+ return siblings.get(index + 1);
+ }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
new file mode 100644
index 000000000000..c4c1ab6482ee
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Type;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Android offers several efficient alternatives to some upstream
+ * {@link Collections} containers, such as {@code SparseIntArray} instead of
+ * {@code Map<Integer, Integer>}.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkEfficientCollections",
+ summary = "Verifies efficient collections best-practices",
+ severity = WARNING)
+public final class EfficientCollectionsChecker extends BugChecker implements NewClassTreeMatcher {
+ private static final Matcher<Tree> IS_LIST = isSubtypeOf("java.util.List");
+ private static final Matcher<Tree> IS_MAP = isSubtypeOf("java.util.Map");
+
+ private static final String INTEGER = "java.lang.Integer";
+ private static final String LONG = "java.lang.Long";
+ private static final String BOOLEAN = "java.lang.Boolean";
+
+ @Override
+ public Description matchNewClass(NewClassTree tree, VisitorState state) {
+ final List<Type> types = ASTHelpers.getType(tree).getTypeArguments();
+ if (IS_LIST.matches(tree, state) && types != null && types.size() == 1) {
+ final Type first = types.get(0);
+ if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with IntArray for efficiency")
+ .build();
+ } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with LongArray for efficiency")
+ .build();
+ }
+ } else if (IS_MAP.matches(tree, state) && types != null && types.size() == 2) {
+ final Type first = types.get(0);
+ final Type second = types.get(1);
+ if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) {
+ if (ASTHelpers.isSameType(second, state.getTypeFromString(INTEGER), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with SparseIntArray for efficiency")
+ .build();
+ } else if (ASTHelpers.isSameType(second, state.getTypeFromString(LONG), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with SparseLongArray for efficiency")
+ .build();
+ } else if (ASTHelpers.isSameType(second, state.getTypeFromString(BOOLEAN), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with SparseBooleanArray for efficiency")
+ .build();
+ } else {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with SparseArray for efficiency")
+ .build();
+ }
+ } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with LongSparseArray for efficiency")
+ .build();
+ }
+ }
+ return Description.NO_MATCH;
+ }
+}