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