edu.umd.cs.findbugs.OpcodeStack Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spotbugs Show documentation
Show all versions of spotbugs Show documentation
SpotBugs: Because it's easy!
/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2004 Dave Brosius
* Copyright (C) 2003-2006 University of Maryland
*
* 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 edu.umd.cs.findbugs;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.meta.TypeQualifier;
import org.apache.bcel.Const;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.BootstrapMethod;
import org.apache.bcel.classfile.BootstrapMethods;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.CodeException;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantDouble;
import org.apache.bcel.classfile.ConstantDynamic;
import org.apache.bcel.classfile.ConstantFloat;
import org.apache.bcel.classfile.ConstantInteger;
import org.apache.bcel.classfile.ConstantInvokeDynamic;
import org.apache.bcel.classfile.ConstantLong;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.ConstantString;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.Type;
import org.apache.commons.lang3.tuple.Pair;
import edu.umd.cs.findbugs.OpcodeStack.Item.SpecialKind;
import edu.umd.cs.findbugs.StackMapAnalyzer.JumpInfoFromStackMap;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.AnalysisFeatures;
import edu.umd.cs.findbugs.ba.ClassMember;
import edu.umd.cs.findbugs.ba.FieldSummary;
import edu.umd.cs.findbugs.ba.SignatureParser;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.classfile.IAnalysisCache;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.classfile.analysis.MethodInfo;
import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
import edu.umd.cs.findbugs.internalAnnotations.StaticConstant;
import edu.umd.cs.findbugs.util.ClassName;
import edu.umd.cs.findbugs.util.Util;
import edu.umd.cs.findbugs.util.Values;
import edu.umd.cs.findbugs.visitclass.DismantleBytecode;
import edu.umd.cs.findbugs.visitclass.LVTHelper;
import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
/**
* tracks the types and numbers of objects that are currently on the operand
* stack throughout the execution of method. To use, a detector should
* instantiate one for each method, and call
*
* stack.sawOpcode(this,seen);
*
* at the bottom of their sawOpcode method. at any point you can then inspect
* the stack and see what the types of objects are on the stack, including
* constant values if they were pushed. The types described are of course, only
* the static types. There are some outstanding opcodes that have yet to be
* implemented, I couldn't find any code that actually generated these, so i
* didn't put them in because I couldn't test them:
*
* - dup2_x2
* - jsr_w
* - wide
*
*/
public class OpcodeStack {
/** You can put this annotation on a OpcodeStack detector
* to indicate that it uses {@link OpcodeStack.Item#userValue},
* and thus should not reuse generic OpcodeStack information
* from an iterative evaluation of the opcode stack. Such detectors
* will not use iterative opcode stack evaluation.
*
* This is primarily for detectors that need to be backwards compatible with
* versions of FindBugs that do not support {@link edu.umd.cs.findbugs.bcel.OpcodeStackDetector.WithCustomJumpInfo }}
*/
@Documented
@Target({ ElementType.TYPE, ElementType.PACKAGE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomUserValue {
}
private static final String JAVA_UTIL_ARRAYS_ARRAY_LIST = "Ljava/util/Arrays$ArrayList;";
private static final boolean DEBUG = SystemProperties.getBoolean("ocstack.debug");
private static final boolean DEBUG2 = DEBUG;
private static final Map, String> IMMUTABLE_RETURNER_MAP = new HashMap<>();
static {
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "emptyList"), "Ljava/util/Collections$EmptyList;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "emptyMap"), "Ljava/util/Collections$EmptyMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "emptyNavigableMap"), "Ljava/util/Collections$EmptyNavigableMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "emptySortedMap"), "Ljava/util/Collections$EmptyNavigableMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "emptySet"), "Ljava/util/Collections$EmptySet;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "emptyNavigableSet"), "Ljava/util/Collections$EmptyNavigableSet;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "emptySortedSet"), "Ljava/util/Collections$EmptyNavigableSet;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "singletonList"), "Ljava/util/Collections$SingletonList;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "singletonMap"), "Ljava/util/Collections$SingletonMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "singleton"), "Ljava/util/Collections$SingletonSet;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "unmodifiableList"), "Ljava/util/Collections$UnmodifiableList;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "unmodifiableMap"), "Ljava/util/Collections$UnmodifiableMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "unmodifiableNavigableMap"), "Ljava/util/Collections$UnmodifiableNavigableMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "unmodifiableSortedMap"), "Ljava/util/Collections$UnmodifiableSortedMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "unmodifiableSet"), "Ljava/util/Collections$UnmodifiableSet;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "unmodifiableNavigableSet"), "Ljava/util/Collections$UnmodifiableNavigableSet;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Collections", "unmodifiableSortedSet"), "Ljava/util/Collections$UnmodifiableSortedSet;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/List", "of"), "Ljava/util/ImmutableCollections$AbstractImmutableList;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/List", "copyOf"), "Ljava/util/ImmutableCollections$AbstractImmutableList;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Map", "of"), "Ljava/util/ImmutableCollections$AbstractImmutableMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Map", "copyOf"), "Ljava/util/ImmutableCollections$AbstractImmutableMap;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Set", "of"), "Ljava/util/ImmutableCollections$AbstractImmutableSet;");
IMMUTABLE_RETURNER_MAP.put(Pair.of("java/util/Set", "copyOf"), "Ljava/util/ImmutableCollections$AbstractImmutableSet;");
}
@StaticConstant
static final HashMap boxedTypes = new HashMap<>();
private List- stack;
private List
- lvValues;
private final List
lastUpdate;
private boolean top;
static class HttpParameterInjection {
HttpParameterInjection(String parameterName, int pc) {
this.parameterName = parameterName;
this.pc = pc;
}
String parameterName;
int pc;
}
private boolean seenTransferOfControl;
private final boolean useIterativeAnalysis = AnalysisContext.currentAnalysisContext().getBoolProperty(
AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS);
boolean encountedTop;
boolean backwardsBranch;
BitSet exceptionHandlers = new BitSet();
private boolean jumpInfoChangedByBackwardsBranch;
private boolean jumpInfoChangedByNewTarget;
private Map> jumpEntries = new HashMap<>();
private Map> jumpStackEntries = new HashMap<>();
private BitSet jumpEntryLocations = new BitSet();
int convertJumpToOneZeroState = 0;
int convertJumpToZeroOneState = 0;
int registerTestedFoundToBeNonnegative = -1;
int zeroOneComing = -1;
boolean oneMeansNull;
boolean needToMerge = true;
private boolean reachOnlyByBranch;
public static class Item {
/**
* A type qualifier to mark {@code int} value as SpecialKind type.
*/
@Documented
@TypeQualifier(applicableTo = Integer.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpecialKind {
}
static @SpecialKind int asSpecialKind(int kind) {
return kind;
}
public static final @SpecialKind int NOT_SPECIAL = 0;
public static final @SpecialKind int SIGNED_BYTE = 1;
public static final @SpecialKind int RANDOM_INT = 2;
public static final @SpecialKind int LOW_8_BITS_CLEAR = 3;
public static final @SpecialKind int HASHCODE_INT = 4;
public static final @SpecialKind int INTEGER_SUM = 5;
public static final @SpecialKind int AVERAGE_COMPUTED_USING_DIVISION = 6;
public static final @SpecialKind int FLOAT_MATH = 7;
public static final @SpecialKind int RANDOM_INT_REMAINDER = 8;
public static final @SpecialKind int HASHCODE_INT_REMAINDER = 9;
public static final @SpecialKind int FILE_SEPARATOR_STRING = 10;
public static final @SpecialKind int MATH_ABS = 11;
public static final @SpecialKind int MATH_ABS_OF_RANDOM = 12;
public static final @SpecialKind int MATH_ABS_OF_HASHCODE = 13;
public static final @SpecialKind int NON_NEGATIVE = 14;
public static final @SpecialKind int NASTY_FLOAT_MATH = 15;
public static final @SpecialKind int FILE_OPENED_IN_APPEND_MODE = 16;
public static final @SpecialKind int SERVLET_REQUEST_TAINTED = 17;
public static final @SpecialKind int NEWLY_ALLOCATED = 18;
public static final @SpecialKind int ZERO_MEANS_NULL = 19;
public static final @SpecialKind int NONZERO_MEANS_NULL = 20;
public static final @SpecialKind int RESULT_OF_I2L = 21;
public static final @SpecialKind int RESULT_OF_L2I = 22;
public static final @SpecialKind int SERVLET_OUTPUT = 23;
public static final @SpecialKind int TYPE_ONLY = 24;
@edu.umd.cs.findbugs.internalAnnotations.StaticConstant
private static final ConcurrentMap specialKindToName = new ConcurrentHashMap<>();
private static AtomicInteger nextSpecialKind = new AtomicInteger(TYPE_ONLY + 1);
private static final int IS_INITIAL_PARAMETER_FLAG = 1;
private static final int COULD_BE_ZERO_FLAG = 2;
private static final int IS_NULL_FLAG = 4;
public static final Object UNKNOWN = null;
private @SpecialKind int specialKind = NOT_SPECIAL;
private String signature;
private Object constValue = UNKNOWN;
private @CheckForNull ClassMember source;
private int pc = -1;
private int flags;
private int registerNumber = -1;
@Nullable
private Object userValue;
private HttpParameterInjection injection;
private int fieldLoadedFromRegister = -1;
public void makeCrossMethod() {
pc = -1;
registerNumber = -1;
fieldLoadedFromRegister = -1;
}
public int getSize() {
if ("J".equals(signature) || "D".equals(signature)) {
return 2;
}
return 1;
}
public int getPC() {
return pc;
}
public void setPC(int pc) {
this.pc = pc;
}
public boolean isWide() {
return getSize() == 2;
}
@Override
public int hashCode() {
int r = 42 + specialKind;
if (signature != null) {
r += signature.hashCode();
}
r *= 31;
if (constValue != null) {
r += constValue.hashCode();
}
r *= 31;
if (source != null) {
r += source.hashCode();
}
r *= 31;
r += flags;
r *= 31;
r += registerNumber;
return r;
}
public boolean usesTwoSlots() {
return getSize() == 2;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Item)) {
return false;
}
Item that = (Item) o;
return Objects.equals(this.signature, that.signature) && Objects.equals(this.constValue, that.constValue)
&& Objects.equals(this.source, that.source) && Objects.equals(this.userValue, that.userValue)
&& Objects.equals(this.injection, that.injection) && this.specialKind == that.specialKind
&& this.registerNumber == that.registerNumber && this.flags == that.flags
&& this.fieldLoadedFromRegister == that.fieldLoadedFromRegister;
}
public boolean sameValue(OpcodeStack.Item that) {
return this.equals(that) && (this.registerNumber != -1 && this.registerNumber == that.registerNumber
|| this.fieldLoadedFromRegister != -1);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder("< ");
buf.append(signature);
switch (specialKind) {
case SIGNED_BYTE:
buf.append(", signed_byte");
break;
case RANDOM_INT:
buf.append(", random_int");
break;
case LOW_8_BITS_CLEAR:
buf.append(", low8clear");
break;
case HASHCODE_INT:
buf.append(", hashcode_int");
break;
case INTEGER_SUM:
buf.append(", int_sum");
break;
case AVERAGE_COMPUTED_USING_DIVISION:
buf.append(", averageComputingUsingDivision");
break;
case FLOAT_MATH:
buf.append(", floatMath");
break;
case NASTY_FLOAT_MATH:
buf.append(", nastyFloatMath");
break;
case HASHCODE_INT_REMAINDER:
buf.append(", hashcode_int_rem");
break;
case RANDOM_INT_REMAINDER:
buf.append(", random_int_rem");
break;
case MATH_ABS_OF_RANDOM:
buf.append(", abs_of_random");
break;
case MATH_ABS_OF_HASHCODE:
buf.append(", abs_of_hashcode");
break;
case FILE_SEPARATOR_STRING:
buf.append(", file_separator_string");
break;
case MATH_ABS:
buf.append(", Math.abs");
break;
case NON_NEGATIVE:
buf.append(", non_negative");
break;
case FILE_OPENED_IN_APPEND_MODE:
buf.append(", file opened in append mode");
break;
case SERVLET_REQUEST_TAINTED:
buf.append(", servlet request tainted");
break;
case NEWLY_ALLOCATED:
buf.append(", new");
break;
case ZERO_MEANS_NULL:
buf.append(", zero means null");
break;
case NONZERO_MEANS_NULL:
buf.append(", nonzero means null");
break;
case SERVLET_OUTPUT:
buf.append(", servlet_output");
break;
case TYPE_ONLY:
buf.append(", type_only");
break;
case NOT_SPECIAL:
break;
default:
buf.append(", #" + specialKind);
buf.append("(" + specialKindToName.get(specialKind) + ")");
break;
}
if (constValue != UNKNOWN) {
if (constValue instanceof String) {
buf.append(", \"");
buf.append(constValue);
buf.append("\"");
} else {
buf.append(", ");
buf.append(constValue);
}
}
if (source instanceof XField) {
buf.append(", ");
if (fieldLoadedFromRegister != -1 && fieldLoadedFromRegister != Integer.MAX_VALUE) {
buf.append(fieldLoadedFromRegister).append(':');
}
buf.append(source);
}
if (source instanceof XMethod) {
buf.append(", return value from ");
buf.append(source);
}
if (isInitialParameter()) {
buf.append(", IP");
}
if (isNull()) {
buf.append(", isNull");
}
if (registerNumber != -1) {
buf.append(", r");
buf.append(registerNumber);
}
if (isCouldBeZero() && !isZero()) {
buf.append(", cbz");
}
if (userValue != null) {
buf.append(", uv: ");
buf.append(userValue.toString());
}
buf.append(" >");
return buf.toString();
}
public static Item merge(Item i1, Item i2) {
if (i1 == null) {
return i2;
}
if (i2 == null) {
return i1;
}
if (i1.equals(i2)) {
return i1;
}
if (i1.getSpecialKind() == TYPE_ONLY && i2.getSpecialKind() != TYPE_ONLY) {
return i2;
} else if (i2.getSpecialKind() == TYPE_ONLY && i1.getSpecialKind() != TYPE_ONLY) {
return i1;
}
Item m = new Item();
m.flags = i1.flags & i2.flags;
m.setCouldBeZero(i1.isCouldBeZero() || i2.isCouldBeZero());
if (i1.pc == i2.pc) {
m.pc = i1.pc;
}
if (Objects.equals(i1.signature, i2.signature)) {
m.signature = i1.signature;
} else if (i1.isNull()) {
m.signature = i2.signature;
} else if (i2.isNull()) {
m.signature = i1.signature;
}
if (Objects.equals(i1.constValue, i2.constValue)) {
m.constValue = i1.constValue;
}
if (Objects.equals(i1.source, i2.source)) {
m.source = i1.source;
} else if ("".equals(i1.constValue)) {
m.source = i2.source;
} else if ("".equals(i2.constValue)) {
m.source = i1.source;
}
if (Objects.equals(i1.userValue, i2.userValue)) {
m.userValue = i1.userValue;
}
if (i1.registerNumber == i2.registerNumber) {
m.registerNumber = i1.registerNumber;
}
if (i1.fieldLoadedFromRegister == i2.fieldLoadedFromRegister) {
m.fieldLoadedFromRegister = i1.fieldLoadedFromRegister;
}
if (i1.specialKind == SERVLET_REQUEST_TAINTED) {
m.specialKind = SERVLET_REQUEST_TAINTED;
m.injection = i1.injection;
} else if (i2.specialKind == SERVLET_REQUEST_TAINTED) {
m.specialKind = SERVLET_REQUEST_TAINTED;
m.injection = i2.injection;
} else if (i1.specialKind == i2.specialKind) {
m.specialKind = i1.specialKind;
} else if (i1.specialKind == NASTY_FLOAT_MATH || i2.specialKind == NASTY_FLOAT_MATH) {
m.specialKind = NASTY_FLOAT_MATH;
} else if (i1.specialKind == FLOAT_MATH || i2.specialKind == FLOAT_MATH) {
m.specialKind = FLOAT_MATH;
}
if (DEBUG) {
System.out.println("Merge " + i1 + " and " + i2 + " gives " + m);
}
return m;
}
public Item(String signature, int constValue) {
this(signature, Integer.valueOf(constValue));
}
public static Item initialArgument(String signature, int reg) {
Item it = new Item(signature);
it.setInitialParameter(true);
it.registerNumber = reg;
return it;
}
public Item(String signature) {
this(signature, UNKNOWN);
}
public static Item typeOnly(String signature) {
Item it = new Item(signature, UNKNOWN);
it.setSpecialKind(TYPE_ONLY);
return it;
}
public Item(Item it) {
this.signature = it.signature;
this.constValue = it.constValue;
this.source = it.source;
this.fieldLoadedFromRegister = it.fieldLoadedFromRegister;
this.registerNumber = it.registerNumber;
this.userValue = it.userValue;
this.injection = it.injection;
this.flags = it.flags;
this.specialKind = it.specialKind;
this.pc = it.pc;
}
public Item(Item it, String signature) {
this(it);
this.signature = signature;
if (constValue instanceof Number) {
Number constantNumericValue = (Number) constValue;
switch (signature) {
case "Z":
case "Ljava/lang/Boolean;":
this.constValue = constantNumericValue.intValue() != 0;
break;
case "B":
case "Ljava/lang/Byte;":
this.constValue = constantNumericValue.byteValue();
break;
case "S":
case "Ljava/lang/Short;":
this.constValue = constantNumericValue.shortValue();
break;
case "C":
case "Ljava/lang/Character;":
this.constValue = (char) constantNumericValue.intValue();
break;
case "I":
case "Ljava/lang/Integer;":
this.constValue = constantNumericValue.intValue();
break;
case "J":
case "Ljava/lang/Long;":
this.constValue = constantNumericValue.longValue();
break;
case "D":
case "Ljava/lang/Double;":
this.constValue = constantNumericValue.doubleValue();
break;
case "F":
case "Ljava/lang/Float;":
this.constValue = constantNumericValue.floatValue();
break;
default:
break;
}
}
char s = signature.charAt(0);
if (s != 'L' && s != '[') {
this.source = null;
}
setSpecialKindFromSignature();
}
public Item(Item it, int reg) {
this(it);
this.registerNumber = reg;
}
public Item(String signature, FieldAnnotation f) {
this.signature = signature;
setSpecialKindFromSignature();
if (f != null) {
source = XFactory.createXField(f);
}
fieldLoadedFromRegister = -1;
}
public Item(String signature, FieldAnnotation f, int fieldLoadedFromRegister) {
this.signature = signature;
if (f != null) {
source = XFactory.createXField(f);
}
this.fieldLoadedFromRegister = fieldLoadedFromRegister;
}
/**
* If this value was loaded from an instance field,
* give the register number containing the object that the field was loaded from.
* If Integer.MAX value, the value was loaded from a static field
* If -1, we don't know or don't have the register containing the object that
* the field was loaded from.
*/
public int getFieldLoadedFromRegister() {
return fieldLoadedFromRegister;
}
public void setLoadedFromField(XField f, int fieldLoadedFromRegister) {
source = f;
this.fieldLoadedFromRegister = fieldLoadedFromRegister;
this.registerNumber = -1;
}
public @CheckForNull String getHttpParameterName() {
if (!isServletParameterTainted()) {
throw new IllegalStateException();
}
if (injection == null) {
return null;
}
return injection.parameterName;
}
public int getInjectionPC() {
if (!isServletParameterTainted()) {
throw new IllegalStateException();
}
if (injection == null) {
return -1;
}
return injection.pc;
}
public Item(String signature, Object constantValue) {
this.signature = signature;
setSpecialKindFromSignature();
constValue = constantValue;
if (constantValue instanceof Integer) {
int value = ((Integer) constantValue).intValue();
if (value != 0 && (value & 0xff) == 0) {
specialKind = LOW_8_BITS_CLEAR;
}
if (value == 0) {
setCouldBeZero(true);
}
} else if (constantValue instanceof Long) {
long value = ((Long) constantValue).longValue();
if (value != 0 && (value & 0xff) == 0) {
specialKind = LOW_8_BITS_CLEAR;
}
if (value == 0) {
setCouldBeZero(true);
}
}
}
private void setSpecialKindFromSignature() {
/*
if (false && specialKind != NOT_SPECIAL) {
return;
}
*/
if ("B".equals(signature)) {
specialKind = SIGNED_BYTE;
} else if ("C".equals(signature)) {
specialKind = NON_NEGATIVE;
}
}
public void setCouldBeNegative() {
if (specialKind == NON_NEGATIVE) {
specialKind = NOT_SPECIAL;
}
}
public Item() {
signature = "Ljava/lang/Object;";
constValue = null;
setNull(true);
}
public static Item nullItem(String signature) {
Item item = new Item(signature);
item.constValue = null;
item.setNull(true);
return item;
}
/** Returns null for primitive and arrays */
public @CheckForNull JavaClass getJavaClass() throws ClassNotFoundException {
String baseSig;
if (isPrimitive() || isArray()) {
return null;
}
baseSig = signature;
if (baseSig.length() == 0) {
return null;
}
baseSig = baseSig.substring(1, baseSig.length() - 1);
baseSig = ClassName.toDottedClassName(baseSig);
return Repository.lookupClass(baseSig);
}
public boolean isArray() {
return signature.startsWith("[");
}
@Deprecated
public String getElementSignature() {
if (!isArray()) {
return signature;
} else {
int pos = 0;
int len = signature.length();
while (pos < len) {
if (signature.charAt(pos) != '[') {
break;
}
pos++;
}
return signature.substring(pos);
}
}
public boolean isNonNegative() {
if (specialKind == NON_NEGATIVE) {
return true;
}
if (constValue instanceof Number) {
double value = ((Number) constValue).doubleValue();
return value >= 0;
}
return false;
}
public boolean isPrimitive() {
return !signature.startsWith("L") && !signature.startsWith("[");
}
public int getRegisterNumber() {
return registerNumber;
}
public String getSignature() {
return signature;
}
/**
* Returns a constant value for this Item, if known. NOTE: if the value
* is a constant Class object, the constant value returned is the name
* of the class.
* if the value is an array of known length, the constant value returned is its length (Integer)
*/
public Object getConstant() {
return constValue;
}
/** Use getXField instead */
@Deprecated
public FieldAnnotation getFieldAnnotation() {
return FieldAnnotation.fromXField(getXField());
}
public XField getXField() {
if (source instanceof XField) {
return (XField) source;
}
return null;
}
/**
* @param specialKind
* The specialKind to set.
*/
public void setSpecialKind(@SpecialKind int specialKind) {
this.specialKind = specialKind;
}
public Item cloneAndSetSpecialKind(@SpecialKind int specialKind) {
Item that = new Item(this);
that.specialKind = specialKind;
return that;
}
/**
* @return Returns the specialKind.
*/
public @SpecialKind int getSpecialKind() {
return specialKind;
}
/**
* @return Returns the specialKind.
*/
public boolean isBooleanNullnessValue() {
return specialKind == ZERO_MEANS_NULL || specialKind == NONZERO_MEANS_NULL;
}
/**
* attaches a detector specified value to this item
* to use this method, detector should be annotated with {@code CustomUserValue}.
*
* @param value
* the custom value to set
* @see OpcodeStack.CustomUserValue
*/
public void setUserValue(@Nullable Object value) {
userValue = value;
}
/**
*
* @return if this value is the return value of a method, give the
* method invoked
*/
public @CheckForNull XMethod getReturnValueOf() {
if (source instanceof XMethod) {
return (XMethod) source;
}
return null;
}
public boolean couldBeZero() {
return isCouldBeZero();
}
public boolean mustBeZero() {
Object value = getConstant();
return value instanceof Number && ((Number) value).intValue() == 0;
}
/**
* gets the detector specified value for this item
*
* @return the custom value
*/
@Nullable
public Object getUserValue() {
return userValue;
}
public boolean isServletParameterTainted() {
return getSpecialKind() == Item.SERVLET_REQUEST_TAINTED;
}
public void setServletParameterTainted() {
setSpecialKind(Item.SERVLET_REQUEST_TAINTED);
}
public void setIsServletWriter() {
setSpecialKind(Item.SERVLET_OUTPUT);
}
public boolean isServletWriter() {
if (getSpecialKind() == Item.SERVLET_OUTPUT) {
return true;
}
if ("Ljavax/servlet/ServletOutputStream;".equals(getSignature())) {
return true;
}
XMethod writingToSource = getReturnValueOf();
return writingToSource != null && ("javax.servlet.http.HttpServletResponse".equals(writingToSource.getClassName())
|| "jakarta.servlet.http.HttpServletResponse".equals(writingToSource.getClassName()))
&& ("getWriter".equals(writingToSource.getName()) || "getOutputStream".equals(writingToSource.getName()));
}
public boolean valueCouldBeNegative() {
return !isNonNegative()
&& (getSpecialKind() == Item.RANDOM_INT || getSpecialKind() == Item.SIGNED_BYTE
|| getSpecialKind() == Item.HASHCODE_INT || getSpecialKind() == Item.RANDOM_INT_REMAINDER
|| getSpecialKind() == Item.HASHCODE_INT_REMAINDER || getSpecialKind() == Item.MATH_ABS_OF_RANDOM
|| getSpecialKind() == Item.MATH_ABS_OF_HASHCODE);
}
public @SpecialKind int getSpecialKindForAbs() {
switch (getSpecialKind()) {
case Item.HASHCODE_INT:
return Item.MATH_ABS_OF_HASHCODE;
case Item.RANDOM_INT:
return Item.MATH_ABS_OF_RANDOM;
default:
return Item.MATH_ABS;
}
}
public @SpecialKind int getSpecialKindForRemainder() {
switch (getSpecialKind()) {
case Item.HASHCODE_INT:
return Item.HASHCODE_INT_REMAINDER;
case Item.RANDOM_INT:
return Item.RANDOM_INT_REMAINDER;
default:
return Item.NOT_SPECIAL;
}
}
/** Value could be Integer.MIN_VALUE */
public boolean checkForIntegerMinValue() {
return !isNonNegative() && (getSpecialKind() == Item.RANDOM_INT || getSpecialKind() == Item.HASHCODE_INT);
}
/** The result of applying Math.abs to a checkForIntegerMinValue() value */
public boolean mightRarelyBeNegative() {
return !isNonNegative()
&& (getSpecialKind() == Item.MATH_ABS_OF_RANDOM || getSpecialKind() == Item.MATH_ABS_OF_HASHCODE);
}
/**
* @param isInitialParameter
* The isInitialParameter to set.
*/
private void setInitialParameter(boolean isInitialParameter) {
setFlag(isInitialParameter, IS_INITIAL_PARAMETER_FLAG);
}
/**
* @return Returns the isInitialParameter.
*/
public boolean isInitialParameter() {
return (flags & IS_INITIAL_PARAMETER_FLAG) != 0;
}
/**
* @param couldBeZero
* The couldBeZero to set.
*/
private void setCouldBeZero(boolean couldBeZero) {
setFlag(couldBeZero, COULD_BE_ZERO_FLAG);
}
/**
* @return Returns the couldBeZero.
*/
private boolean isCouldBeZero() {
return (flags & COULD_BE_ZERO_FLAG) != 0
|| isZero();
}
private boolean isZero() {
return constValue != null && constValue.equals(0);
}
/**
* @param isNull
* The isNull to set.
*/
private void setNull(boolean isNull) {
setFlag(isNull, IS_NULL_FLAG);
}
private void setFlag(boolean value, int flagBit) {
if (value) {
flags |= flagBit;
} else {
flags &= ~flagBit;
}
}
/**
* @return Returns the isNull.
*/
public boolean isNull() {
return (flags & IS_NULL_FLAG) != 0;
}
/**
*
*/
public void clearNewlyAllocated() {
if (specialKind == NEWLY_ALLOCATED) {
if (signature.startsWith("Ljava/lang/StringB")) {
constValue = null;
}
specialKind = NOT_SPECIAL;
}
}
public boolean isNewlyAllocated() {
return specialKind == NEWLY_ALLOCATED;
}
public boolean hasConstantValue(int value) {
if (constValue instanceof Number) {
return ((Number) constValue).intValue() == value;
}
return false;
}
public boolean hasConstantValue(long value) {
if (constValue instanceof Number) {
return ((Number) constValue).longValue() == value;
}
return false;
}
/**
* Define a new special kind and name it as specified.
* @param name Name of new special kind
* @return int value to represent new special kind
* @since 3.1.0
*/
public static @SpecialKind int defineSpecialKind(String name) {
@SpecialKind
int specialKind = nextSpecialKind.getAndIncrement();
specialKindToName.put(Integer.valueOf(specialKind), name);
return specialKind;
}
/**
* @param specialKind special kind to get name
* @return just a name of specified @{link SpecialKind}, or empty {@link Optional}.
* @since 3.1.0
*/
public static Optional getSpecialKindName(@SpecialKind int specialKind) {
return Optional.ofNullable(specialKindToName.get(Integer.valueOf(specialKind)));
}
}
@Override
public String toString() {
if (isTop()) {
return "TOP";
}
return stack.toString() + "::" + lvValues.toString();
}
public OpcodeStack() {
stack = new ArrayList<>();
lvValues = new ArrayList<>();
lastUpdate = new ArrayList<>();
}
public boolean hasIncomingBranches(int pc) {
return jumpEntryLocations.get(pc) && jumpEntries.get(pc) != null;
}
public static String getExceptionSig(DismantleBytecode dbc, CodeException e) {
if (e.getCatchType() == 0) {
return "Ljava/lang/Throwable;";
}
Constant c = dbc.getConstantPool().getConstant(e.getCatchType());
if (c instanceof ConstantClass) {
return "L" + ((ConstantClass) c).getBytes(dbc.getConstantPool()) + ";";
}
return "Ljava/lang/Throwable;";
}
public void mergeJumps(DismantleBytecode dbc) {
if (!needToMerge) {
return;
}
needToMerge = false;
if (dbc.getPC() == zeroOneComing) {
pop();
top = false;
OpcodeStack.Item item = new Item("I");
if (oneMeansNull) {
item.setSpecialKind(Item.NONZERO_MEANS_NULL);
} else {
item.setSpecialKind(Item.ZERO_MEANS_NULL);
}
item.setPC(dbc.getPC() - 8);
item.setCouldBeZero(true);
push(item);
zeroOneComing = -1;
if (DEBUG) {
System.out.println("Updated to " + this);
}
return;
}
boolean stackUpdated = false;
if (!isTop() && (convertJumpToOneZeroState == 3 || convertJumpToZeroOneState == 3)) {
pop();
Item topItem = new Item("I");
topItem.setCouldBeZero(true);
push(topItem);
convertJumpToOneZeroState = convertJumpToZeroOneState = 0;
stackUpdated = true;
}
List- jumpEntry = null;
if (jumpEntryLocations.get(dbc.getPC())) {
jumpEntry = jumpEntries.get(Integer.valueOf(dbc.getPC()));
}
boolean wasReachOnlyByBranch = isReachOnlyByBranch();
if (jumpEntry != null) {
setReachOnlyByBranch(false);
List
- jumpStackEntry = jumpStackEntries.get(Integer.valueOf(dbc.getPC()));
if (DEBUG2) {
if (wasReachOnlyByBranch) {
System.out.println("Reached by branch at " + dbc.getPC() + " with " + jumpEntry);
if (jumpStackEntry != null) {
System.out.println(" and stack " + jumpStackEntry);
}
} else if (!jumpEntry.equals(lvValues)
|| jumpStackEntry != null && !jumpStackEntry.equals(stack)) {
System.out.println("Merging at " + dbc.getPC() + " with " + jumpEntry);
if (jumpStackEntry != null) {
System.out.println(" and stack " + jumpStackEntry);
}
}
}
if (isTop()) {
lvValues = new ArrayList<>(jumpEntry);
if (jumpStackEntry != null) {
stack = new ArrayList<>(jumpStackEntry);
} else {
stack.clear();
}
setTop(false);
return;
}
if (isReachOnlyByBranch()) {
setTop(false);
lvValues = new ArrayList<>(jumpEntry);
if (!stackUpdated) {
if (jumpStackEntry != null) {
stack = new ArrayList<>(jumpStackEntry);
} else {
stack.clear();
}
}
} else {
setTop(false);
mergeLists(lvValues, jumpEntry, false);
if (!stackUpdated && jumpStackEntry != null) {
mergeLists(stack, jumpStackEntry, false);
}
}
if (DEBUG) {
System.out.println(" merged lvValues " + lvValues);
}
} else if (isReachOnlyByBranch() && !stackUpdated) {
stack.clear();
Item item = null;
for (CodeException e : dbc.getCode().getExceptionTable()) {
if (e.getHandlerPC() == dbc.getPC()) {
Item newItem = new Item(getExceptionSig(dbc, e));
if (item == null) {
item = newItem;
} else {
item = Item.merge(item, newItem);
}
}
}
if (item != null) {
push(item);
setReachOnlyByBranch(false);
setTop(false);
} else {
setTop(true);
}
}
}
private void setLastUpdate(int reg, int pc) {
while (lastUpdate.size() <= reg) {
lastUpdate.add(Integer.valueOf(0));
}
lastUpdate.set(reg, Integer.valueOf(pc));
}
public int getLastUpdate(int reg) {
if (lastUpdate.size() <= reg) {
return 0;
}
return lastUpdate.get(reg).intValue();
}
public int getNumLastUpdates() {
return lastUpdate.size();
}
public void sawOpcode(DismantleBytecode dbc, int seen) {
int register;
String signature;
Item it, it2;
Constant cons;
// System.out.printf("%3d %12s%s%n", dbc.getPC(), Const.getOpcodeName(seen),
// this);
if (dbc.isRegisterStore()) {
setLastUpdate(dbc.getRegisterOperand(), dbc.getPC());
}
precomputation(dbc);
needToMerge = true;
try {
if (isTop()) {
encountedTop = true;
return;
}
if (seen == Const.GOTO) {
int nextPC = dbc.getPC() + 3;
if (nextPC <= dbc.getMaxPC()) {
int prevOpcode1 = dbc.getPrevOpcode(1);
int prevOpcode2 = dbc.getPrevOpcode(2);
try {
int nextOpcode = dbc.getCodeByte(dbc.getPC() + 3);
if ((prevOpcode1 == Const.ICONST_0 || prevOpcode1 == Const.ICONST_1)
&& (prevOpcode2 == Const.IFNULL || prevOpcode2 == Const.IFNONNULL)
&& (nextOpcode == Const.ICONST_0 || nextOpcode == Const.ICONST_1) && prevOpcode1 != nextOpcode) {
oneMeansNull = prevOpcode1 == Const.ICONST_0;
if (prevOpcode2 != Const.IFNULL) {
oneMeansNull = !oneMeansNull;
}
zeroOneComing = nextPC + 1;
convertJumpToOneZeroState = convertJumpToZeroOneState = 0;
}
} catch (ArrayIndexOutOfBoundsException e) {
throw e; // throw new
// ArrayIndexOutOfBoundsException(nextPC + " "
// + dbc.getMaxPC());
}
}
}
switch (seen) {
case Const.ICONST_1:
convertJumpToOneZeroState = 1;
break;
case Const.GOTO:
if (convertJumpToOneZeroState == 1 && dbc.getBranchOffset() == 4) {
convertJumpToOneZeroState = 2;
} else {
convertJumpToOneZeroState = 0;
}
break;
case Const.ICONST_0:
if (convertJumpToOneZeroState == 2) {
convertJumpToOneZeroState = 3;
} else {
convertJumpToOneZeroState = 0;
}
break;
default:
convertJumpToOneZeroState = 0;
}
switch (seen) {
case Const.ICONST_0:
convertJumpToZeroOneState = 1;
break;
case Const.GOTO:
if (convertJumpToZeroOneState == 1 && dbc.getBranchOffset() == 4) {
convertJumpToZeroOneState = 2;
} else {
convertJumpToZeroOneState = 0;
}
break;
case Const.ICONST_1:
if (convertJumpToZeroOneState == 2) {
convertJumpToZeroOneState = 3;
} else {
convertJumpToZeroOneState = 0;
}
break;
default:
convertJumpToZeroOneState = 0;
}
switch (seen) {
case Const.ALOAD:
pushByLocalObjectLoad(dbc, dbc.getRegisterOperand());
break;
case Const.ALOAD_0:
case Const.ALOAD_1:
case Const.ALOAD_2:
case Const.ALOAD_3:
pushByLocalObjectLoad(dbc, seen - Const.ALOAD_0);
break;
case Const.DLOAD:
pushByLocalLoad("D", dbc.getRegisterOperand());
break;
case Const.DLOAD_0:
case Const.DLOAD_1:
case Const.DLOAD_2:
case Const.DLOAD_3:
pushByLocalLoad("D", seen - Const.DLOAD_0);
break;
case Const.FLOAD:
pushByLocalLoad("F", dbc.getRegisterOperand());
break;
case Const.FLOAD_0:
case Const.FLOAD_1:
case Const.FLOAD_2:
case Const.FLOAD_3:
pushByLocalLoad("F", seen - Const.FLOAD_0);
break;
case Const.ILOAD:
pushByLocalLoad("I", dbc.getRegisterOperand());
break;
case Const.ILOAD_0:
case Const.ILOAD_1:
case Const.ILOAD_2:
case Const.ILOAD_3:
pushByLocalLoad("I", seen - Const.ILOAD_0);
break;
case Const.LLOAD:
pushByLocalLoad("J", dbc.getRegisterOperand());
break;
case Const.LLOAD_0:
case Const.LLOAD_1:
case Const.LLOAD_2:
case Const.LLOAD_3:
pushByLocalLoad("J", seen - Const.LLOAD_0);
break;
case Const.GETSTATIC: {
FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
XField fieldOperand = dbc.getXFieldOperand();
if (fieldOperand != null && fieldSummary.isComplete() && !fieldOperand.isPublic()) {
OpcodeStack.Item item = fieldSummary.getSummary(fieldOperand);
if (item != null) {
Item itm = new Item(item);
itm.setLoadedFromField(fieldOperand, Integer.MAX_VALUE);
itm.setPC(dbc.getPC());
push(itm);
break;
}
}
FieldAnnotation field = FieldAnnotation.fromReferencedField(dbc);
Item i = new Item(dbc.getSigConstantOperand(), field, Integer.MAX_VALUE);
if ("separator".equals(field.getFieldName()) && Values.DOTTED_JAVA_IO_FILE.equals(field.getClassName())) {
i.setSpecialKind(Item.FILE_SEPARATOR_STRING);
}
i.setPC(dbc.getPC());
push(i);
break;
}
case Const.LDC:
case Const.LDC_W:
case Const.LDC2_W:
cons = dbc.getConstantRefOperand();
pushByConstant(dbc, cons);
break;
case Const.INSTANCEOF:
pop();
push(new Item("I"));
break;
case Const.IFNONNULL:
case Const.IFNULL:
// {
// Item topItem = pop();
// if (seen == IFNONNULL && topItem.isNull())
// break;
// seenTransferOfControl = true;
// addJumpValue(dbc.getPC(), dbc.getBranchTarget());
//
// break;
// }
case Const.IFEQ:
case Const.IFNE:
case Const.IFLT:
case Const.IFLE:
case Const.IFGT:
case Const.IFGE:
seenTransferOfControl = true; {
Item topItem = pop();
// System.out.printf("%4d %10s %s%n",
// dbc.getPC(),Const.getOpcodeName(seen), topItem);
if (seen == Const.IFLT || seen == Const.IFLE) {
registerTestedFoundToBeNonnegative = topItem.registerNumber;
}
// if we see a test comparing a special negative value with
// 0,
// reset all other such values on the opcode stack
if (topItem.valueCouldBeNegative() && (seen == Const.IFLT || seen == Const.IFLE || seen == Const.IFGT || seen == Const.IFGE)) {
int specialKind = topItem.getSpecialKind();
for (Item item : stack) {
if (item != null && item.getSpecialKind() == specialKind) {
item.setSpecialKind(Item.NOT_SPECIAL);
}
}
for (Item item : lvValues) {
if (item != null && item.getSpecialKind() == specialKind) {
item.setSpecialKind(Item.NOT_SPECIAL);
}
}
}
}
addJumpValue(dbc.getPC(), dbc.getBranchTarget());
break;
case Const.LOOKUPSWITCH:
case Const.TABLESWITCH:
seenTransferOfControl = true;
setReachOnlyByBranch(true);
pop();
addJumpValue(dbc.getPC(), dbc.getBranchTarget());
int pc = dbc.getBranchTarget() - dbc.getBranchOffset();
for (int offset : dbc.getSwitchOffsets()) {
addJumpValue(dbc.getPC(), offset + pc);
}
break;
case Const.ARETURN:
case Const.DRETURN:
case Const.FRETURN:
case Const.IRETURN:
case Const.LRETURN:
seenTransferOfControl = true;
setReachOnlyByBranch(true);
pop();
break;
case Const.MONITORENTER:
case Const.MONITOREXIT:
case Const.POP:
pop();
break;
case Const.PUTSTATIC:
pop();
eraseKnowledgeOf(dbc.getXFieldOperand());
break;
case Const.PUTFIELD:
pop(2);
eraseKnowledgeOf(dbc.getXFieldOperand());
break;
case Const.IF_ACMPEQ:
case Const.IF_ACMPNE:
case Const.IF_ICMPEQ:
case Const.IF_ICMPNE:
case Const.IF_ICMPLT:
case Const.IF_ICMPLE:
case Const.IF_ICMPGT:
case Const.IF_ICMPGE:
{
seenTransferOfControl = true;
Item right = pop();
Item left = pop();
Object lConstant = left.getConstant();
Object rConstant = right.getConstant();
boolean takeJump = false;
boolean handled = false;
if (seen == Const.IF_ACMPNE || seen == Const.IF_ACMPEQ) {
if (lConstant != null && rConstant != null && !lConstant.equals(rConstant) || lConstant != null
&& right.isNull() || rConstant != null && left.isNull()) {
handled = true;
takeJump = seen == Const.IF_ACMPNE;
}
} else if (lConstant instanceof Integer && rConstant instanceof Integer) {
int lC = ((Integer) lConstant).intValue();
int rC = ((Integer) rConstant).intValue();
switch (seen) {
case Const.IF_ICMPEQ:
takeJump = lC == rC;
handled = true;
break;
case Const.IF_ICMPNE:
takeJump = lC != rC;
handled = true;
break;
case Const.IF_ICMPGE:
takeJump = lC >= rC;
handled = true;
break;
case Const.IF_ICMPGT:
takeJump = lC > rC;
handled = true;
break;
case Const.IF_ICMPLE:
takeJump = lC <= rC;
handled = true;
break;
case Const.IF_ICMPLT:
takeJump = lC < rC;
handled = true;
break;
default:
assert false;
}
}
if (handled) {
if (takeJump) {
int branchTarget = dbc.getBranchTarget();
addJumpValue(dbc.getPC(), branchTarget);
setTop(true);
break;
} else {
break;
}
}
if (right.hasConstantValue(Integer.MIN_VALUE) && left.mightRarelyBeNegative()
|| left.hasConstantValue(Integer.MIN_VALUE) && right.mightRarelyBeNegative()) {
for (Item i : stack) {
if (i != null && i.mightRarelyBeNegative()) {
i.setSpecialKind(Item.NOT_SPECIAL);
}
}
for (Item i : lvValues) {
if (i != null && i.mightRarelyBeNegative()) {
i.setSpecialKind(Item.NOT_SPECIAL);
}
}
}
int branchTarget = dbc.getBranchTarget();
addJumpValue(dbc.getPC(), branchTarget);
break;
}
case Const.POP2:
it = pop();
if (it.getSize() == 1) {
pop();
}
break;
case Const.IALOAD:
case Const.SALOAD:
pop(2);
push(new Item("I"));
break;
case Const.DUP:
handleDup();
break;
case Const.DUP2:
handleDup2();
break;
case Const.DUP_X1:
handleDupX1();
break;
case Const.DUP_X2:
handleDupX2();
break;
case Const.DUP2_X1:
handleDup2X1();
break;
case Const.DUP2_X2:
handleDup2X2();
break;
case Const.IINC:
register = dbc.getRegisterOperand();
it = getLVValue(register);
it2 = new Item("I", dbc.getIntConstant());
pushByIntMath(dbc, Const.IADD, it2, it);
pushByLocalStore(register);
break;
case Const.ATHROW:
pop();
seenTransferOfControl = true;
setReachOnlyByBranch(true);
setTop(true);
break;
case Const.CHECKCAST: {
String castTo = dbc.getClassConstantOperand();
if (castTo.charAt(0) != '[') {
castTo = "L" + castTo + ";";
}
it = pop();
if (!it.signature.equals(castTo)) {
it = new Item(it, castTo);
}
push(it);
break;
}
case Const.NOP:
break;
case Const.RET:
case Const.RETURN:
seenTransferOfControl = true;
setReachOnlyByBranch(true);
break;
case Const.GOTO:
case Const.GOTO_W:
seenTransferOfControl = true;
setReachOnlyByBranch(true);
addJumpValue(dbc.getPC(), dbc.getBranchTarget());
stack.clear();
setTop(true);
break;
case Const.SWAP:
handleSwap();
break;
case Const.ICONST_M1:
case Const.ICONST_0:
case Const.ICONST_1:
case Const.ICONST_2:
case Const.ICONST_3:
case Const.ICONST_4:
case Const.ICONST_5:
push(new Item("I", (seen - Const.ICONST_0)));
break;
case Const.LCONST_0:
case Const.LCONST_1:
push(new Item("J", Long.valueOf(seen - Const.LCONST_0)));
break;
case Const.DCONST_0:
case Const.DCONST_1:
push(new Item("D", Double.valueOf(seen - Const.DCONST_0)));
break;
case Const.FCONST_0:
case Const.FCONST_1:
case Const.FCONST_2:
push(new Item("F", Float.valueOf(seen - Const.FCONST_0)));
break;
case Const.ACONST_NULL:
push(new Item());
break;
case Const.ASTORE:
case Const.DSTORE:
case Const.FSTORE:
case Const.ISTORE:
case Const.LSTORE:
pushByLocalStore(dbc.getRegisterOperand());
break;
case Const.ASTORE_0:
case Const.ASTORE_1:
case Const.ASTORE_2:
case Const.ASTORE_3:
pushByLocalStore(seen - Const.ASTORE_0);
break;
case Const.DSTORE_0:
case Const.DSTORE_1:
case Const.DSTORE_2:
case Const.DSTORE_3:
pushByLocalStore(seen - Const.DSTORE_0);
break;
case Const.FSTORE_0:
case Const.FSTORE_1:
case Const.FSTORE_2:
case Const.FSTORE_3:
pushByLocalStore(seen - Const.FSTORE_0);
break;
case Const.ISTORE_0:
case Const.ISTORE_1:
case Const.ISTORE_2:
case Const.ISTORE_3:
pushByLocalStore(seen - Const.ISTORE_0);
break;
case Const.LSTORE_0:
case Const.LSTORE_1:
case Const.LSTORE_2:
case Const.LSTORE_3:
pushByLocalStore(seen - Const.LSTORE_0);
break;
case Const.GETFIELD: {
FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
XField fieldOperand = dbc.getXFieldOperand();
if (fieldOperand != null && fieldSummary.isComplete() && !fieldOperand.isPublic()) {
OpcodeStack.Item item = fieldSummary.getSummary(fieldOperand);
if (item != null) {
Item addr = pop();
Item itm = new Item(item);
itm.setLoadedFromField(fieldOperand, addr.getRegisterNumber());
itm.setPC(dbc.getPC());
push(itm);
break;
}
}
Item item = pop();
int reg = item.getRegisterNumber();
Item valueLoaded = new Item(dbc.getSigConstantOperand(), FieldAnnotation.fromReferencedField(dbc), reg);
valueLoaded.setPC(dbc.getPC());
push(valueLoaded);
}
break;
case Const.ARRAYLENGTH: {
Item array = pop();
Item newItem = new Item("I", array.getConstant());
newItem.setSpecialKind(Item.NON_NEGATIVE);
push(newItem);
}
break;
case Const.BALOAD: {
pop(2);
Item newItem = new Item("I");
newItem.setSpecialKind(Item.SIGNED_BYTE);
push(newItem);
break;
}
case Const.CALOAD: {
pop(2);
Item newItem = new Item("I");
newItem.setSpecialKind(Item.NON_NEGATIVE);
push(newItem);
break;
}
case Const.DALOAD:
pop(2);
push(new Item("D"));
break;
case Const.FALOAD:
pop(2);
push(new Item("F"));
break;
case Const.LALOAD:
pop(2);
push(new Item("J"));
break;
case Const.AASTORE:
case Const.BASTORE:
case Const.CASTORE:
case Const.DASTORE:
case Const.FASTORE:
case Const.IASTORE:
case Const.LASTORE:
case Const.SASTORE:
pop(3);
break;
case Const.BIPUSH:
case Const.SIPUSH:
push(new Item("I", Integer.valueOf(dbc.getIntConstant())));
break;
case Const.IADD:
case Const.ISUB:
case Const.IMUL:
case Const.IDIV:
case Const.IAND:
case Const.IOR:
case Const.IXOR:
case Const.ISHL:
case Const.ISHR:
case Const.IREM:
case Const.IUSHR:
it = pop();
it2 = pop();
pushByIntMath(dbc, seen, it2, it);
break;
case Const.INEG:
it = pop();
if (it.getConstant() instanceof Integer) {
push(new Item("I", Integer.valueOf(-constantToInt(it))));
} else {
push(new Item("I"));
}
break;
case Const.LNEG:
it = pop();
if (it.getConstant() instanceof Long) {
push(new Item("J", Long.valueOf(-constantToLong(it))));
} else {
push(new Item("J"));
}
break;
case Const.FNEG:
it = pop();
if (it.getConstant() instanceof Float) {
push(new Item("F", Float.valueOf(-constantToFloat(it))));
} else {
push(new Item("F"));
}
break;
case Const.DNEG:
it = pop();
if (it.getConstant() instanceof Double) {
push(new Item("D", Double.valueOf(-constantToDouble(it))));
} else {
push(new Item("D"));
}
break;
case Const.LADD:
case Const.LSUB:
case Const.LMUL:
case Const.LDIV:
case Const.LAND:
case Const.LOR:
case Const.LXOR:
case Const.LSHL:
case Const.LSHR:
case Const.LREM:
case Const.LUSHR:
it = pop();
it2 = pop();
pushByLongMath(seen, it2, it);
break;
case Const.LCMP:
handleLcmp();
break;
case Const.FCMPG:
case Const.FCMPL:
handleFcmp(seen);
break;
case Const.DCMPG:
case Const.DCMPL:
handleDcmp(seen);
break;
case Const.FADD:
case Const.FSUB:
case Const.FMUL:
case Const.FDIV:
case Const.FREM:
it = pop();
it2 = pop();
pushByFloatMath(seen, it, it2);
break;
case Const.DADD:
case Const.DSUB:
case Const.DMUL:
case Const.DDIV:
case Const.DREM:
it = pop();
it2 = pop();
pushByDoubleMath(seen, it, it2);
break;
case Const.I2B: {
it = pop();
Item newValue = new Item(it, "B");
newValue.setCouldBeNegative();
push(newValue);
}
break;
case Const.I2C: {
it = pop();
Item newValue = new Item(it, "C");
push(newValue);
}
break;
case Const.I2L:
case Const.D2L:
case Const.F2L: {
it = pop();
Item newValue = new Item(it, "J");
int specialKind = it.getSpecialKind();
if (specialKind != Item.SIGNED_BYTE && seen == Const.I2L) {
newValue.setSpecialKind(Item.RESULT_OF_I2L);
}
push(newValue);
}
break;
case Const.I2S: {
Item item1 = pop();
Item newValue = new Item(item1, "S");
newValue.setCouldBeNegative();
push(newValue);
}
break;
case Const.L2I:
case Const.D2I:
case Const.F2I:
it = pop();
int oldSpecialKind = it.getSpecialKind();
it = new Item(it, "I");
if (oldSpecialKind == Item.NOT_SPECIAL) {
it.setSpecialKind(Item.RESULT_OF_L2I);
}
push(it);
break;
case Const.L2F:
case Const.D2F:
case Const.I2F:
it = pop();
if (it.getConstant() != null) {
push(new Item("F", Float.valueOf(constantToFloat(it))));
} else {
push(new Item("F"));
}
break;
case Const.F2D:
case Const.I2D:
case Const.L2D:
it = pop();
if (it.getConstant() != null) {
push(new Item("D", Double.valueOf(constantToDouble(it))));
} else {
push(new Item("D"));
}
break;
case Const.NEW: {
Item item = new Item("L" + dbc.getClassConstantOperand() + ";", (Object) null);
item.setSpecialKind(Item.NEWLY_ALLOCATED);
push(item);
}
break;
case Const.NEWARRAY: {
Item length = pop();
signature = "[" + BasicType.getType((byte) dbc.getIntConstant()).getSignature();
Item item = new Item(signature, length.getConstant());
item.setPC(dbc.getPC());
item.setSpecialKind(Item.NEWLY_ALLOCATED);
push(item);
break;
}
// According to the VM Spec 4.4.1, anewarray and multianewarray
// can refer to normal class/interface types (encoded in
// "internal form"), or array classes (encoded as signatures
// beginning with "[").
case Const.ANEWARRAY: {
Item length = pop();
signature = dbc.getClassConstantOperand();
if (signature.charAt(0) == '[') {
signature = "[" + signature;
} else {
signature = "[L" + signature + ";";
}
Item item = new Item(signature, length.getConstant());
item.setPC(dbc.getPC());
item.setSpecialKind(Item.NEWLY_ALLOCATED);
push(item);
break;
}
case Const.MULTIANEWARRAY:
int dims = dbc.getIntConstant();
for (int i = 0; i < dims; i++) {
pop();
}
signature = dbc.getClassConstantOperand();
pushBySignature(signature, dbc);
getStackItem(0).setSpecialKind(Item.NEWLY_ALLOCATED);
break;
case Const.AALOAD: {
pop();
it = pop();
String arraySig = it.getSignature();
if (arraySig.charAt(0) == '[') {
pushBySignature(arraySig.substring(1), dbc);
} else {
push(new Item());
}
}
break;
case Const.JSR:
seenTransferOfControl = true;
setReachOnlyByBranch(false);
push(new Item("")); // push return address on stack
addJumpValue(dbc.getPC(), dbc.getBranchTarget());
pop();
if (dbc.getBranchOffset() < 0) {
// OK, backwards JSRs are weird; reset the stack.
int stackSize = stack.size();
stack.clear();
for (int i = 0; i < stackSize; i++) {
stack.add(new Item());
}
}
setTop(false);
break;
case Const.INVOKEINTERFACE:
case Const.INVOKESPECIAL:
case Const.INVOKESTATIC:
case Const.INVOKEVIRTUAL:
processMethodCall(dbc, seen);
break;
case Const.INVOKEDYNAMIC:
processInvokeDynamic(dbc);
break;
default:
throw new UnsupportedOperationException("OpCode " + seen + ":" + Const.getOpcodeName(seen) + " not supported ");
}
}
catch (RuntimeException e) {
// If an error occurs, we clear the stack and locals. one of two
// things will occur.
// Either the client will expect more stack items than really exist,
// and so they're condition check will fail,
// or the stack will resync with the code. But hopefully not false
// positives
String msg = "Error processing opcode " + Const.getOpcodeName(seen) + " @ " + dbc.getPC() + " in "
+ dbc.getFullyQualifiedMethodName();
AnalysisContext.logError(msg, e);
if (DEBUG) {
e.printStackTrace();
}
clear();
setTop(true);
} finally {
if (DEBUG) {
System.out.printf("%4d: %14s %s%n", dbc.getPC(), Const.getOpcodeName(seen), this);
}
}
}
private void eraseKnowledgeOf(XField fieldOperand) {
if (fieldOperand == null) {
return;
}
for (Item item : stack) {
if (item != null && fieldOperand.equals(item.getXField())) {
item.setLoadedFromField(null, -1);
}
}
for (Item item : lvValues) {
if (item != null && fieldOperand.equals(item.getXField())) {
item.setLoadedFromField(null, -1);
}
}
}
public void precomputation(DismantleBytecode dbc) {
if (registerTestedFoundToBeNonnegative >= 0) {
for (int i = 0; i < stack.size(); i++) {
Item item = stack.get(i);
if (item != null && item.registerNumber == registerTestedFoundToBeNonnegative) {
stack.set(i, item.cloneAndSetSpecialKind(Item.NON_NEGATIVE));
}
}
for (int i = 0; i < lvValues.size(); i++) {
Item item = lvValues.get(i);
if (item != null && item.registerNumber == registerTestedFoundToBeNonnegative) {
lvValues.set(i, item.cloneAndSetSpecialKind(Item.NON_NEGATIVE));
}
}
}
registerTestedFoundToBeNonnegative = -1;
mergeJumps(dbc);
}
private int constantToInt(Item it) {
Object constant = it.getConstant();
if (constant instanceof Number) {
return ((Number) constant).intValue();
}
if (constant instanceof Character) {
return ((Character) constant).charValue();
}
if (constant instanceof Boolean) {
Boolean val = (Boolean) constant;
return val.booleanValue() ? 1 : 0;
}
throw new IllegalArgumentException(String.valueOf(constant));
}
private float constantToFloat(Item it) {
Object constant = it.getConstant();
if (constant instanceof Number) {
return ((Number) constant).floatValue();
}
if (constant instanceof Character) {
return ((Character) constant).charValue();
}
throw new IllegalArgumentException(String.valueOf(constant));
}
private double constantToDouble(Item it) {
Object constant = it.getConstant();
if (constant instanceof Number) {
return ((Number) constant).doubleValue();
}
if (constant instanceof Character) {
return ((Character) constant).charValue();
}
throw new IllegalArgumentException(String.valueOf(constant));
}
private long constantToLong(Item it) {
Object constant = it.getConstant();
if (constant instanceof Number) {
return ((Number) constant).longValue();
}
if (constant instanceof Character) {
return ((Character) constant).charValue();
}
throw new IllegalArgumentException(String.valueOf(constant));
}
private void handleDcmp(int opcode) {
Item it = pop();
Item it2 = pop();
if ((it.getConstant() != null) && it2.getConstant() != null) {
double d = constantToDouble(it);
double d2 = constantToDouble(it2);
if (Double.isNaN(d) || Double.isNaN(d2)) {
if (opcode == Const.DCMPG) {
push(new Item("I", Integer.valueOf(1)));
} else {
push(new Item("I", Integer.valueOf(-1)));
}
}
if (d2 < d) {
push(new Item("I", Integer.valueOf(-1)));
} else if (d2 > d) {
push(new Item("I", Integer.valueOf(1)));
} else {
push(new Item("I", Integer.valueOf(0)));
}
} else {
push(new Item("I"));
}
}
private void handleFcmp(int opcode) {
Item it = pop();
Item it2 = pop();
if ((it.getConstant() != null) && it2.getConstant() != null) {
float f = constantToFloat(it);
float f2 = constantToFloat(it2);
if (Float.isNaN(f) || Float.isNaN(f2)) {
if (opcode == Const.FCMPG) {
push(new Item("I", Integer.valueOf(1)));
} else {
push(new Item("I", Integer.valueOf(-1)));
}
}
if (f2 < f) {
push(new Item("I", Integer.valueOf(-1)));
} else if (f2 > f) {
push(new Item("I", Integer.valueOf(1)));
} else {
push(new Item("I", Integer.valueOf(0)));
}
} else {
push(new Item("I"));
}
}
private void handleLcmp() {
Item it = pop();
Item it2 = pop();
if ((it.getConstant() != null) && it2.getConstant() != null) {
long l = constantToLong(it);
long l2 = constantToLong(it2);
if (l2 < l) {
push(new Item("I", Integer.valueOf(-1)));
} else if (l2 > l) {
push(new Item("I", Integer.valueOf(1)));
} else {
push(new Item("I", Integer.valueOf(0)));
}
} else {
push(new Item("I"));
}
}
private void handleSwap() {
Item i1 = pop();
Item i2 = pop();
push(i1);
push(i2);
}
private void handleDup() {
Item it;
it = pop();
push(it);
push(it);
}
private void handleDupX1() {
Item it;
Item it2;
it = pop();
it2 = pop();
push(it);
push(it2);
push(it);
}
private void handleDup2() {
Item it, it2;
it = pop();
if (it.getSize() == 2) {
push(it);
push(it);
} else {
it2 = pop();
push(it2);
push(it);
push(it2);
push(it);
}
}
private void handleDup2X1() {
String signature;
Item it;
Item it2;
Item it3;
it = pop();
it2 = pop();
signature = it.getSignature();
if ("J".equals(signature) || "D".equals(signature)) {
push(it);
push(it2);
push(it);
} else {
it3 = pop();
push(it2);
push(it);
push(it3);
push(it2);
push(it);
}
}
private void handleDup2X2() {
Item it = pop();
Item it2 = pop();
if (it.isWide()) {
if (it2.isWide()) {
push(it);
push(it2);
push(it);
} else {
Item it3 = pop();
push(it);
push(it3);
push(it2);
push(it);
}
} else {
Item it3 = pop();
if (it3.isWide()) {
push(it2);
push(it);
push(it3);
push(it2);
push(it);
} else {
Item it4 = pop();
push(it2);
push(it);
push(it4);
push(it3);
push(it2);
push(it);
}
}
}
private void handleDupX2() {
String signature;
Item it;
Item it2;
Item it3;
it = pop();
it2 = pop();
signature = it2.getSignature();
if ("J".equals(signature) || "D".equals(signature)) {
push(it);
push(it2);
push(it);
} else {
it3 = pop();
push(it);
push(it3);
push(it2);
push(it);
}
}
static private void addBoxedType(Class>... clss) {
for (Class> c : clss) {
Class> primitiveType;
try {
primitiveType = (Class>) c.getField("TYPE").get(null);
boxedTypes.put(ClassName.toSlashedClassName(c.getName()), primitiveType.getName());
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
throw new AssertionError(e);
}
}
}
static {
addBoxedType(Integer.class, Long.class, Double.class, Short.class, Float.class, Boolean.class, Character.class,
Byte.class);
}
private void markConstantValueUnknown(Item item) {
item.constValue = null;
if (item.registerNumber >= 0) {
OpcodeStack.Item sbItem = getLVValue(item.registerNumber);
if (sbItem.getSignature().equals(item.getSignature())) {
sbItem.constValue = null;
}
}
}
private void processMethodCall(DismantleBytecode dbc, int seen) {
@SlashedClassName
String clsName = dbc.getClassConstantOperand();
String methodName = dbc.getNameConstantOperand();
String signature = dbc.getSigConstantOperand();
String appenderValue = null;
boolean servletRequestParameterTainted = false;
boolean sawUnknownAppend = false;
Item sbItem = null;
Item topItem = null;
if (getStackDepth() > 0) {
topItem = getStackItem(0);
}
int numberArguments = PreorderVisitor.getNumberArguments(signature);
if (boxedTypes.containsKey(clsName)
&& topItem != null
&& ("valueOf".equals(methodName) && !signature.contains("String") || methodName.equals(boxedTypes.get(clsName)
+ "Value"))) {
// boxing/unboxing conversion
Item value = pop();
String newSignature = new SignatureParser(signature).getReturnTypeSignature();
Item newValue = new Item(value, newSignature);
if (newValue.source == null) {
newValue.source = XFactory.createReferencedXMethod(dbc);
}
if (newValue.specialKind == Item.NOT_SPECIAL) {
if ("B".equals(newSignature) || "Ljava/lang/Boolean;".equals(newSignature)) {
newValue.specialKind = Item.SIGNED_BYTE;
} else if ("C".equals(newSignature) || "Ljava/lang/Character;".equals(newSignature)) {
newValue.specialKind = Item.NON_NEGATIVE;
}
}
push(newValue);
return;
}
int firstArgument = seen == Const.INVOKESTATIC ? 0 : 1;
for (int i = firstArgument; i < firstArgument + numberArguments; i++) {
if (i >= getStackDepth()) {
break;
}
Item item = getStackItem(i);
String itemSignature = item.getSignature();
if ("Ljava/lang/StringBuilder;".equals(itemSignature) || "Ljava/lang/StringBuffer;".equals(itemSignature)) {
markConstantValueUnknown(item);
}
}
boolean initializingServletWriter = false;
if (seen == Const.INVOKESPECIAL && Const.CONSTRUCTOR_NAME.equals(methodName) && clsName.startsWith("java/io") && clsName.endsWith("Writer")
&& numberArguments > 0) {
Item firstArg = getStackItem(numberArguments - 1);
if (firstArg.isServletWriter()) {
initializingServletWriter = true;
}
}
boolean topIsTainted = topItem != null && topItem.isServletParameterTainted();
HttpParameterInjection injection = null;
if (topIsTainted) {
injection = topItem.injection;
}
// TODO: stack merging for trinaries kills the constant.. would be nice
// to maintain.
if ("java/lang/StringBuffer".equals(clsName) || "java/lang/StringBuilder".equals(clsName)) {
if (Const.CONSTRUCTOR_NAME.equals(methodName)) {
if ("(Ljava/lang/String;)V".equals(signature)) {
Item i = getStackItem(0);
appenderValue = (String) i.getConstant();
if (i.isServletParameterTainted()) {
servletRequestParameterTainted = true;
}
} else if ("()V".equals(signature)) {
appenderValue = "";
}
} else if ("toString".equals(methodName) && getStackDepth() >= 1) {
Item i = getStackItem(0);
appenderValue = (String) i.getConstant();
if (i.isServletParameterTainted()) {
servletRequestParameterTainted = true;
}
} else if ("append".equals(methodName)) {
if (signature.indexOf("II)") == -1 && getStackDepth() >= 2) {
sbItem = getStackItem(1);
Item i = getStackItem(0);
if (i.isServletParameterTainted() || sbItem.isServletParameterTainted()) {
servletRequestParameterTainted = true;
}
Object sbVal = sbItem.getConstant();
Object sVal = i.getConstant();
if ((sbVal != null) && (sVal != null)) {
appenderValue = sbVal + sVal.toString();
} else {
markConstantValueUnknown(sbItem);
}
} else if (signature.startsWith("([CII)")) {
sawUnknownAppend = true;
sbItem = getStackItem(3);
markConstantValueUnknown(sbItem);
} else {
sawUnknownAppend = true;
}
}
} else if (seen == Const.INVOKESPECIAL && "java/io/FileOutputStream".equals(clsName) && Const.CONSTRUCTOR_NAME.equals(methodName)
&& ("(Ljava/io/File;Z)V".equals(signature) || "(Ljava/lang/String;Z)V".equals(signature)) && stack.size() > 3) {
OpcodeStack.Item item = getStackItem(0);
Object value = item.getConstant();
if (value instanceof Integer && ((Integer) value).intValue() == 1) {
pop(3);
Item newTop = getStackItem(0);
if ("Ljava/io/FileOutputStream;".equals(newTop.signature)) {
newTop.setSpecialKind(Item.FILE_OPENED_IN_APPEND_MODE);
newTop.source = XFactory.createReferencedXMethod(dbc);
newTop.setPC(dbc.getPC());
}
return;
}
} else if (seen == Const.INVOKESPECIAL && "java/io/BufferedOutputStream".equals(clsName) && Const.CONSTRUCTOR_NAME.equals(methodName)
&& "(Ljava/io/OutputStream;)V".equals(signature)) {
if (getStackItem(0).getSpecialKind() == Item.FILE_OPENED_IN_APPEND_MODE
&& "Ljava/io/BufferedOutputStream;".equals(getStackItem(2).signature)) {
pop(2);
Item newTop = getStackItem(0);
newTop.setSpecialKind(Item.FILE_OPENED_IN_APPEND_MODE);
newTop.source = XFactory.createReferencedXMethod(dbc);
newTop.setPC(dbc.getPC());
return;
}
} else if (seen == Const.INVOKEINTERFACE && "getParameter".equals(methodName)
&& "javax/servlet/http/HttpServletRequest".equals(clsName) || "javax/servlet/http/ServletRequest".equals(clsName)) {
Item requestParameter = pop();
pop();
Item result = new Item("Ljava/lang/String;");
result.setServletParameterTainted();
result.source = XFactory.createReferencedXMethod(dbc);
String parameterName = null;
if (requestParameter.getConstant() instanceof String) {
parameterName = (String) requestParameter.getConstant();
}
result.injection = new HttpParameterInjection(parameterName, dbc.getPC());
result.setPC(dbc.getPC());
push(result);
return;
} else if (seen == Const.INVOKEINTERFACE && "getQueryString".equals(methodName)
&& "javax/servlet/http/HttpServletRequest".equals(clsName) || "javax/servlet/http/ServletRequest".equals(clsName)) {
pop();
Item result = new Item("Ljava/lang/String;");
result.setServletParameterTainted();
result.source = XFactory.createReferencedXMethod(dbc);
result.setPC(dbc.getPC());
push(result);
return;
} else if (seen == Const.INVOKEINTERFACE && "getHeader".equals(methodName)
&& "javax/servlet/http/HttpServletRequest".equals(clsName) || "javax/servlet/http/ServletRequest".equals(clsName)) {
/* Item requestParameter = */pop();
pop();
Item result = new Item("Ljava/lang/String;");
result.setServletParameterTainted();
result.source = XFactory.createReferencedXMethod(dbc);
result.setPC(dbc.getPC());
push(result);
return;
} else if (seen == Const.INVOKESTATIC && "asList".equals(methodName) && "java/util/Arrays".equals(clsName)) {
/* Item requestParameter = */pop();
Item result = new Item(JAVA_UTIL_ARRAYS_ARRAY_LIST);
push(result);
return;
} else if (seen == Const.INVOKESTATIC) {
Item requestParameter = null;
if ("(Ljava/util/List;)Ljava/util/List;".equals(signature)
&& "java/util/Collections".equals(clsName)) {
requestParameter = top();
}
String returnTypeName = IMMUTABLE_RETURNER_MAP.get(Pair.of(clsName, methodName));
if (returnTypeName != null) {
SignatureParser sp = new SignatureParser(signature);
for (int i = 0; i < sp.getNumParameters(); ++i) {
pop();
}
Item result;
if (requestParameter != null && JAVA_UTIL_ARRAYS_ARRAY_LIST.equals(requestParameter.getSignature())) {
result = new Item("Ljava/util/Collections$UnmodifiableRandomAccessList;");
} else {
result = new Item(returnTypeName);
}
push(result);
return;
} else if (requestParameter != null) {
pop();
if (JAVA_UTIL_ARRAYS_ARRAY_LIST.equals(requestParameter.getSignature())) {
Item result = new Item(JAVA_UTIL_ARRAYS_ARRAY_LIST);
push(result);
return;
}
push(requestParameter); // fall back to standard logic
}
}
pushByInvoke(dbc, seen != Const.INVOKESTATIC);
if (sbItem != null && sbItem.isNewlyAllocated()) {
this.getStackItem(0).setSpecialKind(Item.NEWLY_ALLOCATED);
}
if (initializingServletWriter) {
this.getStackItem(0).setIsServletWriter();
}
if ((sawUnknownAppend || appenderValue != null || servletRequestParameterTainted) && getStackDepth() > 0) {
Item i = this.getStackItem(0);
i.constValue = appenderValue;
if (!sawUnknownAppend && servletRequestParameterTainted) {
i.injection = topItem.injection;
i.setServletParameterTainted();
}
if (sbItem != null) {
i.registerNumber = sbItem.registerNumber;
i.source = sbItem.source;
if (i.injection == null) {
i.injection = sbItem.injection;
}
if (sbItem.registerNumber >= 0) {
setLVValue(sbItem.registerNumber, i);
}
}
return;
}
if (("java/util/Random".equals(clsName) || "java/security/SecureRandom".equals(clsName)) &&
("nextInt".equals(methodName) && "()I".equals(signature)
|| "nextLong".equals(methodName) && "()J".equals(signature))) {
Item i = new Item(pop());
i.setSpecialKind(Item.RANDOM_INT);
push(i);
} else if ("size".equals(methodName) && "()I".equals(signature)
&& Subtypes2.instanceOf(ClassName.toDottedClassName(clsName), "java.util.Collection")) {
Item i = new Item(pop());
if (i.getSpecialKind() == Item.NOT_SPECIAL) {
i.setSpecialKind(Item.NON_NEGATIVE);
}
push(i);
} else if ("java/lang/String".equals(clsName) && numberArguments == 0 && topItem != null &&
topItem.getConstant() instanceof String) {
String input = (String) topItem.getConstant();
Object result;
switch (methodName) {
case "length":
result = input.length();
break;
case "trim":
result = input.trim();
break;
case "toString":
case "intern":
result = input;
break;
default:
result = null;
}
if (result != null) {
Item i = new Item(pop());
i.constValue = result;
push(i);
}
} else if (ClassName.isMathClass(clsName) && "abs".equals(methodName)) {
Item i = new Item(pop());
if (i.getSpecialKind() == Item.HASHCODE_INT) {
i.setSpecialKind(Item.MATH_ABS_OF_HASHCODE);
} else if (i.getSpecialKind() == Item.RANDOM_INT) {
i.setSpecialKind(Item.MATH_ABS_OF_RANDOM);
} else {
i.setSpecialKind(Item.MATH_ABS);
}
push(i);
} else if (seen == Const.INVOKEVIRTUAL && "hashCode".equals(methodName) && "()I".equals(signature) || seen == Const.INVOKESTATIC
&& "java/lang/System".equals(clsName) && "identityHashCode".equals(methodName)
&& "(Ljava/lang/Object;)I".equals(signature)) {
Item i = new Item(pop());
i.setSpecialKind(Item.HASHCODE_INT);
push(i);
} else if (topIsTainted
&& (methodName.startsWith("encode") && "javax/servlet/http/HttpServletResponse".equals(clsName) || "trim".equals(methodName)
&& "java/lang/String".equals(clsName))) {
Item i = new Item(pop());
i.setSpecialKind(Item.SERVLET_REQUEST_TAINTED);
i.injection = injection;
push(i);
}
if (!signature.endsWith(")V")) {
Item i = new Item(pop());
i.source = XFactory.createReferencedXMethod(dbc);
push(i);
}
if (seen == Const.INVOKESTATIC && topItem != null && topItem.isInitialParameter()
&& isMethodThatReturnsGivenReference(clsName, methodName)) {
assert getStackDepth() > 0;
assert !getStackItem(0).isInitialParameter();
// keep returned StackItem as initial parameter
getStackItem(0).setInitialParameter(true);
}
}
private boolean isMethodThatReturnsGivenReference(String clsName, String methodName) {
return "java/util/Objects".equals(clsName) && "requireNonNull".equals(methodName)
|| "com/google/common/base/Preconditions".equals(clsName) && "checkNotNull".equals(methodName);
}
private void processInvokeDynamic(DismantleBytecode dbc) {
String methodName = dbc.getNameConstantOperand();
String appenderValue = null;
boolean servletRequestParameterTainted = false;
Item topItem = null;
if (getStackDepth() > 0) {
topItem = getStackItem(0);
}
String signature = dbc.getSigConstantOperand();
if ("makeConcatWithConstants".equals(methodName)) {
String[] args = new SignatureParser(signature).getArguments();
if (args.length == 1) {
Item i = getStackItem(0);
if (i.isServletParameterTainted()) {
servletRequestParameterTainted = true;
}
Object sVal = i.getConstant();
if (sVal == null) {
appenderValue = null;
} else {
JavaClass clazz = dbc.getThisClass();
BootstrapMethod bm = getBootstrapMethod(clazz.getAttributes(), dbc.getConstantRefOperand());
ConstantPool cp = clazz.getConstantPool();
String concatArg = ((ConstantString) cp.getConstant(bm.getBootstrapArguments()[0])).getBytes(cp);
appenderValue = concatArg.replace("\u0001", sVal.toString());
}
} else if (args.length == 2) {
Item i1 = getStackItem(0);
Item i2 = getStackItem(1);
if (i1.isServletParameterTainted() || i2.isServletParameterTainted()) {
servletRequestParameterTainted = true;
}
Object sVal1 = i1.getConstant();
Object sVal2 = i2.getConstant();
if (sVal1 == null || sVal2 == null) {
appenderValue = null;
} else {
appenderValue = sVal2.toString() + sVal1.toString();
}
}
}
int numberArguments = PreorderVisitor.getNumberArguments(signature);
pop(numberArguments);
pushBySignature(new SignatureParser(signature).getReturnTypeSignature(), dbc);
if ((appenderValue != null || servletRequestParameterTainted) && getStackDepth() > 0) {
Item i = this.getStackItem(0);
i.constValue = appenderValue;
if (servletRequestParameterTainted) {
i.injection = topItem.injection;
i.setServletParameterTainted();
}
return;
}
}
private BootstrapMethod getBootstrapMethod(Attribute[] attribs, Constant index) {
ConstantInvokeDynamic bmidx = (ConstantInvokeDynamic) index;
for (Attribute attr : attribs) {
if (attr instanceof BootstrapMethods) {
return ((BootstrapMethods) attr).getBootstrapMethods()[bmidx.getBootstrapMethodAttrIndex()];
}
}
return null;
}
private boolean mergeLists(List
- mergeInto, List
- mergeFrom, boolean errorIfSizesDoNotMatch) {
// merge stacks
int intoSize = mergeInto.size();
int fromSize = mergeFrom.size();
boolean changed = false;
if (errorIfSizesDoNotMatch && intoSize != fromSize) {
if (DEBUG2) {
System.out.println("Bad merging items");
System.out.println("current items: " + mergeInto);
System.out.println("jump items: " + mergeFrom);
}
} else {
if (DEBUG2 && intoSize != fromSize) {
System.out.printf("Bad merging %d items from %d items%n", intoSize, fromSize);
System.out.println("current items: " + mergeInto);
System.out.println("jump items: " + mergeFrom);
}
List
- mergeIntoCopy = null;
if (DEBUG2) {
mergeIntoCopy = new ArrayList<>(mergeInto);
}
int common = Math.min(intoSize, fromSize);
for (int i = 0; i < common; i++) {
Item oldValue = mergeInto.get(i);
Item newValue = mergeFrom.get(i);
Item merged = Item.merge(oldValue, newValue);
if (merged != null && !merged.equals(oldValue)) {
mergeInto.set(i, merged);
changed = true;
}
}
/*
if (false) for (int i = common; i < fromSize; i++) {
Item newValue = mergeFrom.get(i);
mergeInto.add(newValue);
changed = true;
}
*/
if (DEBUG2 && changed) {
System.out.println("Merge results:");
System.out.println("updating: " + mergeIntoCopy);
System.out.println(" with: " + mergeFrom);
System.out.println(" gives: " + mergeInto);
}
}
return changed;
}
public void clear() {
stack.clear();
lvValues.clear();
}
public void printJumpEntries() {
for (int i = jumpEntryLocations.nextSetBit(0); i >= 0; i = jumpEntryLocations.nextSetBit(i + 1)) {
List
- stack = jumpStackEntries.get(i);
List
- locals = jumpEntries.get(i);
if (stack != null) {
System.out.printf("%4d: %s::%s%n", i, stack, locals);
} else {
System.out.printf("%4d: ::%s%n", i, locals);
}
}
}
public static class JumpInfo {
final Map
> jumpEntries;
final Map> jumpStackEntries;
final BitSet jumpEntryLocations;
JumpInfo(Map> jumpEntries, Map> jumpStackEntries, BitSet jumpEntryLocations) {
this.jumpEntries = jumpEntries;
this.jumpStackEntries = jumpStackEntries;
this.jumpEntryLocations = jumpEntryLocations;
}
public int getNextJump(int pc) {
return jumpEntryLocations.nextSetBit(pc);
}
}
public static class JumpInfoFactory extends edu.umd.cs.findbugs.classfile.engine.bcel.AnalysisFactory {
public JumpInfoFactory() {
super("Jump info for opcode stack", JumpInfo.class);
}
@Override
public @CheckForNull JumpInfo analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
Method method = analysisCache.getMethodAnalysis(Method.class, descriptor);
JavaClass jclass = getJavaClass(analysisCache, descriptor.getClassDescriptor());
Code code = method.getCode();
if (code == null) {
return null;
}
JumpStackComputation branchAnalysis = new JumpStackComputation(descriptor);
return computeJumpInfo(jclass, method, branchAnalysis);
}
static class JumpStackComputation extends BytecodeScanningDetector {
static final boolean DEBUG1 = false;
final MethodDescriptor descriptor;
private JumpStackComputation(MethodDescriptor descriptor) {
this.descriptor = descriptor;
}
protected OpcodeStack stack = new OpcodeStack();
public OpcodeStack getStack() {
return stack;
}
@Override
public final void visitCode(Code obj) {
if (!getMethodDescriptor().equals(descriptor)) {
throw new IllegalStateException();
}
if (DEBUG1) {
System.out.println(descriptor);
}
stack.resetForMethodEntry0(this);
super.visitCode(obj);
if (DEBUG1) {
System.out.println();
}
}
@Override
public void sawOpcode(int seen) {
stack.precomputation(this);
if (DEBUG1) {
System.out.printf("%4d %-15s %s%n", getPC(), Const.getOpcodeName(seen), stack);
}
try {
stack.sawOpcode(this, seen);
} catch (RuntimeException e) {
throw e;
}
}
}
public static @CheckForNull JumpInfo computeJumpInfo(JavaClass jclass, Method method,
JumpStackComputation branchAnalysis) {
branchAnalysis.setupVisitorForClass(jclass);
XMethod createXMethod = XFactory.createXMethod(jclass, method);
if (!(createXMethod instanceof MethodInfo)) {
return null;
}
MethodInfo xMethod = (MethodInfo) createXMethod;
int iteration = 1;
OpcodeStack myStack = branchAnalysis.stack;
/*
if (false) {
myStack.learnFrom(myStack.getJumpInfoFromStackMap());
}
*/
do {
if (DEBUG && iteration > 1) {
System.out.println("Iterative jump info for " + xMethod + ", iteration " + iteration);
myStack.printJumpEntries();
System.out.println();
}
// myStack.resetForMethodEntry0(ClassName.toSlashedClassName(jclass.getClassName()), method);
branchAnalysis.doVisitMethod(method);
if (xMethod.hasBackBranch() != myStack.backwardsBranch && !myStack.encountedTop) {
AnalysisContext.logError(
String.format("For %s, mismatch on existence of backedge: %s for precomputation, %s for bytecode analysis",
xMethod, xMethod.hasBackBranch(), myStack.backwardsBranch));
}
if (myStack.isJumpInfoChangedByNewTarget()) {
if (DEBUG) {
System.out.println("new target found, resetting iteration count");
}
iteration = 1;
}
if (iteration++ > 40) {
AnalysisContext.logError("Iterative jump info didn't converge after " + iteration + " iterations in " + xMethod
+ ", size " + method.getCode().getLength());
break;
}
} while (myStack.isJumpInfoChangedByBackwardsBranch() && myStack.backwardsBranch);
if (iteration > 20 && iteration <= 40) {
AnalysisContext.logError("Iterative jump info converged after " + iteration + " iterations in " + xMethod + ", size " + method
.getCode().getLength());
}
return new JumpInfo(myStack.jumpEntries, myStack.jumpStackEntries, myStack.jumpEntryLocations);
}
}
public boolean isJumpTarget(int pc) {
return jumpEntryLocations.get(pc);
}
private void addJumpValue(int from, int target) {
if (DEBUG) {
System.out.println("Set jump entry at " + methodName + ":" + target + " pc to " + stack + " : " + lvValues);
}
if (from >= target) {
backwardsBranch = true;
}
List- atTarget = jumpEntries.get(Integer.valueOf(target));
if (atTarget == null) {
setJumpInfoChangedByBackwardBranch("new target", from, target);
setJumpInfoChangedByNewTarget();
jumpEntries.put(Integer.valueOf(target), new ArrayList<>(lvValues));
jumpEntryLocations.set(target);
if (stack.size() > 0) {
jumpStackEntries.put(Integer.valueOf(target), new ArrayList<>(stack));
}
} else {
if (mergeLists(atTarget, lvValues, false)) {
setJumpInfoChangedByBackwardBranch("locals", from, target);
}
List
- stackAtTarget = jumpStackEntries.get(Integer.valueOf(target));
if (!stack.isEmpty() && stackAtTarget != null && mergeLists(stackAtTarget, stack, false)) {
setJumpInfoChangedByBackwardBranch("stack", from, target);
}
}
}
private String methodName;
DismantleBytecode v;
public void learnFrom(JumpInfo info) {
if (info == null) {
return;
}
jumpEntries = new HashMap<>(info.jumpEntries);
jumpStackEntries = new HashMap<>(info.jumpStackEntries);
jumpEntryLocations = (BitSet) info.jumpEntryLocations.clone();
}
public void initialize() {
setTop(false);
jumpEntries.clear();
jumpStackEntries.clear();
jumpEntryLocations.clear();
encountedTop = false;
backwardsBranch = false;
lastUpdate.clear();
convertJumpToOneZeroState = convertJumpToZeroOneState = 0;
zeroOneComing = -1;
registerTestedFoundToBeNonnegative = -1;
setReachOnlyByBranch(false);
}
public int resetForMethodEntry(final DismantleBytecode visitor) {
this.v = visitor;
initialize();
int result = resetForMethodEntry0(v);
Code code = v.getMethod().getCode();
if (code == null) {
return result;
}
JumpInfo jump = null;
if (useIterativeAnalysis) {
if (visitor instanceof OpcodeStackDetector.WithCustomJumpInfo) {
jump = ((OpcodeStackDetector.WithCustomJumpInfo) visitor).customJumpInfo();
} else if ((visitor instanceof OpcodeStackDetector) && !((OpcodeStackDetector) visitor).isUsingCustomUserValue()) {
jump = getJumpInfo();
} else {
jump = getJumpInfoFromStackMap();
}
} else {
jump = getJumpInfoFromStackMap();
}
learnFrom(jump);
return result;
}
int nullSafeSize(@CheckForNull Collection> c) {
if (c == null) {
return 0;
}
return c.size();
}
private JumpInfo getJumpInfo() {
IAnalysisCache analysisCache = Global.getAnalysisCache();
XMethod xMethod = XFactory.createXMethod(v.getThisClass(), v.getMethod());
if (xMethod instanceof MethodInfo) {
MethodInfo mi = (MethodInfo) xMethod;
if (!mi.hasBackBranch()) {
return null;
}
}
try {
return analysisCache.getMethodAnalysis(JumpInfo.class, xMethod.getMethodDescriptor());
} catch (CheckedAnalysisException e) {
AnalysisContext.logError("Error getting jump information", e);
return null;
}
}
private JumpInfoFromStackMap getJumpInfoFromStackMap() {
IAnalysisCache analysisCache = Global.getAnalysisCache();
XMethod xMethod = XFactory.createXMethod(v.getThisClass(), v.getMethod());
if (xMethod instanceof MethodInfo) {
MethodInfo mi = (MethodInfo) xMethod;
if (!mi.hasBackBranch()) {
return null;
}
}
try {
return analysisCache.getMethodAnalysis(JumpInfoFromStackMap.class, xMethod.getMethodDescriptor());
} catch (CheckedAnalysisException e) {
AnalysisContext.logError("Error getting jump information from StackMap", e);
return null;
}
}
public void setJumpInfoChangedByBackwardBranch(String kind, int from, int to) {
if (from < to) {
return;
}
if (DEBUG && !this.isJumpInfoChangedByBackwardsBranch()) {
System.out.printf("%s jump info at %d changed by jump from %d%n", kind, to, from);
}
this.setJumpInfoChangedByBackwardsBranch(from, to);
return;
}
private int resetForMethodEntry0(PreorderVisitor visitor) {
return resetForMethodEntry0(visitor.getClassName(), visitor.getMethod());
}
int resetForMethodEntry0(@SlashedClassName String className, Method m) {
methodName = m.getName();
if (DEBUG) {
System.out.println(" --- ");
}
String signature = m.getSignature();
stack.clear();
lvValues.clear();
top = false;
encountedTop = false;
backwardsBranch = false;
clearJumpInfoChangedByBackwardsBranch();
clearJumpInfoChangedByNewTarget();
setReachOnlyByBranch(false);
seenTransferOfControl = false;
exceptionHandlers.clear();
Code code = m.getCode();
if (code != null) {
CodeException[] exceptionTable = code.getExceptionTable();
if (exceptionTable != null) {
for (CodeException ex : exceptionTable) {
exceptionHandlers.set(ex.getHandlerPC());
}
}
}
if (DEBUG) {
System.out.println(" --- " + className + " " + m.getName() + " " + signature);
}
Type[] argTypes = Type.getArgumentTypes(signature);
int reg = 0;
if (!m.isStatic()) {
Item it = Item.initialArgument("L" + className + ";", reg);
setLVValue(reg, it);
reg += it.getSize();
}
for (Type argType : argTypes) {
Item it = Item.initialArgument(argType.getSignature(), reg);
setLVValue(reg, it);
reg += it.getSize();
}
return reg;
}
public int getStackDepth() {
return stack.size();
}
public Item getStackItem(int stackOffset) {
if (stackOffset < 0 || stackOffset >= stack.size()) {
AnalysisContext.logError("Can't get stack offset " + stackOffset + " from " + stack.toString() + " @ " + v.getPC()
+ " in " + v.getFullyQualifiedMethodName(), new IllegalArgumentException(stackOffset
+ " is not a value stack offset"));
return new Item("Lfindbugs/OpcodeStackError;");
}
int tos = stack.size() - 1;
int pos = tos - stackOffset;
try {
return stack.get(pos);
} catch (ArrayIndexOutOfBoundsException e) {
throw new ArrayIndexOutOfBoundsException("Requested item at offset " + stackOffset + " in a stack of size "
+ stack.size() + ", made request for position " + pos);
}
}
private Item pop() {
return stack.remove(stack.size() - 1);
}
private Item top() {
return stack.get(stack.size() - 1);
}
public void replace(int stackOffset, Item value) {
if (stackOffset < 0 || stackOffset >= stack.size()) {
AnalysisContext.logError("Can't get replace stack offset " + stackOffset + " from " + stack.toString() + " @ " + v.getPC()
+ " in " + v.getFullyQualifiedMethodName(), new IllegalArgumentException(stackOffset
+ " is not a value stack offset"));
}
int tos = stack.size() - 1;
int pos = tos - stackOffset;
stack.set(pos, value);
}
public void replaceTop(Item newTop) {
pop();
push(newTop);
}
private void pop(int count) {
while ((count--) > 0) {
pop();
}
}
private void push(Item i) {
stack.add(i);
}
private void pushByConstant(DismantleBytecode dbc, Constant c) {
if (c instanceof ConstantClass) {
push(new Item("Ljava/lang/Class;", ((ConstantClass) c).getConstantValue(dbc.getConstantPool())));
} else if (c instanceof ConstantInteger) {
push(new Item("I", Integer.valueOf(((ConstantInteger) c).getBytes())));
} else if (c instanceof ConstantString) {
int s = ((ConstantString) c).getStringIndex();
push(new Item("Ljava/lang/String;", getStringFromIndex(dbc, s)));
} else if (c instanceof ConstantFloat) {
push(new Item("F", Float.valueOf(((ConstantFloat) c).getBytes())));
} else if (c instanceof ConstantDouble) {
push(new Item("D", Double.valueOf(((ConstantDouble) c).getBytes())));
} else if (c instanceof ConstantLong) {
push(new Item("J", Long.valueOf(((ConstantLong) c).getBytes())));
} else if (c instanceof ConstantDynamic) {
ConstantPool cp = dbc.getConstantPool();
ConstantNameAndType sig = cp.getConstant(((ConstantDynamic) c).getNameAndTypeIndex());
String nameConstantOperand = ((ConstantUtf8) cp.getConstant(sig.getNameIndex())).getBytes();
String sigConstantOperand = ((ConstantUtf8) cp.getConstant(sig.getSignatureIndex())).getBytes();
push(new Item(sigConstantOperand, nameConstantOperand));
} else {
throw new UnsupportedOperationException("StaticConstant type not expected");
}
}
private void pushByLocalObjectLoad(DismantleBytecode dbc, int register) {
Method m = dbc.getMethod();
LocalVariableTable lvt = m.getLocalVariableTable();
if (lvt != null) {
LocalVariable lv = LVTHelper.getLocalVariableAtPC(lvt, register, dbc.getPC());
if (lv != null) {
String signature = lv.getSignature();
pushByLocalLoad(signature, register);
return;
}
}
pushByLocalLoad("Ljava/lang/Object;", register);
}
private void pushByIntMath(DismantleBytecode dbc, int seen, Item lhs, Item rhs) {
Item newValue = new Item("I");
if (lhs == null || rhs == null) {
push(newValue);
return;
}
try {
if (DEBUG) {
System.out.println("pushByIntMath " + dbc.getFullyQualifiedMethodName() + " @ " + dbc.getPC() + " : " + lhs
+ Const.getOpcodeName(seen) + rhs);
}
if (rhs.getConstant() != null && lhs.getConstant() != null) {
int lhsValue = constantToInt(lhs);
int rhsValue = constantToInt(rhs);
if ((seen == Const.IDIV || seen == Const.IREM) && rhsValue == 0) {
push(newValue);
return;
}
switch (seen) {
case Const.IADD:
newValue = new Item("I", lhsValue + rhsValue);
break;
case Const.ISUB:
newValue = new Item("I", lhsValue - rhsValue);
break;
case Const.IMUL:
newValue = new Item("I", lhsValue * rhsValue);
break;
case Const.IDIV:
newValue = new Item("I", lhsValue / rhsValue);
break;
case Const.IREM:
newValue = new Item("I", lhsValue % rhsValue);
break;
case Const.IAND:
newValue = new Item("I", lhsValue & rhsValue);
if ((rhsValue & 0xff) == 0 && rhsValue != 0 || (lhsValue & 0xff) == 0 && lhsValue != 0) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
}
break;
case Const.IOR:
newValue = new Item("I", lhsValue | rhsValue);
break;
case Const.IXOR:
newValue = new Item("I", lhsValue ^ rhsValue);
break;
case Const.ISHL:
newValue = new Item("I", lhsValue << rhsValue);
if (rhsValue >= 8) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
}
break;
case Const.ISHR:
newValue = new Item("I", lhsValue >> rhsValue);
break;
case Const.IUSHR:
newValue = new Item("I", lhsValue >>> rhsValue);
}
} else if ((seen == Const.ISHL || seen == Const.ISHR || seen == Const.IUSHR)) {
if (rhs.getConstant() != null) {
int constant = constantToInt(rhs);
if ((constant & 0x1f) == 0) {
newValue = new Item(lhs);
} else if (seen == Const.ISHL && (constant & 0x1f) >= 8) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
}
} else if (lhs.getConstant() != null) {
int constant = constantToInt(lhs);
if (constant == 0) {
newValue = new Item("I", 0);
}
}
} else if (lhs.getConstant() != null && seen == Const.IAND) {
int value = constantToInt(lhs);
if (value == 0) {
newValue = new Item("I", 0);
} else if ((value & 0xff) == 0) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
} else if (value >= 0) {
newValue.setSpecialKind(Item.NON_NEGATIVE);
}
} else if (rhs.getConstant() != null && seen == Const.IAND) {
int value = constantToInt(rhs);
if (value == 0) {
newValue = new Item("I", 0);
} else if ((value & 0xff) == 0) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
} else if (value >= 0) {
newValue.setSpecialKind(Item.NON_NEGATIVE);
}
} else if (seen == Const.IAND && lhs.getSpecialKind() == Item.ZERO_MEANS_NULL) {
newValue.setSpecialKind(Item.ZERO_MEANS_NULL);
newValue.setPC(lhs.getPC());
} else if (seen == Const.IAND && rhs.getSpecialKind() == Item.ZERO_MEANS_NULL) {
newValue.setSpecialKind(Item.ZERO_MEANS_NULL);
newValue.setPC(rhs.getPC());
} else if (seen == Const.IOR && lhs.getSpecialKind() == Item.NONZERO_MEANS_NULL) {
newValue.setSpecialKind(Item.NONZERO_MEANS_NULL);
newValue.setPC(lhs.getPC());
} else if (seen == Const.IOR && rhs.getSpecialKind() == Item.NONZERO_MEANS_NULL) {
newValue.setSpecialKind(Item.NONZERO_MEANS_NULL);
newValue.setPC(rhs.getPC());
}
} catch (ArithmeticException e) {
assert true; // ignore it
} catch (RuntimeException e) {
String msg = "Error processing2 " + lhs + Const.getOpcodeName(seen) + rhs + " @ " + dbc.getPC() + " in "
+ dbc.getFullyQualifiedMethodName();
AnalysisContext.logError(msg, e);
}
if (lhs.getSpecialKind() == Item.INTEGER_SUM && rhs.getConstant() != null) {
int rhsValue = constantToInt(rhs);
if (seen == Const.IDIV && rhsValue == 2 || seen == Const.ISHR && rhsValue == 1) {
newValue.setSpecialKind(Item.AVERAGE_COMPUTED_USING_DIVISION);
}
}
if (seen == Const.IADD && newValue.getSpecialKind() == Item.NOT_SPECIAL && lhs.getConstant() == null
&& rhs.getConstant() == null) {
newValue.setSpecialKind(Item.INTEGER_SUM);
}
if (seen == Const.IREM && lhs.getSpecialKind() == Item.HASHCODE_INT) {
newValue.setSpecialKind(Item.HASHCODE_INT_REMAINDER);
}
if (seen == Const.IREM && lhs.getSpecialKind() == Item.RANDOM_INT) {
newValue.setSpecialKind(Item.RANDOM_INT_REMAINDER);
}
if (seen == Const.IREM && lhs.checkForIntegerMinValue()) {
if (rhs.getConstant() != null) {
int rhsValue = constantToInt(rhs);
if (!Util.isPowerOfTwo(rhsValue)) {
newValue.setSpecialKind(lhs.getSpecialKindForRemainder());
}
} else {
newValue.setSpecialKind(lhs.getSpecialKindForRemainder());
}
}
if (DEBUG) {
System.out.println("push: " + newValue);
}
newValue.setPC(dbc.getPC());
push(newValue);
}
private void pushByLongMath(int seen, Item lhs, Item rhs) {
Item newValue = new Item("J");
try {
if ((rhs.getConstant() != null) && lhs.getConstant() != null) {
long lhsValue = constantToLong(lhs);
if (seen == Const.LSHL) {
newValue = new Item("J", Long.valueOf(lhsValue << constantToInt(rhs)));
if (constantToInt(rhs) >= 8) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
}
} else if (seen == Const.LSHR) {
newValue = new Item("J", Long.valueOf(lhsValue >> constantToInt(rhs)));
} else if (seen == Const.LUSHR) {
newValue = new Item("J", Long.valueOf(lhsValue >>> constantToInt(rhs)));
} else {
long rhsValue = constantToLong(rhs);
if (seen == Const.LADD) {
newValue = new Item("J", Long.valueOf(lhsValue + rhsValue));
} else if (seen == Const.LSUB) {
newValue = new Item("J", Long.valueOf(lhsValue - rhsValue));
} else if (seen == Const.LMUL) {
newValue = new Item("J", Long.valueOf(lhsValue * rhsValue));
} else if (seen == Const.LDIV) {
if (rhsValue != 0) {
newValue = new Item("J", Long.valueOf(lhsValue / rhsValue));
} else {
// Code like "return 1L / (1L / 2);"
// Here we could report bug, because it will throw java.lang.ArithmeticException: / by zero,
// see #413
}
} else if (seen == Const.LAND) {
newValue = new Item("J", Long.valueOf(lhsValue & rhsValue));
if ((rhsValue & 0xff) == 0 && rhsValue != 0 || (lhsValue & 0xff) == 0 && lhsValue != 0) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
}
} else if (seen == Const.LOR) {
newValue = new Item("J", Long.valueOf(lhsValue | rhsValue));
} else if (seen == Const.LXOR) {
newValue = new Item("J", Long.valueOf(lhsValue ^ rhsValue));
} else if (seen == Const.LREM) {
if (rhsValue != 0) {
newValue = new Item("J", Long.valueOf(lhsValue % rhsValue));
} else {
// Code like "return 1L % (1L / 2);"
// Here we could report bug, because it will throw java.lang.ArithmeticException: / by zero,
// see #413
}
}
}
} else if (rhs.getConstant() != null && seen == Const.LSHL && constantToInt(rhs) >= 8) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
} else if (lhs.getConstant() != null && seen == Const.LAND && (constantToLong(lhs) & 0xff) == 0) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
} else if (rhs.getConstant() != null && seen == Const.LAND && (constantToLong(rhs) & 0xff) == 0) {
newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
}
} catch (RuntimeException e) {
// TODO: this catch is probably not needed anymore after fixes for issue 386 and 413
// Added logging to see if there were even more issues with the code.
String context = v != null ? v.getFullyQualifiedMethodName() : toString();
AnalysisContext.logError(
String.format("Exception processing 'pushByLongMath' with opcode %d, lhs %s and rhs %s in %s",
seen, String.valueOf(lhs), String.valueOf(rhs), context), e);
}
push(newValue);
}
private void pushByFloatMath(int seen, Item it, Item it2) {
Item result;
@SpecialKind
int specialKind = Item.FLOAT_MATH;
if ((it.getConstant() instanceof Float) && it2.getConstant() instanceof Float) {
if (seen == Const.FADD) {
result = new Item("F", Float.valueOf(constantToFloat(it2) + constantToFloat(it)));
} else if (seen == Const.FSUB) {
result = new Item("F", Float.valueOf(constantToFloat(it2) - constantToFloat(it)));
} else if (seen == Const.FMUL) {
result = new Item("F", Float.valueOf(constantToFloat(it2) * constantToFloat(it)));
} else if (seen == Const.FDIV) {
result = new Item("F", Float.valueOf(constantToFloat(it2) / constantToFloat(it)));
} else if (seen == Const.FREM) {
result = new Item("F", Float.valueOf(constantToFloat(it2) % constantToFloat(it)));
} else {
result = new Item("F");
}
} else {
result = new Item("F");
if (seen == Const.DDIV) {
specialKind = Item.NASTY_FLOAT_MATH;
}
}
result.setSpecialKind(specialKind);
push(result);
}
private void pushByDoubleMath(int seen, Item it, Item it2) {
Item result;
@SpecialKind
int specialKind = Item.FLOAT_MATH;
if ((it.getConstant() instanceof Double) && it2.getConstant() instanceof Double) {
if (seen == Const.DADD) {
result = new Item("D", Double.valueOf(constantToDouble(it2) + constantToDouble(it)));
} else if (seen == Const.DSUB) {
result = new Item("D", Double.valueOf(constantToDouble(it2) - constantToDouble(it)));
} else if (seen == Const.DMUL) {
result = new Item("D", Double.valueOf(constantToDouble(it2) * constantToDouble(it)));
} else if (seen == Const.DDIV) {
result = new Item("D", Double.valueOf(constantToDouble(it2) / constantToDouble(it)));
} else if (seen == Const.DREM) {
result = new Item("D", Double.valueOf(constantToDouble(it2) % constantToDouble(it)));
} else {
result = new Item("D"); // ?
}
} else {
result = new Item("D");
if (seen == Const.DDIV) {
specialKind = Item.NASTY_FLOAT_MATH;
}
}
result.setSpecialKind(specialKind);
push(result);
}
private void pushByInvoke(DismantleBytecode dbc, boolean popThis) {
String signature = dbc.getSigConstantOperand();
if (Const.CONSTRUCTOR_NAME.equals(dbc.getNameConstantOperand()) && signature.endsWith(")V") && popThis) {
pop(PreorderVisitor.getNumberArguments(signature));
Item constructed = pop();
if (getStackDepth() > 0) {
Item next = getStackItem(0);
if (constructed.equals(next)) {
next = new Item(next);
next.source = XFactory.createReferencedXMethod(dbc);
next.pc = dbc.getPC();
replace(0, next);
}
}
return;
}
pop(PreorderVisitor.getNumberArguments(signature) + (popThis ? 1 : 0));
pushBySignature(new SignatureParser(signature).getReturnTypeSignature(), dbc);
}
public Item getItemMethodInvokedOn(DismantleBytecode dbc) {
int opcode = dbc.getOpcode();
switch (opcode) {
case Const.INVOKEVIRTUAL:
case Const.INVOKEINTERFACE:
case Const.INVOKESPECIAL:
String signature = dbc.getSigConstantOperand();
int stackOffset = PreorderVisitor.getNumberArguments(signature);
return getStackItem(stackOffset);
}
throw new IllegalArgumentException("Not visiting an instance method call");
}
private String getStringFromIndex(DismantleBytecode dbc, int i) {
ConstantUtf8 name = (ConstantUtf8) dbc.getConstantPool().getConstant(i);
return name.getBytes();
}
private void pushBySignature(String s, DismantleBytecode dbc) {
if ("V".equals(s)) {
return;
}
Item item = new Item(s, (Object) null);
if (dbc != null) {
item.setPC(dbc.getPC());
}
if ("B".equals(s)) {
item.setSpecialKind(Item.SIGNED_BYTE);
} else if ("C".equals(s)) {
item.setSpecialKind(Item.NON_NEGATIVE);
}
push(item);
}
private void pushByLocalStore(int register) {
Item it = new Item(pop());
if (it.getRegisterNumber() != register) {
clearRegisterLoad(lvValues, register);
clearRegisterLoad(stack, register);
}
if (it.registerNumber == -1) {
it.registerNumber = register;
}
setLVValue(register, it);
}
private static void clearRegisterLoad(List
- list, int register) {
for (int pos = 0; pos < list.size(); pos++) {
Item i = list.get(pos);
if (i != null && (i.registerNumber == register || i.fieldLoadedFromRegister == register)) {
i = new Item(i);
if (i.registerNumber == register) {
i.registerNumber = -1;
}
if (i.fieldLoadedFromRegister == register) {
i.fieldLoadedFromRegister = -1;
}
list.set(pos, i);
}
}
}
private void pushByLocalLoad(String signature, int register) {
Item oldItem = new Item(getLVValue(register));
Item newItem = oldItem;
if ("Ljava/lang/Object;".equals(newItem.signature) && !"Ljava/lang/Object;".equals(signature)) {
newItem = new Item(oldItem);
newItem.signature = signature;
}
if (newItem.getRegisterNumber() < 0) {
if (newItem == oldItem) {
newItem = new Item(oldItem);
}
newItem.registerNumber = register;
}
push(newItem);
}
private void setLVValue(int index, Item value) {
int addCount = index - lvValues.size() + 1;
while ((addCount--) > 0) {
lvValues.add(null);
}
if (!useIterativeAnalysis && seenTransferOfControl) {
value = Item.merge(value, lvValues.get(index));
}
lvValues.set(index, value);
}
@Nonnull
public Item getLVValue(int index) {
if (index >= lvValues.size()) {
return new Item();
}
Item item = lvValues.get(index);
if (item != null) {
return item;
}
return new Item();
}
public int getNumLocalValues() {
return lvValues.size();
}
private void setTop(boolean top) {
if (top) {
if (!this.top) {
this.top = true;
}
} else if (this.top) {
this.top = false;
}
}
public boolean isTop() {
return top;
}
void setReachOnlyByBranch(boolean reachOnlyByBranch) {
if (reachOnlyByBranch) {
setTop(true);
}
this.reachOnlyByBranch = reachOnlyByBranch;
}
boolean isReachOnlyByBranch() {
return reachOnlyByBranch;
}
boolean isJumpInfoChangedByBackwardsBranch() {
return jumpInfoChangedByBackwardsBranch;
}
void clearJumpInfoChangedByBackwardsBranch() {
this.jumpInfoChangedByBackwardsBranch = false;
}
void setJumpInfoChangedByBackwardsBranch(int from, int to) {
this.jumpInfoChangedByBackwardsBranch = true;
}
/**
* @return Returns the jumpInfoChangedByNewTarget.
*/
protected boolean isJumpInfoChangedByNewTarget() {
return jumpInfoChangedByNewTarget;
}
void clearJumpInfoChangedByNewTarget() {
this.jumpInfoChangedByNewTarget = false;
}
protected void setJumpInfoChangedByNewTarget() {
this.jumpInfoChangedByNewTarget = true;
}
}