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

qilin.core.reflection.TamiflexModel Maven / Gradle / Ivy

/* Qilin - a Java Pointer Analysis Framework
 * Copyright (C) 2021-2030 Qilin developers
 *
 * This program 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 3.0 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 */

package qilin.core.reflection;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import qilin.CoreConfig;
import qilin.core.PTAScene;
import qilin.util.DataFactory;
import qilin.util.PTAUtils;
import sootup.core.jimple.Jimple;
import sootup.core.jimple.basic.*;
import sootup.core.jimple.common.constant.ClassConstant;
import sootup.core.jimple.common.constant.IntConstant;
import sootup.core.jimple.common.constant.NullConstant;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.expr.JNewArrayExpr;
import sootup.core.jimple.common.expr.JNewExpr;
import sootup.core.jimple.common.expr.JSpecialInvokeExpr;
import sootup.core.jimple.common.expr.JStaticInvokeExpr;
import sootup.core.jimple.common.expr.JVirtualInvokeExpr;
import sootup.core.jimple.common.ref.JArrayRef;
import sootup.core.jimple.common.ref.JFieldRef;
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.core.jimple.common.stmt.JInvokeStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.Body;
import sootup.core.model.SootClass;
import sootup.core.model.SootField;
import sootup.core.model.SootMethod;
import sootup.core.signatures.FieldSignature;
import sootup.core.signatures.MethodSubSignature;
import sootup.core.types.ArrayType;
import sootup.core.types.ReferenceType;
import sootup.java.core.JavaIdentifierFactory;
import sootup.java.core.language.JavaJimple;

/**
 * This reflection model handles reflection according to the dynamic traces recorded through
 * Tamiflex.
 */
public class TamiflexModel extends ReflectionModel {
  protected Map>> reflectionMap;

  public TamiflexModel(PTAScene ptaScene) {
    super(ptaScene);
    this.reflectionMap = DataFactory.createMap();
    parseTamiflexLog(CoreConfig.v().getAppConfig().REFLECTION_LOG, false);
  }

  @Override
  Collection transformClassForName(Stmt s) {
    // 
    // 
    Collection ret = DataFactory.createSet();
    Map> classForNames =
        reflectionMap.getOrDefault(ReflectionKind.ClassForName, Collections.emptyMap());
    if (classForNames.containsKey(s)) {
      Collection fornames = classForNames.get(s);
      for (String clazz : fornames) {
        // !TODO potential bug
        ClassConstant cc = JavaJimple.getInstance().newClassConstant(dot2slashStyle(clazz));
        if (s instanceof JAssignStmt) {
          LValue lvalue = ((JAssignStmt) s).getLeftOp();
          ret.add(new JAssignStmt(lvalue, cc, StmtPositionInfo.getNoStmtPositionInfo()));
        }
      }
    }
    return ret;
  }

  public static String dot2slashStyle(String clazz) {
    String x = clazz.replace('.', '/');
    return "L" + x + ";";
  }

  @Override
  protected Collection transformClassNewInstance(Stmt s) {
    // 
    if (!(s instanceof JAssignStmt)) {
      return Collections.emptySet();
    }
    LValue lvalue = ((JAssignStmt) s).getLeftOp();
    Collection ret = DataFactory.createSet();
    Map> classNewInstances =
        reflectionMap.getOrDefault(ReflectionKind.ClassNewInstance, Collections.emptyMap());
    if (classNewInstances.containsKey(s)) {
      Collection classNames = classNewInstances.get(s);
      for (String clsName : classNames) {
        SootClass cls = ptaScene.getSootClass(clsName);
        MethodSubSignature initSubSig =
            JavaIdentifierFactory.getInstance().parseMethodSubSignature("void ()");
        Optional omthd = cls.getMethod(initSubSig);
        if (omthd.isPresent()) {
          JNewExpr newExpr = new JNewExpr(cls.getType());
          ret.add(new JAssignStmt(lvalue, newExpr, StmtPositionInfo.getNoStmtPositionInfo()));
          SootMethod constructor = omthd.get();
          ret.add(
              new JInvokeStmt(
                  new JSpecialInvokeExpr(
                      (Local) lvalue, constructor.getSignature(), Collections.emptyList()),
                  StmtPositionInfo.getNoStmtPositionInfo()));
        }
      }
    }
    return ret;
  }

  @Override
  protected Collection transformContructorNewInstance(Stmt s) {
    // 
    if (!(s instanceof JAssignStmt)) {
      return Collections.emptySet();
    }
    LValue lvalue = ((JAssignStmt) s).getLeftOp();
    Collection ret = DataFactory.createSet();
    Map> constructorNewInstances =
        reflectionMap.getOrDefault(ReflectionKind.ConstructorNewInstance, Collections.emptyMap());
    if (constructorNewInstances.containsKey(s)) {
      Collection constructorSignatures = constructorNewInstances.get(s);
      AbstractInvokeExpr iie = s.getInvokeExpr();
      Value args = iie.getArg(0);
      JArrayRef arrayRef =
          JavaJimple.getInstance().newArrayRef((Local) args, IntConstant.getInstance(0));
      Local arg =
          Jimple.newLocal("intermediate/" + arrayRef, PTAUtils.getClassType("java.lang.Object"));
      ret.add(new JAssignStmt(arg, arrayRef, StmtPositionInfo.getNoStmtPositionInfo()));
      for (String constructorSignature : constructorSignatures) {
        SootMethod constructor = ptaScene.getMethod(constructorSignature);
        JNewExpr newExpr = new JNewExpr(constructor.getDeclaringClassType());
        ret.add(new JAssignStmt(lvalue, newExpr, StmtPositionInfo.getNoStmtPositionInfo()));
        int argCount = constructor.getParameterCount();
        List mArgs = new ArrayList<>(argCount);
        for (int i = 0; i < argCount; i++) {
          mArgs.add(arg);
        }
        ret.add(
            new JInvokeStmt(
                new JSpecialInvokeExpr((Local) lvalue, constructor.getSignature(), mArgs),
                StmtPositionInfo.getNoStmtPositionInfo()));
      }
    }
    return ret;
  }

  @Override
  protected Collection transformMethodInvoke(Stmt s) {
    // 
    Collection ret = DataFactory.createSet();
    Map> methodInvokes =
        reflectionMap.getOrDefault(ReflectionKind.MethodInvoke, Collections.emptyMap());
    if (methodInvokes.containsKey(s)) {
      Collection methodSignatures = methodInvokes.get(s);
      AbstractInvokeExpr iie = s.getInvokeExpr();
      Value base = iie.getArg(0);
      Value args = iie.getArg(1);
      Local arg = null;
      if (args.getType() instanceof ArrayType) {
        JArrayRef arrayRef =
            JavaJimple.getInstance().newArrayRef((Local) args, IntConstant.getInstance(0));
        arg =
            Jimple.newLocal("intermediate/" + arrayRef, PTAUtils.getClassType("java.lang.Object"));
        ret.add(new JAssignStmt(arg, arrayRef, StmtPositionInfo.getNoStmtPositionInfo()));
      }

      for (String methodSignature : methodSignatures) {
        SootMethod method = ptaScene.getMethod(methodSignature);
        int argCount = method.getParameterCount();
        List mArgs = new ArrayList<>(argCount);
        for (int i = 0; i < argCount; i++) {
          if (arg != null) {
            mArgs.add(arg);
          } else {
            mArgs.add(NullConstant.getInstance());
          }
        }
        AbstractInvokeExpr ie;
        if (method.isStatic()) {
          assert base instanceof NullConstant;
          ie = new JStaticInvokeExpr(method.getSignature(), mArgs);
        } else {
          assert !(base instanceof NullConstant);
          ie = new JVirtualInvokeExpr((Local) base, method.getSignature(), mArgs);
        }
        if (s instanceof JAssignStmt) {
          LValue lvalue = ((JAssignStmt) s).getLeftOp();
          ret.add(new JAssignStmt(lvalue, ie, StmtPositionInfo.getNoStmtPositionInfo()));
        } else {
          ret.add(new JInvokeStmt(ie, StmtPositionInfo.getNoStmtPositionInfo()));
        }
      }
    }
    return ret;
  }

  @Override
  protected Collection transformFieldSet(Stmt s) {
    // 
    Collection ret = DataFactory.createSet();
    Map> fieldSets =
        reflectionMap.getOrDefault(ReflectionKind.FieldSet, Collections.emptyMap());
    if (fieldSets.containsKey(s)) {
      Collection fieldSignatures = fieldSets.get(s);
      AbstractInvokeExpr iie = s.getInvokeExpr();
      Value base = iie.getArg(0);
      Value rValue = iie.getArg(1);
      for (String fieldSignature : fieldSignatures) {
        FieldSignature fieldSig =
            JavaIdentifierFactory.getInstance().parseFieldSignature(fieldSignature);
        SootField field = ptaScene.getView().getField(fieldSig).get();
        JFieldRef fieldRef;
        if (field.isStatic()) {
          assert base instanceof NullConstant;
          fieldRef = Jimple.newStaticFieldRef(field.getSignature());
        } else {
          assert !(base instanceof NullConstant);
          fieldRef = Jimple.newInstanceFieldRef((Local) base, field.getSignature());
        }
        Stmt stmt = new JAssignStmt(fieldRef, rValue, StmtPositionInfo.getNoStmtPositionInfo());
        ret.add(stmt);
      }
    }
    return ret;
  }

  @Override
  protected Collection transformFieldGet(Stmt s) {
    // 
    Collection ret = DataFactory.createSet();
    Map> fieldGets =
        reflectionMap.getOrDefault(ReflectionKind.FieldGet, Collections.emptyMap());
    if (fieldGets.containsKey(s) && s instanceof JAssignStmt) {
      Collection fieldSignatures = fieldGets.get(s);
      LValue lvalue = ((JAssignStmt) s).getLeftOp();
      AbstractInvokeExpr iie = s.getInvokeExpr();
      Value base = iie.getArg(0);
      for (String fieldSignature : fieldSignatures) {
        FieldSignature fieldSig =
            JavaIdentifierFactory.getInstance().parseFieldSignature(fieldSignature);
        SootField field = ptaScene.getView().getField(fieldSig).get();
        JFieldRef fieldRef;
        if (field.isStatic()) {
          assert base instanceof NullConstant;
          fieldRef = Jimple.newStaticFieldRef(field.getSignature());
        } else {
          assert !(base instanceof NullConstant);
          fieldRef = Jimple.newInstanceFieldRef((Local) base, field.getSignature());
        }
        if (fieldRef.getType() instanceof ReferenceType) {
          Stmt stmt = new JAssignStmt(lvalue, fieldRef, StmtPositionInfo.getNoStmtPositionInfo());
          ret.add(stmt);
        }
      }
    }
    return ret;
  }

  @Override
  protected Collection transformArrayNewInstance(Stmt s) {
    // 
    Collection ret = DataFactory.createSet();
    Map> mappedToArrayTypes =
        reflectionMap.getOrDefault(ReflectionKind.ArrayNewInstance, Collections.emptyMap());
    Collection arrayTypes = mappedToArrayTypes.getOrDefault(s, Collections.emptySet());
    for (String arrayType : arrayTypes) {
      ArrayType at = (ArrayType) JavaIdentifierFactory.getInstance().getType(arrayType);
      JNewArrayExpr newExpr =
          JavaJimple.getInstance().newNewArrayExpr(at.getElementType(), IntConstant.getInstance(1));
      if (s instanceof JAssignStmt) {
        LValue lvalue = ((JAssignStmt) s).getLeftOp();
        ret.add(new JAssignStmt(lvalue, newExpr, StmtPositionInfo.getNoStmtPositionInfo()));
      }
    }
    return ret;
  }

  @Override
  Collection transformArrayGet(Stmt s) {
    Collection ret = DataFactory.createSet();
    AbstractInvokeExpr iie = s.getInvokeExpr();
    Value base = iie.getArg(0);
    if (s instanceof JAssignStmt) {
      LValue lvalue = ((JAssignStmt) s).getLeftOp();
      Value arrayRef = null;
      if (base.getType() instanceof ArrayType) {
        arrayRef = JavaJimple.getInstance().newArrayRef((Local) base, IntConstant.getInstance(0));
      } else if (base.getType() == PTAUtils.getClassType("java.lang.Object")) {
        Local local =
            Jimple.newLocal(
                "intermediate/" + base,
                new ArrayType(PTAUtils.getClassType("java.lang.Object"), 1));
        ret.add(new JAssignStmt(local, base, StmtPositionInfo.getNoStmtPositionInfo()));
        arrayRef = JavaJimple.getInstance().newArrayRef(local, IntConstant.getInstance(0));
      }
      if (arrayRef != null) {
        ret.add(new JAssignStmt(lvalue, arrayRef, StmtPositionInfo.getNoStmtPositionInfo()));
      }
    }
    return ret;
  }

  @Override
  Collection transformArraySet(Stmt s) {
    Collection ret = DataFactory.createSet();
    AbstractInvokeExpr iie = s.getInvokeExpr();
    Value base = iie.getArg(0);
    if (base.getType() instanceof ArrayType) {
      Value from = iie.getArg(2);
      JArrayRef arrayRef =
          JavaJimple.getInstance().newArrayRef((Local) base, IntConstant.getInstance(0));
      ret.add(new JAssignStmt(arrayRef, from, StmtPositionInfo.getNoStmtPositionInfo()));
    }
    return ret;
  }

  /*
   * parse reflection log generated by Tamiflex.
   * */
  private void parseTamiflexLog(String logFile, boolean verbose) {
    try {
      BufferedReader reader =
          new BufferedReader(new InputStreamReader(new FileInputStream(logFile)));
      String line;
      while ((line = reader.readLine()) != null) {
        String[] portions = line.split(";", -1);
        if (portions.length < 4) {
          if (verbose) {
            System.out.println("Warning: illegal tamiflex log: " + line);
          }
          continue;
        }
        ReflectionKind kind = ReflectionKind.parse(portions[0]);
        String mappedTarget = portions[1];
        String inClzDotMthdStr = portions[2];
        int lineNumber = portions[3].length() == 0 ? -1 : Integer.parseInt(portions[3]);
        // sanity check.
        if (kind == null) {
          if (verbose) {
            System.out.println("Warning: illegal tamiflex reflection kind: " + portions[0]);
          }
          continue;
        }
        switch (kind) {
          case ClassForName:
            break;
          case ClassNewInstance:
            if (!ptaScene.containsClass(mappedTarget)) {
              if (verbose) {
                System.out.println("Warning: Unknown mapped class for signature: " + mappedTarget);
              }
              continue;
            }
            break;
          case ConstructorNewInstance:
          case MethodInvoke:
            if (!ptaScene.containsMethod(mappedTarget)) {
              if (verbose) {
                System.out.println("Warning: Unknown mapped method for signature: " + mappedTarget);
              }
              continue;
            }
            break;
          case FieldSet:
          case FieldGet:
            if (!ptaScene.containsField(mappedTarget)) {
              if (verbose) {
                System.out.println("Warning: Unknown mapped field for signature: " + mappedTarget);
              }
              continue;
            }
            break;
          case ArrayNewInstance:
            break;
          default:
            if (verbose) {
              System.out.println("Warning: Unsupported reflection kind: " + kind);
            }
            break;
        }
        Collection possibleSourceStmts = inferSourceStmt(inClzDotMthdStr, kind, lineNumber);
        for (Stmt stmt : possibleSourceStmts) {
          reflectionMap
              .computeIfAbsent(kind, m -> DataFactory.createMap())
              .computeIfAbsent(stmt, k -> DataFactory.createSet())
              .add(mappedTarget);
        }
      }
      reader.close();
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  private Collection inferSourceMethod(String inClzDotMthd) {
    String inClassStr = inClzDotMthd.substring(0, inClzDotMthd.lastIndexOf("."));
    String inMethodStr = inClzDotMthd.substring(inClzDotMthd.lastIndexOf(".") + 1);
    if (!ptaScene.containsClass(inClassStr)) {
      System.out.println("Warning: unknown class \"" + inClassStr + "\" is referenced.");
      return Collections.emptySet();
    }
    SootClass sootClass = ptaScene.getSootClass(inClassStr);
    Set ret = DataFactory.createSet();
    Set declMethods = sootClass.getMethods();
    for (SootMethod m : declMethods) {
      if (m.isConcrete() && m.getName().equals(inMethodStr)) {
        ret.add(m);
      }
    }
    return ret;
  }

  private Collection inferSourceStmt(
      String inClzDotMthd, ReflectionKind kind, int lineNumber) {
    Set ret = DataFactory.createSet();
    Set potential = DataFactory.createSet();
    Collection sourceMethods = inferSourceMethod(inClzDotMthd);
    for (SootMethod sm : sourceMethods) {
      Body body = PTAUtils.getMethodBody(sm);
      for (Stmt stmt : body.getStmts()) {
        if (stmt.containsInvokeExpr()) {
          String methodSig = stmt.getInvokeExpr().getMethodSignature().toString();
          if (matchReflectionKind(kind, methodSig)) {
            potential.add(stmt);
          }
        }
      }
    }
    for (Stmt stmt : potential) {
      int firstLine = stmt.getPositionInfo().getStmtPosition().getFirstLine();
      int lastLine = stmt.getPositionInfo().getStmtPosition().getLastLine();
      // !TODO potential bug here
      if (lineNumber < 0 || firstLine <= lineNumber && lineNumber <= lastLine) {
        ret.add(stmt);
      }
    }
    if (ret.size() == 0 && potential.size() > 0) {
      System.out.print("Warning: Mismatch between statement and reflection log entry - ");
      System.out.println(kind + ";" + inClzDotMthd + ";" + lineNumber + ";");
      return potential;
    } else {
      return ret;
    }
  }

  private boolean matchReflectionKind(ReflectionKind kind, String methodSig) {
    switch (kind) {
      case ClassForName:
        return methodSig.equals(sigForName) || methodSig.equals(sigForName2);
      case ClassNewInstance:
        return methodSig.equals(sigClassNewInstance);
      case ConstructorNewInstance:
        return methodSig.equals(sigConstructorNewInstance);
      case MethodInvoke:
        return methodSig.equals(sigMethodInvoke);
      case FieldSet:
        return methodSig.equals(sigFieldSet);
      case FieldGet:
        return methodSig.equals(sigFieldGet);
      case ArrayNewInstance:
        return methodSig.equals(sigArrayNewInstance);
      default:
        return false;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy