2014-07-12 21:37:41 +00:00
|
|
|
package cpw.mods.fml.common.asm.transformers;
|
|
|
|
|
2014-07-13 00:20:01 +00:00
|
|
|
import org.objectweb.asm.*;
|
|
|
|
|
|
|
|
import cpw.mods.fml.relauncher.FMLRelaunchLog;
|
|
|
|
import cpw.mods.fml.relauncher.FMLSecurityManager.ExitTrappedException;
|
2014-07-12 21:37:41 +00:00
|
|
|
import net.minecraft.launchwrapper.IClassTransformer;
|
|
|
|
|
2014-07-13 00:20:01 +00:00
|
|
|
public class TerminalTransformer implements IClassTransformer
|
|
|
|
{
|
2014-07-12 21:37:41 +00:00
|
|
|
@Override
|
|
|
|
public byte[] transform(String name, String transformedName, byte[] basicClass)
|
|
|
|
{
|
2014-08-15 20:03:45 +00:00
|
|
|
if (basicClass == null) return null;
|
2014-07-13 00:20:01 +00:00
|
|
|
ClassReader reader = new ClassReader(basicClass);
|
|
|
|
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
|
|
|
|
|
|
|
ClassVisitor visitor = writer;
|
|
|
|
visitor = new ExitVisitor(visitor);
|
|
|
|
|
|
|
|
reader.accept(visitor, 0);
|
|
|
|
return writer.toByteArray();
|
2014-07-12 21:37:41 +00:00
|
|
|
}
|
|
|
|
|
2014-07-13 00:20:01 +00:00
|
|
|
public static class ExitVisitor extends ClassVisitor
|
|
|
|
{
|
|
|
|
private String clsName = null;
|
|
|
|
private static final String callbackOwner = org.objectweb.asm.Type.getInternalName(ExitVisitor.class);
|
|
|
|
|
|
|
|
private ExitVisitor(ClassVisitor cv)
|
|
|
|
{
|
|
|
|
super(Opcodes.ASM4, cv);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
|
|
|
|
{
|
|
|
|
super.visit(version, access, name, signature, superName, interfaces);
|
|
|
|
this.clsName = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public MethodVisitor visitMethod(int mAccess, final String mName, final String mDesc, String mSignature, String[] mExceptions)
|
|
|
|
{
|
2014-08-16 14:32:22 +00:00
|
|
|
final boolean warn = !(clsName.equals("net/minecraft/client/Minecraft") ||
|
|
|
|
clsName.equals("net/minecraft/server/dedicated/DedicatedServer") ||
|
|
|
|
clsName.equals("cpw/mods/fml/common/FMLCommonHandler") ||
|
|
|
|
clsName.startsWith("com/jcraft/jogg/") ||
|
|
|
|
clsName.startsWith("scala/sys/")
|
|
|
|
);
|
2014-07-13 00:20:01 +00:00
|
|
|
|
|
|
|
return new MethodVisitor(Opcodes.ASM4, super.visitMethod(mAccess, mName, mDesc, mSignature, mExceptions))
|
|
|
|
{
|
2014-08-16 14:32:22 +00:00
|
|
|
@Override
|
2014-08-16 17:21:07 +00:00
|
|
|
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean isIntf)
|
2014-07-13 00:20:01 +00:00
|
|
|
{
|
|
|
|
if (opcode == Opcodes.INVOKESTATIC && owner.equals("java/lang/System") && name.equals("exit") && desc.equals("(I)V"))
|
|
|
|
{
|
|
|
|
if (warn)
|
|
|
|
{
|
|
|
|
FMLRelaunchLog.warning("=============================================================");
|
2014-08-16 14:32:22 +00:00
|
|
|
FMLRelaunchLog.warning("MOD HAS DIRECT REFERENCE System.exit() THIS IS NOT ALLOWED REROUTING TO FML!");
|
2014-07-13 00:20:01 +00:00
|
|
|
FMLRelaunchLog.warning("Offendor: %s.%s%s", ExitVisitor.this.clsName, mName, mDesc);
|
2014-08-16 14:32:22 +00:00
|
|
|
FMLRelaunchLog.warning("Use FMLCommonHandler.exitJava instead");
|
2014-07-13 00:20:01 +00:00
|
|
|
FMLRelaunchLog.warning("=============================================================");
|
|
|
|
}
|
|
|
|
owner = ExitVisitor.callbackOwner;
|
|
|
|
name = "systemExitCalled";
|
|
|
|
}
|
|
|
|
else if (opcode == Opcodes.INVOKEVIRTUAL && owner.equals("java/lang/Runtime") && name.equals("exit") && desc.equals("(I)V"))
|
|
|
|
{
|
|
|
|
if (warn)
|
|
|
|
{
|
|
|
|
FMLRelaunchLog.warning("=============================================================");
|
2014-08-16 14:32:22 +00:00
|
|
|
FMLRelaunchLog.warning("MOD HAS DIRECT REFERENCE Runtime.exit() THIS IS NOT ALLOWED REROUTING TO FML!");
|
2014-07-13 00:20:01 +00:00
|
|
|
FMLRelaunchLog.warning("Offendor: %s.%s%s", ExitVisitor.this.clsName, mName, mDesc);
|
2014-08-16 14:32:22 +00:00
|
|
|
FMLRelaunchLog.warning("Use FMLCommonHandler.exitJava instead");
|
2014-07-13 00:20:01 +00:00
|
|
|
FMLRelaunchLog.warning("=============================================================");
|
|
|
|
}
|
|
|
|
opcode = Opcodes.INVOKESTATIC;
|
|
|
|
owner = ExitVisitor.callbackOwner;
|
|
|
|
name = "runtimeExitCalled";
|
|
|
|
desc = "(Ljava/lang/Runtime;I)V";
|
|
|
|
}
|
2014-08-16 14:32:22 +00:00
|
|
|
else if (opcode == Opcodes.INVOKEVIRTUAL && owner.equals("java/lang/Runtime") && name.equals("halt") && desc.equals("(I)V"))
|
|
|
|
{
|
|
|
|
if (warn)
|
|
|
|
{
|
|
|
|
FMLRelaunchLog.warning("=============================================================");
|
|
|
|
FMLRelaunchLog.warning("MOD HAS DIRECT REFERENCE Runtime.halt() THIS IS NOT ALLOWED REROUTING TO FML!");
|
|
|
|
FMLRelaunchLog.warning("Offendor: %s.%s%s", ExitVisitor.this.clsName, mName, mDesc);
|
|
|
|
FMLRelaunchLog.warning("Use FMLCommonHandler.exitJava instead");
|
|
|
|
FMLRelaunchLog.warning("=============================================================");
|
|
|
|
}
|
|
|
|
opcode = Opcodes.INVOKESTATIC;
|
|
|
|
owner = ExitVisitor.callbackOwner;
|
|
|
|
name = "runtimeHaltCalled";
|
|
|
|
desc = "(Ljava/lang/Runtime;I)V";
|
|
|
|
}
|
2014-07-13 00:20:01 +00:00
|
|
|
|
2014-08-16 17:21:07 +00:00
|
|
|
super.visitMethodInsn(opcode, owner, name, desc, isIntf);
|
2014-07-13 00:20:01 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Intercept System.exit, and check if the caller is allowed to use it, if not wrap it in a ExitTrappedException
|
|
|
|
public static void systemExitCalled(int status)
|
|
|
|
{
|
|
|
|
ExitVisitor.checkAccess();
|
|
|
|
System.exit(status);
|
|
|
|
}
|
|
|
|
// Intercept Runtime.getRuntime().exit, and check if the caller is allowed to use it, if not wrap it in a ExitTrappedException
|
|
|
|
public static void runtimeExitCalled(Runtime runtime, int status)
|
|
|
|
{
|
|
|
|
ExitVisitor.checkAccess();
|
|
|
|
runtime.exit(status);
|
|
|
|
}
|
|
|
|
|
2014-08-16 14:32:22 +00:00
|
|
|
// Intercept Runtime.getRuntime().halt, and check if the caller is allowed to use it, if not wrap it in a ExitTrappedException
|
|
|
|
public static void runtimeHaltCalled(Runtime runtime, int status)
|
|
|
|
{
|
|
|
|
ExitVisitor.checkAccess();
|
|
|
|
runtime.halt(status);
|
|
|
|
}
|
|
|
|
|
2014-07-13 00:20:01 +00:00
|
|
|
private static void checkAccess()
|
|
|
|
{
|
|
|
|
StackTraceElement[] cause = Thread.currentThread().getStackTrace();
|
|
|
|
|
|
|
|
String callingClass = cause.length > 2 ? cause[3].getClassName() : "none";
|
|
|
|
String callingParent = cause.length > 3 ? cause[4].getClassName() : "none";
|
|
|
|
// FML is allowed to call system exit and the Minecraft applet (from the quit button), and the dedicated server (from itself)
|
|
|
|
if (!(callingClass.startsWith("cpw.mods.fml.") ||
|
|
|
|
("net.minecraft.client.Minecraft".equals(callingClass) && "net.minecraft.client.Minecraft".equals(callingParent)) ||
|
|
|
|
("net.minecraft.server.dedicated.DedicatedServer".equals(callingClass) && "net.minecraft.server.MinecraftServer".equals(callingParent)))
|
|
|
|
)
|
|
|
|
{
|
|
|
|
throw new ExitTrappedException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-12 21:37:41 +00:00
|
|
|
}
|