1 /* <lambda>null2 * Copyright (C) 2021 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.android.lint 18 19 import com.android.tools.lint.client.api.UElementHandler 20 import com.android.tools.lint.detector.api.Category 21 import com.android.tools.lint.detector.api.Context 22 import com.android.tools.lint.detector.api.Detector 23 import com.android.tools.lint.detector.api.Implementation 24 import com.android.tools.lint.detector.api.Issue 25 import com.android.tools.lint.detector.api.JavaContext 26 import com.android.tools.lint.detector.api.Location 27 import com.android.tools.lint.detector.api.Scope 28 import com.android.tools.lint.detector.api.Severity 29 import com.android.tools.lint.detector.api.SourceCodeScanner 30 import com.intellij.psi.search.PsiSearchScopeUtil 31 import com.intellij.psi.search.SearchScope 32 import org.jetbrains.uast.UBlockExpression 33 import org.jetbrains.uast.UCallExpression 34 import org.jetbrains.uast.UDeclarationsExpression 35 import org.jetbrains.uast.UElement 36 import org.jetbrains.uast.UIfExpression 37 import org.jetbrains.uast.ULocalVariable 38 import org.jetbrains.uast.USimpleNameReferenceExpression 39 import org.jetbrains.uast.UTryExpression 40 import org.jetbrains.uast.getParentOfType 41 import org.jetbrains.uast.getQualifiedParentOrThis 42 import org.jetbrains.uast.getUCallExpression 43 import org.jetbrains.uast.skipParenthesizedExprDown 44 import org.jetbrains.uast.skipParenthesizedExprUp 45 46 /** 47 * Lint Detector that finds issues with improper usages of the token returned by 48 * Binder.clearCallingIdentity() 49 */ 50 @Suppress("UnstableApiUsage") 51 class CallingIdentityTokenDetector : Detector(), SourceCodeScanner { 52 /** Map of <Token variable name, Token object> */ 53 private val tokensMap = mutableMapOf<String, Token>() 54 55 override fun getApplicableUastTypes(): List<Class<out UElement?>> = 56 listOf(ULocalVariable::class.java, UCallExpression::class.java) 57 58 override fun createUastHandler(context: JavaContext): UElementHandler = 59 TokenUastHandler(context) 60 61 /** File analysis starts with a clear map */ 62 override fun beforeCheckFile(context: Context) { 63 tokensMap.clear() 64 } 65 66 /** 67 * - If tokensMap has tokens after checking the file -> reports all locations as unused token 68 * issue incidents 69 * - File analysis ends with a clear map 70 */ 71 override fun afterCheckFile(context: Context) { 72 for (token in tokensMap.values) { 73 context.report( 74 ISSUE_UNUSED_TOKEN, 75 token.location, 76 getIncidentMessageUnusedToken(token.variableName) 77 ) 78 } 79 tokensMap.clear() 80 } 81 82 /** UAST handler that analyses elements and reports incidents */ 83 private inner class TokenUastHandler(val context: JavaContext) : UElementHandler() { 84 /** 85 * For every variable initialization with Binder.clearCallingIdentity(): 86 * - Checks for non-final token issue 87 * - Checks for unused token issue within different scopes 88 * - Checks for nested calls of clearCallingIdentity() issue 89 * - Checks for clearCallingIdentity() not followed by try-finally issue 90 * - Stores token variable name, scope in the file, location and finally block in tokensMap 91 */ 92 override fun visitLocalVariable(node: ULocalVariable) { 93 val initializer = node.uastInitializer?.skipParenthesizedExprDown() 94 val rhsExpression = initializer?.getUCallExpression() ?: return 95 if (!isMethodCall(rhsExpression, Method.BINDER_CLEAR_CALLING_IDENTITY)) return 96 val location = context.getLocation(node as UElement) 97 val variableName = node.getName() 98 if (!node.isFinal) { 99 context.report( 100 ISSUE_NON_FINAL_TOKEN, 101 location, 102 getIncidentMessageNonFinalToken(variableName) 103 ) 104 } 105 // If there exists an unused variable with the same name in the map, we can imply that 106 // we left the scope of the previous declaration, so we need to report the unused token 107 val oldToken = tokensMap[variableName] 108 if (oldToken != null) { 109 context.report( 110 ISSUE_UNUSED_TOKEN, 111 oldToken.location, 112 getIncidentMessageUnusedToken(oldToken.variableName) 113 ) 114 } 115 // If there exists a token in the same scope as the current new token, it means that 116 // clearCallingIdentity() has been called at least twice without immediate restoration 117 // of identity, so we need to report the nested call of clearCallingIdentity() 118 val firstCallToken = findFirstTokenInScope(node) 119 if (firstCallToken != null) { 120 context.report( 121 ISSUE_NESTED_CLEAR_IDENTITY_CALLS, 122 createNestedLocation(firstCallToken, location), 123 getIncidentMessageNestedClearIdentityCallsPrimary( 124 firstCallToken.variableName, 125 variableName 126 ) 127 ) 128 } 129 // If the next statement in the tree is not a try-finally statement, we need to report 130 // the "clearCallingIdentity() is not followed by try-finally" issue 131 val finallyClause = (getNextStatementOfLocalVariable(node) as? UTryExpression) 132 ?.finallyClause 133 if (finallyClause == null) { 134 context.report( 135 ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY, 136 location, 137 getIncidentMessageClearIdentityCallNotFollowedByTryFinally(variableName) 138 ) 139 } 140 tokensMap[variableName] = Token( 141 variableName, 142 node.sourcePsi?.getUseScope(), 143 location, 144 finallyClause 145 ) 146 } 147 148 override fun visitCallExpression(node: UCallExpression) { 149 when { 150 isMethodCall(node, Method.BINDER_CLEAR_CALLING_IDENTITY) -> { 151 checkClearCallingIdentityCall(node) 152 } 153 isMethodCall(node, Method.BINDER_RESTORE_CALLING_IDENTITY) -> { 154 checkRestoreCallingIdentityCall(node) 155 } 156 isCallerAwareMethod(node) -> checkCallerAwareMethod(node) 157 } 158 } 159 160 private fun checkClearCallingIdentityCall(node: UCallExpression) { 161 var firstNonQualifiedParent = getFirstNonQualifiedParent(node) 162 // if the call expression is inside a ternary, and the ternary is assigned 163 // to a variable, then we are still technically assigning 164 // any result of clearCallingIdentity to a variable 165 if (firstNonQualifiedParent is UIfExpression && firstNonQualifiedParent.isTernary) { 166 firstNonQualifiedParent = firstNonQualifiedParent.uastParent 167 } 168 if (firstNonQualifiedParent !is ULocalVariable) { 169 context.report( 170 ISSUE_RESULT_OF_CLEAR_IDENTITY_CALL_NOT_STORED_IN_VARIABLE, 171 context.getLocation(node), 172 getIncidentMessageResultOfClearIdentityCallNotStoredInVariable( 173 node.getQualifiedParentOrThis().asRenderString() 174 ) 175 ) 176 } 177 } 178 179 private fun checkCallerAwareMethod(node: UCallExpression) { 180 val token = findFirstTokenInScope(node) 181 if (token != null) { 182 context.report( 183 ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY, 184 context.getLocation(node), 185 getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity( 186 token.variableName, 187 node.asRenderString() 188 ) 189 ) 190 } 191 } 192 193 /** 194 * - Checks for restoreCallingIdentity() not in the finally block issue 195 * - Removes token from tokensMap if token is within the scope of the method 196 */ 197 private fun checkRestoreCallingIdentityCall(node: UCallExpression) { 198 val arg = node.valueArguments[0] as? USimpleNameReferenceExpression ?: return 199 val variableName = arg.identifier 200 val originalScope = tokensMap[variableName]?.scope ?: return 201 val psi = arg.sourcePsi ?: return 202 // Checks if Binder.restoreCallingIdentity(token) is called within the scope of the 203 // token declaration. If not within the scope, no action is needed because the token is 204 // irrelevant i.e. not in the same scope or was not declared with clearCallingIdentity() 205 if (!PsiSearchScopeUtil.isInScope(originalScope, psi)) return 206 // We do not report "restore identity call not in finally" issue when there is no 207 // finally block because that case is already handled by "clear identity call not 208 // followed by try-finally" issue 209 if (tokensMap[variableName]?.finallyBlock != null && 210 getFirstNonQualifiedParent(node) != 211 tokensMap[variableName]?.finallyBlock 212 ) { 213 context.report( 214 ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK, 215 context.getLocation(node), 216 getIncidentMessageRestoreIdentityCallNotInFinallyBlock(variableName) 217 ) 218 } 219 tokensMap.remove(variableName) 220 } 221 222 private fun getFirstNonQualifiedParent(expression: UCallExpression): UElement? { 223 // UCallExpression can be a child of UQualifiedReferenceExpression, i.e. 224 // receiver.selector, so to get the call's immediate parent we need to get the topmost 225 // parent qualified reference expression and access its parent 226 return skipParenthesizedExprUp(expression.getQualifiedParentOrThis().uastParent) 227 } 228 229 private fun isCallerAwareMethod(expression: UCallExpression): Boolean = 230 callerAwareMethods.any { method -> isMethodCall(expression, method) } 231 232 private fun isMethodCall( 233 expression: UCallExpression, 234 method: Method 235 ): Boolean { 236 val psiMethod = expression.resolve() ?: return false 237 return psiMethod.getName() == method.methodName && 238 context.evaluator.methodMatches( 239 psiMethod, 240 method.className, 241 /* allowInherit */ true, 242 *method.args 243 ) 244 } 245 246 /** 247 * ULocalVariable in the file tree: 248 * 249 * UBlockExpression 250 * UDeclarationsExpression 251 * ULocalVariable 252 * ULocalVariable 253 * UTryStatement 254 * etc. 255 * 256 * To get the next statement of ULocalVariable: 257 * - If there exists a next sibling in UDeclarationsExpression, return the sibling 258 * - If there exists a next sibling of UDeclarationsExpression in UBlockExpression, return 259 * the sibling 260 * - Otherwise, return null 261 * 262 * Example 1 - the next sibling is in UDeclarationsExpression: 263 * Code: 264 * { 265 * int num1 = 0, num2 = methodThatThrowsException(); 266 * } 267 * Returns: num2 = methodThatThrowsException() 268 * 269 * Example 2 - the next sibling is in UBlockExpression: 270 * Code: 271 * { 272 * int num1 = 0; 273 * methodThatThrowsException(); 274 * } 275 * Returns: methodThatThrowsException() 276 * 277 * Example 3 - no next sibling; 278 * Code: 279 * { 280 * int num1 = 0; 281 * } 282 * Returns: null 283 */ 284 private fun getNextStatementOfLocalVariable(node: ULocalVariable): UElement? { 285 val declarationsExpression = node.uastParent as? UDeclarationsExpression ?: return null 286 val declarations = declarationsExpression.declarations 287 val indexInDeclarations = declarations.indexOf(node) 288 if (indexInDeclarations != -1 && declarations.size > indexInDeclarations + 1) { 289 return declarations[indexInDeclarations + 1] 290 } 291 val enclosingBlock = node 292 .getParentOfType<UBlockExpression>(strict = true) ?: return null 293 val expressions = enclosingBlock.expressions 294 val indexInBlock = expressions.indexOf(declarationsExpression as UElement) 295 return if (indexInBlock == -1) null else expressions.getOrNull(indexInBlock + 1) 296 } 297 } 298 299 private fun findFirstTokenInScope(node: UElement): Token? { 300 val psi = node.sourcePsi ?: return null 301 for (token in tokensMap.values) { 302 if (token.scope != null && PsiSearchScopeUtil.isInScope(token.scope, psi)) { 303 return token 304 } 305 } 306 return null 307 } 308 309 /** 310 * Creates a new instance of the primary location with the secondary location 311 * 312 * Here, secondary location is the helper location that shows where the issue originated 313 * 314 * The detector reports locations as objects, so when we add a secondary location to a location 315 * that has multiple issues, the secondary location gets displayed every time a location is 316 * referenced. 317 * 318 * Example: 319 * 1: final long token1 = Binder.clearCallingIdentity(); 320 * 2: long token2 = Binder.clearCallingIdentity(); 321 * 3: Binder.restoreCallingIdentity(token1); 322 * 4: Binder.restoreCallingIdentity(token2); 323 * 324 * Explanation: 325 * token2 has 2 issues: NonFinal and NestedCalls 326 * 327 * Lint report without cloning Lint report with cloning 328 * line 2: [NonFinalIssue] line 2: [NonFinalIssue] 329 * line 1: [NestedCallsIssue] 330 * line 2: [NestedCallsIssue] line 2: [NestedCallsIssue] 331 * line 1: [NestedCallsIssue] line 1: [NestedCallsIssue] 332 */ 333 private fun createNestedLocation( 334 firstCallToken: Token, 335 secondCallTokenLocation: Location 336 ): Location { 337 return cloneLocation(secondCallTokenLocation) 338 .withSecondary( 339 cloneLocation(firstCallToken.location), 340 getIncidentMessageNestedClearIdentityCallsSecondary( 341 firstCallToken.variableName 342 ) 343 ) 344 } 345 346 private fun cloneLocation(location: Location): Location { 347 // smart cast of location.start to 'Position' is impossible, because 'location.start' is a 348 // public API property declared in different module 349 val locationStart = location.start 350 return if (locationStart == null) { 351 Location.create(location.file) 352 } else { 353 Location.create(location.file, locationStart, location.end) 354 } 355 } 356 357 private enum class Method( 358 val className: String, 359 val methodName: String, 360 val args: Array<String> 361 ) { 362 BINDER_CLEAR_CALLING_IDENTITY(CLASS_BINDER, "clearCallingIdentity", emptyArray()), 363 BINDER_RESTORE_CALLING_IDENTITY(CLASS_BINDER, "restoreCallingIdentity", arrayOf("long")), 364 BINDER_GET_CALLING_PID(CLASS_BINDER, "getCallingPid", emptyArray()), 365 BINDER_GET_CALLING_UID(CLASS_BINDER, "getCallingUid", emptyArray()), 366 BINDER_GET_CALLING_UID_OR_THROW(CLASS_BINDER, "getCallingUidOrThrow", emptyArray()), 367 BINDER_GET_CALLING_USER_HANDLE(CLASS_BINDER, "getCallingUserHandle", emptyArray()), 368 USER_HANDLE_GET_CALLING_APP_ID(CLASS_USER_HANDLE, "getCallingAppId", emptyArray()), 369 USER_HANDLE_GET_CALLING_USER_ID(CLASS_USER_HANDLE, "getCallingUserId", emptyArray()) 370 } 371 372 private data class Token( 373 val variableName: String, 374 val scope: SearchScope?, 375 val location: Location, 376 val finallyBlock: UElement? 377 ) 378 379 companion object { 380 const val CLASS_BINDER = "android.os.Binder" 381 const val CLASS_USER_HANDLE = "android.os.UserHandle" 382 383 private val callerAwareMethods = listOf( 384 Method.BINDER_GET_CALLING_PID, 385 Method.BINDER_GET_CALLING_UID, 386 Method.BINDER_GET_CALLING_UID_OR_THROW, 387 Method.BINDER_GET_CALLING_USER_HANDLE, 388 Method.USER_HANDLE_GET_CALLING_APP_ID, 389 Method.USER_HANDLE_GET_CALLING_USER_ID 390 ) 391 392 /** Issue: unused token from Binder.clearCallingIdentity() */ 393 @JvmField 394 val ISSUE_UNUSED_TOKEN: Issue = Issue.create( 395 id = "UnusedTokenOfOriginalCallingIdentity", 396 briefDescription = "Unused token of Binder.clearCallingIdentity()", 397 explanation = """ 398 You cleared the original calling identity with \ 399 `Binder.clearCallingIdentity()`, but have not used the returned token to \ 400 restore the identity. 401 402 Call `Binder.restoreCallingIdentity(token)` in the `finally` block, at the end \ 403 of the method or when you need to restore the identity. 404 405 `token` is the result of `Binder.clearCallingIdentity()` 406 """, 407 category = Category.SECURITY, 408 priority = 6, 409 severity = Severity.WARNING, 410 implementation = Implementation( 411 CallingIdentityTokenDetector::class.java, 412 Scope.JAVA_FILE_SCOPE 413 ) 414 ) 415 416 private fun getIncidentMessageUnusedToken(variableName: String) = "`$variableName` has " + 417 "not been used to restore the calling identity. Introduce a `try`-`finally` " + 418 "after the declaration and call `Binder.restoreCallingIdentity($variableName)` " + 419 "in `finally` or remove `$variableName`." 420 421 /** Issue: non-final token from Binder.clearCallingIdentity() */ 422 @JvmField 423 val ISSUE_NON_FINAL_TOKEN: Issue = Issue.create( 424 id = "NonFinalTokenOfOriginalCallingIdentity", 425 briefDescription = "Non-final token of Binder.clearCallingIdentity()", 426 explanation = """ 427 You cleared the original calling identity with \ 428 `Binder.clearCallingIdentity()`, but have not made the returned token `final`. 429 430 The token should be `final` in order to prevent it from being overwritten, \ 431 which can cause problems when restoring the identity with \ 432 `Binder.restoreCallingIdentity(token)`. 433 """, 434 category = Category.SECURITY, 435 priority = 6, 436 severity = Severity.WARNING, 437 implementation = Implementation( 438 CallingIdentityTokenDetector::class.java, 439 Scope.JAVA_FILE_SCOPE 440 ) 441 ) 442 443 private fun getIncidentMessageNonFinalToken(variableName: String) = "`$variableName` is " + 444 "a non-final token from `Binder.clearCallingIdentity()`. Add `final` keyword to " + 445 "`$variableName`." 446 447 /** Issue: nested calls of Binder.clearCallingIdentity() */ 448 @JvmField 449 val ISSUE_NESTED_CLEAR_IDENTITY_CALLS: Issue = Issue.create( 450 id = "NestedClearCallingIdentityCalls", 451 briefDescription = "Nested calls of Binder.clearCallingIdentity()", 452 explanation = """ 453 You cleared the original calling identity with \ 454 `Binder.clearCallingIdentity()` twice without restoring identity with the \ 455 result of the first call. 456 457 Make sure to restore the identity after each clear identity call. 458 """, 459 category = Category.SECURITY, 460 priority = 6, 461 severity = Severity.WARNING, 462 implementation = Implementation( 463 CallingIdentityTokenDetector::class.java, 464 Scope.JAVA_FILE_SCOPE 465 ) 466 ) 467 468 private fun getIncidentMessageNestedClearIdentityCallsPrimary( 469 firstCallVariableName: String, 470 secondCallVariableName: String 471 ): String = "The calling identity has already been cleared and returned into " + 472 "`$firstCallVariableName`. Move `$secondCallVariableName` declaration after " + 473 "restoring the calling identity with " + 474 "`Binder.restoreCallingIdentity($firstCallVariableName)`." 475 476 private fun getIncidentMessageNestedClearIdentityCallsSecondary( 477 firstCallVariableName: String 478 ): String = "Location of the `$firstCallVariableName` declaration." 479 480 /** Issue: Binder.clearCallingIdentity() is not followed by `try-finally` statement */ 481 @JvmField 482 val ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY: Issue = Issue.create( 483 id = "ClearIdentityCallNotFollowedByTryFinally", 484 briefDescription = "Binder.clearCallingIdentity() is not followed by try-finally " + 485 "statement", 486 explanation = """ 487 You cleared the original calling identity with \ 488 `Binder.clearCallingIdentity()`, but the next statement is not a `try` \ 489 statement. 490 491 Use the following pattern for running operations with your own identity: 492 493 ``` 494 final long token = Binder.clearCallingIdentity(); 495 try { 496 // Code using your own identity 497 } finally { 498 Binder.restoreCallingIdentity(token); 499 } 500 ``` 501 502 Any calls/operations between `Binder.clearCallingIdentity()` and `try` \ 503 statement risk throwing an exception without doing a safe and unconditional \ 504 restore of the identity with `Binder.restoreCallingIdentity()` as an immediate \ 505 child of the `finally` block. If you do not follow the pattern, you may run \ 506 code with your identity that was originally intended to run with the calling \ 507 application's identity. 508 """, 509 category = Category.SECURITY, 510 priority = 6, 511 severity = Severity.WARNING, 512 implementation = Implementation( 513 CallingIdentityTokenDetector::class.java, 514 Scope.JAVA_FILE_SCOPE 515 ) 516 ) 517 518 private fun getIncidentMessageClearIdentityCallNotFollowedByTryFinally( 519 variableName: String 520 ): String = "You cleared the calling identity and returned the result into " + 521 "`$variableName`, but the next statement is not a `try`-`finally` statement. " + 522 "Define a `try`-`finally` block after `$variableName` declaration to ensure a " + 523 "safe restore of the calling identity by calling " + 524 "`Binder.restoreCallingIdentity($variableName)` and making it an immediate child " + 525 "of the `finally` block." 526 527 /** Issue: Binder.restoreCallingIdentity() is not in finally block */ 528 @JvmField 529 val ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK: Issue = Issue.create( 530 id = "RestoreIdentityCallNotInFinallyBlock", 531 briefDescription = "Binder.restoreCallingIdentity() is not in finally block", 532 explanation = """ 533 You are restoring the original calling identity with \ 534 `Binder.restoreCallingIdentity()`, but the call is not an immediate child of \ 535 the `finally` block of the `try` statement. 536 537 Use the following pattern for running operations with your own identity: 538 539 ``` 540 final long token = Binder.clearCallingIdentity(); 541 try { 542 // Code using your own identity 543 } finally { 544 Binder.restoreCallingIdentity(token); 545 } 546 ``` 547 548 If you do not surround the code using your identity with the `try` statement \ 549 and call `Binder.restoreCallingIdentity()` as an immediate child of the \ 550 `finally` block, you may run code with your identity that was originally \ 551 intended to run with the calling application's identity. 552 """, 553 category = Category.SECURITY, 554 priority = 6, 555 severity = Severity.WARNING, 556 implementation = Implementation( 557 CallingIdentityTokenDetector::class.java, 558 Scope.JAVA_FILE_SCOPE 559 ) 560 ) 561 562 private fun getIncidentMessageRestoreIdentityCallNotInFinallyBlock( 563 variableName: String 564 ): String = "`Binder.restoreCallingIdentity($variableName)` is not an immediate child of " + 565 "the `finally` block of the try statement after `$variableName` declaration. " + 566 "Surround the call with `finally` block and call it unconditionally." 567 568 /** Issue: Use of caller-aware methods after Binder.clearCallingIdentity() */ 569 @JvmField 570 val ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY: Issue = Issue.create( 571 id = "UseOfCallerAwareMethodsWithClearedIdentity", 572 briefDescription = "Use of caller-aware methods after " + 573 "Binder.clearCallingIdentity()", 574 explanation = """ 575 You cleared the original calling identity with \ 576 `Binder.clearCallingIdentity()`, but used one of the methods below before \ 577 restoring the identity. These methods will use your own identity instead of \ 578 the caller's identity, so if this is expected replace them with methods that \ 579 explicitly query your own identity such as `Process.myUid()`, \ 580 `Process.myPid()` and `UserHandle.myUserId()`, otherwise move those methods \ 581 out of the `Binder.clearCallingIdentity()` / `Binder.restoreCallingIdentity()` \ 582 section. 583 584 ``` 585 Binder.getCallingPid() 586 Binder.getCallingUid() 587 Binder.getCallingUidOrThrow() 588 Binder.getCallingUserHandle() 589 UserHandle.getCallingAppId() 590 UserHandle.getCallingUserId() 591 ``` 592 """, 593 category = Category.SECURITY, 594 priority = 6, 595 severity = Severity.WARNING, 596 implementation = Implementation( 597 CallingIdentityTokenDetector::class.java, 598 Scope.JAVA_FILE_SCOPE 599 ) 600 ) 601 602 private fun getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity( 603 variableName: String, 604 methodName: String 605 ): String = "You cleared the original identity with `Binder.clearCallingIdentity()` " + 606 "and returned into `$variableName`, so `$methodName` will be using your own " + 607 "identity instead of the caller's. Either explicitly query your own identity or " + 608 "move it after restoring the identity with " + 609 "`Binder.restoreCallingIdentity($variableName)`." 610 611 /** Issue: Result of Binder.clearCallingIdentity() is not stored in a variable */ 612 @JvmField 613 val ISSUE_RESULT_OF_CLEAR_IDENTITY_CALL_NOT_STORED_IN_VARIABLE: Issue = Issue.create( 614 id = "ResultOfClearIdentityCallNotStoredInVariable", 615 briefDescription = "Result of Binder.clearCallingIdentity() is not stored in a " + 616 "variable", 617 explanation = """ 618 You cleared the original calling identity with \ 619 `Binder.clearCallingIdentity()`, but did not store the result of the method \ 620 call in a variable. You need to store the result in a variable and restore it later. 621 622 Use the following pattern for running operations with your own identity: 623 624 ``` 625 final long token = Binder.clearCallingIdentity(); 626 try { 627 // Code using your own identity 628 } finally { 629 Binder.restoreCallingIdentity(token); 630 } 631 ``` 632 """, 633 category = Category.SECURITY, 634 priority = 6, 635 severity = Severity.WARNING, 636 implementation = Implementation( 637 CallingIdentityTokenDetector::class.java, 638 Scope.JAVA_FILE_SCOPE 639 ) 640 ) 641 642 private fun getIncidentMessageResultOfClearIdentityCallNotStoredInVariable( 643 methodName: String 644 ): String = "You cleared the original identity with `$methodName` but did not store the " + 645 "result in a variable. You need to store the result in a variable and restore it " + 646 "later." 647 } 648 } 649