Moved issues/IssueRegistry to mimic AS convention + README for AndroidFrameworkLintChecker

Moved CallingIdentityTokenDetector's issues from IssueRegistry to itself
since this is the pattern used for built-in Android Studio checks.

Wrote README.md on how to build a lint report and how to add new lint
checks.

Bug: 192925086
Test: m out/soong/.intermediates/frameworks/base/services/accessibility/services.accessibility/android_common/lint/lint-report.html
Change-Id: I6be3d49e5eacc34cf60db125a52ae3da9174be4a
This commit is contained in:
Azhara Assanova
2021-07-12 12:34:46 +00:00
parent f0de8d34a3
commit 8afcc7d5ba
5 changed files with 342 additions and 307 deletions

51
tools/lint/README.md Normal file
View File

@@ -0,0 +1,51 @@
# Android Framework Lint Checker
Custom lint checks written here are going to be executed for modules that opt in to those (e.g. any
`services.XXX` module) and results will be automatically reported on CLs on gerrit.
## How to add new lint checks
1. Write your detector with its issues and put it into
`checks/src/main/java/com/google/android/lint`.
2. Add your detector's issues into `AndroidFrameworkIssueRegistry`'s `issues` field.
3. Write unit tests for your detector in one file and put it into
`checks/test/java/com/google/android/lint`.
4. Done! Your lint checks should be applied in lint report builds for modules that include
`AndroidFrameworkLintChecker`.
## How to run lint against your module
1. Add the following `lint` attribute to the module definition, e.g. `services.autofill`:
```
java_library_static {
name: "services.autofill",
...
lint: {
extra_check_modules: ["AndroidFrameworkLintChecker"],
},
}
```
2. Run the following command to verify that the report is being correctly built:
```
m out/soong/.intermediates/frameworks/base/services/autofill/services.autofill/android_common/lint/lint-report.html
```
(Lint report can be found in the same path, i.e. `out/../lint-report.html`)
3. Now lint issues should appear on gerrit!
**Notes:**
- Lint report will not be produced if you just build the module, i.e. `m services.autofill` will not
build the lint report.
- If you want to build lint reports for more than 1 module and they depend on a common module, e.g.
`platform_service_defaults`, you can add the `lint` property to that common module instead of
adding it in every module.
## Documentation
- [go/android-security-lint-checks](http://go/android-security-lint-checks) - presentation about
this module
- [Android Lint Docs](http://googlesamples.github.io/android-custom-lint-rules/)
- [Android Lint source files](https://source.corp.google.com/studio-main/tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/)
- [PSI source files](https://github.com/JetBrains/intellij-community/tree/master/java/java-psi-api/src/com/intellij/psi)
- [UAST source files](https://upsource.jetbrains.com/idea-ce/structure/idea-ce-7b9b8cc138bbd90aec26433f82cd2c6838694003/uast/uast-common/src/org/jetbrains/uast)
- [IntelliJ plugin for viewing PSI tree of files](https://plugins.jetbrains.com/plugin/227-psiviewer)

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2021 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.android.lint
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
import com.google.auto.service.AutoService
@AutoService(IssueRegistry::class)
@Suppress("UnstableApiUsage")
class AndroidFrameworkIssueRegistry : IssueRegistry() {
override val issues = listOf(
CallingIdentityTokenDetector.ISSUE_UNUSED_TOKEN,
CallingIdentityTokenDetector.ISSUE_NON_FINAL_TOKEN,
CallingIdentityTokenDetector.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
)
override val api: Int
get() = CURRENT_API
override val minApi: Int
get() = 8
override val vendor: Vendor = Vendor(
vendorName = "Android",
feedbackUrl = "http://b/issues/new?component=315013",
contact = "brufino@google.com"
)
}

View File

@@ -16,24 +16,16 @@
package com.google.android.lint
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_NESTED_CLEAR_IDENTITY_CALLS
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_NON_FINAL_TOKEN
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_UNUSED_TOKEN
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageClearIdentityCallNotFollowedByTryFinally
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNestedClearIdentityCallsPrimary
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNestedClearIdentityCallsSecondary
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageNonFinalToken
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageRestoreIdentityCallNotInFinallyBlock
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageUnusedToken
import com.google.android.lint.CallingIdentityTokenIssueRegistry.Companion.getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity
import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Context
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Location
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.PsiSearchScopeUtil
@@ -54,21 +46,6 @@ import org.jetbrains.uast.getParentOfType
*/
@Suppress("UnstableApiUsage")
class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
private companion object {
const val CLASS_BINDER = "android.os.Binder"
const val CLASS_USER_HANDLE = "android.os.UserHandle"
@JvmField
val callerAwareMethods = listOf(
Method.BINDER_GET_CALLING_PID,
Method.BINDER_GET_CALLING_UID,
Method.BINDER_GET_CALLING_UID_OR_THROW,
Method.BINDER_GET_CALLING_USER_HANDLE,
Method.USER_HANDLE_GET_CALLING_APP_ID,
Method.USER_HANDLE_GET_CALLING_USER_ID
)
}
/** Map of <Token variable name, Token object> */
private val tokensMap = mutableMapOf<String, Token>()
@@ -357,4 +334,237 @@ class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
val location: Location,
val finallyBlock: UElement?
)
companion object {
const val CLASS_BINDER = "android.os.Binder"
const val CLASS_USER_HANDLE = "android.os.UserHandle"
private val callerAwareMethods = listOf(
Method.BINDER_GET_CALLING_PID,
Method.BINDER_GET_CALLING_UID,
Method.BINDER_GET_CALLING_UID_OR_THROW,
Method.BINDER_GET_CALLING_USER_HANDLE,
Method.USER_HANDLE_GET_CALLING_APP_ID,
Method.USER_HANDLE_GET_CALLING_USER_ID
)
/** Issue: unused token from Binder.clearCallingIdentity() */
@JvmField
val ISSUE_UNUSED_TOKEN: Issue = Issue.create(
id = "UnusedTokenOfOriginalCallingIdentity",
briefDescription = "Unused token of Binder.clearCallingIdentity()",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()`, but have not used the returned token to \
restore the identity.
Call `Binder.restoreCallingIdentity(token)` in the `finally` block, at the end \
of the method or when you need to restore the identity.
`token` is the result of `Binder.clearCallingIdentity()`
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
private fun getIncidentMessageUnusedToken(variableName: String) = "`$variableName` has " +
"not been used to restore the calling identity. Introduce a `try`-`finally` " +
"after the declaration and call `Binder.restoreCallingIdentity($variableName)` " +
"in `finally` or remove `$variableName`."
/** Issue: non-final token from Binder.clearCallingIdentity() */
@JvmField
val ISSUE_NON_FINAL_TOKEN: Issue = Issue.create(
id = "NonFinalTokenOfOriginalCallingIdentity",
briefDescription = "Non-final token of Binder.clearCallingIdentity()",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()`, but have not made the returned token `final`.
The token should be `final` in order to prevent it from being overwritten, \
which can cause problems when restoring the identity with \
`Binder.restoreCallingIdentity(token)`.
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
private fun getIncidentMessageNonFinalToken(variableName: String) = "`$variableName` is " +
"a non-final token from `Binder.clearCallingIdentity()`. Add `final` keyword to " +
"`$variableName`."
/** Issue: nested calls of Binder.clearCallingIdentity() */
@JvmField
val ISSUE_NESTED_CLEAR_IDENTITY_CALLS: Issue = Issue.create(
id = "NestedClearCallingIdentityCalls",
briefDescription = "Nested calls of Binder.clearCallingIdentity()",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()` twice without restoring identity with the \
result of the first call.
Make sure to restore the identity after each clear identity call.
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
private fun getIncidentMessageNestedClearIdentityCallsPrimary(
firstCallVariableName: String,
secondCallVariableName: String
): String = "The calling identity has already been cleared and returned into " +
"`$firstCallVariableName`. Move `$secondCallVariableName` declaration after " +
"restoring the calling identity with " +
"`Binder.restoreCallingIdentity($firstCallVariableName)`."
private fun getIncidentMessageNestedClearIdentityCallsSecondary(
firstCallVariableName: String
): String = "Location of the `$firstCallVariableName` declaration."
/** Issue: Binder.clearCallingIdentity() is not followed by `try-finally` statement */
@JvmField
val ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY: Issue = Issue.create(
id = "ClearIdentityCallNotFollowedByTryFinally",
briefDescription = "Binder.clearCallingIdentity() is not followed by try-finally " +
"statement",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()`, but the next statement is not a `try` \
statement.
Use the following pattern for running operations with your own identity:
```
final long token = Binder.clearCallingIdentity();
try {
// Code using your own identity
} finally {
Binder.restoreCallingIdentity(token);
}
```
Any calls/operations between `Binder.clearCallingIdentity()` and `try` \
statement risk throwing an exception without doing a safe and unconditional \
restore of the identity with `Binder.restoreCallingIdentity()` as an immediate \
child of the `finally` block. If you do not follow the pattern, you may run \
code with your identity that was originally intended to run with the calling \
application's identity.
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
private fun getIncidentMessageClearIdentityCallNotFollowedByTryFinally(
variableName: String
): String = "You cleared the calling identity and returned the result into " +
"`$variableName`, but the next statement is not a `try`-`finally` statement. " +
"Define a `try`-`finally` block after `$variableName` declaration to ensure a " +
"safe restore of the calling identity by calling " +
"`Binder.restoreCallingIdentity($variableName)` and making it an immediate child " +
"of the `finally` block."
/** Issue: Binder.restoreCallingIdentity() is not in finally block */
@JvmField
val ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK: Issue = Issue.create(
id = "RestoreIdentityCallNotInFinallyBlock",
briefDescription = "Binder.restoreCallingIdentity() is not in finally block",
explanation = """
You are restoring the original calling identity with \
`Binder.restoreCallingIdentity()`, but the call is not an immediate child of \
the `finally` block of the `try` statement.
Use the following pattern for running operations with your own identity:
```
final long token = Binder.clearCallingIdentity();
try {
// Code using your own identity
} finally {
Binder.restoreCallingIdentity(token);
}
```
If you do not surround the code using your identity with the `try` statement \
and call `Binder.restoreCallingIdentity()` as an immediate child of the \
`finally` block, you may run code with your identity that was originally \
intended to run with the calling application's identity.
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
private fun getIncidentMessageRestoreIdentityCallNotInFinallyBlock(
variableName: String
): String = "`Binder.restoreCallingIdentity($variableName)` is not an immediate child of " +
"the `finally` block of the try statement after `$variableName` declaration. " +
"Surround the call with `finally` block and call it unconditionally."
/** Issue: Use of caller-aware methods after Binder.clearCallingIdentity() */
@JvmField
val ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY: Issue = Issue.create(
id = "UseOfCallerAwareMethodsWithClearedIdentity",
briefDescription = "Use of caller-aware methods after " +
"Binder.clearCallingIdentity()",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()`, but used one of the methods below before \
restoring the identity. These methods will use your own identity instead of \
the caller's identity, so if this is expected replace them with methods that \
explicitly query your own identity such as `Process.myUid()`, \
`Process.myPid()` and `UserHandle.myUserId()`, otherwise move those methods \
out of the `Binder.clearCallingIdentity()` / `Binder.restoreCallingIdentity()` \
section.
```
Binder.getCallingPid()
Binder.getCallingUid()
Binder.getCallingUidOrThrow()
Binder.getCallingUserHandle()
UserHandle.getCallingAppId()
UserHandle.getCallingUserId()
```
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
private fun getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
variableName: String,
methodName: String
): String = "You cleared the original identity with `Binder.clearCallingIdentity()` " +
"and returned into `$variableName`, so `$methodName` will be using your own " +
"identity instead of the caller's. Either explicitly query your own identity or " +
"move it after restoring the identity with " +
"`Binder.restoreCallingIdentity($variableName)`."
}
}

View File

@@ -1,272 +0,0 @@
/*
* Copyright (C) 2021 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.android.lint
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.google.auto.service.AutoService
@AutoService(IssueRegistry::class)
@Suppress("UnstableApiUsage")
class CallingIdentityTokenIssueRegistry : IssueRegistry() {
override val issues = listOf(
ISSUE_UNUSED_TOKEN,
ISSUE_NON_FINAL_TOKEN,
ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
)
override val api: Int
get() = CURRENT_API
override val minApi: Int
get() = 8
override val vendor: Vendor = Vendor(
vendorName = "Android",
feedbackUrl = "http://b/issues/new?component=315013",
contact = "brufino@google.com"
)
companion object {
/** Issue: unused token from Binder.clearCallingIdentity() */
@JvmField
val ISSUE_UNUSED_TOKEN: Issue = Issue.create(
id = "UnusedTokenOfOriginalCallingIdentity",
briefDescription = "Unused token of Binder.clearCallingIdentity()",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()`, but have not used the returned token to \
restore the identity.
Call `Binder.restoreCallingIdentity(token)` in the `finally` block, at the end \
of the method or when you need to restore the identity.
`token` is the result of `Binder.clearCallingIdentity()`
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
fun getIncidentMessageUnusedToken(variableName: String) = "`$variableName` has not been " +
"used to restore the calling identity. Introduce a `try`-`finally` after the " +
"declaration and call `Binder.restoreCallingIdentity($variableName)` in " +
"`finally` or remove `$variableName`."
/** Issue: non-final token from Binder.clearCallingIdentity() */
@JvmField
val ISSUE_NON_FINAL_TOKEN: Issue = Issue.create(
id = "NonFinalTokenOfOriginalCallingIdentity",
briefDescription = "Non-final token of Binder.clearCallingIdentity()",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()`, but have not made the returned token `final`.
The token should be `final` in order to prevent it from being overwritten, \
which can cause problems when restoring the identity with \
`Binder.restoreCallingIdentity(token)`.
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
fun getIncidentMessageNonFinalToken(variableName: String) = "`$variableName` is a " +
"non-final token from `Binder.clearCallingIdentity()`. Add `final` keyword to " +
"`$variableName`."
/** Issue: nested calls of Binder.clearCallingIdentity() */
@JvmField
val ISSUE_NESTED_CLEAR_IDENTITY_CALLS: Issue = Issue.create(
id = "NestedClearCallingIdentityCalls",
briefDescription = "Nested calls of Binder.clearCallingIdentity()",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()` twice without restoring identity with the \
result of the first call.
Make sure to restore the identity after each clear identity call.
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
fun getIncidentMessageNestedClearIdentityCallsPrimary(
firstCallVariableName: String,
secondCallVariableName: String
): String = "The calling identity has already been cleared and returned into " +
"`$firstCallVariableName`. Move `$secondCallVariableName` declaration after " +
"restoring the calling identity with " +
"`Binder.restoreCallingIdentity($firstCallVariableName)`."
fun getIncidentMessageNestedClearIdentityCallsSecondary(
firstCallVariableName: String
): String = "Location of the `$firstCallVariableName` declaration."
/** Issue: Binder.clearCallingIdentity() is not followed by `try-finally` statement */
@JvmField
val ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY: Issue = Issue.create(
id = "ClearIdentityCallNotFollowedByTryFinally",
briefDescription = "Binder.clearCallingIdentity() is not followed by try-finally " +
"statement",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()`, but the next statement is not a `try` \
statement.
Use the following pattern for running operations with your own identity:
```
final long token = Binder.clearCallingIdentity();
try {
// Code using your own identity
} finally {
Binder.restoreCallingIdentity(token);
}
```
Any calls/operations between `Binder.clearCallingIdentity()` and `try` \
statement risk throwing an exception without doing a safe and unconditional \
restore of the identity with `Binder.restoreCallingIdentity()` as an immediate \
child of the `finally` block. If you do not follow the pattern, you may run \
code with your identity that was originally intended to run with the calling \
application's identity.
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
fun getIncidentMessageClearIdentityCallNotFollowedByTryFinally(
variableName: String
): String = "You cleared the calling identity and returned the result into " +
"`$variableName`, but the next statement is not a `try`-`finally` statement. " +
"Define a `try`-`finally` block after `$variableName` declaration to ensure a " +
"safe restore of the calling identity by calling " +
"`Binder.restoreCallingIdentity($variableName)` and making it an immediate child " +
"of the `finally` block."
/** Issue: Binder.restoreCallingIdentity() is not in finally block */
@JvmField
val ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK: Issue = Issue.create(
id = "RestoreIdentityCallNotInFinallyBlock",
briefDescription = "Binder.restoreCallingIdentity() is not in finally block",
explanation = """
You are restoring the original calling identity with \
`Binder.restoreCallingIdentity()`, but the call is not an immediate child of \
the `finally` block of the `try` statement.
Use the following pattern for running operations with your own identity:
```
final long token = Binder.clearCallingIdentity();
try {
// Code using your own identity
} finally {
Binder.restoreCallingIdentity(token);
}
```
If you do not surround the code using your identity with the `try` statement \
and call `Binder.restoreCallingIdentity()` as an immediate child of the \
`finally` block, you may run code with your identity that was originally \
intended to run with the calling application's identity.
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
fun getIncidentMessageRestoreIdentityCallNotInFinallyBlock(variableName: String): String =
"`Binder.restoreCallingIdentity($variableName)` is not an immediate child of the " +
"`finally` block of the try statement after `$variableName` declaration. " +
"Surround the call with `finally` block and call it unconditionally."
/** Issue: Use of caller-aware methods after Binder.clearCallingIdentity() */
@JvmField
val ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY: Issue = Issue.create(
id = "UseOfCallerAwareMethodsWithClearedIdentity",
briefDescription = "Use of caller-aware methods after " +
"Binder.clearCallingIdentity()",
explanation = """
You cleared the original calling identity with \
`Binder.clearCallingIdentity()`, but used one of the methods below before \
restoring the identity. These methods will use your own identity instead of \
the caller's identity, so if this is expected replace them with methods that \
explicitly query your own identity such as `Process.myUid()`, \
`Process.myPid()` and `UserHandle.myUserId()`, otherwise move those methods \
out of the `Binder.clearCallingIdentity()` / `Binder.restoreCallingIdentity()` \
section.
```
Binder.getCallingPid()
Binder.getCallingUid()
Binder.getCallingUidOrThrow()
Binder.getCallingUserHandle()
UserHandle.getCallingAppId()
UserHandle.getCallingUserId()
```
""",
category = Category.SECURITY,
priority = 6,
severity = Severity.WARNING,
implementation = Implementation(
CallingIdentityTokenDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
fun getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
variableName: String,
methodName: String
): String = "You cleared the original identity with `Binder.clearCallingIdentity()` " +
"and returned into `$variableName`, so `$methodName` will be using your own " +
"identity instead of the caller's. Either explicitly query your own identity or " +
"move it after restoring the identity with " +
"`Binder.restoreCallingIdentity($variableName)`."
}
}

View File

@@ -26,13 +26,12 @@ class CallingIdentityTokenDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector = CallingIdentityTokenDetector()
override fun getIssues(): List<Issue> = listOf(
CallingIdentityTokenIssueRegistry.ISSUE_UNUSED_TOKEN,
CallingIdentityTokenIssueRegistry.ISSUE_NON_FINAL_TOKEN,
CallingIdentityTokenIssueRegistry.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
CallingIdentityTokenIssueRegistry.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
CallingIdentityTokenIssueRegistry
.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
CallingIdentityTokenIssueRegistry.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
CallingIdentityTokenDetector.ISSUE_UNUSED_TOKEN,
CallingIdentityTokenDetector.ISSUE_NON_FINAL_TOKEN,
CallingIdentityTokenDetector.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
)
/** No issue scenario */