[Soot-list] Points-to (GeomPTA): No reaching objects on static field if type is an array type with a RefType

Florian Angerer florian.angerer at jku.at
Mon Jul 27 11:48:55 EDT 2015


Hi everyone,

I discovered an unexpected result when running GeomPTA to compute the
points-to information.
The problem is that there are no reaching objects for a static field if
the type of the field is an array type AND the element type is a RefType
(e.g. "java.lang.String[]").

However, GeomPTA works absolutely fine if the element type is "int".

I attached a JUnit4 test that reproduces the problem.
The unit test configures Soot (including GeomPTA) as I have configured
it and it generates Jimple code for a small class "test.ArrayTypeTest".

The class consists of four static fields:
static java.lang.String[] stringArray
static int[] basicArray
static java.lang.String stringObject
static test.MyClass[] refTypeArray

The static constructor of "test.ArrayTypeTest" contains statements that
creates an objects for each static field.

After executing GeomPTA, there are reaching objects for fields
"basicArray" and "stringObject" but not for the other two fields.

And of course, I used the latest available nightly build (downloaded on
July 27th, 2015 from https://ssebuild.cased.de/nightly/soot/).

Since I'm not sure if this is a real bug in Soot/GeomPTA, I did not
(yet) file a bug in the bug tracker.

Has anyone an idea?

Looking forward to responses,
Florian
-------------- next part --------------


import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import soot.ArrayType;
import soot.EntryPoints;
import soot.IntType;
import soot.Local;
import soot.Modifier;
import soot.PointsToAnalysis;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.Type;
import soot.Unit;
import soot.VoidType;
import soot.javaToJimple.LocalGenerator;
import soot.jimple.AnyNewExpr;
import soot.jimple.IntConstant;
import soot.jimple.Jimple;
import soot.jimple.JimpleBody;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.StaticFieldRef;
import soot.jimple.Stmt;
import soot.jimple.spark.SparkTransformer;
import soot.jimple.spark.geom.geomPA.GeomPointsTo;
import soot.jimple.spark.pag.AllocNode;
import soot.jimple.spark.pag.Node;
import soot.jimple.spark.sets.P2SetVisitor;
import soot.jimple.spark.sets.PointsToSetInternal;
import soot.options.Options;

public class ReachingObjectsForArrayTest {

    public static final String SCLASS_NAME = "test.ArrayTypeTest";
    public static final String NAME_FIELD0 = "stringArray";
    public static final String NAME_FIELD1 = "basicArray";
    public static final String NAME_FIELD2 = "stringObject";
    public static final String NAME_FIELD3 = "refTypeArray";

    private static LocalGenerator localGenerator = null;

    @BeforeClass
    public static void setup() {

        ReachingObjectsForArrayTest.setupOptions();

        SootMethod m = ReachingObjectsForArrayTest.generateJimpleCode();

        Scene.v().loadNecessaryClasses();

        List<SootMethod> entries = new ArrayList<>(EntryPoints.v().all());
        entries.add(m);
        soot.Scene.v().setEntryPoints(entries);

        // Execute pointer analysis
        ReachingObjectsForArrayTest.setupAndRunGeomPTA();
    }

    /**
     * configure Soot
     */
    private static void setupOptions() {

        Options.v().set_app(true);
        Options.v().set_whole_program(true);
        Options.v().set_no_bodies_for_excluded(true);
        Options.v().set_allow_phantom_refs(true);

        List<String> excludes = new ArrayList<>();
        excludes.add("java");
        excludes.add("javax");
        excludes.add("org");
        Options.v().set_exclude(excludes);

        Options.v().set_prepend_classpath(true);
        Options.v().set_keep_line_number(true);
    }

    /**
     * Generates Jimple code for the dummy class "test.MyClass".
     * 
     * @return The generated Soot class object.
     */
    private static SootClass generateKnownType() {

        SootClass customClass = new SootClass("test.MyClass", Modifier.PUBLIC);
        customClass.setSuperclass(RefType.v("java.lang.Object").getSootClass());
        // Add a new class to the scene
        Scene.v().addClass(customClass);
        customClass.setApplicationClass();
        customClass.setResolvingLevel(SootClass.BODIES);

        SootMethod m = new SootMethod(SootMethod.constructorName, Collections.<Type> emptyList(),
                VoidType.v(), Modifier.PUBLIC);
        JimpleBody b = Jimple.v().newBody(m);
        m.setActiveBody(b);
        customClass.addMethod(m);

        Local specialThisLocal = Jimple.v().newLocal("this", customClass.getType());
        b.getLocals().add(specialThisLocal);
        Stmt thisStmt = Jimple.v().newIdentityStmt(specialThisLocal,
                Jimple.v().newThisRef(customClass.getType()));
        b.getUnits().add(thisStmt);

        return customClass;
    }

    /**
     * Generates four static fields with different types (mainly array types) and instantiates them. This is
     * done in the static constructor of the newly generated class {@value #SCLASS_NAME}.
     * 
     * @return The Soot method object representing the static constructor.
     */
    private static SootMethod generateJimpleCode() {

        SootClass knownT = generateKnownType();

        SootClass sClass = new SootClass(SCLASS_NAME, Modifier.PUBLIC);
        sClass.setSuperclass(RefType.v("java.lang.Object").getSootClass());
        // Add a new class to the scene
        Scene.v().addClass(sClass);
        sClass.setApplicationClass();

        final RefType stringType = RefType.v("java.lang.String");

        // add field with type "String[]"
        SootField f = new SootField(NAME_FIELD0, ArrayType.v(stringType, 1), Modifier.STATIC);
        sClass.addField(f);

        // add field with type "int[]"
        SootField f1 = new SootField(NAME_FIELD1, ArrayType.v(IntType.v(), 1), Modifier.STATIC);
        sClass.addField(f1);

        // add field with type "String"
        SootField f2 = new SootField(NAME_FIELD2, stringType, Modifier.STATIC);
        sClass.addField(f2);

        // add field with type "MyClass[]"
        SootField f3 = new SootField(NAME_FIELD3, ArrayType.v(knownT.getType(), 1), Modifier.STATIC);
        sClass.addField(f3);

        SootMethod m = new SootMethod(SootMethod.staticInitializerName, Collections.<Type> emptyList(),
                VoidType.v(), Modifier.STATIC);
        JimpleBody b = Jimple.v().newBody(m);
        m.setActiveBody(b);
        ReachingObjectsForArrayTest.localGenerator = new LocalGenerator(b);
        sClass.addMethod(m);

        instantiateField(f, b);
        instantiateField(f1, b);
        instantiateField(f2, b);
        instantiateField(f3, b);

        return m;
    }

    /**
     * Generates a new expression and an assignment to the provided field.
     * 
     * @param f
     *            The field to instantiate.
     * @param b
     *            The body to add units to.
     */
    public static void instantiateField(SootField f, JimpleBody b) {

        // create newArray expr
        Unit constrCall = null;
        AnyNewExpr nExpr = null;
        Local l = localGenerator.generateLocal(f.getType());
        if (f.getType() instanceof ArrayType) {
            nExpr = Jimple.v().newNewArrayExpr(IntType.v(), IntConstant.v(1));
        } else if (f.getType() instanceof RefType) {
            nExpr = Jimple.v().newNewExpr((RefType) f.getType());

            SootClass sootClass = ((RefType) f.getType()).getSootClass();

            // For searching the constructor, resolving level SIGNATURES is needed!
            // Therefore, directly create a SootMethodRef.
            SootMethodRef methodRef = Scene.v().makeConstructorRef(sootClass, Collections.<Type> emptyList());

            SpecialInvokeExpr newSpecialInvokeExpr = Jimple.v().newSpecialInvokeExpr(l, methodRef);
            constrCall = Jimple.v().newInvokeStmt(newSpecialInvokeExpr);
        }
        b.getUnits().add(Jimple.v().newAssignStmt(l, nExpr));
        if (constrCall != null) {
            b.getUnits().add(constrCall);

        }

        // create field access
        StaticFieldRef newStaticFieldRef = Jimple.v().newStaticFieldRef(f.makeRef());
        b.getUnits().add(Jimple.v().newAssignStmt(newStaticFieldRef, l));
    }

    /**
     * Searches in the Scene for a SootClass by its name. Returns null if no class with the specified name is
     * is found.
     * 
     * @param name
     * @return
     */
    public static SootClass getClassByName(String name) {

        for (SootClass sClass : Scene.v().getClasses()) {
            if (sClass.getName().equals(name)) {
                return sClass;
            }
        }
        return null;
    }

    /**
     * configure and run GeomPTA
     */
    private static void setupAndRunGeomPTA() {

        Map<String, String> opt = new HashMap<>();
        opt.put("geom-pta", "true");
        opt.put("geom-encoding", "geom");
        opt.put("geom-worklist", "PQ");
        opt.put("geom-eval", "0");
        opt.put("geom-trans", "false");
        opt.put("geom-frac-base", "40");
        opt.put("geom-blocking", "true");
        opt.put("geom-runs", "4");
        opt.put("enabled", "true");
        opt.put("verbose", "true");
        opt.put("ignore-types", "false");
        opt.put("force-gc", "false");
        opt.put("pre-jimplify", "false");
        opt.put("vta", "false");
        opt.put("rta", "false");
        opt.put("field-based", "false");
        opt.put("types-for-sites", "false");
        opt.put("merge-stringbuffer", "false");
        opt.put("string-constants", "true");
        opt.put("simulate-natives", "true");
        opt.put("simple-edges-bidirectional", "false");
        opt.put("on-fly-cg", "true");
        opt.put("simplify-offline", "false");
        opt.put("simplify-sccs", "false");
        opt.put("ignore-types-for-sccs", "false");
        opt.put("propagator", "worklist");
        opt.put("set-impl", "double");
        opt.put("double-set-old", "hybrid");
        opt.put("double-set-new", "hybrid");
        opt.put("dump-html", "false");
        opt.put("dump-pag", "false");
        opt.put("dump-solution", "false");
        opt.put("topo-sort", "false");
        opt.put("dump-types", "true");
        opt.put("class-method-var", "true");
        opt.put("dump-answer", "false");
        opt.put("add-tags", "true");
        opt.put("set-mass", "false");

        SparkTransformer.v().transform("", opt);
    }

    /**
     * Simple visitor class to collect the {@link AllocNode}s from a points-to set.
     */
    private static class AllocNodeFinder extends P2SetVisitor {

        private List<AllocNode> allocNodes;

        @Override
        public void visit(Node n) {

            if (n instanceof AllocNode) {
                if (allocNodes == null) allocNodes = new ArrayList<>();
                allocNodes.add((AllocNode) n);
            }
        }

        public List<AllocNode> getAllocNodes() {

            if (allocNodes != null) {
                return allocNodes;
            }
            return Collections.emptyList();
        }
    }

    @Test
    public void testField0() {

        List<AllocNode> allocNodes = getAllocNodesOfField(NAME_FIELD0);
        Assert.assertEquals("There is no reaching object for field " + NAME_FIELD0, 1, allocNodes.size());

    }

    @Test
    public void testField1() {

        List<AllocNode> allocNodes = getAllocNodesOfField(NAME_FIELD1);
        Assert.assertEquals("There is no reaching object for field " + NAME_FIELD1, 1, allocNodes.size());

    }

    @Test
    public void testField2() {

        List<AllocNode> allocNodes = getAllocNodesOfField(NAME_FIELD2);
        Assert.assertEquals("There is no reaching object for field " + NAME_FIELD2, 1, allocNodes.size());

    }

    @Test
    public void testField3() {

        List<AllocNode> allocNodes = getAllocNodesOfField(NAME_FIELD3);
        Assert.assertEquals("There is no reaching object for field " + NAME_FIELD3, 1, allocNodes.size());

    }

    private List<AllocNode> getAllocNodesOfField(String fieldName) {

        PointsToAnalysis pta = Scene.v().getPointsToAnalysis();
        GeomPointsTo geomPTA = (GeomPointsTo) pta;

        SootClass s = ReachingObjectsForArrayTest.getClassByName(SCLASS_NAME);
        SootField f = s.getFieldByName(fieldName);

        PointsToSetInternal reachingObjects = (PointsToSetInternal) geomPTA.reachingObjects(f);
        ReachingObjectsForArrayTest.AllocNodeFinder finder = new ReachingObjectsForArrayTest.AllocNodeFinder();
        reachingObjects.forall(finder);

        List<AllocNode> allocNodes = finder.getAllocNodes();
        return allocNodes;
    }

}


More information about the Soot-list mailing list