pascal.taie.language.natives.DefaultNativeModel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tai-e Show documentation
Show all versions of tai-e Show documentation
An easy-to-learn/use static analysis framework for Java
The newest version!
/*
* Tai-e: A Static Analysis Framework for Java
*
* Copyright (C) 2022 Tian Tan
* Copyright (C) 2022 Yue Li
*
* This file is part of Tai-e.
*
* Tai-e 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
* of the License, or (at your option) any later version.
*
* Tai-e 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 Tai-e. If not, see .
*/
package pascal.taie.language.natives;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pascal.taie.ir.IR;
import pascal.taie.ir.IRBuildHelper;
import pascal.taie.ir.exp.ArrayAccess;
import pascal.taie.ir.exp.CastExp;
import pascal.taie.ir.exp.InvokeSpecial;
import pascal.taie.ir.exp.InvokeVirtual;
import pascal.taie.ir.exp.NewArray;
import pascal.taie.ir.exp.NewInstance;
import pascal.taie.ir.exp.StaticFieldAccess;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.stmt.Cast;
import pascal.taie.ir.stmt.Copy;
import pascal.taie.ir.stmt.Invoke;
import pascal.taie.ir.stmt.LoadArray;
import pascal.taie.ir.stmt.New;
import pascal.taie.ir.stmt.Stmt;
import pascal.taie.ir.stmt.StoreArray;
import pascal.taie.ir.stmt.StoreField;
import pascal.taie.language.classes.ClassHierarchy;
import pascal.taie.language.classes.ClassNames;
import pascal.taie.language.classes.JField;
import pascal.taie.language.classes.JMethod;
import pascal.taie.language.type.ArrayType;
import pascal.taie.language.type.BooleanType;
import pascal.taie.language.type.ClassType;
import pascal.taie.language.type.IntType;
import pascal.taie.language.type.Type;
import pascal.taie.language.type.TypeSystem;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serial;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static pascal.taie.language.classes.ClassNames.OBJECT;
import static pascal.taie.language.classes.ClassNames.STRING;
import static pascal.taie.util.collection.Maps.newMap;
public class DefaultNativeModel implements NativeModel {
private static final Logger logger = LogManager.getLogger(DefaultNativeModel.class);
private final TypeSystem typeSystem;
private final ClassHierarchy hierarchy;
private final int javaVersion;
/**
* Notes: add {@code transient} to manually control
* the re-initialization of this field by {@link #initModels()}
* during deserialization.
*
* @see #initModels()
* @see #writeObject(ObjectOutputStream)
* @see #readObject(ObjectInputStream)
*/
private transient Map> models = newMap();
public DefaultNativeModel(TypeSystem typeSystem,
ClassHierarchy hierarchy,
int javaVersion) {
this.typeSystem = typeSystem;
this.hierarchy = hierarchy;
this.javaVersion = javaVersion;
initModels();
}
@Override
public IR buildNativeIR(JMethod method) {
return models.getOrDefault(method,
m -> new IRBuildHelper(method).buildEmpty())
.apply(method);
}
private void initModels() {
// --------------------------------------------------------------------
// java.lang.Class
// --------------------------------------------------------------------
// Models Class.getDeclared*s0() methods.
// Note that these modelling just return place holder objects,
// which are not related to the receiver Class object.
//
register("", m ->
allocateArray(m, typeSystem.getClassType(ClassNames.FIELD))
);
//
register("", m ->
allocateArray(m, typeSystem.getClassType(ClassNames.METHOD))
);
//
register("", m ->
allocateArray(m, typeSystem.getClassType(ClassNames.CONSTRUCTOR))
);
//
register("", m ->
allocateArray(m, typeSystem.getClassType(ClassNames.CLASS))
);
// --------------------------------------------------------------------
// java.lang.Object
// --------------------------------------------------------------------
//
// TODO: could throw CloneNotSupportedException
// TODO: should check if the object is Cloneable.
// TODO: should return a clone of the heap allocation (not
// identity). The behaviour implemented here is based on Soot.
register("", m -> {
IRBuildHelper helper = new IRBuildHelper(m);
List stmts = new ArrayList<>();
stmts.add(new Copy(helper.getReturnVar(), helper.getThisVar()));
stmts.add(helper.newReturn());
return helper.build(stmts);
});
// --------------------------------------------------------------------
// java.lang.ref.Finalizer
// --------------------------------------------------------------------
//
//
// Indirect invocations of finalize methods from java.lang.ref.Finalizer.
// Object.finalize is a protected method, so it cannot be directly
// invoked. Finalizer uses an indirection via native code to
// circumvent this. This rule implements this indirection.
// This API is deprecated since Java 7.
if (javaVersion <= 6) {
register("", m ->
invokeVirtualMethod(m, "",
b -> b.getParam(0), b -> null)
);
}
// --------------------------------------------------------------------
// java.lang.System
// --------------------------------------------------------------------
//
register("", m -> {
IRBuildHelper helper = new IRBuildHelper(m);
Var src = helper.getParam(0);
Var dest = helper.getParam(2);
Type objType = typeSystem.getClassType(OBJECT);
Type arrayType = typeSystem.getArrayType(objType, 1);
Var srcArray = helper.newTempVar(arrayType);
Var destArray = helper.newTempVar(arrayType);
// Here the index is just a placeholder for array access.
// It does not correctly model the semantics of arraycopy,
// but it is sufficient for flow-insensitive pointer analysis.
Var index = helper.getParam(1);
Var temp = helper.newTempVar(objType);
// src/dest may point to non-array objects due to imprecision
// of pointer analysis, thus we add cast statements to filter
// out load/store operations on non-array objects.
List stmts = new ArrayList<>();
stmts.add(new Cast(srcArray, new CastExp(src, arrayType)));
stmts.add(new Cast(destArray, new CastExp(dest, arrayType)));
stmts.add(new LoadArray(temp, new ArrayAccess(srcArray, index)));
stmts.add(new StoreArray(new ArrayAccess(destArray, index), temp));
stmts.add(helper.newReturn());
return helper.build(stmts);
});
//
register("", m ->
storeStaticField(m,
"",
b -> b.getParam(0))
);
//
register("", m ->
storeStaticField(m,
"",
b -> b.getParam(0))
);
//
register("", m ->
storeStaticField(m,
"",
b -> b.getParam(0))
);
// --------------------------------------------------------------------
// java.lang.Thread
// --------------------------------------------------------------------
//
// Redirect calls to Thread.start() to Thread.run().
// Before Java 5, Thread.start() itself is native. Since Java 5,
// start() is written in Java which calls native method start0().
final String start = javaVersion <= 4
? ""
: "";
register(start, m ->
invokeVirtualMethod(m, "",
IRBuildHelper::getThisVar, b -> null)
);
// --------------------------------------------------------------------
// java.io.FileSystem
// --------------------------------------------------------------------
final List concreteFileSystems = List.of(
"java.io.UnixFileSystem",
"java.io.WinNTFileSystem",
"java.io.Win32FileSystem"
);
String fsName = concreteFileSystems.stream()
.filter(s -> hierarchy.getJREClass(s) != null)
.findFirst()
.orElse(null);
if (fsName != null) {
//
// This API is deprecated since Java 7.
// TODO: ideally, this method should return the same EnvObj across
// multiple invocations. A possible solution is to use a flag
// to mark these native-related allocation sites, so that
// HeapModel can recognize them and convert them to EnvObj.
if (javaVersion <= 6) {
register("", m ->
allocateObject(m, "<" + fsName + ": void ()>",
b -> List.of())
);
}
//
register("<" + fsName + ": java.lang.String[] list(java.io.File)>", m -> {
IRBuildHelper helper = new IRBuildHelper(m);
ClassType string = typeSystem.getClassType(STRING);
ArrayType stringArray = typeSystem.getArrayType(string, 1);
Var str = helper.newTempVar(string);
Var arr = helper.getReturnVar();
// here n is just a placeholder of array-related statements,
// thus is value is irrelevant.
Var n = helper.newTempVar(IntType.INT);
List stmts = new ArrayList<>();
stmts.add(new New(m, str, new NewInstance(string)));
stmts.add(new New(m, arr, new NewArray(stringArray, n)));
stmts.add(new StoreArray(new ArrayAccess(arr, n), str));
stmts.add(helper.newReturn());
return helper.build(stmts);
});
} else {
logger.warn("Cannot find implementation of FileSystem");
}
// --------------------------------------------------------------------
// java.security.AccessController
// --------------------------------------------------------------------
// The run methods of privileged actions are invoked through the
// AccessController.doPrivileged method. This introduces an
// indirection via native code that needs to be simulated in a pointer
// analysis.
//
// Call from an invocation of doPrivileged to an implementation of the
// PrivilegedAction.run method that will be indirectly invoked.
//
// The first parameter of a doPrivileged invocation (a
// PrivilegedAction) is assigned to the 'this' variable of 'run()'
// method invocation.
//
// The return variable of the 'run()' method of a privileged action is
// assigned to the return result of the doPrivileged method
// invocation.
//
// TODO for PrivilegedExceptionAction, catch exceptions and wrap them
// in a PrivilegedActionException.
//
register("", m ->
invokeVirtualMethod(m,
"",
b -> b.getParam(0), IRBuildHelper::getReturnVar)
);
//
register("", m ->
invokeVirtualMethod(m,
"",
b -> b.getParam(0), IRBuildHelper::getReturnVar)
);
//
register("", m ->
invokeVirtualMethod(m,
"",
b -> b.getParam(0), IRBuildHelper::getReturnVar)
);
//
register("", m ->
invokeVirtualMethod(m,
"",
b -> b.getParam(0), IRBuildHelper::getReturnVar)
);
//
register("", m ->
allocateObject(m, "(java.security.ProtectionDomain[],boolean)>", b -> {
Var context = b.newTempVar(typeSystem.getArrayType(
typeSystem.getClassType("java.security.ProtectionDomain"), 1));
Var isPrivileged = b.newTempVar(BooleanType.BOOLEAN);
return List.of(context, isPrivileged);
}));
// --------------------------------------------------------------------
// sun.misc.Perf
// --------------------------------------------------------------------
//
register("", m ->
allocateObject(m, "(int)>",
b -> Collections.singletonList(b.getParam(2)))
);
// --------------------------------------------------------------------
// sun.misc.Unsafe
// --------------------------------------------------------------------
// Currently, we only model Unsafe operations on arrays.
// Generic model for Unsafe.put/get is impossible here due to
// strong typing of ArrayAccess. It can be modeled in Plugin system.
Type objArrayType = typeSystem.getArrayType(
typeSystem.getClassType(OBJECT), 1);
//
register("", m -> {
IRBuildHelper helper = new IRBuildHelper(m);
Var array = helper.newTempVar(objArrayType);
Var i = helper.newTempVar(IntType.INT);
List stmts = List.of(
new Cast(array, new CastExp(helper.getParam(0), objArrayType)),
new StoreArray(new ArrayAccess(array, i), helper.getParam(3)),
helper.newReturn()
);
return helper.build(stmts);
});
Function unsafePut = m -> {
IRBuildHelper helper = new IRBuildHelper(m);
Var array = helper.newTempVar(objArrayType);
Var i = helper.newTempVar(IntType.INT);
List stmts = List.of(
new Cast(array, new CastExp(helper.getParam(0), objArrayType)),
new StoreArray(new ArrayAccess(array, i), helper.getParam(2)),
helper.newReturn()
);
return helper.build(stmts);
};
//
register("", unsafePut);
//
register("", unsafePut);
//
register("", unsafePut);
//
register("", unsafePut);
Function unsafeGet = m -> {
IRBuildHelper helper = new IRBuildHelper(m);
Var array = helper.newTempVar(objArrayType);
Var i = helper.newTempVar(IntType.INT);
List stmts = List.of(
new Cast(array, new CastExp(helper.getParam(0), objArrayType)),
new LoadArray(helper.getReturnVar(), new ArrayAccess(array, i)),
helper.newReturn()
);
return helper.build(stmts);
};
//
register("", unsafeGet);
//
register("", unsafeGet);
//
register("", unsafeGet);
}
// --------------------------------------------------------------------
// Convenient methods for helping create native model.
// --------------------------------------------------------------------
/**
* Registers models for specific native methods.
*/
private void register(String methodSig, Function model) {
JMethod method = hierarchy.getJREMethod(methodSig);
if (method != null) {
models.put(method, model);
}
}
/**
* Creates an IR which contains a store statement to the specified static field.
*/
private IR storeStaticField(
JMethod method, String fieldSig,
Function getFrom) {
IRBuildHelper helper = new IRBuildHelper(method);
JField field = hierarchy.getJREField(fieldSig);
List stmts = new ArrayList<>();
stmts.add(new StoreField(new StaticFieldAccess(field.getRef()),
getFrom.apply(helper)));
stmts.add(helper.newReturn());
return helper.build(stmts);
}
/**
* Creates an IR which contains a invoke statement to the specific virtual method.
*/
private IR invokeVirtualMethod(
JMethod method, String calleeSig,
Function getRecv,
Function getRet) {
IRBuildHelper helper = new IRBuildHelper(method);
JMethod callee = hierarchy.getJREMethod(calleeSig);
List stmts = new ArrayList<>();
InvokeVirtual callSite = new InvokeVirtual(
callee.getRef(), getRecv.apply(helper), List.of());
stmts.add(new Invoke(method, callSite, getRet.apply(helper)));
stmts.add(helper.newReturn());
return helper.build(stmts);
}
/**
* Creates an IR which allocates a new object, invokes its specified
* constructor, and returns it.
*/
private IR allocateObject(
JMethod method, String ctorSig,
Function> getArgs) {
IRBuildHelper helper = new IRBuildHelper(method);
JMethod ctor = hierarchy.getJREMethod(ctorSig);
ClassType type = ctor.getDeclaringClass().getType();
Var base = helper.newTempVar(type);
List stmts = new ArrayList<>();
stmts.add(new New(method, base, new NewInstance(type)));
stmts.add(new Invoke(method,
new InvokeSpecial(ctor.getRef(), base, getArgs.apply(helper))));
stmts.add(new Copy(helper.getReturnVar(), base));
stmts.add(helper.newReturn());
return helper.build(stmts);
}
/**
* Creates an IR which allocates a new array object, fills the array by a
* new-created (but uninitialized) object as content, and returns the array.
*/
private IR allocateArray(JMethod method, Type elemType) {
IRBuildHelper helper = new IRBuildHelper(method);
List stmts = new ArrayList<>();
Var len = helper.newTempVar(IntType.INT);
ArrayType arrayType = typeSystem.getArrayType(elemType, 1);
Var array = helper.getReturnVar();
stmts.add(new New(method, array, new NewArray(arrayType, len)));
if (elemType instanceof ClassType) {
// if the element type is of class type, then we create an
// uninitialized object as array content (can be seen as
// a place holder object).
Var elem = helper.newTempVar(elemType);
stmts.add(new New(method, elem,
new NewInstance((ClassType) elemType)));
stmts.add(new StoreArray(new ArrayAccess(array, len), elem));
}
stmts.add(helper.newReturn());
return helper.build(stmts);
}
@Serial
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
}
@Serial
private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
s.defaultReadObject();
models = newMap();
initModels();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy