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

src.main.java.com.mebigfatguy.fbcontrib.detect.MoreDumbMethods 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.

The newest version!
/*
 * fb-contrib - Auxiliary detectors for Java programs
 * Copyright (C) 2005-2019 Chris Peterson
 * Copyright (C) 2005-2019 Jean-Noel Rouvignac
 *
 * 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 org.apache.bcel.Const;
import org.apache.bcel.classfile.Code;

import com.mebigfatguy.fbcontrib.utils.FQMethod;
import com.mebigfatguy.fbcontrib.utils.OpcodeUtils;
import com.mebigfatguy.fbcontrib.utils.SignatureBuilder;
import com.mebigfatguy.fbcontrib.utils.ToString;
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.ba.ClassContext;

/**
 * looks for method calls that are unsafe or might indicate bugs.
 */
public class MoreDumbMethods extends BytecodeScanningDetector {
    private final static Map dumbMethods = new HashMap<>();

    private static final Set assertableReports = UnmodifiableSet
            .create(new ReportInfo("MDM_LOCK_ISLOCKED", LOW_PRIORITY));

    private static final String byteArrayToVoid = new SignatureBuilder().withParamTypes(SignatureBuilder.SIG_BYTE_ARRAY)
            .toString();
    private static final String intToByteArray = new SignatureBuilder().withParamTypes(Values.SIG_PRIMITIVE_INT)
            .withReturnType(SignatureBuilder.SIG_BYTE_ARRAY).toString();

    static {
        dumbMethods.put(new FQMethod("java/lang/Runtime", "exit", SignatureBuilder.SIG_INT_TO_VOID),
                new ReportInfo("MDM_RUNTIME_EXIT_OR_HALT", LOW_PRIORITY));
        dumbMethods.put(new FQMethod("java/lang/Runtime", "halt", SignatureBuilder.SIG_INT_TO_VOID),
                new ReportInfo("MDM_RUNTIME_EXIT_OR_HALT", HIGH_PRIORITY));

        dumbMethods.put(new FQMethod("java/lang/Runtime", "runFinalization", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_RUNFINALIZATION", NORMAL_PRIORITY));
        dumbMethods.put(
                new FQMethod(Values.SLASHED_JAVA_LANG_SYSTEM, "runFinalization", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_RUNFINALIZATION", NORMAL_PRIORITY));

        dumbMethods.put(new FQMethod("java/math/BigDecimal", "equals", SignatureBuilder.SIG_OBJECT_TO_BOOLEAN),
                new ReportInfo("MDM_BIGDECIMAL_EQUALS", NORMAL_PRIORITY));

        //
        // Network checks
        //
        dumbMethods.put(
                new FQMethod("java/net/InetAddress", "getLocalHost",
                        new SignatureBuilder().withReturnType("java/net/InetAddress").toString()),
                new ReportInfo("MDM_INETADDRESS_GETLOCALHOST", NORMAL_PRIORITY));

        dumbMethods.put(new FQMethod("java/net/ServerSocket", Values.CONSTRUCTOR, SignatureBuilder.SIG_INT_TO_VOID),
                new ReportInfo("MDM_PROMISCUOUS_SERVERSOCKET", NORMAL_PRIORITY));
        dumbMethods.put(
                new FQMethod("java/net/ServerSocket", Values.CONSTRUCTOR, new SignatureBuilder()
                        .withParamTypes(Values.SIG_PRIMITIVE_INT, Values.SIG_PRIMITIVE_INT).toString()),
                new ReportInfo("MDM_PROMISCUOUS_SERVERSOCKET", NORMAL_PRIORITY));
        dumbMethods.put(
                new FQMethod("javax/net/ServerSocketFactory", "createServerSocket",
                        new SignatureBuilder().withParamTypes(Values.SIG_PRIMITIVE_INT)
                                .withReturnType("java/net/ServerSocket").toString()),
                new ReportInfo("MDM_PROMISCUOUS_SERVERSOCKET", LOW_PRIORITY));
        dumbMethods.put(
                new FQMethod("javax/net/ServerSocketFactory", "createServerSocket",
                        new SignatureBuilder().withParamTypes(Values.SIG_PRIMITIVE_INT, Values.SIG_PRIMITIVE_INT)
                                .withReturnType("java/net/ServerSocket").toString()),
                new ReportInfo("MDM_PROMISCUOUS_SERVERSOCKET", LOW_PRIORITY));

        //
        // Random Number Generator checks
        //
        dumbMethods.put(new FQMethod("java/util/Random", Values.CONSTRUCTOR, SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_RANDOM_SEED", LOW_PRIORITY));

        //
        // Thread checks
        //
        dumbMethods.put(new FQMethod("java/lang/Thread", "getPriority", SignatureBuilder.SIG_VOID_TO_INT),
                new ReportInfo("MDM_THREAD_PRIORITIES", LOW_PRIORITY));
        dumbMethods.put(new FQMethod("java/lang/Thread", "setPriority", SignatureBuilder.SIG_INT_TO_VOID),
                new ReportInfo("MDM_THREAD_PRIORITIES", LOW_PRIORITY));

        dumbMethods.put(new FQMethod("java/lang/Thread", "sleep", SignatureBuilder.SIG_LONG_TO_VOID),
                new ReportInfo("MDM_THREAD_YIELD", LOW_PRIORITY));
        dumbMethods.put(new FQMethod("java/lang/Thread", "sleep", SignatureBuilder.SIG_LONG_AND_INT_TO_VOID),
                new ReportInfo("MDM_THREAD_YIELD", LOW_PRIORITY));
        dumbMethods.put(new FQMethod("java/lang/Thread", "yield", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_THREAD_YIELD", NORMAL_PRIORITY));

        dumbMethods.put(new FQMethod("java/lang/Thread", "join", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_WAIT_WITHOUT_TIMEOUT", LOW_PRIORITY));
        dumbMethods.put(new FQMethod(Values.SLASHED_JAVA_LANG_OBJECT, "wait", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_WAIT_WITHOUT_TIMEOUT", LOW_PRIORITY));
        dumbMethods.put(
                new FQMethod("java/util/concurrent/locks/Condition", "await", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_WAIT_WITHOUT_TIMEOUT", LOW_PRIORITY));
        dumbMethods.put(new FQMethod("java/util/concurrent/locks/Lock", "lock", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_WAIT_WITHOUT_TIMEOUT", LOW_PRIORITY));
        dumbMethods.put(
                new FQMethod("java/util/concurrent/locks/Lock", "lockInterruptibly", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_WAIT_WITHOUT_TIMEOUT", LOW_PRIORITY));
        dumbMethods.put(
                new FQMethod("java/util/concurrent/locks/ReentrantLock", "lock", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_WAIT_WITHOUT_TIMEOUT", LOW_PRIORITY));
        dumbMethods.put(new FQMethod("java/util/concurrent/locks/ReentrantLock", "lockInterruptibly",
                SignatureBuilder.SIG_VOID_TO_VOID), new ReportInfo("MDM_WAIT_WITHOUT_TIMEOUT", LOW_PRIORITY));

        dumbMethods.put(
                new FQMethod("java/util/concurrent/locks/Condition", "signal", SignatureBuilder.SIG_VOID_TO_VOID),
                new ReportInfo("MDM_SIGNAL_NOT_SIGNALALL", NORMAL_PRIORITY));

        dumbMethods.put(
                new FQMethod("java/util/concurrent/locks/Lock", "tryLock", SignatureBuilder.SIG_VOID_TO_BOOLEAN),
                new ReportInfo("MDM_THREAD_FAIRNESS", LOW_PRIORITY));
        dumbMethods.put(new FQMethod("java/util/concurrent/locks/ReentrantLock", "tryLock",
                SignatureBuilder.SIG_VOID_TO_BOOLEAN), new ReportInfo("MDM_THREAD_FAIRNESS", LOW_PRIORITY));

        dumbMethods.put(new FQMethod("java/util/concurrent/locks/ReentrantLock", "isHeldByCurrentThread",
                SignatureBuilder.SIG_VOID_TO_BOOLEAN), new ReportInfo("MDM_LOCK_ISLOCKED", LOW_PRIORITY));
        dumbMethods.put(new FQMethod("java/util/concurrent/locks/ReentrantLock", "isLocked",
                SignatureBuilder.SIG_VOID_TO_BOOLEAN), new ReportInfo("MDM_LOCK_ISLOCKED", LOW_PRIORITY));

        //
        // String checks
        //
        dumbMethods.put(
                new FQMethod(Values.SLASHED_JAVA_LANG_STRING, Values.CONSTRUCTOR,
                        new SignatureBuilder().withParamTypes(SignatureBuilder.SIG_BYTE_ARRAY).toString()),
                new ReportInfo("MDM_STRING_BYTES_ENCODING", NORMAL_PRIORITY));
        dumbMethods.put(
                new FQMethod(Values.SLASHED_JAVA_LANG_STRING, "getBytes",
                        new SignatureBuilder().withReturnType(SignatureBuilder.SIG_BYTE_ARRAY).toString()),
                new ReportInfo("MDM_STRING_BYTES_ENCODING", NORMAL_PRIORITY));
        dumbMethods.put(
                new FQMethod("java/util/Locale", "setDefault",
                        new SignatureBuilder().withParamTypes("java/util/Locale").toString()),
                new ReportInfo("MDM_SETDEFAULTLOCALE", NORMAL_PRIORITY));
    }

    private final BugReporter bugReporter;

    private boolean sawAssertionDisabled;
    private int assertionEnd;

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

    @Override
    public void visitClassContext(ClassContext classContext) {
        if (classContext.getJavaClass().getMajor() <= Const.MAJOR_1_5) {
            dumbMethods.put(
                    new FQMethod("java/security/SecureRandom", Values.CONSTRUCTOR, SignatureBuilder.SIG_VOID_TO_VOID),
                    new ReportInfo("MDM_SECURERANDOM", LOW_PRIORITY));
            dumbMethods.put(new FQMethod("java/security/SecureRandom", Values.CONSTRUCTOR, byteArrayToVoid),
                    new ReportInfo("MDM_SECURERANDOM", LOW_PRIORITY));
            dumbMethods.put(new FQMethod("java/security/SecureRandom", "getSeed", intToByteArray),
                    new ReportInfo("MDM_SECURERANDOM", LOW_PRIORITY));
        } else {
            dumbMethods.remove(
                    new FQMethod("java/security/SecureRandom", Values.CONSTRUCTOR, SignatureBuilder.SIG_VOID_TO_VOID));
            dumbMethods.remove(new FQMethod("java/security/SecureRandom", Values.CONSTRUCTOR, byteArrayToVoid));
            dumbMethods.remove(new FQMethod("java/security/SecureRandom", "getSeed", intToByteArray));
        }

        super.visitClassContext(classContext);
    }

    @Override
    public void visitCode(Code obj) {
        sawAssertionDisabled = false;
        assertionEnd = 0;
        super.visitCode(obj);
    }

    @Override
    public void sawOpcode(int seen) {

        if (OpcodeUtils.isStandardInvoke(seen)) {
            final ReportInfo info = dumbMethods.get(getFQMethod());
            if ((info != null) && ((assertionEnd < getPC()) || !assertableReports.contains(info))) {
                reportBug(info);
            }
        } else if (seen == Const.GETSTATIC) {
            if ("$assertionsDisabled".equals(getNameConstantOperand())) {
                sawAssertionDisabled = true;
                return;
            }
        } else if ((seen == Const.IFNE) && sawAssertionDisabled) {
            assertionEnd = getBranchTarget();
        }

        sawAssertionDisabled = false;
    }

    private FQMethod getFQMethod() {
        final String className = getClassConstantOperand();
        final String methodName = getNameConstantOperand();
        final String methodSig = getSigConstantOperand();
        return new FQMethod(className, methodName, methodSig);
    }

    private void reportBug(ReportInfo info) {
        bugReporter.reportBug(new BugInstance(this, info.getPattern(), info.getPriority()).addClass(this)
                .addMethod(this).addCalledMethod(this).addSourceLine(this));
    }

    private static class ReportInfo {
        private final String bugPattern;
        private final int bugPriority;

        ReportInfo(String pattern, int priority) {
            bugPattern = pattern;
            bugPriority = priority;
        }

        String getPattern() {
            return bugPattern;
        }

        int getPriority() {
            return bugPriority;
        }

        @Override
        public int hashCode() {
            return bugPattern.hashCode() ^ bugPriority;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof ReportInfo)) {
                return false;
            }

            ReportInfo that = (ReportInfo) o;
            return (bugPriority == that.bugPriority) && bugPattern.equals(that.bugPattern);
        }

        @Override
        public String toString() {
            return ToString.build(this);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy