serianalyzer.JVMImpl.java Source code

Java tutorial

Introduction

Here is the source code for serianalyzer.JVMImpl.java

Source

/**
 *   This file is part of Serianalyzer.
 *
 *   Serianalyzer is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Serianalyzer 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Serianalyzer.  If not, see <http://www.gnu.org/licenses/>.
 *   
 * Copyright 2015,2016 Moritz Bechler <mbechler@eenterphace.org>
 * 
 * Created: 15.11.2015 by mbechler
 */
package serianalyzer;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.jboss.jandex.DotName;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import serianalyzer.types.BaseType;
import serianalyzer.types.BasicConstant;
import serianalyzer.types.BasicVariable;
import serianalyzer.types.FieldReference;
import serianalyzer.types.MultiAlternatives;
import serianalyzer.types.ObjectReferenceConstant;
import serianalyzer.types.SimpleType;

/**
 * @author mbechler
 *
 */
public final class JVMImpl {

    private static final Logger log = Logger.getLogger(JVMImpl.class);

    /**
     * @param opcode
     * @param label
     * @param s
     * @return
     */
    static boolean handleJVMJump(int opcode, Label label, JVMStackState s) {
        boolean tainted;
        switch (opcode) {
        case Opcodes.IF_ICMPEQ:
        case Opcodes.IF_ICMPNE:
        case Opcodes.IF_ICMPLT:
        case Opcodes.IF_ICMPGE:
        case Opcodes.IF_ICMPGT:
        case Opcodes.IF_ICMPLE:
        case Opcodes.IF_ACMPEQ:
        case Opcodes.IF_ACMPNE:
            BaseType o1 = s.pop();
            BaseType o2 = s.pop();
            tainted = !(o1 != null) || !(o2 != null) || o1.isTainted() || o2.isTainted();
            break;
        case Opcodes.IFEQ:
        case Opcodes.IFNE:
        case Opcodes.IFLT:
        case Opcodes.IFGE:
        case Opcodes.IFGT:
        case Opcodes.IFLE:
        case Opcodes.IFNULL:
        case Opcodes.IFNONNULL:
            BaseType c = s.pop();
            tainted = (c == null || c.isTainted());
            break;

        case Opcodes.JSR:
            s.push(new BasicConstant(Type.INT_TYPE, label));
            tainted = false;
            break;
        case Opcodes.GOTO:
            tainted = false;
            break;
        default:
            log.warn("Unsupported opcode " + opcode); //$NON-NLS-1$
            tainted = true;
        }
        return tainted;
    }

    /**
     * @param opcode
     * @param operand
     * @param s
     */
    static void handleJVMIntInsn(int opcode, int operand, JVMStackState s) {
        switch (opcode) {
        case Opcodes.BIPUSH:
            s.push(new BasicConstant(Type.BYTE_TYPE, operand));
            break;
        case Opcodes.SIPUSH:
            s.push(new BasicConstant(Type.SHORT_TYPE, operand));
            break;
        case Opcodes.NEWARRAY:
            s.pop();
            s.push(new BasicVariable(makeBasicArrayType(operand), "array", false)); //$NON-NLS-1$
        }
    }

    /**
     * @param opcode
     * @param s
     */
    static void handleJVMInsn(int opcode, JVMStackState s) {
        BaseType o1;
        BaseType o2;
        BaseType o3;
        List<BaseType> l1;
        List<BaseType> l2;
        switch (opcode) {
        case Opcodes.NOP:
            break;

        case Opcodes.ARRAYLENGTH:
            o1 = s.pop();
            s.push(new BasicConstant(Type.INT_TYPE, 0, !(o1 != null && !o1.isTainted())));
            break;
        case Opcodes.ACONST_NULL:
            s.push(new BasicConstant(Type.VOID_TYPE, "<null>")); //$NON-NLS-1$
            break;
        case Opcodes.ICONST_M1:
        case Opcodes.ICONST_0:
        case Opcodes.ICONST_1:
        case Opcodes.ICONST_2:
        case Opcodes.ICONST_3:
        case Opcodes.ICONST_4:
        case Opcodes.ICONST_5:
            s.push(new BasicConstant(Type.INT_TYPE, opcode - 3));
            break;
        case Opcodes.LCONST_0:
        case Opcodes.LCONST_1:
            s.push(new BasicConstant(Type.LONG_TYPE, opcode - 9L));
            break;
        case Opcodes.FCONST_0:
        case Opcodes.FCONST_1:
        case Opcodes.FCONST_2:
            s.push(new BasicConstant(Type.FLOAT_TYPE, opcode - 11f));
            break;
        case Opcodes.DCONST_0:
        case Opcodes.DCONST_1:
            s.push(new BasicConstant(Type.DOUBLE_TYPE, opcode - 14d));
            break;
        case Opcodes.IALOAD:
        case Opcodes.LALOAD:
        case Opcodes.FALOAD:
        case Opcodes.DALOAD:
        case Opcodes.BALOAD:
        case Opcodes.CALOAD:
        case Opcodes.SALOAD:
            o1 = s.pop();
            o2 = s.pop();
            s.push(new BasicVariable(toType(opcode), "primitive array elem", //$NON-NLS-1$
                    (o1 == null || o1.isTainted()) | (o2 == null || o2.isTainted())));
            break;

        case Opcodes.AALOAD:
            o1 = s.pop();
            o2 = s.pop();
            if (o1 != null && o2 instanceof SimpleType && ((SimpleType) o2).getType().toString().startsWith("[")) { //$NON-NLS-1$
                Type atype = Type.getType(((SimpleType) o2).getType().toString().substring(1));
                if (o2.getAlternativeTypes() != null && !o2.getAlternativeTypes().isEmpty()) {
                    s.clear();
                    break;
                }
                s.push(new BasicVariable(atype, "array elem " + atype, o1.isTainted() | o2.isTainted())); //$NON-NLS-1$
            } else {
                s.clear();
            }
            break;

        case Opcodes.IASTORE:
        case Opcodes.LASTORE:
        case Opcodes.FASTORE:
        case Opcodes.DASTORE:
        case Opcodes.AASTORE:
        case Opcodes.BASTORE:
        case Opcodes.CASTORE:
        case Opcodes.SASTORE:
            s.pop(3);
            break;

        case Opcodes.POP2:
            s.pop();
        case Opcodes.MONITORENTER:
        case Opcodes.MONITOREXIT:
        case Opcodes.POP:
            s.pop();
            break;

        case Opcodes.DUP:
            if (!s.isEmpty()) {
                o1 = s.pop();
                s.push(o1);
                s.push(o1);
            }
            break;
        case Opcodes.DUP_X1:
            o1 = s.pop();
            o2 = s.pop();
            s.push(o1);
            s.push(o2);
            s.push(o1);
            break;
        case Opcodes.DUP_X2:
            o1 = s.pop();
            o2 = s.pop();
            o3 = s.pop();
            s.push(o1);
            s.push(o3);
            s.push(o2);
            s.push(o1);
            break;
        case Opcodes.DUP2:
            l1 = s.popWord();
            if (l1.isEmpty()) {
                log.trace("DUP2 with unknown operand"); //$NON-NLS-1$
                s.clear();
            } else {
                s.pushWord(l1);
                s.pushWord(l1);
            }
            break;
        case Opcodes.DUP2_X1:
            l1 = s.popWord();
            o1 = s.pop();
            if (l1.isEmpty()) {
                log.trace("DUP2 with unknown operand"); //$NON-NLS-1$
                s.clear();
            } else {
                s.pushWord(l1);
                s.push(o1);
                s.pushWord(l1);
            }
            break;
        case Opcodes.DUP2_X2:
            l1 = s.popWord();
            l2 = s.popWord();
            if (l1.isEmpty() || l2.isEmpty()) {
                log.trace("DUP2 with unknown operand"); //$NON-NLS-1$
                s.clear();
            } else {
                s.pushWord(l1);
                s.pushWord(l2);
                s.pushWord(l1);
            }
            break;

        case Opcodes.SWAP:
            o1 = s.pop();
            o2 = s.pop();
            s.push(o1);
            s.push(o2);
            break;

        case Opcodes.IADD:
        case Opcodes.LADD:
        case Opcodes.FADD:
        case Opcodes.DADD:
        case Opcodes.ISUB:
        case Opcodes.LSUB:
        case Opcodes.FSUB:
        case Opcodes.DSUB:
        case Opcodes.IMUL:
        case Opcodes.LMUL:
        case Opcodes.FMUL:
        case Opcodes.DMUL:
        case Opcodes.IDIV:
        case Opcodes.LDIV:
        case Opcodes.FDIV:
        case Opcodes.DDIV:
        case Opcodes.IREM:
        case Opcodes.LREM:
        case Opcodes.FREM:
        case Opcodes.DREM:
        case Opcodes.IAND:
        case Opcodes.LAND:
        case Opcodes.IOR:
        case Opcodes.LOR:
        case Opcodes.IXOR:
        case Opcodes.LXOR:
        case Opcodes.LCMP:
        case Opcodes.FCMPL:
        case Opcodes.FCMPG:
        case Opcodes.DCMPL:
        case Opcodes.DCMPG:
            s.merge(2);
            break;

        case Opcodes.ISHL:
        case Opcodes.LSHL:
        case Opcodes.ISHR:
        case Opcodes.LSHR:
        case Opcodes.IUSHR:
        case Opcodes.LUSHR:
            s.pop(); // amount
            // ignore value
            break;

        case Opcodes.INEG:
        case Opcodes.F2I:
        case Opcodes.D2I:
        case Opcodes.L2I:
            s.push(cast(s.pop(), Type.INT_TYPE));
            break;

        case Opcodes.LNEG:
        case Opcodes.I2L:
        case Opcodes.F2L:
        case Opcodes.D2L:
            s.push(cast(s.pop(), Type.LONG_TYPE));
            break;

        case Opcodes.FNEG:
        case Opcodes.I2F:
        case Opcodes.L2F:
        case Opcodes.D2F:
            s.push(cast(s.pop(), Type.FLOAT_TYPE));

        case Opcodes.DNEG:
        case Opcodes.I2D:
        case Opcodes.L2D:
        case Opcodes.F2D:
            s.push(cast(s.pop(), Type.DOUBLE_TYPE));

        case Opcodes.I2B:
            s.push(cast(s.pop(), Type.BYTE_TYPE));
            break;
        case Opcodes.I2C:
            s.push(cast(s.pop(), Type.CHAR_TYPE));
            break;
        case Opcodes.I2S:
            s.push(cast(s.pop(), Type.SHORT_TYPE));
            break;

        case Opcodes.ARETURN:
            s.clear();
            break;

        case Opcodes.IRETURN:
        case Opcodes.LRETURN:
        case Opcodes.FRETURN:
        case Opcodes.DRETURN:
        case Opcodes.RETURN:
            if (log.isTraceEnabled()) {
                log.trace("Found return " + s.pop()); //$NON-NLS-1$
            }
            s.clear();
            break;

        case Opcodes.ATHROW:
            Object thrw = s.pop();
            log.trace("Found throw " + thrw); //$NON-NLS-1$
            s.clear();
            break;

        default:
            log.warn("Unsupported instruction code " + opcode); //$NON-NLS-1$
        }
    }

    /**
     * @param opcode
     * @param type
     * @param s
     */
    static void handleJVMTypeInsn(int opcode, String type, JVMStackState s) {
        BaseType o;
        switch (opcode) {
        case Opcodes.NEW:
            s.push(new ObjectReferenceConstant(false, Type.getObjectType(type), type.replace('/', '.')));
            break;
        case Opcodes.ANEWARRAY:
            s.pop();
            if (type.charAt(0) == '[') {
                s.push(new BasicVariable(Type.getObjectType("[" + type), "array", false)); //$NON-NLS-1$//$NON-NLS-2$
            } else {
                s.push(new BasicVariable(Type.getObjectType("[L" + type + ";"), "array", false)); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
            }
            break;
        case Opcodes.CHECKCAST:
            if (log.isDebugEnabled()) {
                log.debug("Checkcast " + type); //$NON-NLS-1$
            }
            o = s.pop();
            if (o != null) {
                o.addAlternativeType(Type.getObjectType(type));
                s.push(o);
            } else {
                s.clear();
            }
            break;
        case Opcodes.INSTANCEOF:
            o = s.pop();
            if (o != null) {
                o.addAlternativeType(Type.getObjectType(type));
            }
            s.push(new BasicConstant(Type.BOOLEAN_TYPE, "typeof " + o + " = " + type, //$NON-NLS-1$//$NON-NLS-2$
                    !(o != null) || o.isTainted()));
            break;
        }
    }

    /**
     * @param opcode
     * @param var
     * @param s
     */
    static void handleVarInsn(int opcode, int var, JVMStackState s) {
        Set<BaseType> v;
        switch (opcode) {
        case Opcodes.LLOAD:
        case Opcodes.ILOAD:
        case Opcodes.FLOAD:
        case Opcodes.DLOAD:
        case Opcodes.ALOAD:
            v = s.getVariable(var);
            if (log.isTraceEnabled()) {
                log.trace("LOAD " + opcode + "@" + var + ":" + v); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }
            if (v == null || v.isEmpty()) {
                s.push(new BasicVariable(toType(opcode), "unknown " + var, true)); //$NON-NLS-1$
            } else if (v.size() == 1) {
                s.push(v.iterator().next());
            } else {
                Set<BaseType> alts = new HashSet<>();
                for (BaseType o : v) {
                    if (o instanceof MultiAlternatives && !((MultiAlternatives) o).getAlternatives().isEmpty()) {
                        alts.addAll(((MultiAlternatives) o).getAlternatives());
                    } else {
                        alts.add(o);
                    }
                }
                s.push(new MultiAlternatives(alts));
            }
            break;
        case Opcodes.LSTORE:
        case Opcodes.ISTORE:
        case Opcodes.FSTORE:
        case Opcodes.DSTORE:
        case Opcodes.ASTORE:
            s.getVariable(var).add(s.pop());
            break;
        case Opcodes.RET:
            break;
        default:
            log.warn("Unimplemented opcode " + opcode); //$NON-NLS-1$
        }
    }

    /**
     * @param cst
     * @param s
     */
    static void handleLdcInsn(Object cst, JVMStackState s) {
        if (cst instanceof Type) {
            int sort = ((Type) cst).getSort();
            // TODO: not really sure about this, but the type seems expected
            if (sort == Type.OBJECT) {
                if (log.isDebugEnabled()) {
                    log.debug("Constant type sort object " + cst); //$NON-NLS-1$
                }
                s.push(new BasicVariable(Type.getType("Ljava/lang/Class;"), "type " + cst, false)); //$NON-NLS-1$ //$NON-NLS-2$
            } else if (sort == Type.ARRAY) {
                if (log.isDebugEnabled()) {
                    log.debug("Constant type sort array" + cst); //$NON-NLS-1$
                }
                s.push(new BasicVariable(Type.getType("Ljava/lang/Class;"), "type " + cst, false)); //$NON-NLS-1$ //$NON-NLS-2$
            } else {
                log.warn("Unhandled constant type sort " + sort); //$NON-NLS-1$
            }
        } else if (cst instanceof String || cst instanceof Integer || cst instanceof Float || cst instanceof Long
                || cst instanceof Double) {
            Type type = typeFromClass(cst);
            s.push(new BasicVariable(type, "const prim " + cst + " type " + type, false)); //$NON-NLS-1$ //$NON-NLS-2$
        } else {
            log.warn("Unhandled constant type " + cst.getClass()); //$NON-NLS-1$
        }
    }

    /**
     * @param opcode
     * @param owner
     * @param name
     * @param desc
     * @param s
     */
    static void handleFieldInsn(int opcode, String owner, String name, String desc, JVMStackState s) {
        switch (opcode) {
        case Opcodes.GETFIELD:
            Object tgt = s.pop();
            if (log.isTraceEnabled()) {
                log.trace("From " + tgt); //$NON-NLS-1$
            }
        case Opcodes.GETSTATIC:
            // this can be more specific
            if (log.isTraceEnabled()) {
                log.trace("Load field " + name + " (" + desc + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }
            s.push(new FieldReference(DotName.createSimple(owner.replace('/', '.')), name, Type.getType(desc),
                    true));
            break;
        case Opcodes.PUTFIELD:
            s.pop();
            s.pop();
            break;
        case Opcodes.PUTSTATIC:
            s.pop();
            break;
        default:
            log.warn("Unsupported opcode " + opcode); //$NON-NLS-1$
        }
    }

    /**
     * @param cst
     * @return
     */
    private static Type typeFromClass(Object cst) {
        if (cst instanceof Integer) {
            return Type.INT_TYPE;
        } else if (cst instanceof Float) {
            return Type.FLOAT_TYPE;
        } else if (cst instanceof Long) {
            return Type.LONG_TYPE;
        } else if (cst instanceof Double) {
            return Type.DOUBLE_TYPE;
        }
        return Type.getType(cst.getClass());
    }

    /**
     * @param opcode
     * @return
     */
    private static Type toType(int opcode) {
        switch (opcode) {
        case Opcodes.LLOAD:
            return Type.LONG_TYPE;
        case Opcodes.ILOAD:
            return Type.INT_TYPE;
        case Opcodes.FLOAD:
            return Type.FLOAT_TYPE;
        case Opcodes.DLOAD:
            return Type.DOUBLE_TYPE;
        case Opcodes.ALOAD:
            return Type.getType("Ljava/lang/Object;"); //$NON-NLS-1$
        case Opcodes.IALOAD:
            return Type.INT_TYPE;
        case Opcodes.LALOAD:
            return Type.LONG_TYPE;
        case Opcodes.FALOAD:
            return Type.FLOAT_TYPE;
        case Opcodes.DALOAD:
            return Type.DOUBLE_TYPE;
        case Opcodes.BALOAD:
            return Type.BYTE_TYPE;
        case Opcodes.CALOAD:
            return Type.CHAR_TYPE;
        case Opcodes.SALOAD:
            return Type.SHORT_TYPE;
        }
        return Type.VOID_TYPE;
    }

    /**
     * @param pop
     * @param type
     * @return
     */
    private static BaseType cast(BaseType pop, Type type) {
        if (pop instanceof BasicConstant) {
            return new BasicConstant(type, ((BasicConstant) pop).getValue(), ((BasicConstant) pop).isTainted());
        } else if (pop != null) {
            return new BasicVariable(type, pop.toString(), pop.isTainted());
        }

        return new BasicVariable(type, null, true);
    }

    /**
     * @param operand
     * @return
     */
    private static Type makeBasicArrayType(int operand) {

        switch (operand) {
        case Opcodes.T_BOOLEAN:
            return Type.getType("[Z"); //$NON-NLS-1$
        case Opcodes.T_BYTE:
            return Type.getType("[B"); //$NON-NLS-1$
        case Opcodes.T_CHAR:
            return Type.getType("[C"); //$NON-NLS-1$
        case Opcodes.T_DOUBLE:
            return Type.getType("[D"); //$NON-NLS-1$
        case Opcodes.T_FLOAT:
            return Type.getType("[F"); //$NON-NLS-1$
        case Opcodes.T_INT:
            return Type.getType("[I"); //$NON-NLS-1$
        case Opcodes.T_LONG:
            return Type.getType("[J"); //$NON-NLS-1$
        case Opcodes.T_SHORT:
            return Type.getType("[S"); //$NON-NLS-1$

        default:
            log.error("Unknown array type " + operand); //$NON-NLS-1$
        }
        return null;
    }

    /**
     * @param desc
     * @param dims
     * @param s
     */
    static void handleMultiANewArrayInsn(String desc, int dims, JVMStackState s) {
        s.pop(dims);
        s.push(new BasicVariable(Type.getType(desc), dims + "-dim array", false)); //$NON-NLS-1$
    }
}