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

qilin.core.pag.PAG 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.pag;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import qilin.CoreConfig;
import qilin.core.PTA;
import qilin.core.PointsToAnalysis;
import qilin.core.builder.CallGraphBuilder;
import qilin.core.context.Context;
import qilin.core.natives.NativeMethodDriver;
import qilin.core.reflection.NopReflectionModel;
import qilin.core.reflection.ReflectionModel;
import qilin.core.reflection.TamiflexModel;
import qilin.util.ArrayNumberer;
import qilin.util.DataFactory;
import qilin.util.PTAUtils;
import qilin.util.Triple;
import qilin.util.queue.ChunkedQueue;
import qilin.util.queue.QueueReader;
import sootup.core.graph.MutableStmtGraph;
import sootup.core.jimple.Jimple;
import sootup.core.jimple.basic.LValue;
import sootup.core.jimple.basic.Local;
import sootup.core.jimple.basic.StmtPositionInfo;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.constant.ClassConstant;
import sootup.core.jimple.common.constant.IntConstant;
import sootup.core.jimple.common.constant.StringConstant;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.expr.JStaticInvokeExpr;
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.Body;
import sootup.core.model.SootClass;
import sootup.core.model.SootMethod;
import sootup.core.signatures.FieldSignature;
import sootup.core.types.ArrayType;
import sootup.core.types.ClassType;
import sootup.core.types.Type;
import sootup.core.views.View;
import sootup.java.core.language.JavaJimple;

/**
 * Pointer assignment graph.
 *
 * @author Ondrej Lhotak
 */
public class PAG {
  protected final NativeMethodDriver nativeDriver;
  protected final ReflectionModel reflectionModel;

  // ========================= context-sensitive nodes =================================
  protected final Map> contextVarNodeMap;
  protected final Map> contextAllocNodeMap;
  protected final Map> contextMethodMap;
  protected final Map> addedContexts;
  protected final Map> contextFieldMap;

  // ==========================data=========================
  protected ArrayNumberer allocNodeNumberer = new ArrayNumberer<>();
  protected ArrayNumberer valNodeNumberer = new ArrayNumberer<>();
  protected ArrayNumberer fieldRefNodeNumberer = new ArrayNumberer<>();
  private static AtomicInteger maxFinishNumber = new AtomicInteger(0);

  // ========================= ir to Node ==============================================
  protected final Map valToAllocNode;
  protected final Map valToValNode;
  protected final Map methodToPag;
  protected final Set globals;
  protected final Set> locals;
  // ==========================outer objects==============================
  protected ChunkedQueue edgeQueue;

  protected final Map> simple;
  protected final Map> simpleInv;
  protected final Map> load;
  protected final Map> loadInv;
  protected final Map> alloc;
  protected final Map> allocInv;
  protected final Map> store;
  protected final Map> storeInv;

  protected final PTA pta;

  public PAG(PTA pta) {
    this.pta = pta;
    this.simple = DataFactory.createMap();
    this.simpleInv = DataFactory.createMap();
    this.load = DataFactory.createMap();
    this.loadInv = DataFactory.createMap();
    this.alloc = DataFactory.createMap();
    this.allocInv = DataFactory.createMap();
    this.store = DataFactory.createMap();
    this.storeInv = DataFactory.createMap();
    this.nativeDriver = new NativeMethodDriver(pta.getScene());
    this.reflectionModel = createReflectionModel();
    this.contextVarNodeMap = DataFactory.createMap(16000);
    this.contextAllocNodeMap = DataFactory.createMap(6000);
    this.contextMethodMap = DataFactory.createMap(6000);
    this.addedContexts = DataFactory.createMap();
    this.contextFieldMap = DataFactory.createMap(6000);
    this.valToAllocNode = DataFactory.createMap(10000);
    this.valToValNode = DataFactory.createMap(100000);
    this.methodToPag = DataFactory.createMap();
    this.globals = DataFactory.createSet(100000);
    this.locals = DataFactory.createSet(100000);
  }

  public void setEdgeQueue(ChunkedQueue edgeQueue) {
    this.edgeQueue = edgeQueue;
  }

  public Map> getAlloc() {
    return alloc;
  }

  public Map> getSimple() {
    return simple;
  }

  public Map> getSimpleInv() {
    return simpleInv;
  }

  public Map> getLoad() {
    return load;
  }

  public Map> getStoreInv() {
    return storeInv;
  }

  public PTA getPta() {
    return this.pta;
  }

  public CallGraphBuilder getCgb() {
    return pta.getCgb();
  }

  public QueueReader edgeReader() {
    return edgeQueue.reader();
  }

  // =======================add edge===============================
  protected  boolean addToMap(Map> m, K key, V value) {
    Set valueList = m.computeIfAbsent(key, k -> DataFactory.createSet(4));
    return valueList.add(value);
  }

  private boolean addAllocEdge(AllocNode from, VarNode to) {
    if (addToMap(alloc, from, to)) {
      addToMap(allocInv, to, from);
      return true;
    }
    return false;
  }

  private boolean addSimpleEdge(ValNode from, ValNode to) {
    if (addToMap(simple, from, to)) {
      addToMap(simpleInv, to, from);
      return true;
    }
    return false;
  }

  private boolean addStoreEdge(VarNode from, FieldRefNode to) {
    if (addToMap(storeInv, to, from)) {
      addToMap(store, from, to);
      return true;
    }
    return false;
  }

  private boolean addLoadEdge(FieldRefNode from, VarNode to) {
    if (addToMap(load, from, to)) {
      addToMap(loadInv, to, from);
      return true;
    }
    return false;
  }

  public void addGlobalPAGEdge(Node from, Node to) {
    from = pta.parameterize(from, pta.emptyContext());
    to = pta.parameterize(to, pta.emptyContext());
    addEdge(from, to);
  }

  /** Adds an edge to the graph, returning false if it was already there. */
  public final void addEdge(Node from, Node to) {
    if (addEdgeIntenal(from, to)) {
      edgeQueue.add(from);
      edgeQueue.add(to);
    }
  }

  private boolean addEdgeIntenal(Node from, Node to) {
    if (from instanceof ValNode) {
      if (to instanceof ValNode) {
        return addSimpleEdge((ValNode) from, (ValNode) to);
      } else {
        return addStoreEdge((VarNode) from, (FieldRefNode) to);
      }
    } else if (from instanceof FieldRefNode) {
      return addLoadEdge((FieldRefNode) from, (VarNode) to);
    } else {
      AllocNode heap = (AllocNode) from;
      return addAllocEdge(heap, (VarNode) to);
    }
  }

  // ======================lookups===========================
  protected  Set lookup(Map> m, K key) {
    return m.getOrDefault(key, Collections.emptySet());
  }

  public Set allocLookup(AllocNode key) {
    return lookup(alloc, key);
  }

  public Set allocInvLookup(VarNode key) {
    return lookup(allocInv, key);
  }

  public Set simpleLookup(ValNode key) {
    return lookup(simple, key);
  }

  public Set simpleInvLookup(ValNode key) {
    return lookup(simpleInv, key);
  }

  public Set loadInvLookup(VarNode key) {
    return lookup(loadInv, key);
  }

  public Set loadLookup(FieldRefNode key) {
    return lookup(load, key);
  }

  public Set storeLookup(VarNode key) {
    return lookup(store, key);
  }

  public Set storeInvLookup(FieldRefNode key) {
    return lookup(storeInv, key);
  }

  public static int nextFinishNumber() {
    return maxFinishNumber.incrementAndGet();
  }

  public ArrayNumberer getAllocNodeNumberer() {
    return allocNodeNumberer;
  }

  public ArrayNumberer getFieldRefNodeNumberer() {
    return fieldRefNodeNumberer;
  }

  public ArrayNumberer getValNodeNumberer() {
    return valNodeNumberer;
  }

  public Collection getValNodes() {
    return valToValNode.values();
  }

  public Collection getAllocNodes() {
    return valToAllocNode.values();
  }

  public Set getGlobalPointers() {
    return globals;
  }

  public Set> getLocalPointers() {
    return locals;
  }

  /** Finds the ValNode for the variable value, or returns null. */
  public ValNode findValNode(Object value, SootMethod containingMethod) {
    if (value instanceof Local) {
      Local local = (Local) value;
      Triple localTriple =
          new Triple<>(containingMethod, local, local.getType());
      return valToValNode.get(localTriple);
    } else {
      return valToValNode.get(value);
    }
  }

  public AllocNode findAllocNode(Object obj) {
    return valToAllocNode.get(obj);
  }

  // ==========================create nodes==================================
  public AllocNode makeAllocNode(Object newExpr, Type type, SootMethod m) {
    if (type instanceof ClassType) {
      ClassType rt = (ClassType) type;
      View view = pta.getView();
      Optional osc = view.getClass(rt);
      if (osc.isPresent() && osc.get().isAbstract()) {
        boolean usesReflectionLog = CoreConfig.v().getAppConfig().REFLECTION_LOG != null;
        if (!usesReflectionLog) {
          throw new RuntimeException("Attempt to create allocnode with abstract type " + rt);
        }
      }
    }
    AllocNode ret = valToAllocNode.get(newExpr);
    if (ret == null) {
      valToAllocNode.put(newExpr, ret = new AllocNode(newExpr, type, m));
      allocNodeNumberer.add(ret);
    } else if (!(ret.getType().equals(type))) {
      throw new RuntimeException(
          "NewExpr " + newExpr + " of type " + type + " previously had type " + ret.getType());
    }
    return ret;
  }

  public AllocNode makeStringConstantNode(StringConstant sc) {
    StringConstant stringConstant = sc;
    if (!CoreConfig.v().getPtaConfig().stringConstants) {
      stringConstant = JavaJimple.getInstance().newStringConstant(PointsToAnalysis.STRING_NODE);
    }
    AllocNode ret = valToAllocNode.get(stringConstant);
    if (ret == null) {
      valToAllocNode.put(stringConstant, ret = new StringConstantNode(stringConstant));
      allocNodeNumberer.add(ret);
    }
    return ret;
  }

  public AllocNode makeClassConstantNode(ClassConstant cc) {
    AllocNode ret = valToAllocNode.get(cc);
    if (ret == null) {
      valToAllocNode.put(cc, ret = new ClassConstantNode(cc));
      allocNodeNumberer.add(ret);
    }
    return ret;
  }

  /** Finds or creates the GlobalVarNode for the variable value, of type type. */
  public GlobalVarNode makeGlobalVarNode(Object value, Type type) {
    // value could only be a StringConstant, ClassConstant, or SootField.
    GlobalVarNode ret = (GlobalVarNode) valToValNode.get(value);
    if (ret == null) {
      ret =
          (GlobalVarNode) valToValNode.computeIfAbsent(value, k -> new GlobalVarNode(value, type));
      valNodeNumberer.add(ret);
      if (value instanceof FieldSignature) {
        globals.add((FieldSignature) value);
      }
    } else if (!(ret.getType().equals(type))) {
      throw new RuntimeException(
          "Value " + value + " of type " + type + " previously had type " + ret.getType());
    }
    return ret;
  }

  /** Finds or creates the LocalVarNode for the variable value, of type type. */
  public LocalVarNode makeLocalVarNode(Object value, Type type, SootMethod method) {
    Triple localTriple = new Triple<>(method, value, type);
    LocalVarNode ret = (LocalVarNode) valToValNode.get(localTriple);
    if (ret == null) {
      valToValNode.put(localTriple, ret = new LocalVarNode(value, type, method));
      valNodeNumberer.add(ret);
      if (value instanceof Local) {
        Local local = (Local) value;
        locals.add(new Triple<>(method, local, type));
      }
    } else if (!(ret.getType().equals(type))) {
      throw new RuntimeException(
          "Value " + value + " of type " + type + " previously had type " + ret.getType());
    }
    return ret;
  }

  /**
   * Finds or creates the FieldVarNode for the Java field or array element. Treat Java field and
   * array element as normal local variable.
   */
  public FieldValNode makeFieldValNode(SparkField field) {
    FieldValNode ret = (FieldValNode) valToValNode.get(field);
    if (ret == null) {
      valToValNode.put(field, ret = new FieldValNode(field));
      valNodeNumberer.add(ret);
    }
    return ret;
  }

  /** Finds or creates the FieldRefNode for base variable base and field field, of type type. */
  public FieldRefNode makeFieldRefNode(VarNode base, SparkField field) {
    FieldRefNode ret = base.dot(field);
    if (ret == null) {
      ret = new FieldRefNode(base, field);
      fieldRefNodeNumberer.add(ret);
    }
    return ret;
  }

  /** Finds or creates the ContextVarNode for base variable base and context. */
  public ContextVarNode makeContextVarNode(VarNode base, Context context) {
    Map contextMap =
        contextVarNodeMap.computeIfAbsent(base, k1 -> DataFactory.createMap());
    ContextVarNode ret = contextMap.get(context);
    if (ret == null) {
      contextMap.put(context, ret = new ContextVarNode(base, context));
      valNodeNumberer.add(ret);
    }
    return ret;
  }

  /** Finds or creates the ContextAllocNode for base alloc site and context. */
  public ContextAllocNode makeContextAllocNode(AllocNode allocNode, Context context) {
    Map contextMap =
        contextAllocNodeMap.computeIfAbsent(allocNode, k1 -> DataFactory.createMap());
    ContextAllocNode ret = contextMap.get(context);
    if (ret == null) {
      contextMap.put(context, ret = new ContextAllocNode(allocNode, context));
      allocNodeNumberer.add(ret);
    }
    return ret;
  }

  /** Finds or creates the ContextMethod for method and context. */
  public ContextMethod makeContextMethod(Context context, SootMethod method) {
    Map contextMap =
        contextMethodMap.computeIfAbsent(method, k1 -> DataFactory.createMap());
    return contextMap.computeIfAbsent(context, k -> new ContextMethod(method, context));
  }

  public AllocNode getAllocNode(Object val) {
    return valToAllocNode.get(val);
  }

  public Map> getMethod2ContextsMap() {
    return addedContexts;
  }

  public Collection getContextFields() {
    return contextFieldMap.values().stream()
        .flatMap(m -> m.values().stream())
        .collect(Collectors.toSet());
  }

  public Map> getContextVarNodeMap() {
    return contextVarNodeMap;
  }

  public Map> getContextAllocNodeMap() {
    return contextAllocNodeMap;
  }

  public Map> getContextMethodMap() {
    return contextMethodMap;
  }

  public Map> getContextFieldVarNodeMap() {
    return contextFieldMap;
  }

  public ContextField makeContextField(Context context, FieldValNode fieldValNode) {
    SparkField field = fieldValNode.getField();
    Map field2odotf =
        contextFieldMap.computeIfAbsent(context, k -> DataFactory.createMap());
    ContextField ret = field2odotf.get(field);
    if (ret == null) {
      field2odotf.put(field, ret = new ContextField(context, field));
      valNodeNumberer.add(ret);
    }
    return ret;
  }

  public Collection getVarNodes(SootMethod m, Local local) {
    LocalVarNode lvn = findLocalVarNode(m, local, local.getType());
    Map subMap = contextVarNodeMap.get(lvn);
    if (subMap == null) {
      return Collections.emptySet();
    }
    return new HashSet<>(subMap.values());
  }

  // ===================find nodes==============================

  /** Finds the GlobalVarNode for the variable value, or returns null. */
  public GlobalVarNode findGlobalVarNode(Object value) {
    if (value instanceof Local) {
      System.out.println("Warning: find global varnode for local value:" + value);
      return null;
    } else {
      return (GlobalVarNode) valToValNode.get(value);
    }
  }

  /** Finds the LocalVarNode for the variable value, or returns null. */
  public LocalVarNode findLocalVarNode(SootMethod m, Object value, Type type) {
    Triple key = new Triple<>(m, value, type);
    ValNode ret = valToValNode.get(key);
    if (ret instanceof LocalVarNode) {
      return (LocalVarNode) ret;
    }
    return null;
  }

  /** Finds the ContextVarNode for base variable value and context context, or returns null. */
  public ContextVarNode findContextVarNode(SootMethod m, Local baseValue, Context context) {
    LocalVarNode lvn = findLocalVarNode(m, baseValue, baseValue.getType());
    Map contextMap = contextVarNodeMap.get(lvn);
    return contextMap == null ? null : contextMap.get(context);
  }

  protected ReflectionModel createReflectionModel() {
    ReflectionModel model;
    if (CoreConfig.v().getAppConfig().REFLECTION_LOG != null
        && CoreConfig.v().getAppConfig().REFLECTION_LOG.length() > 0) {
      model = new TamiflexModel(pta.getScene());
    } else {
      model = new NopReflectionModel(pta.getScene());
    }
    return model;
  }

  public MethodPAG getMethodPAG(SootMethod m) {
    if (methodToPag.containsKey(m)) {
      return methodToPag.get(m);
    }
    if (m.isConcrete()) {
      reflectionModel.buildReflection(m);
    }
    if (m.isNative()) {
      nativeDriver.buildNative(m);
    } else {
      // we will revert these back in the future.
      /*
       * To keep same with Doop, we move the simulation of
       * 
       * directly to its caller methods.
       * */
      if (pta.getScene().arraycopyBuilt.add(m)) {
        handleArrayCopy(m);
      }
    }
    Body body = PTAUtils.getMethodBody(m);
    return methodToPag.computeIfAbsent(m, k -> new MethodPAG(this, m, body));
  }

  private void handleArrayCopy(SootMethod method) {
    Map> newUnits = DataFactory.createMap();
    Body body = PTAUtils.getMethodBody(method);
    Body.BodyBuilder builder = Body.builder(body, Collections.emptySet());
    int localCount = body.getLocalCount();
    for (Stmt s : body.getStmts()) {
      if (s.containsInvokeExpr()) {
        AbstractInvokeExpr invokeExpr = s.getInvokeExpr();
        if (invokeExpr instanceof JStaticInvokeExpr) {
          JStaticInvokeExpr sie = (JStaticInvokeExpr) invokeExpr;
          String sig = sie.getMethodSignature().toString();
          if (sig.equals(
              "")) {
            Value srcArr = sie.getArg(0);
            if (PTAUtils.isPrimitiveArrayType(srcArr.getType())) {
              continue;
            }
            Type objType = PTAUtils.getClassType("java.lang.Object");
            if (srcArr.getType() == objType) {
              Local localSrc =
                  Jimple.newLocal("intermediate/" + (localCount++), new ArrayType(objType, 1));
              builder.addLocal(localSrc);
              newUnits
                  .computeIfAbsent(s, k -> new HashSet<>())
                  .add(new JAssignStmt(localSrc, srcArr, StmtPositionInfo.getNoStmtPositionInfo()));
              srcArr = localSrc;
            }
            Value dstArr = sie.getArg(2);
            if (PTAUtils.isPrimitiveArrayType(dstArr.getType())) {
              continue;
            }
            if (dstArr.getType() == objType) {
              Local localDst =
                  Jimple.newLocal("intermediate/" + (localCount++), new ArrayType(objType, 1));
              builder.addLocal(localDst);
              newUnits
                  .computeIfAbsent(s, k -> new HashSet<>())
                  .add(new JAssignStmt(localDst, dstArr, StmtPositionInfo.getNoStmtPositionInfo()));
              dstArr = localDst;
            }
            Value src =
                JavaJimple.getInstance().newArrayRef((Local) srcArr, IntConstant.getInstance(0));
            LValue dst =
                JavaJimple.getInstance().newArrayRef((Local) dstArr, IntConstant.getInstance(0));
            Local local =
                Jimple.newLocal(
                    "nativeArrayCopy" + (localCount++), PTAUtils.getClassType("java.lang.Object"));
            builder.addLocal(local);
            newUnits
                .computeIfAbsent(s, k -> DataFactory.createSet())
                .add(new JAssignStmt(local, src, StmtPositionInfo.getNoStmtPositionInfo()));
            newUnits
                .computeIfAbsent(s, k -> DataFactory.createSet())
                .add(new JAssignStmt(dst, local, StmtPositionInfo.getNoStmtPositionInfo()));
          }
        }
      }
    }

    final MutableStmtGraph stmtGraph = builder.getStmtGraph();
    for (Stmt unit : newUnits.keySet()) {
      for (JAssignStmt succ : newUnits.get(unit)) {
        stmtGraph.insertBefore(unit, succ);
      }
    }
    PTAUtils.updateMethodBody(method, builder.build());
  }

  public void resetPointsToSet() {
    this.addedContexts.clear();
    contextVarNodeMap.values().stream()
        .flatMap(m -> m.values().stream())
        .forEach(ValNode::discardP2Set);
    contextFieldMap.values().stream()
        .flatMap(m -> m.values().stream())
        .forEach(ValNode::discardP2Set);
    valToValNode.values().forEach(ValNode::discardP2Set);
    addedContexts.clear();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy