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

src.main.java.com.mebigfatguy.fbcontrib.detect.ArrayIndexOutOfBounds 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.BitSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantInteger;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.Type;

import com.mebigfatguy.fbcontrib.utils.BugType;
import com.mebigfatguy.fbcontrib.utils.RegisterUtils;
import com.mebigfatguy.fbcontrib.utils.SignatureUtils;

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 usage of arrays with statically known indices where it can be determined that the index is out of bounds based on how the array was allocated. This
 * delector is obviously limited to a small subset of out of bounds exceptions that can be statically determined, and not the large family of problems that can
 * occur at runtime.
 */
@CustomUserValue
public class ArrayIndexOutOfBounds extends BytecodeScanningDetector {

    private BugReporter bugReporter;
    private OpcodeStack stack;
    private BitSet initializedRegs;
    private BitSet modifyRegs;
    private Map nullStoreToLocation;

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

    @Override
    public void visitClassContext(ClassContext classContext) {
        try {
            stack = new OpcodeStack();
            initializedRegs = new BitSet();
            modifyRegs = new BitSet();
            nullStoreToLocation = new HashMap();
            super.visitClassContext(classContext);
        } finally {
            stack = null;
            initializedRegs = null;
            modifyRegs = null;
            nullStoreToLocation = null;
        }
    }

    /**
     * overrides the visitor to collect parameter registers
     *
     * @param obj
     *            the code block of the currently parsed method
     */
    @Override
    public void visitCode(Code obj) {
        Method m = getMethod();
        stack.resetForMethodEntry(this);
        initializedRegs.clear();
        modifyRegs.clear();
        Type[] argTypes = m.getArgumentTypes();
        int arg = m.isStatic() ? 0 : 1;
        for (Type argType : argTypes) {
            String argSig = argType.getSignature();
            initializedRegs.set(arg);
            arg += SignatureUtils.getSignatureSize(argSig);
        }
        nullStoreToLocation.clear();
        super.visitCode(obj);

        for (Integer pc : nullStoreToLocation.values()) {
            bugReporter.reportBug(new BugInstance(this, BugType.AIOB_ARRAY_STORE_TO_NULL_REFERENCE.name(), HIGH_PRIORITY).addClass(this).addMethod(this)
                    .addSourceLine(this, pc.intValue()));
        }
    }

    /**
     * overrides the visitor to look for stores to arrays that can be statically determined to be outside the bounds of the initialized array
     *
     * @param seen
     *            the currently parsed opcode
     */
    @Override
    public void sawOpcode(int seen) {
        Integer size = null;
        boolean sizeSet = false;
        try {
            stack.precomputation(this);

            switch (seen) {
                case ICONST_0:
                case ICONST_1:
                case ICONST_2:
                case ICONST_3:
                case ICONST_4:
                case ICONST_5:
                    size = Integer.valueOf(seen - ICONST_0);
                    sizeSet = true;
                break;

                case ILOAD:
                case ILOAD_0:
                case ILOAD_1:
                case ILOAD_2:
                case ILOAD_3: {
                    int reg = RegisterUtils.getLoadReg(this, seen);
                    if (modifyRegs.get(reg)) {
                        modifyRegs.clear(reg);
                        sizeSet = true;
                    }
                }
                break;

                case BIPUSH:
                case SIPUSH:
                    size = Integer.valueOf(getIntConstant());
                    sizeSet = true;
                break;

                case IINC:
                    modifyRegs.set(getRegisterOperand());
                break;

                case IADD:
                case ISUB:
                case IMUL:
                case IDIV:
                case F2I:
                case D2I:
                case L2I:
                    sizeSet = true;
                break;

                case ISTORE:
                case ISTORE_0:
                case ISTORE_1:
                case ISTORE_2:
                case ISTORE_3:
                    if (stack.getStackDepth() > 0) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        if (item.getUserValue() == null) {
                            modifyRegs.set(getRegisterOperand());
                        }
                    }
                break;

                case LDC:
                    Constant c = getConstantRefOperand();
                    if (c instanceof ConstantInteger) {
                        size = Integer.valueOf(((ConstantInteger) c).getBytes());
                        sizeSet = true;
                    }
                break;

                case NEWARRAY:
                case ANEWARRAY:
                    if (stack.getStackDepth() >= 1) {
                        OpcodeStack.Item item = stack.getStackItem(0);
                        size = (Integer) item.getUserValue();
                        sizeSet = true;
                    }
                break;

                case IASTORE:
                case LASTORE:
                case FASTORE:
                case DASTORE:
                case AASTORE:
                case BASTORE:
                case CASTORE:
                case SASTORE:
                    processArrayStore();
                break;

                case IALOAD:
                case LALOAD:
                case FALOAD:
                case DALOAD:
                case AALOAD:
                case BALOAD:
                case CALOAD:
                case SALOAD:
                    processArrayLoad();
                break;

                case ASTORE_0:
                case ASTORE_1:
                case ASTORE_2:
                case ASTORE_3:
                case ASTORE:
                    if (stack.getStackDepth() > 0) {
                        OpcodeStack.Item value = stack.getStackItem(0);
                        if (!value.isNull()) {
                            initializedRegs.set(getRegisterOperand());
                        }
                    } else {
                        initializedRegs.set(getRegisterOperand());
                    }
                break;

                case IFEQ:
                case IFNE:
                case IFLT:
                case IFGE:
                case IFGT:
                case IFLE:
                case IF_ICMPEQ:
                case IF_ICMPNE:
                case IF_ICMPLT:
                case IF_ICMPGE:
                case IF_ICMPGT:
                case IF_ICMPLE:
                case IF_ACMPEQ:
                case IF_ACMPNE:
                case GOTO:
                case GOTO_W:
                    int branchTarget = getBranchTarget();
                    Iterator> it = nullStoreToLocation.entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry entry = it.next();
                        int pc = entry.getValue().intValue();
                        if ((branchTarget < pc) && initializedRegs.get(entry.getKey().intValue())) {
                            it.remove();
                        }
                    }
                break;
            }

        } finally {
            stack.sawOpcode(this, seen);
            if (sizeSet && (stack.getStackDepth() >= 1)) {
                OpcodeStack.Item item = stack.getStackItem(0);
                item.setUserValue(size);
            }
        }
    }

    private void processArrayLoad() {
        if (stack.getStackDepth() >= 2) {
            OpcodeStack.Item indexItem = stack.getStackItem(0);
            Integer index = (Integer) indexItem.getConstant();
            if (index != null) {
                OpcodeStack.Item arrayItem = stack.getStackItem(1);
                Integer sz = (Integer) arrayItem.getUserValue();
                if ((sz != null) && (index.intValue() >= sz.intValue())) {
                    bugReporter.reportBug(new BugInstance(this, BugType.AIOB_ARRAY_INDEX_OUT_OF_BOUNDS.name(), HIGH_PRIORITY).addClass(this).addMethod(this)
                            .addSourceLine(this));
                }
            }
        }
    }

    private void processArrayStore() {
        if (stack.getStackDepth() >= 3) {
            OpcodeStack.Item indexItem = stack.getStackItem(1);
            Number index = (Number) indexItem.getConstant();
            if (index != null) {
                OpcodeStack.Item arrayItem = stack.getStackItem(2);
                Integer sz = (Integer) arrayItem.getUserValue();
                if ((sz != null) && (index.intValue() >= sz.intValue())) {
                    bugReporter.reportBug(new BugInstance(this, BugType.AIOB_ARRAY_INDEX_OUT_OF_BOUNDS.name(), HIGH_PRIORITY).addClass(this).addMethod(this)
                            .addSourceLine(this));
                }

                int reg = arrayItem.getRegisterNumber();
                if ((reg >= 0) && !initializedRegs.get(reg)) {
                    nullStoreToLocation.put(Integer.valueOf(reg), Integer.valueOf(getPC()));
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy