List of usage examples for org.objectweb.asm.tree InsnList size
int size
To view the source code for org.objectweb.asm.tree InsnList size.
Click Source Link
From source file:com.android.tools.klint.client.api.LintDriver.java
License:Apache License
@Nullable private static MethodInsnNode findConstructorInvocation(@NonNull MethodNode method, @NonNull String className) { InsnList nodes = method.instructions; for (int i = 0, n = nodes.size(); i < n; i++) { AbstractInsnNode instruction = nodes.get(i); if (instruction.getOpcode() == Opcodes.INVOKESPECIAL) { MethodInsnNode call = (MethodInsnNode) instruction; if (className.equals(call.owner)) { return call; }/* ww w.ja va 2 s.c om*/ } } return null; }
From source file:com.android.tools.lint.checks.AllowAllHostnameVerifierDetector.java
License:Apache License
@SuppressWarnings("rawtypes") @Override/* ww w . j a v a 2 s . com*/ public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) { if (!classNode.interfaces.contains("javax/net/ssl/HostnameVerifier")) { return; } List methodList = classNode.methods; for (Object m : methodList) { MethodNode method = (MethodNode) m; if ("verify".equals(method.name)) { InsnList nodes = method.instructions; boolean emptyMethod = true; // Stays true if method has no instructions // other than ICONST_1 or IRETURN. boolean containsIconst1 = false; for (int i = 0, n = nodes.size(); i < n; i++) { // Future work: Improve this check to be less sensitive to irrelevant // instructions/statements/invocations (e.g. System.out.println). AbstractInsnNode instruction = nodes.get(i); int type = instruction.getType(); if (type != AbstractInsnNode.LABEL && type != AbstractInsnNode.LINE && !(type == AbstractInsnNode.INSN && (instruction.getOpcode() == Opcodes.ICONST_1 || instruction.getOpcode() == Opcodes.IRETURN))) { emptyMethod = false; break; } else if (type == AbstractInsnNode.INSN && instruction.getOpcode() == Opcodes.ICONST_1) { containsIconst1 = true; } } if (containsIconst1 && emptyMethod) { Location location = context.getLocation(method, classNode); context.report(ISSUE, location, method.name + " always returns true, which " + "could cause insecure network traffic due to trusting TLS/SSL " + "server certificates for wrong hostnames"); } } } }
From source file:com.android.tools.lint.checks.ApiDetector.java
License:Apache License
@SuppressWarnings("rawtypes") // ASM API @Override/*from w ww . j a va2s. c o m*/ public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) { if (mApiDatabase == null) { return; } if (AOSP_BUILD && classNode.name.startsWith("android/support/")) { //$NON-NLS-1$ return; } // Requires util package (add prebuilts/tools/common/asm-tools/asm-debug-all-4.0.jar) //classNode.accept(new TraceClassVisitor(new PrintWriter(System.out))); int classMinSdk = getClassMinSdk(context, classNode); if (classMinSdk == -1) { classMinSdk = getMinSdk(context); } List methodList = classNode.methods; if (methodList.isEmpty()) { return; } boolean checkCalls = context.isEnabled(UNSUPPORTED) || context.isEnabled(INLINED); boolean checkMethods = context.isEnabled(OVERRIDE) && context.getMainProject().getBuildSdk() >= 1; String frameworkParent = null; if (checkMethods) { LintDriver driver = context.getDriver(); String owner = classNode.superName; while (owner != null) { // For virtual dispatch, walk up the inheritance chain checking // each inherited method if ((owner.startsWith("android/") //$NON-NLS-1$ && !owner.startsWith("android/support/")) //$NON-NLS-1$ || owner.startsWith("java/") //$NON-NLS-1$ || owner.startsWith("javax/")) { //$NON-NLS-1$ frameworkParent = owner; break; } owner = driver.getSuperClass(owner); } if (frameworkParent == null) { checkMethods = false; } } if (checkCalls) { // Check implements/extends if (classNode.superName != null) { String signature = classNode.superName; checkExtendsClass(context, classNode, classMinSdk, signature); } if (classNode.interfaces != null) { @SuppressWarnings("unchecked") // ASM API List<String> interfaceList = classNode.interfaces; for (String signature : interfaceList) { checkExtendsClass(context, classNode, classMinSdk, signature); } } } for (Object m : methodList) { MethodNode method = (MethodNode) m; int minSdk = getLocalMinSdk(method.invisibleAnnotations); if (minSdk == -1) { minSdk = classMinSdk; } InsnList nodes = method.instructions; if (checkMethods && Character.isJavaIdentifierStart(method.name.charAt(0))) { int buildSdk = context.getMainProject().getBuildSdk(); String name = method.name; assert frameworkParent != null; int api = mApiDatabase.getCallVersion(frameworkParent, name, method.desc); if (api > buildSdk && buildSdk != -1) { // TODO: Don't complain if it's annotated with @Override; that means // somehow the build target isn't correct. String fqcn; String owner = classNode.name; if (CONSTRUCTOR_NAME.equals(name)) { fqcn = "new " + ClassContext.getFqcn(owner); //$NON-NLS-1$ } else { fqcn = ClassContext.getFqcn(owner) + '#' + name; } String message = String.format( "This method is not overriding anything with the current build " + "target, but will in API level %1$d (current target is %2$d): %3$s", api, buildSdk, fqcn); Location location = context.getLocation(method, classNode); context.report(OVERRIDE, method, null, location, message, null); } } if (!checkCalls) { continue; } if (CHECK_DECLARATIONS) { // Check types in parameter list and types of local variables List localVariables = method.localVariables; if (localVariables != null) { for (Object v : localVariables) { LocalVariableNode var = (LocalVariableNode) v; String desc = var.desc; if (desc.charAt(0) == 'L') { // "Lpackage/Class;" => "package/Bar" String className = desc.substring(1, desc.length() - 1); int api = mApiDatabase.getClassVersion(className); if (api > minSdk) { String fqcn = ClassContext.getFqcn(className); String message = String.format( "Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn); report(context, message, var.start, method, className.substring(className.lastIndexOf('/') + 1), null, SearchHints.create(NEAREST).matchJavaSymbol()); } } } } // Check return type // The parameter types are already handled as local variables so we can skip // right to the return type. // Check types in parameter list String signature = method.desc; if (signature != null) { int args = signature.indexOf(')'); if (args != -1 && signature.charAt(args + 1) == 'L') { String type = signature.substring(args + 2, signature.length() - 1); int api = mApiDatabase.getClassVersion(type); if (api > minSdk) { String fqcn = ClassContext.getFqcn(type); String message = String.format( "Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn); AbstractInsnNode first = nodes.size() > 0 ? nodes.get(0) : null; report(context, message, first, method, method.name, null, SearchHints.create(BACKWARD).matchJavaSymbol()); } } } } for (int i = 0, n = nodes.size(); i < n; i++) { AbstractInsnNode instruction = nodes.get(i); int type = instruction.getType(); if (type == AbstractInsnNode.METHOD_INSN) { MethodInsnNode node = (MethodInsnNode) instruction; String name = node.name; String owner = node.owner; String desc = node.desc; // No need to check methods in this local class; we know they // won't be an API match if (node.getOpcode() == Opcodes.INVOKEVIRTUAL && owner.equals(classNode.name)) { owner = classNode.superName; } boolean checkingSuperClass = false; while (owner != null) { int api = mApiDatabase.getCallVersion(owner, name, desc); if (api > minSdk) { if (method.name.startsWith(SWITCH_TABLE_PREFIX)) { // We're in a compiler-generated method to generate an // array indexed by enum ordinal values to enum values. The enum // itself must be requiring a higher API number than is // currently used, but the call site for the switch statement // will also be referencing it, so no need to report these // calls. break; } if (!checkingSuperClass && node.getOpcode() == Opcodes.INVOKEVIRTUAL && methodDefinedLocally(classNode, name, desc)) { break; } String fqcn; if (CONSTRUCTOR_NAME.equals(name)) { fqcn = "new " + ClassContext.getFqcn(owner); //$NON-NLS-1$ } else { fqcn = ClassContext.getFqcn(owner) + '#' + name; } String message = String.format( "Call requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn); if (name.equals(ORDINAL_METHOD) && instruction.getNext() != null && instruction.getNext().getNext() != null && instruction.getNext().getOpcode() == Opcodes.IALOAD && instruction.getNext().getNext().getOpcode() == Opcodes.TABLESWITCH) { message = String.format( "Enum for switch requires API level %1$d " + "(current min is %2$d): %3$s", api, minSdk, ClassContext.getFqcn(owner)); } report(context, message, node, method, name, null, SearchHints.create(FORWARD).matchJavaSymbol()); } // For virtual dispatch, walk up the inheritance chain checking // each inherited method if (owner.startsWith("android/") //$NON-NLS-1$ || owner.startsWith("javax/")) { //$NON-NLS-1$ // The API map has already inlined all inherited methods // so no need to keep checking up the chain // -- unless it's the support library which is also in // the android/ namespace: if (owner.startsWith("android/support/")) { //$NON-NLS-1$ owner = context.getDriver().getSuperClass(owner); } else { owner = null; } } else if (owner.startsWith("java/")) { //$NON-NLS-1$ if (owner.equals(LocaleDetector.DATE_FORMAT_OWNER)) { checkSimpleDateFormat(context, method, node, minSdk); } // Already inlined; see comment above owner = null; } else if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) { owner = context.getDriver().getSuperClass(owner); } else if (node.getOpcode() == Opcodes.INVOKESTATIC && api == -1) { // Inherit through static classes as well owner = context.getDriver().getSuperClass(owner); } else { owner = null; } checkingSuperClass = true; } } else if (type == AbstractInsnNode.FIELD_INSN) { FieldInsnNode node = (FieldInsnNode) instruction; String name = node.name; String owner = node.owner; int api = mApiDatabase.getFieldVersion(owner, name); if (api > minSdk) { if (method.name.startsWith(SWITCH_TABLE_PREFIX)) { checkSwitchBlock(context, classNode, node, method, name, owner, api, minSdk); continue; } String fqcn = ClassContext.getFqcn(owner) + '#' + name; if (mPendingFields != null) { mPendingFields.remove(fqcn); } String message = String.format("Field requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn); report(context, message, node, method, name, null, SearchHints.create(FORWARD).matchJavaSymbol()); } } else if (type == AbstractInsnNode.LDC_INSN) { LdcInsnNode node = (LdcInsnNode) instruction; if (node.cst instanceof Type) { Type t = (Type) node.cst; String className = t.getInternalName(); int api = mApiDatabase.getClassVersion(className); if (api > minSdk) { String fqcn = ClassContext.getFqcn(className); String message = String.format( "Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn); report(context, message, node, method, className.substring(className.lastIndexOf('/') + 1), null, SearchHints.create(FORWARD).matchJavaSymbol()); } } } } } }
From source file:com.android.tools.lint.checks.ApiDetector.java
License:Apache License
@SuppressWarnings("rawtypes") // ASM API private static void checkSwitchBlock(ClassContext context, ClassNode classNode, FieldInsnNode field, MethodNode method, String name, String owner, int api, int minSdk) { // Switch statements on enums are tricky. The compiler will generate a method // which returns an array of the enum constants, indexed by their ordinal() values. // However, we only want to complain if the code is actually referencing one of // the non-available enum fields. ///*from w w w . j a va 2 s .co m*/ // For the android.graphics.PorterDuff.Mode enum for example, the first few items // in the array are populated like this: // // L0 // ALOAD 0 // GETSTATIC android/graphics/PorterDuff$Mode.ADD : Landroid/graphics/PorterDuff$Mode; // INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I // ICONST_1 // IASTORE // L1 // GOTO L3 // L2 // FRAME FULL [[I] [java/lang/NoSuchFieldError] // POP // L3 // FRAME SAME // ALOAD 0 // GETSTATIC android/graphics/PorterDuff$Mode.CLEAR : Landroid/graphics/PorterDuff$Mode; // INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I // ICONST_2 // IASTORE // ... // So if we for example find that the "ADD" field isn't accessible, since it requires // API 11, we need to // (1) First find out what its ordinal number is. We can look at the following // instructions to discover this; it's the "ICONST_1" and "IASTORE" instructions. // (After ICONST_5 it moves on to BIPUSH 6, BIPUSH 7, etc.) // (2) Find the corresponding *usage* of this switch method. For the above enum, // the switch ordinal lookup method will be called // "$SWITCH_TABLE$android$graphics$PorterDuff$Mode" with desc "()[I". // This means we will be looking for an invocation in some other method which looks // like this: // INVOKESTATIC (current class).$SWITCH_TABLE$android$graphics$PorterDuff$Mode ()[I // (obviously, it can be invoked more than once) // Note that it can be used more than once in this class and all sites should be // checked! // (3) Look up the corresponding table switch, which should look something like this: // INVOKESTATIC (current class).$SWITCH_TABLE$android$graphics$PorterDuff$Mode ()[I // ALOAD 0 // INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I // IALOAD // LOOKUPSWITCH // 2: L1 // 11: L2 // default: L3 // Here we need to see if the LOOKUPSWITCH instruction is referencing our target // case. Above we were looking for the "ADD" case which had ordinal 1. Since this // isn't explicitly referenced, we can ignore this field reference. AbstractInsnNode next = field.getNext(); if (next == null || next.getOpcode() != Opcodes.INVOKEVIRTUAL) { return; } next = next.getNext(); if (next == null) { return; } int ordinal; switch (next.getOpcode()) { case Opcodes.ICONST_0: ordinal = 0; break; case Opcodes.ICONST_1: ordinal = 1; break; case Opcodes.ICONST_2: ordinal = 2; break; case Opcodes.ICONST_3: ordinal = 3; break; case Opcodes.ICONST_4: ordinal = 4; break; case Opcodes.ICONST_5: ordinal = 5; break; case Opcodes.BIPUSH: { IntInsnNode iin = (IntInsnNode) next; ordinal = iin.operand; break; } default: return; } // Find usages of this call site List methodList = classNode.methods; for (Object m : methodList) { InsnList nodes = ((MethodNode) m).instructions; for (int i = 0, n = nodes.size(); i < n; i++) { AbstractInsnNode instruction = nodes.get(i); if (instruction.getOpcode() != Opcodes.INVOKESTATIC) { continue; } MethodInsnNode node = (MethodInsnNode) instruction; if (node.name.equals(method.name) && node.desc.equals(method.desc) && node.owner.equals(classNode.name)) { // Find lookup switch AbstractInsnNode target = getNextInstruction(node); while (target != null) { if (target.getOpcode() == Opcodes.LOOKUPSWITCH) { LookupSwitchInsnNode lookup = (LookupSwitchInsnNode) target; @SuppressWarnings("unchecked") // ASM API List<Integer> keys = lookup.keys; if (keys != null && keys.contains(ordinal)) { String fqcn = ClassContext.getFqcn(owner) + '#' + name; String message = String.format( "Enum value requires API level %1$d " + "(current min is %2$d): %3$s", api, minSdk, fqcn); report(context, message, lookup, (MethodNode) m, name, null, SearchHints.create(FORWARD).matchJavaSymbol()); // Break out of the inner target search only; the switch // statement could be used in other places in this class as // well and we want to report all problematic usages. break; } } target = getNextInstruction(target); } } } } }
From source file:com.android.tools.lint.checks.ControlFlowGraph.java
License:Apache License
/** * Creates a new {@link ControlFlowGraph} and populates it with the flow * control for the given method. If the optional {@code initial} parameter is * provided with an existing graph, then the graph is simply populated, not * created. This allows subclassing of the graph instance, if necessary. * * @param initial usually null, but can point to an existing instance of a * {@link ControlFlowGraph} in which that graph is reused (but * populated with new edges)//from w w w . ja v a 2s. co m * @param classNode the class containing the method to be analyzed * @param method the method to be analyzed * @return a {@link ControlFlowGraph} with nodes for the control flow in the * given method * @throws AnalyzerException if the underlying bytecode library is unable to * analyze the method bytecode */ @NonNull public static ControlFlowGraph create(@Nullable ControlFlowGraph initial, @NonNull ClassNode classNode, @NonNull MethodNode method) throws AnalyzerException { final ControlFlowGraph graph = initial != null ? initial : new ControlFlowGraph(); final InsnList instructions = method.instructions; graph.mNodeMap = Maps.newHashMapWithExpectedSize(instructions.size()); // Create a flow control graph using ASM4's analyzer. According to the ASM 4 guide // (download.forge.objectweb.org/asm/asm4-guide.pdf) there are faster ways to construct // it, but those require a lot more code. Analyzer analyzer = new Analyzer(new BasicInterpreter()) { @Override protected void newControlFlowEdge(int insn, int successor) { // Update the information as of whether the this object has been // initialized at the given instruction. AbstractInsnNode from = instructions.get(insn); AbstractInsnNode to = instructions.get(successor); graph.add(from, to); } @Override protected boolean newControlFlowExceptionEdge(int insn, TryCatchBlockNode tcb) { AbstractInsnNode from = instructions.get(insn); graph.exception(from, tcb); return super.newControlFlowExceptionEdge(insn, tcb); } @Override protected boolean newControlFlowExceptionEdge(int insn, int successor) { AbstractInsnNode from = instructions.get(insn); AbstractInsnNode to = instructions.get(successor); graph.exception(from, to); return super.newControlFlowExceptionEdge(insn, successor); } }; analyzer.analyze(classNode.name, method); return graph; }
From source file:com.android.tools.lint.checks.CordovaVersionDetector.java
License:Apache License
@Override public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { //noinspection VariableNotUsedInsideIf if (mCordovaVersion != null) { // Exit early if we have already found the cordova version in the JS file. // This will be the case for all versions > 3.x.x return;/*from w ww. j av a 2 s. co m*/ } // For cordova versions such as 2.7.1, the version is a static *non-final* field in // a class named Device. Since it is non-final, it is initialized in the <clinit> method. // Example: // // ldc #5 // String 2.7.1 // putstatic #6 // Field cordovaVersion:Ljava/lang/String; // ... if (classNode.name.equals(FQN_CORDOVA_DEVICE)) { //noinspection unchecked ASM api. List<MethodNode> methods = classNode.methods; for (MethodNode method : methods) { if (SdkConstants.CLASS_CONSTRUCTOR.equals(method.name)) { InsnList nodes = method.instructions; for (int i = 0, n = nodes.size(); i < n; i++) { AbstractInsnNode instruction = nodes.get(i); int type = instruction.getType(); if (type == AbstractInsnNode.FIELD_INSN) { checkInstructionInternal(context, classNode, instruction); break; } } } } } else if (classNode.name.equals(FQN_CORDOVA_WEBVIEW)) { // For versions > 3.x.x, the version string is stored as a static final String in // CordovaWebView. // Note that this is also stored in the cordova.js.* but from a lint api perspective, // it's much faster to look it up here than load and check in the JS file. // e.g. // public static final java.lang.String CORDOVA_VERSION; // descriptor: Ljava/lang/String; // flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL // ConstantValue: String 4.1.1 // //noinspection unchecked ASM api. List<FieldNode> fields = classNode.fields; for (FieldNode node : fields) { if (FIELD_NAME_CORDOVA_VERSION_WEBVIEW.equals(node.name) && (node.access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL // is final && (node.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC // is static && node.value instanceof String) { mCordovaVersion = createVersion((String) node.value); if (mCordovaVersion != null) { validateCordovaVersion(context, mCordovaVersion, context.getLocation(classNode)); } } } } }
From source file:com.android.tools.lint.checks.InvalidPackageDetector.java
License:Apache License
@SuppressWarnings("rawtypes") // ASM API @Override/*from ww w . jav a2 s . c o m*/ public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) { if (!context.isFromClassLibrary() || shouldSkip(context.file)) { return; } if (mApiDatabase == null) { return; } if (classNode.name.startsWith(JAVAX_PKG_PREFIX)) { mJavaxLibraryClasses.add(classNode.name); } List methodList = classNode.methods; for (Object m : methodList) { MethodNode method = (MethodNode) m; InsnList nodes = method.instructions; // Check return type // The parameter types are already handled as local variables so we can skip // right to the return type. // Check types in parameter list String signature = method.desc; if (signature != null) { int args = signature.indexOf(')'); if (args != -1 && signature.charAt(args + 1) == 'L') { String type = signature.substring(args + 2, signature.length() - 1); if (isInvalidPackage(type)) { AbstractInsnNode first = nodes.size() > 0 ? nodes.get(0) : null; record(context, method, first, type); } } } for (int i = 0, n = nodes.size(); i < n; i++) { AbstractInsnNode instruction = nodes.get(i); int type = instruction.getType(); if (type == AbstractInsnNode.METHOD_INSN) { MethodInsnNode node = (MethodInsnNode) instruction; String owner = node.owner; // No need to check methods in this local class; we know they // won't be an API match if (node.getOpcode() == Opcodes.INVOKEVIRTUAL && owner.equals(classNode.name)) { owner = classNode.superName; } while (owner != null) { if (isInvalidPackage(owner)) { record(context, method, instruction, owner); } // For virtual dispatch, walk up the inheritance chain checking // each inherited method if (owner.startsWith("android/") //$NON-NLS-1$ || owner.startsWith(JAVA_PKG_PREFIX) || owner.startsWith(JAVAX_PKG_PREFIX)) { owner = null; } else if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) { owner = context.getDriver().getSuperClass(owner); } else if (node.getOpcode() == Opcodes.INVOKESTATIC) { // Inherit through static classes as well owner = context.getDriver().getSuperClass(owner); } else { owner = null; } } } else if (type == AbstractInsnNode.FIELD_INSN) { FieldInsnNode node = (FieldInsnNode) instruction; String owner = node.owner; if (isInvalidPackage(owner)) { record(context, method, instruction, owner); } } else if (type == AbstractInsnNode.LDC_INSN) { LdcInsnNode node = (LdcInsnNode) instruction; if (node.cst instanceof Type) { Type t = (Type) node.cst; String className = t.getInternalName(); if (isInvalidPackage(className)) { record(context, method, instruction, className); } } } } } }
From source file:com.android.tools.lint.checks.TrustAllX509TrustManagerDetector.java
License:Apache License
@Override @SuppressWarnings("rawtypes") public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) { if (!context.isFromClassLibrary()) { // Non-library code checked at the AST level return;/*from www. j a v a 2s. c o m*/ } if (!classNode.interfaces.contains("javax/net/ssl/X509TrustManager")) { return; } List methodList = classNode.methods; for (Object m : methodList) { MethodNode method = (MethodNode) m; if ("checkServerTrusted".equals(method.name) || "checkClientTrusted".equals(method.name)) { InsnList nodes = method.instructions; boolean emptyMethod = true; // Stays true if method doesn't perform any "real" // operations for (int i = 0, n = nodes.size(); i < n; i++) { // Future work: Improve this check to be less sensitive to irrelevant // instructions/statements/invocations (e.g. System.out.println) by // looking for calls that could lead to a CertificateException being // thrown, e.g. throw statement within the method itself or invocation // of another method that may throw a CertificateException, and only // reporting an issue if none of these calls are found. ControlFlowGraph // may be useful here. AbstractInsnNode instruction = nodes.get(i); int type = instruction.getType(); if (type != AbstractInsnNode.LABEL && type != AbstractInsnNode.LINE && !(type == AbstractInsnNode.INSN && instruction.getOpcode() == Opcodes.RETURN)) { emptyMethod = false; break; } } if (emptyMethod) { Location location = context.getLocation(method, classNode); context.report(ISSUE, location, getErrorMessage(method.name)); } } } }
From source file:com.android.tools.lint.checks.UnsafeBroadcastReceiverDetector.java
License:Apache License
@SuppressWarnings("rawtypes") public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) { // If class isn't in mReceiversWithIntentFilter (set below by // XmlScanner), skip it. if (!mReceiversWithProtectedBroadcastIntentFilter.contains(classNode.name)) { return;//w ww . jav a 2 s .com } // Search for "void onReceive(android.content.Context, android.content.Intent)" method List methodList = classNode.methods; for (Object m : methodList) { MethodNode method = (MethodNode) m; if (!"onReceive".equals(method.name) || !"(Landroid/content/Context;Landroid/content/Intent;)V".equals(method.desc)) { continue; } // Search for call to getAction but also search for references to aload_2, // which indicates that the method is making use of the received intent in // some way. // // If the onReceive method doesn't call getAction but does make use of // the received intent, it is possible that it is passing it to another // method that might be performing the getAction check, so we warn that the // finding may be a false positive. (An alternative option would be to not // report a finding at all in this case.) boolean getActionEncountered = false; boolean intentParameterEncountered = false; InsnList nodes = method.instructions; for (int i = 0, n = nodes.size(); i < n; i++) { AbstractInsnNode instruction = nodes.get(i); int type = instruction.getType(); if (type == AbstractInsnNode.VAR_INSN) { VarInsnNode node = (VarInsnNode) instruction; if (node.getOpcode() == Opcodes.ALOAD) { if (node.var == 2) { intentParameterEncountered = true; } } } else if (type == AbstractInsnNode.METHOD_INSN) { MethodInsnNode node = (MethodInsnNode) instruction; if ("android/content/Intent".equals(node.owner) && "getAction".equals(node.name)) { getActionEncountered = true; break; } } } if (!getActionEncountered) { Location location = context.getLocation(method, classNode); String report; if (!intentParameterEncountered) { report = "This broadcast receiver declares an intent-filter for a protected " + "broadcast action string, which can only be sent by the system, " + "not third-party applications. However, the receiver's onReceive " + "method does not appear to call getAction to ensure that the " + "received Intent's action string matches the expected value, " + "potentially making it possible for another actor to send a " + "spoofed intent with no action string or a different action " + "string and cause undesired behavior."; } else { // An alternative implementation option is to not report a finding at all in // this case, if we are worried about false positives causing confusion or // resulting in developers ignoring other lint warnings. report = "This broadcast receiver declares an intent-filter for a protected " + "broadcast action string, which can only be sent by the system, " + "not third-party applications. However, the receiver's onReceive " + "method does not appear to call getAction to ensure that the " + "received Intent's action string matches the expected value, " + "potentially making it possible for another actor to send a " + "spoofed intent with no action string or a different action " + "string and cause undesired behavior. In this case, it is " + "possible that the onReceive method passed the received Intent " + "to another method that checked the action string. If so, this " + "finding can safely be ignored."; } context.report(ACTION_STRING, method, null, location, report); } } }
From source file:com.android.tools.lint.checks.WakelockDetector.java
License:Apache License
private void checkFlow(@NonNull ClassContext context, @NonNull ClassNode classNode, @NonNull MethodNode method, @NonNull MethodInsnNode acquire) { // Track allocations such that we know whether the type of the call // is on a SecureRandom rather than a Random final InsnList instructions = method.instructions; MethodInsnNode release = null;/* w ww . java2 s.c o m*/ // Find release call for (int i = 0, n = instructions.size(); i < n; i++) { AbstractInsnNode instruction = instructions.get(i); int type = instruction.getType(); if (type == AbstractInsnNode.METHOD_INSN) { MethodInsnNode call = (MethodInsnNode) instruction; if (call.name.equals(RELEASE_METHOD) && call.owner.equals(WAKELOCK_OWNER)) { release = call; break; } } } if (release == null) { // Didn't find both acquire and release in this method; no point in doing // local flow analysis return; } try { MyGraph graph = new MyGraph(); ControlFlowGraph.create(graph, classNode, method); if (DEBUG) { // Requires util package //ClassNode clazz = classNode; //clazz.accept(new TraceClassVisitor(new PrintWriter(System.out))); System.out.println(graph.toString(graph.getNode(acquire))); } int status = dfs(graph.getNode(acquire)); if ((status & SEEN_RETURN) != 0) { String message; if ((status & SEEN_EXCEPTION) != 0) { message = "The release() call is not always reached (via exceptional flow)"; } else { message = "The release() call is not always reached"; } context.report(ISSUE, method, acquire, context.getLocation(release), message, null); } } catch (AnalyzerException e) { context.log(e, null); } }