All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.main.java.com.mebigfatguy.fbcontrib.detect.ReflectionOnObjectMethods Maven / Gradle / Ivy

Go to download

An auxiliary findbugs.sourceforge.net plugin for java bug detectors that fall outside the narrow scope of detectors to be packaged with the product itself.

There is a newer version: 7.6.8
Show newest version
/*
 * fb-contrib - Auxiliary detectors for Java programs
 * Copyright (C) 2005-2018 Dave Brosius
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.mebigfatguy.fbcontrib.detect;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;

import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;

import com.mebigfatguy.fbcontrib.utils.BugType;
import com.mebigfatguy.fbcontrib.utils.RegisterUtils;
import com.mebigfatguy.fbcontrib.utils.SignatureBuilder;
import com.mebigfatguy.fbcontrib.utils.SignatureUtils;
import com.mebigfatguy.fbcontrib.utils.TernaryPatcher;
import com.mebigfatguy.fbcontrib.utils.UnmodifiableSet;
import com.mebigfatguy.fbcontrib.utils.Values;

import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.OpcodeStack;
import edu.umd.cs.findbugs.OpcodeStack.CustomUserValue;
import edu.umd.cs.findbugs.ba.ClassContext;

/**
 * looks for method calls through reflection on methods found in java.lang.Object. As these methods are always available, there's no reason to do this.
 */
@CustomUserValue
public class ReflectionOnObjectMethods extends BytecodeScanningDetector {

    public static final String SIG_STRING_AND_CLASS_ARRAY_TO_METHOD = new SignatureBuilder()
            .withParamTypes(Values.SLASHED_JAVA_LANG_STRING, SignatureUtils.toArraySignature(Values.SLASHED_JAVA_LANG_CLASS)).withReturnType(Method.class)
            .toString();

    private static final Set objectSigs = UnmodifiableSet.create(
            // "clone()", // clone is declared protected
            "equals(Ljava/lang/Object;)", "finalize()", "getClass()", "hashCode()", "notify()", "notifyAll()", "toString()", "wait", "wait(J)", "wait(JI)");

    private final BugReporter bugReporter;
    private OpcodeStack stack;
    private Map localClassTypes;
    private Map fieldClassTypes;
    /** This object is not thread-safe, but can be reused, provided that all necessary fields are overwritten. */
    private final SignatureBuilder sigWithoutReturn = new SignatureBuilder().withoutReturnType();

    /**
     * constructs a ROOM detector given the reporter to report bugs on
     *
     * @param bugReporter
     *            the sync of bug reports
     */
    public ReflectionOnObjectMethods(BugReporter bugReporter) {
        this.bugReporter = bugReporter;
    }

    /**
     * implements the visitor to create the stack and local and field maps for Class arrays to be used for getting the reflection method
     *
     * @param classContext
     *            the context object of the currently parse class
     */
    @Override
    public void visitClassContext(ClassContext classContext) {
        try {
            stack = new OpcodeStack();
            localClassTypes = new HashMap<>();
            fieldClassTypes = new HashMap<>();
            JavaClass cls = classContext.getJavaClass();
            Method staticInit = findStaticInitializer(cls);
            if (staticInit != null) {
                setupVisitorForClass(cls);
                doVisitMethod(staticInit);
            }

            super.visitClassContext(classContext);
        } finally {
            stack = null;
            localClassTypes = null;
            fieldClassTypes = null;
        }
    }

    /**
     * implements the visitor to reset the opcode stack and clear the local variable map@
     *
     * @param obj
     *            the context object of the currently parsed code block
     */
    @Override
    public void visitCode(Code obj) {
        stack.resetForMethodEntry(this);
        localClassTypes.clear();
        super.visitCode(obj);
    }

    /**
     * implements the visitor to look for calls that invoke a method through reflection where the method is defined in java.lang.Object
     *
     * @param seen
     *            the currently parsed opcode
     */
    @Override
    public void sawOpcode(int seen) {
        Integer arraySize = null;
        String[] loadedTypes = null;

        try {
            stack.precomputation(this);

            switch (seen) {
                case ANEWARRAY: {
                    if (Values.SLASHED_JAVA_LANG_CLASS.equals(getClassConstantOperand()) && (stack.getStackDepth() >= 1)) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        arraySize = (Integer) item.getConstant();
                    }
                }
                break;

                case AASTORE: {
                    if (stack.getStackDepth() >= 3) {
                        OpcodeStack.Item arrayItem = stack.getStackItem(2);
                        String[] arrayTypes = (String[]) arrayItem.getUserValue();
                        if (arrayTypes != null) {
                            OpcodeStack.Item valueItem = stack.getStackItem(0);
                            String type = (String) valueItem.getConstant();
                            if (type != null) {
                                OpcodeStack.Item indexItem = stack.getStackItem(1);
                                Integer index = (Integer) indexItem.getConstant();
                                if (index != null) {
                                    arrayTypes[index.intValue()] = type;
                                }
                            }
                        }
                    }
                }
                break;

                case PUTFIELD:
                case PUTSTATIC: {
                    String name = getNameConstantOperand();
                    if (stack.getStackDepth() >= 1) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        String[] arrayTypes = (String[]) item.getUserValue();
                        if (arrayTypes != null) {
                            fieldClassTypes.put(name, arrayTypes);
                            return;
                        }
                    }
                    fieldClassTypes.remove(name);
                }
                break;

                case GETFIELD:
                case GETSTATIC: {
                    String name = getNameConstantOperand();
                    loadedTypes = fieldClassTypes.get(name);
                }
                break;

                case ASTORE_0:
                case ASTORE_1:
                case ASTORE_2:
                case ASTORE_3:
                case ASTORE: {
                    Integer reg = Integer.valueOf(RegisterUtils.getAStoreReg(this, seen));
                    if (stack.getStackDepth() >= 1) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        String[] arrayTypes = (String[]) item.getUserValue();
                        if (arrayTypes != null) {
                            localClassTypes.put(reg, arrayTypes);
                            return;
                        }
                    }
                    localClassTypes.remove(reg);
                }
                break;

                case ALOAD_0:
                case ALOAD_1:
                case ALOAD_2:
                case ALOAD_3:
                case ALOAD: {
                    int reg = RegisterUtils.getAStoreReg(this, seen);
                    loadedTypes = localClassTypes.get(Integer.valueOf(reg));
                }
                break;

                case INVOKEVIRTUAL: {
                    String cls = getClassConstantOperand();
                    if (Values.SLASHED_JAVA_LANG_CLASS.equals(cls)) {
                        String method = getNameConstantOperand();
                        if ("getMethod".equals(method)) {
                            String sig = getSigConstantOperand();
                            if (SIG_STRING_AND_CLASS_ARRAY_TO_METHOD.equals(sig) && (stack.getStackDepth() >= 2)) {
                                OpcodeStack.Item clsArgs = stack.getStackItem(0);
                                String[] arrayTypes = (String[]) clsArgs.getUserValue();
                                if ((arrayTypes != null) || clsArgs.isNull()) {
                                    OpcodeStack.Item methodItem = stack.getStackItem(1);
                                    String methodName = (String) methodItem.getConstant();
                                    if (methodName != null) {
                                        String reflectionSig = sigWithoutReturn.withMethodName(methodName).withParamTypes(arrayTypes).build();
                                        if (objectSigs.contains(reflectionSig)) {
                                            loadedTypes = (arrayTypes == null) ? new String[0] : arrayTypes;
                                        }
                                    }
                                }
                            }
                        }
                    } else if ("java/lang/reflect/Method".equals(cls)) {
                        String method = getNameConstantOperand();
                        if ("invoke".equals(method) && (stack.getStackDepth() >= 3)) {
                            OpcodeStack.Item methodItem = stack.getStackItem(2);
                            String[] arrayTypes = (String[]) methodItem.getUserValue();
                            if (arrayTypes != null) {
                                bugReporter.reportBug(new BugInstance(this, BugType.ROOM_REFLECTION_ON_OBJECT_METHODS.name(), NORMAL_PRIORITY).addClass(this)
                                        .addMethod(this).addSourceLine(this));
                            }
                        }
                    }
                }
                break;

                default:
                break;
            }
        } finally {
            TernaryPatcher.pre(stack, seen);
            stack.sawOpcode(this, seen);
            TernaryPatcher.post(stack, seen);

            if (stack.getStackDepth() >= 1) {
                if (arraySize != null) {
                    OpcodeStack.Item item = stack.getStackItem(0);
                    item.setUserValue(new String[arraySize.intValue()]);
                } else if (loadedTypes != null) {
                    OpcodeStack.Item item = stack.getStackItem(0);
                    item.setUserValue(loadedTypes);
                }
            }
        }
    }

    /**
     * finds the method that is the static initializer for the class
     *
     * @param cls
     *            the class to find the initializer for
     *
     * @return the Method of the static initializer or null if this class has none
     */
    @Nullable
    private static Method findStaticInitializer(JavaClass cls) {
        Method[] methods = cls.getMethods();
        for (Method m : methods) {
            if (Values.STATIC_INITIALIZER.equals(m.getName())) {
                return m;
            }
        }

        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy