Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0dee2ba2 authored by Azhara Assanova's avatar Azhara Assanova
Browse files

[CallingIdentityTokenDetector] Support methods with implicit receiver

Modified CallingIdentityTokenDetector to visit UCallExpressions instead
of UQualifiedReferenceExpression.

UQualifiedReferenceExpression has a structure of receiver.selector which
applies to method calls that use the class reference, however,
standalone methods will be ignored, i.e. method(). It is required to
consider standalone method calls because the class may be a child of
Binder and it can call its own method without any class reference.

Bug: 194085454
Test: atest AndroidFrameworkLintCheckerTest --host
Change-Id: I2b40afb747664cf4a36e441eaba1ecd64960e3b1
parent d92d1e4c
Loading
Loading
Loading
Loading
+16 −13
Original line number Original line Diff line number Diff line
@@ -27,7 +27,6 @@ import com.android.tools.lint.detector.api.Location
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.SearchScope
import org.jetbrains.uast.UBlockExpression
import org.jetbrains.uast.UBlockExpression
@@ -35,10 +34,11 @@ import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UDeclarationsExpression
import org.jetbrains.uast.UDeclarationsExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UElement
import org.jetbrains.uast.ULocalVariable
import org.jetbrains.uast.ULocalVariable
import org.jetbrains.uast.UQualifiedReferenceExpression
import org.jetbrains.uast.USimpleNameReferenceExpression
import org.jetbrains.uast.USimpleNameReferenceExpression
import org.jetbrains.uast.UTryExpression
import org.jetbrains.uast.UTryExpression
import org.jetbrains.uast.getParentOfType
import org.jetbrains.uast.getParentOfType
import org.jetbrains.uast.getQualifiedParentOrThis
import org.jetbrains.uast.getUCallExpression


/**
/**
 * Lint Detector that finds issues with improper usages of the token returned by
 * Lint Detector that finds issues with improper usages of the token returned by
@@ -50,7 +50,7 @@ class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
    private val tokensMap = mutableMapOf<String, Token>()
    private val tokensMap = mutableMapOf<String, Token>()


    override fun getApplicableUastTypes(): List<Class<out UElement?>> =
    override fun getApplicableUastTypes(): List<Class<out UElement?>> =
            listOf(ULocalVariable::class.java, UQualifiedReferenceExpression::class.java)
            listOf(ULocalVariable::class.java, UCallExpression::class.java)


    override fun createUastHandler(context: JavaContext): UElementHandler =
    override fun createUastHandler(context: JavaContext): UElementHandler =
            TokenUastHandler(context)
            TokenUastHandler(context)
@@ -87,7 +87,7 @@ class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
         * - Stores token variable name, scope in the file, location and finally block in tokensMap
         * - Stores token variable name, scope in the file, location and finally block in tokensMap
         */
         */
        override fun visitLocalVariable(node: ULocalVariable) {
        override fun visitLocalVariable(node: ULocalVariable) {
            val rhsExpression = node.uastInitializer as? UQualifiedReferenceExpression ?: return
            val rhsExpression = node.uastInitializer?.getUCallExpression() ?: return
            if (!isMethodCall(rhsExpression, Method.BINDER_CLEAR_CALLING_IDENTITY)) return
            if (!isMethodCall(rhsExpression, Method.BINDER_CLEAR_CALLING_IDENTITY)) return
            val location = context.getLocation(node as UElement)
            val location = context.getLocation(node as UElement)
            val variableName = node.getName()
            val variableName = node.getName()
@@ -142,13 +142,13 @@ class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
        }
        }


        /**
        /**
         * For every class.method():
         * For every method():
         * - Checks use of caller-aware methods issue
         * - Checks use of caller-aware methods issue
         * For every call of Binder.restoreCallingIdentity(token):
         * For every call of Binder.restoreCallingIdentity(token):
         * - Checks for restoreCallingIdentity() not in the finally block issue
         * - Checks for restoreCallingIdentity() not in the finally block issue
         * - Removes token from tokensMap if token is within the scope of the method
         * - Removes token from tokensMap if token is within the scope of the method
         */
         */
        override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) {
        override fun visitCallExpression(node: UCallExpression) {
            val token = findFirstTokenInScope(node)
            val token = findFirstTokenInScope(node)
            if (isCallerAwareMethod(node) && token != null) {
            if (isCallerAwareMethod(node) && token != null) {
                context.report(
                context.report(
@@ -162,8 +162,7 @@ class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
                return
                return
            }
            }
            if (!isMethodCall(node, Method.BINDER_RESTORE_CALLING_IDENTITY)) return
            if (!isMethodCall(node, Method.BINDER_RESTORE_CALLING_IDENTITY)) return
            val selector = node.selector as UCallExpression
            val arg = node.valueArguments[0] as? USimpleNameReferenceExpression ?: return
            val arg = selector.valueArguments[0] as? USimpleNameReferenceExpression ?: return
            val variableName = arg.identifier
            val variableName = arg.identifier
            val originalScope = tokensMap[variableName]?.scope ?: return
            val originalScope = tokensMap[variableName]?.scope ?: return
            val psi = arg.sourcePsi ?: return
            val psi = arg.sourcePsi ?: return
@@ -171,11 +170,15 @@ class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
            // token declaration. If not within the scope, no action is needed because the token is
            // token declaration. If not within the scope, no action is needed because the token is
            // irrelevant i.e. not in the same scope or was not declared with clearCallingIdentity()
            // irrelevant i.e. not in the same scope or was not declared with clearCallingIdentity()
            if (!PsiSearchScopeUtil.isInScope(originalScope, psi)) return
            if (!PsiSearchScopeUtil.isInScope(originalScope, psi)) return
            // We do not report "restore identity call not in finally" issue when there is no
            // - We do not report "restore identity call not in finally" issue when there is no
            // finally block because that case is already handled by "clear identity call not
            // finally block because that case is already handled by "clear identity call not
            // followed by try-finally" issue
            // followed by try-finally" issue
            // - UCallExpression can be a child of UQualifiedReferenceExpression, i.e.
            // receiver.selector, so to get the call's immediate parent we need to get the topmost
            // parent qualified reference expression and access its parent
            if (tokensMap[variableName]?.finallyBlock != null &&
            if (tokensMap[variableName]?.finallyBlock != null &&
                    node.uastParent != tokensMap[variableName]?.finallyBlock) {
                    node.getQualifiedParentOrThis().uastParent !=
                        tokensMap[variableName]?.finallyBlock) {
                context.report(
                context.report(
                        ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
                        ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
                        context.getLocation(node),
                        context.getLocation(node),
@@ -185,14 +188,14 @@ class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
            tokensMap.remove(variableName)
            tokensMap.remove(variableName)
        }
        }


        private fun isCallerAwareMethod(expression: UQualifiedReferenceExpression): Boolean =
        private fun isCallerAwareMethod(expression: UCallExpression): Boolean =
                callerAwareMethods.any { method -> isMethodCall(expression, method) }
                callerAwareMethods.any { method -> isMethodCall(expression, method) }


        private fun isMethodCall(
        private fun isMethodCall(
            expression: UQualifiedReferenceExpression,
            expression: UCallExpression,
            method: Method
            method: Method
        ): Boolean {
        ): Boolean {
            val psiMethod = expression.resolve() as? PsiMethod ?: return false
            val psiMethod = expression.resolve() ?: return false
            return psiMethod.getName() == method.methodName &&
            return psiMethod.getName() == method.methodName &&
                    context.evaluator.methodMatches(
                    context.evaluator.methodMatches(
                            psiMethod,
                            psiMethod,
+66 −43

File changed.

Preview size limit exceeded, changes collapsed.