Skip to content

Commit

Permalink
* Fixed issue Unsafe#put/getObject methods in Java 11 causing JVM crash.
Browse files Browse the repository at this point in the history
* Refactored JLink packaging and Phosphor class patching into its
* Removed double declaration of compiler plugin from pom.xml
* Factored packing of Phosphor into java.base module out of PhosphorJLinkPlugin into new class PhosphorPacker
* Factored patching of Phosphor class from Instrumenter into new class PhosphorPatcher
* Added Java 11 to workflows
  • Loading branch information
katherine-hough committed Jul 20, 2023
1 parent 21b0b87 commit 05fc3d6
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 204 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
fail-fast: false
matrix:
# java: [ '8', '11', '16', '17' ]
java: [ '8', '16' ]
java: [ '8', '11', '16' ]
steps:
- uses: actions/checkout@v2
- name: Build Phosphor
Expand Down
153 changes: 3 additions & 150 deletions Phosphor/src/main/java/edu/columbia/cs/psl/phosphor/Instrumenter.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package edu.columbia.cs.psl.phosphor;

import edu.columbia.cs.psl.phosphor.instrumenter.ConfigurationEmbeddingMV;
import edu.columbia.cs.psl.phosphor.instrumenter.TaintTrackingClassVisitor;
import edu.columbia.cs.psl.phosphor.runtime.StringUtils;
import edu.columbia.cs.psl.phosphor.runtime.jdk.unsupported.UnsafeProxy;
import edu.columbia.cs.psl.phosphor.struct.harmony.util.*;
import org.apache.commons.cli.CommandLine;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.ModuleHashesAttribute;
import org.objectweb.asm.commons.ModuleResolutionAttribute;
import org.objectweb.asm.commons.ModuleTargetAttribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.ModuleExportNode;

import java.io.*;
import java.lang.instrument.ClassFileTransformer;
Expand Down Expand Up @@ -709,148 +705,6 @@ private static class Result {
byte[] buf;
}

public static byte[] transformJavaBaseModuleInfo(InputStream is, java.util.Collection<String> packages) throws IOException {
ClassNode classNode = new ClassNode();
ClassReader cr = new ClassReader(is);
java.util.List<Attribute> attrs = new java.util.ArrayList<>();
attrs.add(new ModuleTargetAttribute());
attrs.add(new ModuleResolutionAttribute());
attrs.add(new ModuleHashesAttribute());

cr.accept(classNode, attrs.toArray(new Attribute[0]), 0);
//Add export
classNode.module.exports.add(new ModuleExportNode("edu/columbia/cs/psl/phosphor", 0, null));
classNode.module.exports.add(new ModuleExportNode("edu/columbia/cs/psl/phosphor/control", 0, null));
classNode.module.exports.add(new ModuleExportNode("edu/columbia/cs/psl/phosphor/control/standard", 0, null));
classNode.module.exports.add(new ModuleExportNode("edu/columbia/cs/psl/phosphor/runtime", 0, null));
classNode.module.exports.add(new ModuleExportNode("edu/columbia/cs/psl/phosphor/struct", 0, null));

//Add pac
classNode.module.packages.addAll(packages);
ClassWriter cw = new ClassWriter(0);
classNode.accept(cw);
return cw.toByteArray();
}

public static boolean isPhosphorClassPatchedAtInstTime(String name){
return name.equals("edu/columbia/cs/psl/phosphor/Configuration.class") || name.equals("edu/columbia/cs/psl/phosphor/runtime/RuntimeJDKInternalUnsafePropagator.class");
}

/**
* We do rewriting of various phosphor classes to ensure easy compilation for java < 9
*/
public static byte[] patchPhosphorClass(String name, InputStream is) throws IOException {
if(name.equals("edu/columbia/cs/psl/phosphor/Configuration.class")){
return embedPhopshorConfiguration(is);
}
if (name.equals("edu/columbia/cs/psl/phosphor/runtime/RuntimeJDKInternalUnsafePropagator.class")) {
return transformRuntimeUnsafePropagator(is, "jdk/internal/misc/Unsafe");
}
throw new UnsupportedEncodingException("We do not plan to instrument " + name);
}

public static byte[] transformRuntimeUnsafePropagator(InputStream is, String targetUnsafeInternalName) throws IOException {
final String UNSAFE_PROXY_INTERNAL_NAME = Type.getInternalName(UnsafeProxy.class);
final String UNSAFE_PROXY_DESC = Type.getDescriptor(UnsafeProxy.class);
final String TARGET_UNSAFE_INTERNAL_NAME = targetUnsafeInternalName;
final String TARGET_UNSAFE_DESC = "L" + targetUnsafeInternalName + ";";
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
private String patchInternalName(String in) {
if (in.equals(UNSAFE_PROXY_INTERNAL_NAME)) {
return TARGET_UNSAFE_INTERNAL_NAME;
}
return in;
}

private String patchDesc(String in) {
if (in == null) {
return null;
}
return in.replace(UNSAFE_PROXY_DESC, TARGET_UNSAFE_DESC);
}

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, patchDesc(descriptor), patchDesc(signature), exceptions);
return new MethodVisitor(Opcodes.ASM9, mv) {

@Override
public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) {
for (int i = 0; i < numLocal; i++) {
if (local[i] instanceof String) {
local[i] = patchInternalName((String) local[i]);
}
}
for (int i = 0; i < numStack; i++) {
if (stack[i] instanceof String) {
stack[i] = patchInternalName((String) stack[i]);
}
}
super.visitFrame(type, numLocal, local, numStack, stack);
}

@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
super.visitFieldInsn(opcode, patchInternalName(owner), name, patchDesc(descriptor));
}

@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
super.visitMethodInsn(opcode, patchInternalName(owner), name, patchDesc(descriptor), isInterface);
}

@Override
public void visitTypeInsn(int opcode, String type) {
super.visitTypeInsn(opcode, patchInternalName(type));
}

@Override
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
super.visitLocalVariable(name, patchDesc(descriptor), signature, start, end, index);
}
};
}
};
cr.accept(cv, 0);
return cw.toByteArray();

}
/**
* To boot the JVM... we need to have this flag set correctly.
*
* Default to setting it to be Java 8.
*
* In Java 9+, we pack Phosphor into the java.base module, and rewrite the configuration file
* to set the flag to false.
*
* @param is
* @return
* @throws IOException
*/
public static byte[] embedPhopshorConfiguration(InputStream is) throws IOException {
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr, 0);

ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (name.equals("<clinit>")) {
return new ConfigurationEmbeddingMV(mv);
} else {
return mv;
}
}
};

cr.accept(cv, 0);
return cw.toByteArray();

}


public static boolean isJava8JVMDir(File java_home) {
return new File(java_home, "bin" + File.separator + "java").exists()
&& !new File(java_home, "jmods").exists()
Expand All @@ -861,5 +715,4 @@ public static boolean isUnsafeClass(String className){
return (Configuration.IS_JAVA_8 && "sun/misc/Unsafe".equals(className))
|| (!Configuration.IS_JAVA_8 && "jdk/internal/misc/Unsafe".equals(className));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package edu.columbia.cs.psl.phosphor;

import edu.columbia.cs.psl.phosphor.instrumenter.ConfigurationEmbeddingMV;
import edu.columbia.cs.psl.phosphor.runtime.jdk.unsupported.UnsafeProxy;
import org.objectweb.asm.*;
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.MethodNode;
import org.objectweb.asm.tree.ModuleExportNode;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;

public class PhosphorPatcher {
private final boolean patchUnsafeNames;

public PhosphorPatcher(InputStream unsafeContent) throws IOException {
this.patchUnsafeNames = shouldPatchUnsafeNames(unsafeContent);
}

public byte[] patch(String name, byte[] content) throws IOException {
if (name.equals("edu/columbia/cs/psl/phosphor/Configuration.class")) {
return setConfigurationVersion(new ByteArrayInputStream(content));
} else if (name.equals("edu/columbia/cs/psl/phosphor/runtime/RuntimeJDKInternalUnsafePropagator.class")) {
return transformUnsafePropagator(new ByteArrayInputStream(content),
"jdk/internal/misc/Unsafe", patchUnsafeNames);
} else {
return content;
}
}

public byte[] transformBaseModuleInfo(InputStream in, Set<String> 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 boolean shouldPatchUnsafeNames(InputStream in) throws IOException {
ClassNode cn = new ClassNode();
new ClassReader(in).accept(cn, 0);
for (MethodNode mn : cn.methods) {
if (mn.name.contains("putReference")) {
return false;
}
}
return true;
}

/**
* Modify {@link Configuration} to set {@link Configuration#IS_JAVA_8} to false.
* This flag must be set correctly in order to boot the JVM.
*/
private static byte[] setConfigurationVersion(InputStream is) throws IOException {
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new ClassVisitor(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);
if (name.equals("<clinit>")) {
return new ConfigurationEmbeddingMV(mv);
} else {
return mv;
}
}
};
cr.accept(cv, 0);
return cw.toByteArray();
}

public static byte[] transformUnsafePropagator(InputStream in, String unsafeInternalName,
boolean patchUnsafeNames) throws IOException {
ClassReader cr = new ClassReader(in);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new UnsafePatchingCV(cw, unsafeInternalName, patchUnsafeNames);
cr.accept(cv, 0);
return cw.toByteArray();
}

private static class UnsafePatchingCV extends ClassVisitor {
private static final String UNSAFE_PROXY_INTERNAL_NAME = Type.getInternalName(UnsafeProxy.class);
private static final String UNSAFE_PROXY_DESC = Type.getDescriptor(UnsafeProxy.class);
private final String unsafeDesc;
private final boolean patchNames;
private final String unsafeInternalName;

public UnsafePatchingCV(ClassWriter cw, String unsafeInternalName, boolean patchNames) {
super(Configuration.ASM_VERSION, cw);
this.unsafeInternalName = unsafeInternalName;
this.unsafeDesc = "L" + unsafeInternalName + ";";
this.patchNames = patchNames;
}

private String patchInternalName(String name) {
return UNSAFE_PROXY_INTERNAL_NAME.equals(name) ? unsafeInternalName : name;
}

private String patchDesc(String desc) {
return desc == null ? null : desc.replace(UNSAFE_PROXY_DESC, unsafeDesc);
}

private String patchMethodName(String owner, String name) {
return patchNames && owner.equals(UNSAFE_PROXY_INTERNAL_NAME) ?
name.replace("Reference", "Object") : name;
}

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, patchDesc(descriptor), patchDesc(signature), exceptions);
return new MethodVisitor(api, mv) {
@Override
public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) {
for (int i = 0; i < numLocal; i++) {
if (local[i] instanceof String) {
local[i] = patchInternalName((String) local[i]);
}
}
for (int i = 0; i < numStack; i++) {
if (stack[i] instanceof String) {
stack[i] = patchInternalName((String) stack[i]);
}
}
super.visitFrame(type, numLocal, local, numStack, stack);
}

@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
super.visitFieldInsn(opcode, patchInternalName(owner), name, patchDesc(descriptor));
}

@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
super.visitMethodInsn(opcode, patchInternalName(owner), patchMethodName(owner, name),
patchDesc(descriptor), isInterface);
}

@Override
public void visitTypeInsn(int opcode, String type) {
super.visitTypeInsn(opcode, patchInternalName(type));
}

@Override
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end,
int index) {
super.visitLocalVariable(name, patchDesc(descriptor), signature, start, end, index);
}
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@


import edu.columbia.cs.psl.phosphor.Configuration;
import edu.columbia.cs.psl.phosphor.Instrumenter;
import edu.columbia.cs.psl.phosphor.PhosphorPatcher;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.util.CheckClassAdapter;

import java.io.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

Expand All @@ -21,7 +23,8 @@ 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 = Instrumenter.transformRuntimeUnsafePropagator(sunMiscUnsafeIn, "sun/misc/Unsafe");
byte[] instrumentedUnsafe = PhosphorPatcher.transformUnsafePropagator(sunMiscUnsafeIn,
"sun/misc/Unsafe", false);
Files.write(Paths.get(pathToUnsafePropagator), instrumentedUnsafe);

for (String clazz : CLASSES) {
Expand Down
Loading

0 comments on commit 05fc3d6

Please sign in to comment.