From 07725505345c28fd96b6a66141e323dc981ac5ea Mon Sep 17 00:00:00 2001 From: katherine-hough <32645020+katherine-hough@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:49:24 -0500 Subject: [PATCH] * Fixed issue with failure in AnnotationInstCase not being reported for Java 8 * Fixed failure in AnnotationInstCase for Java 8 by patching ASM for Java 8 * Fixed JavaDoc warnings * Refactored ConfigurationEmbeddingMV * Moved classes not used to by the agent at runtime to the package edu.columbia.cs.psl.phosphor.agent --- Phosphor/pom.xml | 54 +++++--- .../cs/psl/phosphor/Configuration.java | 94 +------------ .../cs/psl/phosphor/PCLoggingTransformer.java | 12 +- .../columbia/cs/psl/phosphor/Phosphor.java | 5 +- .../cs/psl/phosphor/PhosphorOption.java | 8 +- .../cs/psl/phosphor/agent/AsmPatcher.java | 44 +++++++ .../agent/ConfigurationEmbeddingMV.java | 80 ++++++++++++ .../EmbeddedPhosphorPatcher.java} | 55 ++------ .../psl/phosphor/agent}/InstrumentUtil.java | 17 ++- .../InstrumentedJREProxyGenerator.java | 5 +- .../cs/psl/phosphor/agent/PhosphorAgent.java | 7 +- .../psl/phosphor/agent/PhosphorPatcher.java | 53 ++++++++ .../psl/phosphor/control/graph/FlowGraph.java | 2 +- .../ConfigurationEmbeddingMV.java | 110 ---------------- .../phosphor/instrumenter/TaintAdapter.java | 2 +- .../instrumenter/TaintMethodRecord.java | 5 +- .../TaintTrackingClassVisitor.java | 20 +-- .../struct/harmony/util/AbstractList.java | 4 +- .../struct/harmony/util/Comparator.java | 4 +- .../psl/test/phosphor/AnnotationInstCase.java | 2 +- .../driver/InstrumentJLinkPlugin.java | 15 ++- .../cs/psl/phosphor/driver/Instrumenter.java | 1 + .../cs/psl/phosphor/driver/JLinkInvoker.java | 7 +- .../cs/psl/phosphor/driver/Packer.java | 123 ++++++------------ .../driver/PhosphorInstrumentation.java | 6 +- .../phosphor/driver/ResourcePoolPacker.java | 80 ++++++++++++ .../psl/phosphor/plugin/InstrumentMojo.java | 2 +- 27 files changed, 400 insertions(+), 417 deletions(-) create mode 100644 Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/AsmPatcher.java create mode 100644 Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/ConfigurationEmbeddingMV.java rename Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/{PhosphorPatcher.java => agent/EmbeddedPhosphorPatcher.java} (74%) rename {phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver => Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent}/InstrumentUtil.java (82%) rename Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/{instrumenter => agent}/InstrumentedJREProxyGenerator.java (96%) create mode 100644 Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/PhosphorPatcher.java delete mode 100644 Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/ConfigurationEmbeddingMV.java create mode 100644 phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/ResourcePoolPacker.java diff --git a/Phosphor/pom.xml b/Phosphor/pom.xml index 5a648514e..925f7e7bd 100644 --- a/Phosphor/pom.xml +++ b/Phosphor/pom.xml @@ -59,26 +59,6 @@ true - - org.codehaus.mojo - exec-maven-plugin - - - generate-stubs - - java - - process-classes - - edu.columbia.cs.psl.phosphor.instrumenter.InstrumentedJREProxyGenerator - - - ${project.build.outputDirectory} - - - - - org.apache.maven.plugins maven-shade-plugin @@ -131,6 +111,40 @@ + + org.codehaus.mojo + exec-maven-plugin + + + generate-stubs + + java + + process-classes + + edu.columbia.cs.psl.phosphor.agent.InstrumentedJREProxyGenerator + + + ${project.build.outputDirectory} + + + + + patch + + java + + package + + edu.columbia.cs.psl.phosphor.agent.PhosphorPatcher + + + ${project.build.directory}/${project.build.finalName}.jar + + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Configuration.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Configuration.java index ca722dda9..d19cbb78d 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Configuration.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Configuration.java @@ -3,9 +3,7 @@ import edu.columbia.cs.psl.phosphor.control.ControlFlowManager; import edu.columbia.cs.psl.phosphor.control.standard.StandardControlFlowManager; import edu.columbia.cs.psl.phosphor.instrumenter.DataAndControlFlowTagFactory; -import edu.columbia.cs.psl.phosphor.instrumenter.TaintAdapter; import edu.columbia.cs.psl.phosphor.instrumenter.TaintTagFactory; -import edu.columbia.cs.psl.phosphor.instrumenter.TaintTrackingClassVisitor; import edu.columbia.cs.psl.phosphor.runtime.DerivedTaintListener; import edu.columbia.cs.psl.phosphor.runtime.Taint; import edu.columbia.cs.psl.phosphor.runtime.TaintSourceWrapper; @@ -14,36 +12,30 @@ import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Opcodes; -import java.io.IOException; -import java.net.URL; -import java.util.Properties; - public class Configuration { - public static final int ASM_VERSION = Opcodes.ASM9; public static final String TAINT_TAG_DESC = "Ledu/columbia/cs/psl/phosphor/runtime/Taint;"; public static final String TAINT_TAG_INTERNAL_NAME = "edu/columbia/cs/psl/phosphor/runtime/Taint"; public static final String TAINT_TAG_ARRAY_INTERNAL_NAME = "edu/columbia/cs/psl/phosphor/struct/TaggedArray"; public static final Object TAINT_TAG_STACK_TYPE = "edu/columbia/cs/psl/phosphor/runtime/Taint"; public static final int TAINT_LOAD_OPCODE = Opcodes.ALOAD; - public static final int TAINT_STORE_OPCODE = Opcodes.ASTORE; public static final Class TAINT_TAG_OBJ_CLASS = (Taint.class); public static final boolean DEBUG_STACK_FRAME_WRAPPERS = false; public static boolean SKIP_LOCAL_VARIABLE_TABLE = false; - public static String ADDL_IGNORE = null; + public static String IGNORE = null; public static boolean REFERENCE_TAINTING = true; - public static boolean DATAFLOW_TRACKING = true; //default + // default + public static boolean DATAFLOW_TRACKING = true; public static boolean ARRAY_INDEX_TRACKING = false; - public static boolean IMPLICIT_TRACKING = false; //TODO need to set this at runtime somewhere + // TODO need to set this at runtime somewhere + public static boolean IMPLICIT_TRACKING = false; public static boolean IMPLICIT_LIGHT_TRACKING; public static boolean IMPLICIT_HEADERS_NO_TRACKING = false; public static boolean IMPLICIT_EXCEPTION_FLOW = false; public static boolean WITHOUT_BRANCH_NOT_TAKEN = false; - public static boolean SINGLE_TAINT_LABEL = false; public static boolean ANNOTATE_LOOPS = false; public static boolean WITH_ENUM_BY_VAL = false; public static boolean WITH_UNBOX_ACMPEQ = false; - public static boolean PREALLOC_STACK_OPS = false; public static boolean WITHOUT_PROPAGATION = false; public static boolean WITHOUT_FIELD_HIDING = false; public static boolean READ_AND_SAVE_BCI = false; @@ -55,17 +47,11 @@ public class Configuration { public static String controlFlowManagerPackage = null; public static boolean QUIET_MODE = false; public static boolean IS_JAVA_8 = true; - public static Set ignoredMethods = new HashSet<>(); - - public static Class extensionMethodVisitor; - public static Class extensionClassVisitor; public static TaintTagFactory taintTagFactory = new DataAndControlFlowTagFactory(); public static String taintTagFactoryPackage = null; public static TaintSourceWrapper autoTainter = new TaintSourceWrapper<>(); public static DerivedTaintListener derivedTaintListener = new DerivedTaintListener(); - public static boolean WITH_HEAVY_OBJ_EQUALS_HASHCODE = false; - public static TransformationCache CACHE = null; public static boolean TAINT_THROUGH_SERIALIZATION = true; private Configuration() { @@ -76,75 +62,5 @@ public static void init() { if (IMPLICIT_TRACKING) { ARRAY_INDEX_TRACKING = true; } - if (TaintTrackingClassVisitor.class.getClassLoader() != null) { - URL r = TaintTrackingClassVisitor.class.getClassLoader().getResource("phosphor-mv"); - if (r != null) { - try { - Properties props = new Properties(); - props.load(r.openStream()); - if (props.containsKey("extraMV")) { - extensionMethodVisitor = (Class) Class.forName(props.getProperty("extraMV")); - } - if (props.containsKey("extraCV")) { - extensionClassVisitor = (Class) Class.forName(props.getProperty("extraCV")); - } - if(props.containsKey("taintTagFactory")) { - taintTagFactory = (TaintTagFactory) Class.forName(props.getProperty("taintTagFactory")).newInstance(); - } - if(props.containsKey("derivedTaintListener")) { - derivedTaintListener = (DerivedTaintListener) Class.forName(props.getProperty("derivedTaintListener")).newInstance(); - } - } catch(IOException ex) { - //fail silently - } catch(ClassNotFoundException | InstantiationException | IllegalAccessException e) { - e.printStackTrace(); - } - } - } - } - - public static class Method { - final String name; - final String owner; - - public Method(String name, String owner) { - this.name = name; - this.owner = owner; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((owner == null) ? 0 : owner.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if(this == obj) { - return true; - } - if(obj == null) { - return false; - } - if(getClass() != obj.getClass()) { - return false; - } - Method other = (Method) obj; - if(name == null) { - if(other.name != null) { - return false; - } - } else if(!name.equals(other.name)) { - return false; - } - if(owner == null) { - return other.owner == null; - } else { - return owner.equals(other.owner); - } - } } } 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 218385167..e9454868d 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 @@ -73,8 +73,8 @@ public byte[] transform(ClassLoader loader, final String className2, Class cl return classfileBuffer; } } - if (Configuration.CACHE != null) { - byte[] cachedClass = Configuration.CACHE.load(className, classfileBuffer); + if (Phosphor.CACHE != null) { + byte[] cachedClass = Phosphor.CACHE.load(className, classfileBuffer); if (cachedClass != null) { return cachedClass; } @@ -114,8 +114,8 @@ public byte[] transform(ClassLoader loader, final String className2, Class cl fos.write(instrumentedBytes); fos.close(); } - if (Configuration.CACHE != null) { - Configuration.CACHE.store(className, classfileBuffer, instrumentedBytes); + if (Phosphor.CACHE != null) { + Phosphor.CACHE.store(className, classfileBuffer, instrumentedBytes); } return instrumentedBytes; } catch (Throwable ex) { @@ -162,10 +162,6 @@ static byte[] instrumentWithRetry(ClassReader cr, byte[] classFileBuffer, boolea // } } - if (Configuration.extensionClassVisitor != null) { - Constructor extra = Configuration.extensionClassVisitor.getConstructor(ClassVisitor.class, Boolean.TYPE); - _cv = extra.newInstance(_cv, skipFrames); - } if (Phosphor.DEBUG || TaintUtils.VERIFY_CLASS_GENERATION) { _cv = new CheckClassAdapter(_cv, false); } diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Phosphor.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Phosphor.java index ad3719a4d..335a5988f 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Phosphor.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Phosphor.java @@ -14,6 +14,7 @@ public final class Phosphor { public static boolean INSTRUMENTATION_EXCEPTION_OCCURRED = false; public static ClassLoader bigLoader = Phosphor.class.getClassLoader(); public static InstrumentationAdaptor instrumentation; + public static TransformationCache CACHE = null; private Phosphor() { throw new AssertionError("Tried to instantiate static agent class: " + getClass()); @@ -27,7 +28,7 @@ public static void initialize(String agentArgs, InstrumentationAdaptor instrumen PhosphorOption.configure(true, parseOptions(agentArgs)); } if (System.getProperty("phosphorCacheDirectory") != null) { - Configuration.CACHE = TransformationCache.getInstance(System.getProperty("phosphorCacheDirectory")); + CACHE = TransformationCache.getInstance(System.getProperty("phosphorCacheDirectory")); } // Ensure that BasicSourceSinkManager and anything needed to call isSourceOrSinkOrTaintThrough gets initialized BasicSourceSinkManager.loadTaintMethods(); @@ -74,7 +75,7 @@ public static boolean isIgnoredClass(String owner) { || taintTagFactoryPackage != null && StringUtils.startsWith(owner, taintTagFactoryPackage) || controlFlowManagerPackage != null && StringUtils.startsWith(owner, controlFlowManagerPackage) || (Configuration.controlFlowManager != null && Configuration.controlFlowManager.isIgnoredClass(owner)) - || (Configuration.ADDL_IGNORE != null && StringUtils.startsWith(owner, Configuration.ADDL_IGNORE)) + || (Configuration.IGNORE != null && StringUtils.startsWith(owner, Configuration.IGNORE)) // || !owner.startsWith("edu/columbia/cs/psl") // For these classes: HotSpot expects fields to be at hardcoded offsets of these classes. // If we instrument them, it will break those assumptions and segfault. diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PhosphorOption.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PhosphorOption.java index 8af955070..26a5609d0 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PhosphorOption.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PhosphorOption.java @@ -192,7 +192,7 @@ public void configure(boolean forRuntimeInst, boolean isPresent, CommandLine com .argType(String.class)) { @Override public void configure(boolean forRuntimeInst, boolean isPresent, CommandLine commandLine) { - Configuration.CACHE = isPresent ? TransformationCache.getInstance(commandLine.getOptionValue(optionName)) : + Phosphor.CACHE = isPresent ? TransformationCache.getInstance(commandLine.getOptionValue(optionName)) : null; } }, @@ -273,7 +273,7 @@ public void configure(boolean forRuntimeInst, boolean isPresent, CommandLine com @Override public void configure(boolean forRuntimeInst, boolean isPresent, CommandLine commandLine) { if(isPresent) { - Configuration.ADDL_IGNORE = commandLine.getOptionValue(optionName); + Configuration.IGNORE = commandLine.getOptionValue(optionName); } } }, @@ -295,9 +295,7 @@ public void configure(boolean forRuntimeInst, boolean isPresent, CommandLine com true, false).argType(String.class).alternativeName("jvmModules")) { @Override public void configure(boolean forRuntimeInst, boolean isPresent, CommandLine commandLine) { - /* - Only used by instrumenter, which reads properties map - */ + // Only used by instrumenter, which reads properties map } }; diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/AsmPatcher.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/AsmPatcher.java new file mode 100644 index 000000000..e7f3cb9ab --- /dev/null +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/AsmPatcher.java @@ -0,0 +1,44 @@ +package edu.columbia.cs.psl.phosphor.agent; + +import edu.columbia.cs.psl.phosphor.Configuration; +import edu.columbia.cs.psl.phosphor.instrumenter.TaintMethodRecord; +import org.objectweb.asm.*; + +public class AsmPatcher extends ClassVisitor { + private static final String ASM_PREFIX = "edu/columbia/cs/psl/phosphor/org/objectweb/asm/"; + private static final Type OBJECT_TYPE = Type.getType(Object.class); + + public AsmPatcher(ClassWriter cw) { + super(Configuration.ASM_VERSION, cw); + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + return new MethodVisitor(api, mv) { + + @Override + public void visitMethodInsn( + int opcode, String owner, String name, String descriptor, boolean isInterface) { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + // Ensure that the return value is unwrapped if necessary + if (owner.startsWith("java/") && OBJECT_TYPE.equals(Type.getReturnType(descriptor))) { + TaintMethodRecord.TAINTED_REFERENCE_ARRAY_UNWRAP.delegateVisit(mv); + } + } + }; + } + + public static byte[] patch(byte[] classFileBuffer) { + ClassReader cr = new ClassReader(classFileBuffer); + ClassWriter cw = new ClassWriter(cr, 0); + ClassVisitor cv = new AsmPatcher(cw); + cr.accept(cv, 0); + return cw.toByteArray(); + } + + public static boolean isApplicable(String className) { + return className.startsWith(ASM_PREFIX); + } +} \ No newline at end of file diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/ConfigurationEmbeddingMV.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/ConfigurationEmbeddingMV.java new file mode 100644 index 000000000..e204be3a7 --- /dev/null +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/ConfigurationEmbeddingMV.java @@ -0,0 +1,80 @@ +package edu.columbia.cs.psl.phosphor.agent; + +import edu.columbia.cs.psl.phosphor.Configuration; +import edu.columbia.cs.psl.phosphor.instrumenter.TaintMethodRecord; +import edu.columbia.cs.psl.phosphor.struct.harmony.util.Set; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import static org.objectweb.asm.Opcodes.*; + +/** + * Embeds the current values for the field of {@link Configuration} into the class file for {@link Configuration}. + */ +public class ConfigurationEmbeddingMV extends MethodVisitor { + public ConfigurationEmbeddingMV(MethodVisitor mv) { + super(Configuration.ASM_VERSION, mv); + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { + if (opcode == Opcodes.PUTSTATIC) { + try { + Field f = Configuration.class.getField(name); + f.setAccessible(true); + if (Modifier.isPublic(f.getModifiers()) || !Modifier.isFinal(f.getModifiers())) { + replaceValue(Type.getType(descriptor), f.get(null)); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to access field owned by " + Configuration.class, e); + } + } + super.visitFieldInsn(opcode, owner, name, descriptor); + } + + public void replaceValue(Type type, Object newValue) { + switch (type.getSort()) { + case Type.VOID: + case Type.ARRAY: + case Type.METHOD: + return; + } + // Pop the original value + super.visitInsn(type.getSize() == 1 ? POP : POP2); + // Push the new value + if (type.getSort() != Type.OBJECT || newValue instanceof String) { + super.visitLdcInsn(newValue); + } else if (newValue == null) { + super.visitInsn(ACONST_NULL); + } else if (newValue instanceof Class) { + mv.visitLdcInsn(Type.getType((Class) newValue)); + } else if (newValue instanceof Set) { + Set set = (Set) newValue; + newInstance(newValue.getClass()); + for (Object element : set) { + super.visitInsn(DUP); + super.visitLdcInsn(element); + TaintMethodRecord.SET_ADD.delegateVisit(mv); + super.visitInsn(POP); + } + } else { + newInstance(newValue.getClass()); + } + } + + private void newInstance(Class clazz) { + try { + clazz.getConstructor(); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Public, zero-argument constructor not found for: " + clazz); + } + String className = Type.getInternalName(clazz); + super.visitTypeInsn(Opcodes.NEW, className); + super.visitInsn(DUP); + super.visitMethodInsn(INVOKESPECIAL, className, "", "()V", false); + } +} diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PhosphorPatcher.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/EmbeddedPhosphorPatcher.java similarity index 74% rename from Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PhosphorPatcher.java rename to Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/EmbeddedPhosphorPatcher.java index 5c2328ecb..373d18a55 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/PhosphorPatcher.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/EmbeddedPhosphorPatcher.java @@ -1,7 +1,6 @@ -package edu.columbia.cs.psl.phosphor; +package edu.columbia.cs.psl.phosphor.agent; -import edu.columbia.cs.psl.phosphor.instrumenter.ConfigurationEmbeddingMV; -import edu.columbia.cs.psl.phosphor.instrumenter.TaintMethodRecord; +import edu.columbia.cs.psl.phosphor.Configuration; import edu.columbia.cs.psl.phosphor.runtime.jdk.unsupported.UnsafeProxy; import org.objectweb.asm.*; import org.objectweb.asm.tree.ClassNode; @@ -11,14 +10,17 @@ import java.io.IOException; import java.io.InputStream; -public class PhosphorPatcher { +/** + * Performs patching of embedded Phosphors (for Java 9+). + */ +public class EmbeddedPhosphorPatcher { private final boolean patchUnsafeNames; - public PhosphorPatcher(byte[] unsafeClassFileBuffer) { + public EmbeddedPhosphorPatcher(byte[] unsafeClassFileBuffer) { this(shouldPatchUnsafeNames(unsafeClassFileBuffer)); } - public PhosphorPatcher(boolean patchUnsafeNames) { + public EmbeddedPhosphorPatcher(boolean patchUnsafeNames) { this.patchUnsafeNames = patchUnsafeNames; } @@ -28,8 +30,6 @@ public byte[] patch(String name, byte[] content) throws IOException { } else if (name.equals("edu/columbia/cs/psl/phosphor/runtime/RuntimeJDKInternalUnsafePropagator.class")) { return transformUnsafePropagator( new ByteArrayInputStream(content), "jdk/internal/misc/Unsafe", patchUnsafeNames); - } else if (AsmPatchingCV.isApplicable(name)) { - return AsmPatchingCV.patch(content); } else { return content; } @@ -153,43 +153,4 @@ public void visitLocalVariable( }; } } - - private static class AsmPatchingCV extends ClassVisitor { - private static final String ASM_PREFIX = "edu/columbia/cs/psl/phosphor/org/objectweb/asm/"; - private static final Type OBJECT_TYPE = Type.getType(Object.class); - - public AsmPatchingCV(ClassWriter cw) { - super(Configuration.ASM_VERSION, cw); - } - - @Override - public MethodVisitor visitMethod( - int access, String name, String descriptor, String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); - return new MethodVisitor(api, mv) { - - @Override - public void visitMethodInsn( - int opcode, String owner, String name, String descriptor, boolean isInterface) { - super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); - // Ensure that the return value is unwrapped if necessary - if (owner.startsWith("java/") && OBJECT_TYPE.equals(Type.getReturnType(descriptor))) { - TaintMethodRecord.TAINTED_REFERENCE_ARRAY_UNWRAP.delegateVisit(mv); - } - } - }; - } - - public static byte[] patch(byte[] classFileBuffer) { - ClassReader cr = new ClassReader(classFileBuffer); - ClassWriter cw = new ClassWriter(cr, 0); - ClassVisitor cv = new AsmPatchingCV(cw); - cr.accept(cv, 0); - return cw.toByteArray(); - } - - public static boolean isApplicable(String className) { - return className.startsWith(ASM_PREFIX); - } - } } diff --git a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/InstrumentUtil.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/InstrumentUtil.java similarity index 82% rename from phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/InstrumentUtil.java rename to Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/InstrumentUtil.java index 1312ad2e5..926db47a0 100644 --- a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/InstrumentUtil.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/InstrumentUtil.java @@ -1,6 +1,7 @@ -package edu.columbia.cs.psl.phosphor.driver; +package edu.columbia.cs.psl.phosphor.agent; import java.io.*; +import java.nio.file.Files; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -72,7 +73,6 @@ public static byte[] checksum(byte[] input) { return md5Inst.digest(input); } - /** * Creates the specified directory if it does not already exist. * @@ -84,4 +84,17 @@ public static void ensureDirectory(File dir) throws IOException { throw new IOException("Failed to create directory: " + dir); } } + + public static void deleteFile(File file) throws IOException { + if (file.exists() && !file.delete()) { + throw new IOException("Failed to delete file: " + file); + } + } + + public static File createTemporaryFile(String prefix, String suffix) throws IOException { + File file = Files.createTempFile(prefix, suffix).toFile(); + file.deleteOnExit(); + ensureDirectory(file.getParentFile()); + return file; + } } \ No newline at end of file diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/InstrumentedJREProxyGenerator.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/InstrumentedJREProxyGenerator.java similarity index 96% rename from Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/InstrumentedJREProxyGenerator.java rename to Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/InstrumentedJREProxyGenerator.java index 7a8a988a6..b0f5ff366 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/InstrumentedJREProxyGenerator.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/InstrumentedJREProxyGenerator.java @@ -1,8 +1,7 @@ -package edu.columbia.cs.psl.phosphor.instrumenter; +package edu.columbia.cs.psl.phosphor.agent; import edu.columbia.cs.psl.phosphor.Configuration; -import edu.columbia.cs.psl.phosphor.PhosphorPatcher; import org.objectweb.asm.*; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.util.CheckClassAdapter; @@ -23,7 +22,7 @@ public static void main(String[] args) throws IOException { String pathToUnsafePropagator = outputDir + "/edu/columbia/cs/psl/phosphor/runtime/jdk/unsupported/RuntimeSunMiscUnsafePropagator.class"; InputStream sunMiscUnsafeIn = new FileInputStream(pathToUnsafePropagator); - byte[] instrumentedUnsafe = PhosphorPatcher.transformUnsafePropagator(sunMiscUnsafeIn, + byte[] instrumentedUnsafe = EmbeddedPhosphorPatcher.transformUnsafePropagator(sunMiscUnsafeIn, "sun/misc/Unsafe", false); Files.write(Paths.get(pathToUnsafePropagator), instrumentedUnsafe); diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/PhosphorAgent.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/PhosphorAgent.java index f10b85ae5..0e7959dbd 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/PhosphorAgent.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/PhosphorAgent.java @@ -8,7 +8,6 @@ import edu.columbia.cs.psl.phosphor.runtime.PhosphorStackFrame; import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; import java.security.ProtectionDomain; @@ -70,8 +69,7 @@ public byte[] transform( Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer, - PhosphorStackFrame frame) - throws IllegalClassFormatException { + PhosphorStackFrame frame) { return transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); } @@ -81,8 +79,7 @@ public byte[] transform( String className, Class classBeingRedefined, ProtectionDomain protectionDomain, - byte[] classfileBuffer) - throws IllegalClassFormatException { + byte[] classfileBuffer) { try { if (!INITED) { Configuration.init(); diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/PhosphorPatcher.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/PhosphorPatcher.java new file mode 100644 index 000000000..eeb834e50 --- /dev/null +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/agent/PhosphorPatcher.java @@ -0,0 +1,53 @@ +package edu.columbia.cs.psl.phosphor.agent; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +public class PhosphorPatcher { + public static void main(String[] args) throws IOException { + File archive = new File(args[0]); + File temp = InstrumentUtil.createTemporaryFile("patch-", ".jar"); + try (ZipInputStream zin = new ZipInputStream(Files.newInputStream(archive.toPath())); + ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(temp.toPath()))) { + for (ZipEntry entry; (entry = zin.getNextEntry()) != null; ) { + byte[] content = InstrumentUtil.readAllBytes(zin); + if (entry.getName().endsWith(".class")) { + content = patch(entry.getName(), content); + } + writeEntry(zos, entry, content); + } + } + InstrumentUtil.deleteFile(archive); + if (!temp.renameTo(archive)) { + throw new IOException("Failed to move patched JAR: " + temp); + } + } + + private static byte[] patch(String name, byte[] classFileBuffer) { + if (AsmPatcher.isApplicable(name)) { + return AsmPatcher.patch(classFileBuffer); + } + return classFileBuffer; + } + + private static void writeEntry(ZipOutputStream zos, ZipEntry entry, byte[] content) throws IOException { + ZipEntry outEntry = new ZipEntry(entry.getName()); + outEntry.setMethod(entry.getMethod()); + if (entry.getMethod() == ZipEntry.STORED) { + // Uncompressed entries require entry size and CRC + outEntry.setSize(content.length); + outEntry.setCompressedSize(content.length); + CRC32 crc = new CRC32(); + crc.update(content, 0, content.length); + outEntry.setCrc(crc.getValue()); + } + zos.putNextEntry(outEntry); + zos.write(content); + zos.closeEntry(); + } +} diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/control/graph/FlowGraph.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/control/graph/FlowGraph.java index d548a437e..a2229372a 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/control/graph/FlowGraph.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/control/graph/FlowGraph.java @@ -528,7 +528,7 @@ public int hashCode() { * @param fontSize value used for the fontsize attribute of the graph * @throws NullPointerException if writer is null or printer is null * @throws IOException if an I/O error occurs while writing to the specified writer - * @throws IllegalArgumentException if fontSize <= 0 + * @throws IllegalArgumentException if fontSize <= 0 */ public void write(Writer writer, String graphName, Comparator vertexComparator, Function printer, int fontSize) throws IOException { diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/ConfigurationEmbeddingMV.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/ConfigurationEmbeddingMV.java deleted file mode 100644 index 3d9ed7471..000000000 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/ConfigurationEmbeddingMV.java +++ /dev/null @@ -1,110 +0,0 @@ -package edu.columbia.cs.psl.phosphor.instrumenter; - -import edu.columbia.cs.psl.phosphor.Configuration; -import edu.columbia.cs.psl.phosphor.struct.harmony.util.Set; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; - -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static org.objectweb.asm.Opcodes.ACONST_NULL; -import static org.objectweb.asm.Opcodes.DUP; -import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; -import static org.objectweb.asm.Opcodes.INVOKESPECIAL; -import static org.objectweb.asm.Opcodes.POP; -import static org.objectweb.asm.Opcodes.POP2; - -public class ConfigurationEmbeddingMV extends MethodVisitor { - public ConfigurationEmbeddingMV(MethodVisitor mv) { - super(Configuration.ASM_VERSION, mv); - } - - // Embed initialized Configuration class into class file. - @Override - public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { - if (opcode == Opcodes.PUTSTATIC) { - Type type = Type.getType(descriptor); - Object fieldValue; - if (name.equals("IS_JAVA_8")) { - fieldValue = false; - } else { - try { - Field f = Configuration.class.getField(name); - if ((f.getModifiers() & ACC_PUBLIC) == 0) { - super.visitFieldInsn(opcode, owner, name, descriptor); - return; - } - f.setAccessible(true); - fieldValue = f.get(null); - if (fieldValue instanceof Class) { - fieldValue = Type.getType((Class) fieldValue); - } - } catch (NoSuchFieldException | IllegalAccessException e) { - super.visitFieldInsn(opcode, owner, name, descriptor); - return; - } - } - switch (type.getSort()) { - case Type.LONG: - case Type.DOUBLE: - super.visitInsn(POP2); - super.visitLdcInsn(fieldValue); - break; - case Type.BOOLEAN: - case Type.INT: - case Type.FLOAT: - case Type.SHORT: - case Type.BYTE: - case Type.CHAR: - super.visitInsn(POP); - super.visitLdcInsn(fieldValue); - break; - case Type.OBJECT: - if (fieldValue == null) { - super.visitInsn(POP); - super.visitInsn(ACONST_NULL); - } else if (descriptor.equals("Ljava/lang/Class;") || - descriptor.equals("Ljava/lang/String;") || - fieldValue instanceof String) { - super.visitInsn(POP); - super.visitLdcInsn(fieldValue); - } else if (descriptor.equals("Ledu/columbia/cs/psl/phosphor/struct/harmony/util/Set;")) { - // All sets are Set. - super.visitInsn(POP); - String className = fieldValue.getClass().getName().replace(".", "/"); - super.visitTypeInsn(Opcodes.NEW, className); - super.visitInsn(DUP); - super.visitMethodInsn(INVOKESPECIAL, className, "", "()V", false); - - for (String s : ((Set) fieldValue)) { - super.visitInsn(DUP); - super.visitLdcInsn(s); - super.visitMethodInsn(INVOKEINTERFACE, - "Ledu/columbia/cs/psl/phosphor/struct/harmony/util/Set;", - "add", "(Ljava/lang/Object;)Z", true); - super.visitInsn(POP); - } - } else { - Class objectClass = fieldValue.getClass(); - try { - // Make sure constructor is callable. - Constructor constructor = objectClass.getDeclaredConstructor(); - if ((constructor.getModifiers() & ACC_PUBLIC) != 0) { - String className = objectClass.getName().replace(".", "/"); - super.visitInsn(POP); - super.visitTypeInsn(Opcodes.NEW, className); - super.visitInsn(DUP); - super.visitMethodInsn(INVOKESPECIAL, className, "", "()V", false); - } - } catch (NoSuchMethodException e) { - } - } - break; - } - } - super.visitFieldInsn(opcode, owner, name, descriptor); - } -} diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintAdapter.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintAdapter.java index 09e5b07d8..9fc252e52 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintAdapter.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintAdapter.java @@ -146,7 +146,7 @@ public Object getTopOfStackObject() { } /** - * Returns the type of the stack element n down from the top: n=0 -> top of + * Returns the type of the stack element n down from the top: n = 0 corresponds to the top of * stack */ public Type getStackTypeAtOffset(int n) { diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintMethodRecord.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintMethodRecord.java index f0259f787..c9670a594 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintMethodRecord.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/instrumenter/TaintMethodRecord.java @@ -5,6 +5,7 @@ import edu.columbia.cs.psl.phosphor.control.ControlFlowStack; import edu.columbia.cs.psl.phosphor.runtime.*; import edu.columbia.cs.psl.phosphor.struct.*; +import edu.columbia.cs.psl.phosphor.struct.harmony.util.Set; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -121,7 +122,9 @@ public enum TaintMethodRecord implements MethodRecord { // Methods from TaintSourceWrapper AUTO_TAINT(INVOKEVIRTUAL, TaintSourceWrapper.class, "autoTaint", Object.class, false, Object.class, String.class, String.class, int.class), // TaggedReferenceArray - TAINTED_REFERENCE_ARRAY_UNWRAP(INVOKESTATIC, TaggedReferenceArray.class, "unwrap", Object.class, false, Object.class); + TAINTED_REFERENCE_ARRAY_UNWRAP(INVOKESTATIC, TaggedReferenceArray.class, "unwrap", Object.class, false, Object.class), + // Set + SET_ADD(INVOKEVIRTUAL, Set.class, "add", boolean.class, true, Object.class); private final int opcode; private final String owner; 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 44ad701ac..59666cc3f 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 @@ -18,7 +18,6 @@ import org.objectweb.asm.tree.*; import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -378,28 +377,12 @@ public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[ super.visitFrame(type, nLocal, local, nStack, stack); } }; - - if (Configuration.extensionMethodVisitor != null) { - try { - TaintAdapter custom = Configuration.extensionMethodVisitor.getConstructor(Integer.TYPE, - String.class, String.class, String.class, String.class, String[].class, MethodVisitor.class, - NeverNullArgAnalyzerAdapter.class, String.class, String.class).newInstance(access, - className, name, desc, signature, _exceptions, rawMethod, null, classSource, - classDebug); - custom.setFields(fields); - custom.setSuperName(superName); - return custom; - } catch (InstantiationException | SecurityException | NoSuchMethodException | InvocationTargetException - | IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - } return rawMethod; } else { // this is a native method. we want here to make a $taint method that will call // the original one. final MethodVisitor prev = super.visitMethod(access, name, desc, signature, _exceptions); - MethodNode rawMethod = new MethodNode(Configuration.ASM_VERSION, access, name, desc, signature, + return new MethodNode(Configuration.ASM_VERSION, access, name, desc, signature, _exceptions) { @Override public void visitEnd() { @@ -410,7 +393,6 @@ public void visitEnd() { } } }; - return rawMethod; } } diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/struct/harmony/util/AbstractList.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/struct/harmony/util/AbstractList.java index 31ffdad4e..39fb4b116 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/struct/harmony/util/AbstractList.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/struct/harmony/util/AbstractList.java @@ -728,9 +728,9 @@ public E set(int location, E object) { * @return a subList view of this list starting from {@code start} * (inclusive), and ending with {@code end} (exclusive) * @throws IndexOutOfBoundsException - * if (start < 0 || end > size()) + * if {@code (start < 0 || end > size())} * @throws IllegalArgumentException - * if (start > end) + * if {@code (start > end)} */ public List subList(int start, int end) { if (0 <= start && end <= size()) { diff --git a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/struct/harmony/util/Comparator.java b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/struct/harmony/util/Comparator.java index dc3316022..4000c84d7 100644 --- a/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/struct/harmony/util/Comparator.java +++ b/Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/struct/harmony/util/Comparator.java @@ -50,8 +50,8 @@ public interface Comparator { * an {@code Object}. * @param object2 * a second {@code Object} to compare with {@code object1}. - * @return an integer < 0 if {@code object1} is less than {@code object2}, 0 if they are - * equal, and > 0 if {@code object1} is greater than {@code object2}. + * @return an integer less than 0 if {@code object1} is less than {@code object2}, 0 if they are + * equal, and greater than 0 if {@code object1} is greater than {@code object2}. * @throws ClassCastException * if objects are not of the correct type. */ diff --git a/integration-tests/src/test/java/edu/columbia/cs/psl/test/phosphor/AnnotationInstCase.java b/integration-tests/src/test/java/edu/columbia/cs/psl/test/phosphor/AnnotationInstCase.java index 5f8456bf2..178131703 100644 --- a/integration-tests/src/test/java/edu/columbia/cs/psl/test/phosphor/AnnotationInstCase.java +++ b/integration-tests/src/test/java/edu/columbia/cs/psl/test/phosphor/AnnotationInstCase.java @@ -9,7 +9,7 @@ import java.lang.annotation.Target; import java.lang.reflect.Method; -public class AnnotationInstCase { +public class AnnotationInstCase extends BasePhosphorTest{ @Test public void testEnum() throws ReflectiveOperationException { Method method = Example.class.getDeclaredMethod("x"); diff --git a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/InstrumentJLinkPlugin.java b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/InstrumentJLinkPlugin.java index e4d14a193..c01b62aa4 100644 --- a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/InstrumentJLinkPlugin.java +++ b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/InstrumentJLinkPlugin.java @@ -13,7 +13,7 @@ public class InstrumentJLinkPlugin implements Plugin { private Instrumentation instrumentation; - private Packer packer; + private ResourcePoolPacker packer; @Override public String getName() { @@ -47,18 +47,19 @@ public void configure(Map config) { } @Override - public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { - packer = new Packer(in, instrumentation); - in.transformAndCopy(e -> transform(e, out), out); + public ResourcePool transform(ResourcePool pool, ResourcePoolBuilder out) { + packer = new ResourcePoolPacker(instrumentation, pool, out); + pool.transformAndCopy(this::transform, out); return out.build(); } - private ResourcePoolEntry transform(ResourcePoolEntry entry, ResourcePoolBuilder out) { - if (entry.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE) && entry.path().endsWith(".class")) { + private ResourcePoolEntry transform(ResourcePoolEntry entry) { + if (entry.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE) + && entry.path().endsWith(".class")) { if (entry.path().endsWith("module-info.class")) { if (entry.path().startsWith("/java.base")) { // Transform java.base's module-info.class file and pack core classes into java.base - return packer.pack(entry, out); + return packer.pack(entry); } } else { byte[] instrumented = instrumentation.apply(entry.contentBytes()); diff --git a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/Instrumenter.java b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/Instrumenter.java index af852281e..21106bef7 100644 --- a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/Instrumenter.java +++ b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/Instrumenter.java @@ -1,5 +1,6 @@ package edu.columbia.cs.psl.phosphor.driver; +import edu.columbia.cs.psl.phosphor.agent.InstrumentUtil; import org.jacoco.core.internal.InputStreams; import org.jacoco.core.internal.instr.SignatureRemover; diff --git a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/JLinkInvoker.java b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/JLinkInvoker.java index 45437af59..9cd31f9aa 100644 --- a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/JLinkInvoker.java +++ b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/JLinkInvoker.java @@ -1,9 +1,10 @@ package edu.columbia.cs.psl.phosphor.driver; +import edu.columbia.cs.psl.phosphor.agent.InstrumentUtil; + import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.nio.file.Files; import java.util.*; import java.util.stream.Collectors; @@ -39,9 +40,7 @@ public static void invoke( private static File storeOptions(Properties options) throws IOException { // Write the options to a temporary file - File file = Files.createTempFile("phosphor-", ".properties").toFile(); - file.deleteOnExit(); - InstrumentUtil.ensureDirectory(file.getParentFile()); + File file = InstrumentUtil.createTemporaryFile("phosphor-", ".properties"); try (FileWriter writer = new FileWriter(file)) { options.store(writer, null); } diff --git a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/Packer.java b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/Packer.java index 82c0cdcaa..e3e3c4024 100644 --- a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/Packer.java +++ b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/Packer.java @@ -1,134 +1,87 @@ package edu.columbia.cs.psl.phosphor.driver; -import jdk.tools.jlink.plugin.ResourcePool; -import jdk.tools.jlink.plugin.ResourcePoolBuilder; -import jdk.tools.jlink.plugin.ResourcePoolEntry; -import org.objectweb.asm.Attribute; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.commons.ModuleHashesAttribute; -import org.objectweb.asm.commons.ModuleResolutionAttribute; -import org.objectweb.asm.commons.ModuleTargetAttribute; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.ModuleExportNode; +import edu.columbia.cs.psl.phosphor.agent.InstrumentUtil; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -public class Packer { +public abstract class Packer { private final Instrumentation instrumentation; private final Patcher patcher; - public Packer(ResourcePool in, Instrumentation instrumentation) { - if (in == null || instrumentation == null) { - throw new NullPointerException(); - } + public Packer(Instrumentation instrumentation, Function entryLocator) { this.instrumentation = instrumentation; - this.patcher = instrumentation.createPatcher(path -> Packer.findEntry(in, path)); + this.patcher = instrumentation.createPatcher(entryLocator); } - public ResourcePoolEntry pack(ResourcePoolEntry entry, ResourcePoolBuilder out) { - try { - // Pack classes into the java.bas - Set packages = packClasses(out); - // Transform java.base's module-info.class file - try (InputStream in = entry.content()) { - return entry.copyWithContent(transformBaseModuleInfo(in, packages)); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } + public abstract void pack(String name, byte[] content) throws IOException; - private Set packClasses(ResourcePoolBuilder out) throws IOException { - // Pack the JARs and directories into the resource pool + public Set pack() throws IOException { + // Pack the JARs and directories // Return the set of packages for packed classes Set packages = new HashSet<>(); for (File element : instrumentation.getElementsToPack()) { if (element.isDirectory()) { - packDirectory(out, element, packages); + packDirectory(element, packages); } else { - packJar(out, element, packages); + packJar(element, packages); } } return packages; } - private void packClass(ResourcePoolBuilder out, String name, File classFile, Set packages) { - try { - if (instrumentation.shouldPack(name)) { - byte[] content = patcher.patch(name, Files.readAllBytes(classFile.toPath())); - out.add(ResourcePoolEntry.create("/java.base/" + name, content)); + private void packFile(String name, File classFile, Set packages) throws IOException { + if (instrumentation.shouldPack(name)) { + byte[] content = patcher.patch(name, InstrumentUtil.readAllBytes(classFile)); + pack(name, content); + if (name.endsWith(".class")) { packages.add(name.substring(0, name.lastIndexOf('/'))); } - } catch (IOException e) { - throw new IllegalArgumentException("Failed to pack: " + name, e); } } - private void packDirectory(ResourcePoolBuilder out, File directory, Set packages) throws IOException { + private void packDirectory(File directory, Set packages) throws IOException { try (Stream walk = Files.walk(directory.toPath())) { - walk.filter(Files::isRegularFile) - .forEach(p -> packClass( - out, - directory.toPath().relativize(p).toFile().getPath(), - p.toAbsolutePath().toFile(), - packages)); + for (Path path : walk.filter(Files::isRegularFile).collect(Collectors.toList())) { + String name = directory.toPath().relativize(path).toFile().getPath(); + File file = path.toAbsolutePath().toFile(); + if (name.endsWith(".jar")) { + packJar(file, packages); + } else { + packFile(name, file, packages); + } + } } } - private void packJar(ResourcePoolBuilder out, File element, Set packages) throws IOException { + private void packJar(File element, Set packages) throws IOException { try (ZipFile zip = new ZipFile(element)) { Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); - if (instrumentation.shouldPack(entry.getName())) { + String name = entry.getName(); + byte[] content; + if (instrumentation.shouldPack(name)) { try (InputStream is = zip.getInputStream(entry)) { - byte[] content = patcher.patch(entry.getName(), InstrumentUtil.readAllBytes(is)); - out.add(ResourcePoolEntry.create("/java.base/" + entry.getName(), content)); + content = InstrumentUtil.readAllBytes(is); + } + pack(entry.getName(), patcher.patch(name, content)); + if (name.endsWith(".class")) { + packages.add(name.substring(0, name.lastIndexOf('/'))); } - packages.add(entry.getName().substring(0, entry.getName().lastIndexOf('/'))); } } } } - - private byte[] transformBaseModuleInfo(InputStream in, Set packages) { - try { - ClassNode classNode = new ClassNode(); - ClassReader cr = new ClassReader(in); - Attribute[] attributes = new Attribute[] { - new ModuleTargetAttribute(), new ModuleResolutionAttribute(), new ModuleHashesAttribute() - }; - cr.accept(classNode, attributes, 0); - // Add exports - for (String packageName : packages) { - classNode.module.exports.add(new ModuleExportNode(packageName, 0, null)); - } - // Add packages - classNode.module.packages.addAll(packages); - ClassWriter cw = new ClassWriter(0); - classNode.accept(cw); - return cw.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static byte[] findEntry(ResourcePool pool, String path) { - ResourcePoolEntry entry = pool.findEntry(path) - .orElseThrow(() -> new IllegalArgumentException("Unable to find entry for: " + path)); - try (InputStream in = entry.content()) { - return InstrumentUtil.readAllBytes(in); - } catch (IOException e) { - throw new RuntimeException("Unable to read entry for: " + path, e); - } - } } diff --git a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/PhosphorInstrumentation.java b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/PhosphorInstrumentation.java index 50dd9ee88..809485056 100644 --- a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/PhosphorInstrumentation.java +++ b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/PhosphorInstrumentation.java @@ -1,6 +1,8 @@ package edu.columbia.cs.psl.phosphor.driver; import edu.columbia.cs.psl.phosphor.*; +import edu.columbia.cs.psl.phosphor.agent.InstrumentUtil; +import edu.columbia.cs.psl.phosphor.agent.EmbeddedPhosphorPatcher; import edu.columbia.cs.psl.phosphor.instrumenter.TaintTrackingClassVisitor; import edu.columbia.cs.psl.phosphor.org.apache.commons.cli.CommandLine; @@ -61,8 +63,8 @@ public byte[] apply(byte[] classFileBuffer) { @Override public Patcher createPatcher(Function entryLocator) { - String path = "/java.base/jdk/internal/misc/Unsafe.class"; - PhosphorPatcher patcher = new PhosphorPatcher(entryLocator.apply(path)); + String path = "/java.base/jdk/internal/misc/Unsafe.class"; + EmbeddedPhosphorPatcher patcher = new EmbeddedPhosphorPatcher(entryLocator.apply(path)); return patcher::patch; } diff --git a/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/ResourcePoolPacker.java b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/ResourcePoolPacker.java new file mode 100644 index 000000000..19997c543 --- /dev/null +++ b/phosphor-driver/src/main/java/edu/columbia/cs/psl/phosphor/driver/ResourcePoolPacker.java @@ -0,0 +1,80 @@ +package edu.columbia.cs.psl.phosphor.driver; + +import edu.columbia.cs.psl.phosphor.agent.InstrumentUtil; +import jdk.tools.jlink.plugin.ResourcePool; +import jdk.tools.jlink.plugin.ResourcePoolBuilder; +import jdk.tools.jlink.plugin.ResourcePoolEntry; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.commons.ModuleHashesAttribute; +import org.objectweb.asm.commons.ModuleResolutionAttribute; +import org.objectweb.asm.commons.ModuleTargetAttribute; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.ModuleExportNode; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Set; + +public class ResourcePoolPacker extends Packer { + private final ResourcePoolBuilder out; + + public ResourcePoolPacker(Instrumentation instrumentation, ResourcePool pool, ResourcePoolBuilder out) { + super(instrumentation, path -> ResourcePoolPacker.findEntry(pool, path)); + if (out == null) { + throw new NullPointerException(); + } + this.out = out; + } + + @Override + public void pack(String name, byte[] content) { + out.add(ResourcePoolEntry.create("/java.base/" + name, content)); + } + + public ResourcePoolEntry pack(ResourcePoolEntry entry) { + try { + // Pack classes into java.base + Set packages = pack(); + // Transform java.base's module-info.class file + try (InputStream in = entry.content()) { + return entry.copyWithContent(transformBaseModuleInfo(in, packages)); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static byte[] transformBaseModuleInfo(InputStream in, Set packages) { + try { + ClassNode classNode = new ClassNode(); + ClassReader cr = new ClassReader(in); + Attribute[] attributes = new Attribute[] { + new ModuleTargetAttribute(), new ModuleResolutionAttribute(), new ModuleHashesAttribute() + }; + cr.accept(classNode, attributes, 0); + // Add exports + for (String packageName : packages) { + classNode.module.exports.add(new ModuleExportNode(packageName, 0, null)); + } + // Add packages + classNode.module.packages.addAll(packages); + ClassWriter cw = new ClassWriter(0); + classNode.accept(cw); + return cw.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static byte[] findEntry(ResourcePool pool, String path) { + ResourcePoolEntry entry = pool.findEntry(path) + .orElseThrow(() -> new IllegalArgumentException("Unable to find entry for: " + path)); + try (InputStream in = entry.content()) { + return InstrumentUtil.readAllBytes(in); + } catch (IOException e) { + throw new RuntimeException("Unable to read entry for: " + path, e); + } + } +} diff --git a/phosphor-instrument-maven-plugin/src/main/java/edu/columbia/cs/psl/phosphor/plugin/InstrumentMojo.java b/phosphor-instrument-maven-plugin/src/main/java/edu/columbia/cs/psl/phosphor/plugin/InstrumentMojo.java index 702f1f7d5..7a70c8fde 100644 --- a/phosphor-instrument-maven-plugin/src/main/java/edu/columbia/cs/psl/phosphor/plugin/InstrumentMojo.java +++ b/phosphor-instrument-maven-plugin/src/main/java/edu/columbia/cs/psl/phosphor/plugin/InstrumentMojo.java @@ -1,7 +1,7 @@ package edu.columbia.cs.psl.phosphor.plugin; import edu.columbia.cs.psl.phosphor.driver.DeletingFileVisitor; -import edu.columbia.cs.psl.phosphor.driver.InstrumentUtil; +import edu.columbia.cs.psl.phosphor.agent.InstrumentUtil; import edu.columbia.cs.psl.phosphor.driver.Instrumentation; import edu.columbia.cs.psl.phosphor.driver.Instrumenter; import org.apache.maven.plugin.AbstractMojo;