src.main.java.com.mebigfatguy.fbcontrib.detect.ArrayIndexOutOfBounds Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fb-contrib Show documentation
Show all versions of fb-contrib Show documentation
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.
/*
* 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()));
}
}
}
}
}