diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/TargetInfo.java b/src/main/java/me/coley/cafedude/classfile/annotation/TargetInfo.java
index b1b24c7..8d7ef51 100644
--- a/src/main/java/me/coley/cafedude/classfile/annotation/TargetInfo.java
+++ b/src/main/java/me/coley/cafedude/classfile/annotation/TargetInfo.java
@@ -1,5 +1,7 @@
package me.coley.cafedude.classfile.annotation;
+import me.coley.cafedude.classfile.attribute.ExceptionsAttribute;
+import me.coley.cafedude.classfile.attribute.LocalVariableTableAttribute;
import me.coley.cafedude.classfile.behavior.CpAccessor;
import java.util.Collections;
@@ -86,6 +88,7 @@ public int computeLength() {
* of a class or interface declaration.
*/
public static class SuperTypeTargetInfo extends TargetInfo {
+ public static final int EXTENDS = 65535;
private final int superTypeIndex;
/**
@@ -101,6 +104,13 @@ public SuperTypeTargetInfo(int targetType, int superTypeIndex) {
this.superTypeIndex = superTypeIndex;
}
+ /**
+ * @return {@code} true when {@link #getSuperTypeIndex()} is 65535.
+ */
+ public boolean isExtends() {
+ return superTypeIndex == EXTENDS;
+ }
+
/**
* @return For {@code extends} index is 65535.
* Otherwise the index indicates the interface index of the associated class.
@@ -231,9 +241,8 @@ public static class ThrowsTargetInfo extends TargetInfo {
* indicating the purpose of the {@code target_info}.
* @param throwsTypeIndex
* Index of the thrown type in the associated {@code exception_index_table}
- * of the {@code Exceptions} attribute.
+ * of the {@link ExceptionsAttribute}.
*/
- // TODO: When done parsing Exceptions update javadocs to link to the type here
public ThrowsTargetInfo(int targetType, int throwsTypeIndex) {
super(TargetInfoType.THROWS_TARGET, targetType);
this.throwsTypeIndex = throwsTypeIndex;
@@ -258,9 +267,8 @@ public int computeLength() {
* Indicates that an annotation appears on the type of a local variable.
*
* Marked variables types are annotated but are not listed directly.
- * The information provided should be matched with what appears in the local variable attribute.
+ * The information provided should be matched with what appears in the {@link LocalVariableTableAttribute}.
*/
- // TODO: When LocalVariableAttribute is creates update the javadoc to link to it
public static class LocalVarTargetInfo extends TargetInfo {
private final List variableTable;
diff --git a/src/main/java/me/coley/cafedude/io/AttributeReader.java b/src/main/java/me/coley/cafedude/io/AttributeReader.java
index a0b41f2..478cecb 100644
--- a/src/main/java/me/coley/cafedude/io/AttributeReader.java
+++ b/src/main/java/me/coley/cafedude/io/AttributeReader.java
@@ -115,11 +115,16 @@ public Attribute readAttribute(AttributeContext context) throws IOException {
return null;
}
return attribute;
- } catch (IOException ex) {
+ } catch (Exception ex) {
if (reader.doDropEofAttributes()) {
- String name = ((CpUtf8) builder.getPool().get(nameIndex)).getText();
- logger.debug("Invalid '{}' on {}, EOF thrown when parsing attribute, expected {} bytes",
- name, context.name(), expectedContentLength);
+ if (nameIndex < builder.getPool().size()) {
+ String name = ((CpUtf8) builder.getPool().get(nameIndex)).getText();
+ logger.debug("Invalid '{}' on {}, EOF thrown when parsing attribute, expected {} bytes",
+ name, context.name(), expectedContentLength);
+ } else {
+ logger.debug("Invalid attribute on {}, invalid attribute name index", context.name());
+ }
+
return null;
} else
throw ex;
diff --git a/src/main/java/me/coley/cafedude/transform/IllegalStrippingTransformer.java b/src/main/java/me/coley/cafedude/transform/IllegalStrippingTransformer.java
index 0bf8d58..7b6da8b 100644
--- a/src/main/java/me/coley/cafedude/transform/IllegalStrippingTransformer.java
+++ b/src/main/java/me/coley/cafedude/transform/IllegalStrippingTransformer.java
@@ -12,6 +12,10 @@
import me.coley.cafedude.classfile.annotation.ElementValue;
import me.coley.cafedude.classfile.annotation.EnumElementValue;
import me.coley.cafedude.classfile.annotation.PrimitiveElementValue;
+import me.coley.cafedude.classfile.annotation.TargetInfo;
+import me.coley.cafedude.classfile.annotation.TargetInfo.CatchTargetInfo;
+import me.coley.cafedude.classfile.annotation.TargetInfo.SuperTypeTargetInfo;
+import me.coley.cafedude.classfile.annotation.TypeAnnotation;
import me.coley.cafedude.classfile.annotation.Utf8ElementValue;
import me.coley.cafedude.classfile.attribute.AnnotationDefaultAttribute;
import me.coley.cafedude.classfile.attribute.AnnotationsAttribute;
@@ -68,6 +72,7 @@
* @author Matt Coley
*/
public class IllegalStrippingTransformer extends Transformer {
+ private static final int FORCE_FAIL = -1;
private static final Logger logger = LoggerFactory.getLogger(IllegalStrippingTransformer.class);
/**
@@ -217,7 +222,9 @@ private boolean isValid(AttributeHolder holder, Attribute attribute) {
expectedTypeMasks.put(innerClass.getOuterClassInfoIndex(), i -> i == 0 || i == ConstantPool.CLASS);
// 0 if anonymous, otherwise name index
expectedTypeMasks.put(innerClass.getInnerNameIndex(), i -> i == 0 || i == ConstantPool.UTF8);
- allow0Case |= innerClass.getInnerClassInfoIndex() == 0 || innerClass.getOuterClassInfoIndex() == 0;
+ allow0Case |= innerClass.getInnerClassInfoIndex() == 0
+ || innerClass.getOuterClassInfoIndex() == 0
+ || innerClass.getInnerNameIndex() == 0;
}
break;
case CODE: {
@@ -395,7 +402,62 @@ private void addAnnotationValidation(AttributeHolder holder,
cpEntryValidators.put(elementTypeIndex, matchUtf8ClassType());
addElementValueValidation(holder, expectedTypeMasks, cpEntryValidators, entry.getValue());
}
- // TODO: Intra-reference checks
+ if (anno instanceof TypeAnnotation) {
+ TypeAnnotation typeAnnotation = (TypeAnnotation) anno;
+ TargetInfo targetInfo = typeAnnotation.getTargetInfo();
+ switch (targetInfo.getTargetTypeKind()) {
+ case TYPE_PARAMETER_BOUND_TARGET:
+ break;
+ case TYPE_PARAMETER_TARGET:
+ break;
+ case FORMAL_PARAMETER_TARGET:
+ break;
+ case TYPE_ARGUMENT_TARGET:
+ break;
+ case LOCALVAR_TARGET:
+ // TODO: Ensure variables outline matches what is in code's variables attribute
+ break;
+ case THROWS_TARGET:
+ // TODO: Verify with a sample what the target holder type should be
+ break;
+ case OFFSET_TARGET:
+ // TODO: relies on instructions being parsed
+ break;
+ case SUPERTYPE_TARGET:
+ if (holder instanceof ClassFile) {
+ SuperTypeTargetInfo superTypeTargetInfo = (SuperTypeTargetInfo) targetInfo;
+ if (!superTypeTargetInfo.isExtends()) {
+ ClassFile classFile = (ClassFile) holder;
+ // Enforce interfaces range
+ if (superTypeTargetInfo.getSuperTypeIndex() >= classFile.getInterfaceIndices().size()) {
+ expectedTypeMasks.put(FORCE_FAIL, i -> false);
+ }
+ }
+ } else {
+ // Illegal target kind for situation
+ expectedTypeMasks.put(FORCE_FAIL, i -> false);
+ }
+ break;
+
+ case CATCH_TARGET:
+ if (holder instanceof CodeAttribute) {
+ CodeAttribute code = (CodeAttribute) holder;
+ CatchTargetInfo catchTargetInfo = (CatchTargetInfo) targetInfo;
+ // Enforce table range
+ if (catchTargetInfo.getExceptionTableIndex() >= code.getExceptionTable().size()) {
+ expectedTypeMasks.put(FORCE_FAIL, i -> false);
+ }
+ } else {
+ // Illegal target kind for situation
+ expectedTypeMasks.put(FORCE_FAIL, i -> false);
+ }
+ break;
+ case EMPTY_TARGET:
+ default:
+ // no-op
+ break;
+ }
+ }
}
private void addElementValueValidation(AttributeHolder holder,
diff --git a/src/test/resources/samples/crasher/sample28.class b/src/test/resources/samples/crasher/sample28.class
new file mode 100644
index 0000000..b50c5df
Binary files /dev/null and b/src/test/resources/samples/crasher/sample28.class differ
diff --git a/src/test/resources/samples/crasher/sample29.class b/src/test/resources/samples/crasher/sample29.class
new file mode 100644
index 0000000..1606668
Binary files /dev/null and b/src/test/resources/samples/crasher/sample29.class differ