diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PCLoggingTransformer.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PCLoggingTransformer.java index e9454868d..f85b9f114 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PCLoggingTransformer.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PCLoggingTransformer.java @@ -2,6 +2,7 @@ import edu.columbia.cs.psl.phosphor.instrumenter.*; import edu.columbia.cs.psl.phosphor.instrumenter.asm.OffsetPreservingClassReader; +import edu.columbia.cs.psl.phosphor.mask.SerializationFixingCV; import edu.columbia.cs.psl.phosphor.org.objectweb.asm.commons.OurSerialVersionUIDAdder; import edu.columbia.cs.psl.phosphor.runtime.StringUtils; import edu.columbia.cs.psl.phosphor.runtime.TaintInstrumented; diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintTrackingClassVisitor.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintTrackingClassVisitor.java index 59666cc3f..1e0dc258e 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintTrackingClassVisitor.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintTrackingClassVisitor.java @@ -8,6 +8,8 @@ import edu.columbia.cs.psl.phosphor.control.ControlStackInitializingMV; import edu.columbia.cs.psl.phosphor.control.ControlStackRestoringMV; import edu.columbia.cs.psl.phosphor.instrumenter.analyzer.NeverNullArgAnalyzerAdapter; +import edu.columbia.cs.psl.phosphor.mask.ReflectionMV; +import edu.columbia.cs.psl.phosphor.mask.ReflectionMVFactory; import edu.columbia.cs.psl.phosphor.runtime.PhosphorStackFrame; import edu.columbia.cs.psl.phosphor.runtime.TaintInstrumented; import edu.columbia.cs.psl.phosphor.runtime.TaintSourceWrapper; @@ -245,7 +247,7 @@ private MethodVisitor createInstrumentingMVChain(int access, String name, String controlStackRestoringMV = new ControlStackRestoringMV(next, rootmV, className, name, controlFlowPolicy); next = controlStackRestoringMV; } - ReflectionHidingMV reflectionMasker = new ReflectionHidingMV(next, className, name, isEnum); + ReflectionMV reflectionMasker = ReflectionMVFactory.create(next, className, name); PrimitiveBoxingFixer boxFixer = new PrimitiveBoxingFixer(access, className, name, instrumentedDesc, signature, _exceptions, reflectionMasker, analyzer); @@ -293,8 +295,7 @@ private MethodVisitor createInstrumentingMVChain(int access, String name, String } lvs.setPrimitiveArrayAnalyzer(primitiveArrayFixer); reflectionMasker.setLvs(lvs); - final MethodVisitor prev = preAnalyzer; - return prev; + return preAnalyzer; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] _exceptions) { diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/DisabledReflectionMV.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/DisabledReflectionMV.java new file mode 100644 index 000000000..d9d350d40 --- /dev/null +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/DisabledReflectionMV.java @@ -0,0 +1,42 @@ +package edu.columbia.cs.psl.phosphor.mask; + +import edu.columbia.cs.psl.phosphor.Configuration; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import static edu.columbia.cs.psl.phosphor.instrumenter.TaintMethodRecord.IS_INSTANCE; + +class DisabledReflectionMV extends ReflectionMV implements Opcodes { + DisabledReflectionMV(MethodVisitor mv, String className, String methodName) { + super(Configuration.ASM_VERSION, mv); + if (!isApplicable(className, methodName)) { + throw new IllegalArgumentException(); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean isInterface) { + if (owner.equals("java/lang/Class") && name.startsWith("isInstance")) { + // Even if we are not masking other methods, this must be masked + IS_INSTANCE.delegateVisit(mv); + } else { + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + } + } + + public static boolean isApplicable(String className, String methodName) { + switch (className) { + case "org/codehaus/groovy/vmplugin/v5/Java5": + return methodName.equals("makeInterfaceTypes"); + case "jdk/internal/reflect/ReflectionFactory": + case "java/lang/reflect/ReflectAccess": + // Java >= 9 + // TODO keep? + case "java/io/ObjectOutputStream": + case "java/io/ObjectInputStream": + return true; + default: + return false; + } + } +} diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ObjectStreamReflectionMV.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ObjectStreamReflectionMV.java new file mode 100644 index 000000000..8a1f498a7 --- /dev/null +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ObjectStreamReflectionMV.java @@ -0,0 +1,55 @@ +package edu.columbia.cs.psl.phosphor.mask; + +import edu.columbia.cs.psl.phosphor.Configuration; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import static edu.columbia.cs.psl.phosphor.instrumenter.TaintMethodRecord.IS_INSTANCE; + +class ObjectStreamReflectionMV extends ReflectionMV { + private final String methodName; + + ObjectStreamReflectionMV(MethodVisitor mv, String className, String methodName) { + super(Configuration.ASM_VERSION, mv); + if (!isApplicable(className, methodName)) { + throw new IllegalArgumentException(); + } + this.methodName = methodName; + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean isInterface) { + if (owner.equals("java/lang/Class") && name.startsWith("isInstance")) { + // Even if we are not masking other methods, this must be masked + IS_INSTANCE.delegateVisit(mv); + } else if (owner.equals("sun/misc/Unsafe") && shouldMask(name)) { + owner = ReflectionMVFactory.getRuntimeUnsafePropagatorClassName(); + super.visitMethodInsn( + Opcodes.INVOKESTATIC, owner, name, "(Lsun/misc/Unsafe;" + desc.substring(1), isInterface); + } else { + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + } + } + + private boolean shouldMask(String name) { + switch (methodName) { + case "setObjFieldValues": + return name.startsWith("putObject") || name.startsWith("compareAndSwapObject"); + case "getObjFieldValues": + return name.startsWith("getObject"); + case "getPrimFieldValues": + case "setPrimFieldValues": + // Check for name.startsWith("put") || name.startsWith("get") was included but unhandled with a + // TODO and the note: name = name + "$$NOUNBOX" + // It is unclear if this needs to fixed + default: + return false; + } + } + + public static boolean isApplicable(String className, String methodName) { + return (className.equals("java/io/ObjectStreamClass") || className.equals("java/io/ObjectStreamField")) + && Configuration.TAINT_THROUGH_SERIALIZATION + && !methodName.equals("getDeclaredSerialFields$$PHOSPHORTAGGED"); + } +} diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/ReflectionHidingMV.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionHidingMV.java similarity index 58% rename from Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/ReflectionHidingMV.java rename to Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionHidingMV.java index 484d244de..7ef3e7063 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/ReflectionHidingMV.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionHidingMV.java @@ -1,9 +1,12 @@ -package edu.columbia.cs.psl.phosphor.instrumenter; +package edu.columbia.cs.psl.phosphor.mask; import edu.columbia.cs.psl.phosphor.Configuration; import edu.columbia.cs.psl.phosphor.Phosphor; -import edu.columbia.cs.psl.phosphor.runtime.*; -import edu.columbia.cs.psl.phosphor.runtime.jdk.unsupported.RuntimeSunMiscUnsafePropagator; +import edu.columbia.cs.psl.phosphor.instrumenter.LocalVariableManager; +import edu.columbia.cs.psl.phosphor.runtime.ArrayReflectionMasker; +import edu.columbia.cs.psl.phosphor.runtime.CharacterUtils; +import edu.columbia.cs.psl.phosphor.runtime.PhosphorStackFrame; +import edu.columbia.cs.psl.phosphor.runtime.ReflectionMasker; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -14,50 +17,25 @@ import static edu.columbia.cs.psl.phosphor.instrumenter.TaintMethodRecord.*; import static edu.columbia.cs.psl.phosphor.instrumenter.TaintTrackingClassVisitor.CONTROL_STACK_DESC; -public class ReflectionHidingMV extends MethodVisitor implements Opcodes { - +class ReflectionHidingMV extends ReflectionMV implements Opcodes { private final String className; private final String methodName; - private final boolean disable; - private final boolean isEnumValueOf; private final boolean patchAnonymousClasses; private LocalVariableManager lvs; - public ReflectionHidingMV(MethodVisitor mv, String className, String name, boolean isEnum) { + ReflectionHidingMV(MethodVisitor mv, String className, String name) { super(Configuration.ASM_VERSION, mv); this.className = className; this.methodName = name; - this.disable = shouldDisable(className, name); - this.patchAnonymousClasses = className.equals("java/lang/invoke/InnerClassLambdaMetafactory") || className.equals("sun/reflect/ClassDefiner"); - this.isEnumValueOf = isEnum && name.equals("valueOf"); + this.patchAnonymousClasses = className.equals("java/lang/invoke/InnerClassLambdaMetafactory") + || className.equals("sun/reflect/ClassDefiner"); } + @Override public void setLvs(LocalVariableManager lvs) { this.lvs = lvs; } - private int[] storeToLocals(int n) { - int[] ret = new int[n]; - for (int i = 0; i < n; i++) { - ret[i] = lvs.getTmpLV(); - super.visitVarInsn(ASTORE, ret[i]); - } - return ret; - } - - private void loadAndFree(int[] r) { - for (int i = r.length - 1; i >= 0; i--) { - super.visitVarInsn(ALOAD, r[i]); - lvs.freeTmpLV(r[i]); - } - } - - private void maskGetter(TaintMethodRecord mask, Type[] args) { - int[] tmps = storeToLocals(args.length); - visit(mask); - loadAndFree(tmps); - } - /* Returns whether a method instruction with the specified information is for a method added to Unsafe by Phosphor * that retrieves the value of a field of a Java heap object. */ private boolean isUnsafeFieldGetter(int opcode, String owner, String name, Type[] args, String nameWithoutSuffix) { @@ -139,22 +117,20 @@ private boolean isUnsafeFieldSetter(int opcode, String owner, String name, Type[ /* Returns whether a method instruction with the specified information is for a method added to Unsafe by Phosphor * for a compareAndSwap method. */ - private boolean isUnsafeCAS(String owner, String name, String nameWithoutSuffix) { - if (!Phosphor.isUnsafeClass(owner)) { - return false; - } else { + private boolean isUnsafeCAS(String owner, String nameWithoutSuffix) { + if (Phosphor.isUnsafeClass(owner)) { if (Configuration.IS_JAVA_8) { return "compareAndSwapInt".equals(nameWithoutSuffix) || "compareAndSwapLong".equals(nameWithoutSuffix) || "compareAndSwapObject".equals(nameWithoutSuffix); } - return false; } + return false; } private boolean isUnsafeIntrinsic(String owner, String name, String desc) { - if(Configuration.IS_JAVA_8){ - return false; //These intrinsics are only for 9+ + if (Configuration.IS_JAVA_8) { + return false; // These intrinsics are only for 9+ } if (!Phosphor.isUnsafeClass(owner)) { return false; @@ -434,7 +410,6 @@ private boolean isUnsafeCopyMemory(String owner, String name, String nameWithout case "copySwapMemory": return true; } - } return false; } @@ -442,7 +417,8 @@ private boolean isUnsafeCopyMemory(String owner, String name, String nameWithout @Override public void visitCode() { super.visitCode(); - if (this.className.equals("java/lang/invoke/MethodHandles$Lookup") && this.methodName.startsWith("defineHiddenClass")) { + if (this.className.equals("java/lang/invoke/MethodHandles$Lookup") + && this.methodName.startsWith("defineHiddenClass")) { super.visitVarInsn(ALOAD, 1); INSTRUMENT_CLASS_BYTES.delegateVisit(mv); super.visitVarInsn(ASTORE, 1); @@ -452,182 +428,211 @@ public void visitCode() { @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean isInterface) { Type[] args = Type.getArgumentTypes(desc); - String nameWithoutSuffix = name; String descWithoutStackFrame = desc.replace(PhosphorStackFrame.DESCRIPTOR, ""); - if ((disable || className.equals("java/io/ObjectOutputStream") || className.equals("java/io/ObjectInputStream")) && owner.equals("java/lang/Class") && !owner.equals(className) && name.startsWith("isInstance")) { - // Even if we are ignoring other hiding here, we definitely need to do this. - visit(IS_INSTANCE); - } else if (disable) { - if (this.methodName.startsWith("setObjFieldValues") && owner.equals("sun/misc/Unsafe") && (name.startsWith("putObject") || name.startsWith("compareAndSwapObject"))) { - owner = getRuntimeUnsafePropogatorClassName(); - super.visitMethodInsn(INVOKESTATIC, owner, name, "(Lsun/misc/Unsafe;" + desc.substring(1), isInterface); - } else if (this.methodName.startsWith("getObjFieldValues") && owner.equals("sun/misc/Unsafe") && name.startsWith("getObject")) { - owner = getRuntimeUnsafePropogatorClassName(); - super.visitMethodInsn(INVOKESTATIC, owner, name, "(Lsun/misc/Unsafe;" + desc.substring(1), isInterface); - } else if ((this.methodName.startsWith("getPrimFieldValues") || this.methodName.startsWith("setPrimFieldValues")) && owner.equals("sun/misc/Unsafe") && (name.startsWith("put") || name.startsWith("get"))) { - //name = name + "$$NOUNBOX"; - //TODO - super.visitMethodInsn(opcode, owner, name, desc, isInterface); - } else { - super.visitMethodInsn(opcode, owner, name, desc, isInterface); - } - } else { - if (patchAnonymousClasses && name.equals("defineAnonymousClass") && Phosphor.isUnsafeClass(owner) && descWithoutStackFrame.equals("(Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;")) { - super.visitInsn(POP); - super.visitInsn(SWAP); - INSTRUMENT_CLASS_BYTES_ANONYMOUS.delegateVisit(mv); - super.visitInsn(SWAP); - desc = descWithoutStackFrame; // Go directly to the native call - } else if (patchAnonymousClasses && name.equals("defineClass") - && Configuration.IS_JAVA_8 && "sun/misc/Unsafe".equals(owner) - && descWithoutStackFrame.equals("(Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;")) { - desc = "(L" + owner + ";" + desc.substring(1); - super.visitMethodInsn(Opcodes.INVOKESTATIC, getRuntimeUnsafePropogatorClassName(), name, desc, false); + switch (owner) { + case "jdk/internal/reflect/Reflection": + maskReflection(opcode, owner, name, desc, isInterface); return; - } - /* - * Fix for #188 - if we are in a wrapped method, and called by the wrapper, we need to get the caller class - * of the wrapper, not of this stack frame - */ - if(owner.equals("jdk/internal/reflect/Reflection") && name.equals("getCallerClass")){ - int phosphorStackFrame = lvs.getLocalVariableAdder().getIndexOfPhosphorStackData(); - super.visitVarInsn(ALOAD, phosphorStackFrame); - super.visitMethodInsn(opcode, owner, name, desc, isInterface); - super.visitLdcInsn(Type.getObjectType(className)); - GET_CALLER_CLASS_WRAPPER.delegateVisit(mv); + case "java/lang/reflect/Array": + maskArray(opcode, owner, name, desc, isInterface); return; - } - if (owner.equals("java/lang/reflect/Array") && !owner.equals(className)) { - owner = Type.getInternalName(ArrayReflectionMasker.class); - } - if (name.equals("allocateUninitializedArray") && Phosphor.isUnsafeClass(owner)) { - desc = "(L" + owner + ";" + desc.substring(1); - super.visitMethodInsn(Opcodes.INVOKESTATIC, getRuntimeUnsafePropogatorClassName(), name, desc, false); + case "java/lang/Character": + maskCharacter(opcode, owner, name, desc, isInterface); return; - } else if (isUnsafeIntrinsic(owner, name, descWithoutStackFrame) || isUnsafeFieldGetter(opcode, owner, name, args, nameWithoutSuffix)) { - if (Configuration.IMPLICIT_TRACKING || Configuration.IMPLICIT_HEADERS_NO_TRACKING) { - desc = desc.replace(CONTROL_STACK_DESC, ""); - //in control tracking mode, pop the control stack off of the stack to reuse the existing method - //but first, pop the null that's there for the erased return type. - if (isUnsafeReferenceFieldGetter(nameWithoutSuffix)) { - super.visitInsn(POP); - } - super.visitInsn(SWAP); - super.visitInsn(POP); - if (isUnsafeReferenceFieldGetter(nameWithoutSuffix)) { - super.visitInsn(ACONST_NULL); - } - } - desc = "(L" + owner + ";" + desc.substring(1); - if (isUnsafeIntrinsic(owner, name, descWithoutStackFrame)) { - // Java 11 uses get/putObject instead of Reference - name = name.replace("Object", "Reference"); - } - super.visitMethodInsn(Opcodes.INVOKESTATIC, getRuntimeUnsafePropogatorClassName(), name, desc, false); + case "sun/reflect/NativeMethodAccessorImpl": + case "jdk/internal/reflect/NativeMethodAccessorImpl": + maskMethodAccessor(opcode, owner, name, desc, isInterface); return; - } else if (isUnsafeFieldSetter(opcode, owner, name, args, nameWithoutSuffix)) { - if (Configuration.IMPLICIT_TRACKING || Configuration.IMPLICIT_HEADERS_NO_TRACKING) { - desc = desc.replace(CONTROL_STACK_DESC, ""); - super.visitInsn(POP); - } - desc = "(L" + owner + ";" + desc.substring(1); - super.visitMethodInsn(Opcodes.INVOKESTATIC, getRuntimeUnsafePropogatorClassName(), name, desc, false); + case "sun/reflect/NativeConstructorAccessorImpl": + case "jdk/internal/reflect/NativeConstructorAccessorImpl": + maskConstructorAccessor(opcode, owner, name, desc, isInterface); return; - } else if (isUnsafeCAS(owner, name, nameWithoutSuffix) || isUnsafeCopyMemory(owner, name, nameWithoutSuffix)) { - if (Configuration.IMPLICIT_TRACKING || Configuration.IMPLICIT_HEADERS_NO_TRACKING) { - desc = desc.replace(CONTROL_STACK_DESC, ""); - super.visitInsn(SWAP); + default: + if (Phosphor.isUnsafeClass(owner)) { + patchUnsafe(opcode, owner, name, desc, isInterface, descWithoutStackFrame, args, name); + } else { + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + fixReturn(owner, name, name, descWithoutStackFrame); + } + } + } + + private void patchUnsafe( + int opcode, + String owner, + String name, + String desc, + boolean isInterface, + String descWithoutStackFrame, + Type[] args, + String nameWithoutSuffix) { + if (patchAnonymousClasses + && name.equals("defineAnonymousClass") + && descWithoutStackFrame.equals("(Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;")) { + super.visitInsn(POP); + super.visitInsn(SWAP); + INSTRUMENT_CLASS_BYTES_ANONYMOUS.delegateVisit(mv); + super.visitInsn(SWAP); + desc = descWithoutStackFrame; // Go directly to the native call + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + } else if (patchAnonymousClasses + && name.equals("defineClass") + && Configuration.IS_JAVA_8 + && descWithoutStackFrame.equals( + "(Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;")) { + desc = "(Lsun/misc/Unsafe;" + desc.substring(1); + super.visitMethodInsn( + Opcodes.INVOKESTATIC, ReflectionMVFactory.getRuntimeUnsafePropagatorClassName(), name, desc, false); + } else if (name.equals("allocateUninitializedArray")) { + desc = "(L" + owner + ";" + desc.substring(1); + super.visitMethodInsn( + Opcodes.INVOKESTATIC, ReflectionMVFactory.getRuntimeUnsafePropagatorClassName(), name, desc, false); + } else if (isUnsafeIntrinsic(owner, name, descWithoutStackFrame) + || isUnsafeFieldGetter(opcode, owner, name, args, nameWithoutSuffix)) { + if (Configuration.IMPLICIT_TRACKING || Configuration.IMPLICIT_HEADERS_NO_TRACKING) { + desc = desc.replace(CONTROL_STACK_DESC, ""); + // in control tracking mode, pop the control stack off of the stack to reuse the existing method + // but first, pop the null that's there for the erased return type. + if (isUnsafeReferenceFieldGetter(nameWithoutSuffix)) { super.visitInsn(POP); } - desc = "(L" + owner + ";" + desc.substring(1); - owner = getRuntimeUnsafePropogatorClassName(); - opcode = INVOKESTATIC; - super.visitMethodInsn(opcode, owner, name, desc, isInterface); - return; - } else if (owner.equals(Type.getInternalName(Character.class)) && (name.equals("codePointAt") - || name.equals("toChars") || name.equals("codePointBefore") || name.equals("reverseBytes") - || name.equals("toLowerCase") || name.equals("toTitleCase") || name.equals("toUpperCase"))) { - owner = Type.getInternalName(CharacterUtils.class); - desc = lvs.patchDescToAcceptPhosphorStackFrameAndPushIt(desc, mv); - } else if ((owner.equals("sun/reflect/NativeMethodAccessorImpl") || owner.equals("jdk/internal/reflect/NativeMethodAccessorImpl")) && name.equals("invoke0")) { - //Stack: Method Receiver Args StackData - PREPARE_FOR_CALL_REFLECTIVE.delegateVisit(this); - String methodTupleInternalName = Type.getInternalName(ReflectionMasker.MethodInvocationTuple.class); - mv.visitInsn(DUP); - mv.visitFieldInsn(GETFIELD, methodTupleInternalName, "method", Type.getType(Method.class).getDescriptor()); - mv.visitInsn(SWAP); - mv.visitInsn(DUP); - mv.visitFieldInsn(GETFIELD, methodTupleInternalName, "receiver", "Ljava/lang/Object;"); - mv.visitInsn(SWAP); - mv.visitFieldInsn(GETFIELD, methodTupleInternalName, "args", "[Ljava/lang/Object;"); - mv.visitVarInsn(ALOAD, lvs.getLocalVariableAdder().getIndexOfPhosphorStackData()); - } else if ((owner.equals("sun/reflect/NativeConstructorAccessorImpl") || owner.equals("jdk/internal/reflect/NativeConstructorAccessorImpl")) && name.equals("newInstance0")) { - PREPARE_FOR_CALL_REFLECTIVE_CONSTRUCTOR.delegateVisit(this); - String constructorInvocationPairInternalName = Type.getInternalName(ReflectionMasker.ConstructorInvocationPair.class); - mv.visitInsn(DUP); - mv.visitFieldInsn(GETFIELD, constructorInvocationPairInternalName, "constructor", Type.getDescriptor(Constructor.class)); - mv.visitInsn(SWAP); - mv.visitFieldInsn(GETFIELD, constructorInvocationPairInternalName, "args", "[Ljava/lang/Object;"); - mv.visitVarInsn(ALOAD, lvs.getLocalVariableAdder().getIndexOfPhosphorStackData()); + super.visitInsn(SWAP); + super.visitInsn(POP); + if (isUnsafeReferenceFieldGetter(nameWithoutSuffix)) { + super.visitInsn(ACONST_NULL); + } } - super.visitMethodInsn(opcode, owner, name, desc, isInterface); - if (owner.equals("java/lang/Class") && nameWithoutSuffix.endsWith("Fields") && !className.equals("java/lang/Class")) { - if (!Configuration.WITHOUT_FIELD_HIDING) { - visit(REMOVE_TAINTED_FIELDS); - } - } else if (owner.equals("java/lang/Class") && nameWithoutSuffix.endsWith("Methods") && !className.equals(owner) && descWithoutStackFrame.equals("()" + Type.getDescriptor(Method[].class))) { - visit(REMOVE_TAINTED_METHODS); - } else if (owner.equals("java/lang/Class") && nameWithoutSuffix.endsWith("Constructors") && !className.equals(owner) && descWithoutStackFrame.equals("()" + Type.getDescriptor(Constructor[].class))) { - visit(REMOVE_TAINTED_CONSTRUCTORS); - } else if (owner.equals("java/lang/Class") && name.equals("getInterfaces")) { - visit(REMOVE_TAINTED_INTERFACES); - } else if (owner.equals("java/lang/Throwable") && (name.equals("getOurStackTrace") || name.equals("getStackTrace")) && descWithoutStackFrame.equals("()" + Type.getDescriptor(StackTraceElement[].class))) { - //if (className.equals("java/lang/Throwable")) { - // super.visitVarInsn(ALOAD, 0); - // super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); - //} else { - // super.visitLdcInsn(Type.getObjectType(className)); - //} - //visit(REMOVE_EXTRA_STACK_TRACE_ELEMENTS); - } else if (owner.equals("java/lang/reflect/Method") && name.equals("invoke")) { - mv.visitVarInsn(ALOAD, lvs.getLocalVariableAdder().getIndexOfPhosphorStackData()); - UNWRAP_RETURN.delegateVisit(mv); + desc = "(L" + owner + ";" + desc.substring(1); + if (isUnsafeIntrinsic(owner, name, descWithoutStackFrame)) { + // Java 11 uses get/putObject instead of Reference + name = name.replace("Object", "Reference"); + } + super.visitMethodInsn( + Opcodes.INVOKESTATIC, ReflectionMVFactory.getRuntimeUnsafePropagatorClassName(), name, desc, false); + } else if (isUnsafeFieldSetter(opcode, owner, name, args, nameWithoutSuffix)) { + if (Configuration.IMPLICIT_TRACKING || Configuration.IMPLICIT_HEADERS_NO_TRACKING) { + desc = desc.replace(CONTROL_STACK_DESC, ""); + super.visitInsn(POP); } + desc = "(L" + owner + ";" + desc.substring(1); + super.visitMethodInsn( + Opcodes.INVOKESTATIC, ReflectionMVFactory.getRuntimeUnsafePropagatorClassName(), name, desc, false); + } else if (isUnsafeCAS(owner, nameWithoutSuffix) || isUnsafeCopyMemory(owner, name, nameWithoutSuffix)) { + if (Configuration.IMPLICIT_TRACKING || Configuration.IMPLICIT_HEADERS_NO_TRACKING) { + desc = desc.replace(CONTROL_STACK_DESC, ""); + super.visitInsn(SWAP); + super.visitInsn(POP); + } + desc = "(L" + owner + ";" + desc.substring(1); + super.visitMethodInsn( + INVOKESTATIC, ReflectionMVFactory.getRuntimeUnsafePropagatorClassName(), name, desc, isInterface); + } else { + super.visitMethodInsn(opcode, owner, name, desc, isInterface); } } - private String controlTrackDescOrNone() { - return (Configuration.IMPLICIT_TRACKING || Configuration.IMPLICIT_HEADERS_NO_TRACKING ? CONTROL_STACK_DESC : ""); + private void maskReflection(int opcode, String owner, String name, String desc, boolean isInterface) { + // If we in a wrapped method and called by the wrapper + // get the caller class of the wrapper not this stack frame + if (name.equals("getCallerClass")) { + int phosphorStackFrame = lvs.getLocalVariableAdder().getIndexOfPhosphorStackData(); + super.visitVarInsn(ALOAD, phosphorStackFrame); + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + super.visitLdcInsn(Type.getObjectType(className)); + GET_CALLER_CLASS_WRAPPER.delegateVisit(mv); + } else { + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + } } - /** - * Visits a method instruction for the specified method. - * - * @param method the method to be visited - */ - private void visit(TaintMethodRecord method) { - super.visitMethodInsn(method.getOpcode(), method.getOwner(), method.getName(), method.getDescriptor(), method.isInterface()); + private void maskArray(int opcode, String owner, String name, String desc, boolean isInterface) { + if (!owner.equals(className)) { + owner = Type.getInternalName(ArrayReflectionMasker.class); + } + super.visitMethodInsn(opcode, owner, name, desc, isInterface); } - private static boolean shouldDisable(String className, String methodName) { - if (className.equals("org/codehaus/groovy/vmplugin/v5/Java5") && methodName.equals("makeInterfaceTypes")) { - return true; - } else { - if (className.equals("jdk/internal/reflect/ReflectionFactory") || className.equals("java/lang/reflect/ReflectAccess")) { - //Java >= 9 - //TODO keep? - return true; - } - return Configuration.TAINT_THROUGH_SERIALIZATION && !methodName.equals("getDeclaredSerialFields$$PHOSPHORTAGGED") && - (className.startsWith("java/io/ObjectStreamClass") || className.equals("java/io/ObjectStreamField")); + private void maskCharacter(int opcode, String owner, String name, String desc, boolean isInterface) { + switch (name) { + case "codePointAt": + case "toChars": + case "codePointBefore": + case "reverseBytes": + case "toLowerCase": + case "toTitleCase": + case "toUpperCase": + owner = Type.getInternalName(CharacterUtils.class); + desc = lvs.patchDescToAcceptPhosphorStackFrameAndPushIt(desc, mv); } + super.visitMethodInsn(opcode, owner, name, desc, isInterface); } - private static String getRuntimeUnsafePropogatorClassName() { - if (Configuration.IS_JAVA_8) { - return Type.getInternalName(RuntimeSunMiscUnsafePropagator.class); + private void maskConstructorAccessor(int opcode, String owner, String name, String desc, boolean isInterface) { + if (name.equals("newInstance0")) { + PREPARE_FOR_CALL_REFLECTIVE_CONSTRUCTOR.delegateVisit(this); + String constructorInvocationPairInternalName = + Type.getInternalName(ReflectionMasker.ConstructorInvocationPair.class); + mv.visitInsn(DUP); + mv.visitFieldInsn( + GETFIELD, + constructorInvocationPairInternalName, + "constructor", + Type.getDescriptor(Constructor.class)); + mv.visitInsn(SWAP); + mv.visitFieldInsn(GETFIELD, constructorInvocationPairInternalName, "args", "[Ljava/lang/Object;"); + mv.visitVarInsn(ALOAD, lvs.getLocalVariableAdder().getIndexOfPhosphorStackData()); + } + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + } + + private void maskMethodAccessor(int opcode, String owner, String name, String desc, boolean isInterface) { + if (name.equals("invoke0")) { + // Stack: Method Receiver Args StackData + PREPARE_FOR_CALL_REFLECTIVE.delegateVisit(this); + String methodTupleInternalName = Type.getInternalName(ReflectionMasker.MethodInvocationTuple.class); + mv.visitInsn(DUP); + mv.visitFieldInsn( + GETFIELD, + methodTupleInternalName, + "method", + Type.getType(Method.class).getDescriptor()); + mv.visitInsn(SWAP); + mv.visitInsn(DUP); + mv.visitFieldInsn(GETFIELD, methodTupleInternalName, "receiver", "Ljava/lang/Object;"); + mv.visitInsn(SWAP); + mv.visitFieldInsn(GETFIELD, methodTupleInternalName, "args", "[Ljava/lang/Object;"); + mv.visitVarInsn(ALOAD, lvs.getLocalVariableAdder().getIndexOfPhosphorStackData()); + } + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + } + + private void fixReturn(String owner, String name, String nameWithoutSuffix, String descWithoutStackFrame) { + if (owner.equals("java/lang/Class") && !className.equals("java/lang/Class")) { + if (nameWithoutSuffix.endsWith("Fields")) { + if (!Configuration.WITHOUT_FIELD_HIDING) { + REMOVE_TAINTED_FIELDS.delegateVisit(mv); + } + } else if (nameWithoutSuffix.endsWith("Methods") + && descWithoutStackFrame.equals("()" + Type.getDescriptor(Method[].class))) { + REMOVE_TAINTED_METHODS.delegateVisit(mv); + } else if (nameWithoutSuffix.endsWith("Constructors") + && descWithoutStackFrame.equals("()" + Type.getDescriptor(Constructor[].class))) { + REMOVE_TAINTED_CONSTRUCTORS.delegateVisit(mv); + } else if (name.equals("getInterfaces")) { + REMOVE_TAINTED_INTERFACES.delegateVisit(mv); + } + } else if (owner.equals("java/lang/Throwable")) { + if (name.equals("getOurStackTrace") || name.equals("getStackTrace")) { + if (descWithoutStackFrame.equals("()" + Type.getDescriptor(StackTraceElement[].class))) { + // TODO REMOVE_EXTRA_STACK_TRACE_ELEMENTS? + } + } + } else if (owner.equals("java/lang/reflect/Method")) { + if (name.equals("invoke")) { + mv.visitVarInsn(ALOAD, lvs.getLocalVariableAdder().getIndexOfPhosphorStackData()); + UNWRAP_RETURN.delegateVisit(mv); + } } - return Type.getInternalName(RuntimeJDKInternalUnsafePropagator.class); } private static boolean isUnsafeReferenceFieldGetter(String methodName) { diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionMV.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionMV.java new file mode 100644 index 000000000..605e0a29f --- /dev/null +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionMV.java @@ -0,0 +1,13 @@ +package edu.columbia.cs.psl.phosphor.mask; + +import edu.columbia.cs.psl.phosphor.instrumenter.LocalVariableManager; +import org.objectweb.asm.MethodVisitor; + +public abstract class ReflectionMV extends MethodVisitor { + protected ReflectionMV(int api, MethodVisitor methodVisitor) { + super(api, methodVisitor); + } + + public void setLvs(LocalVariableManager lvs) { + } +} diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionMVFactory.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionMVFactory.java new file mode 100644 index 000000000..4ee38ae9c --- /dev/null +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/ReflectionMVFactory.java @@ -0,0 +1,26 @@ +package edu.columbia.cs.psl.phosphor.mask; + +import edu.columbia.cs.psl.phosphor.Configuration; +import edu.columbia.cs.psl.phosphor.runtime.RuntimeJDKInternalUnsafePropagator; +import edu.columbia.cs.psl.phosphor.runtime.jdk.unsupported.RuntimeSunMiscUnsafePropagator; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; + +public final class ReflectionMVFactory { + public static ReflectionMV create(MethodVisitor mv, String className, String methodName) { + if (ObjectStreamReflectionMV.isApplicable(className, methodName)) { + return new ObjectStreamReflectionMV(mv, className, methodName); + } else if (DisabledReflectionMV.isApplicable(className, methodName)) { + return new DisabledReflectionMV(mv, className, methodName); + } else { + return new ReflectionHidingMV(mv, className, methodName); + } + } + + public static String getRuntimeUnsafePropagatorClassName() { + return Type.getInternalName( + Configuration.IS_JAVA_8 + ? RuntimeSunMiscUnsafePropagator.class + : RuntimeJDKInternalUnsafePropagator.class); + } +} diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/SerializationFixingCV.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/SerializationFixingCV.java similarity index 99% rename from Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/SerializationFixingCV.java rename to Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/SerializationFixingCV.java index f03b809a0..51198070e 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/SerializationFixingCV.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/mask/SerializationFixingCV.java @@ -1,7 +1,8 @@ -package edu.columbia.cs.psl.phosphor.instrumenter; +package edu.columbia.cs.psl.phosphor.mask; import edu.columbia.cs.psl.phosphor.Configuration; import edu.columbia.cs.psl.phosphor.control.OpcodesUtil; +import edu.columbia.cs.psl.phosphor.instrumenter.TaintMethodRecord; import edu.columbia.cs.psl.phosphor.runtime.PhosphorStackFrame; import edu.columbia.cs.psl.phosphor.runtime.Taint; import org.objectweb.asm.*;