📄 verify.c
字号:
/* * verify.c * Perform stages 2 & 3 of class verification. Stage 1 is performed * when the class is being loaded (so isn't here) and stage 4 is performed * as the method is being executed. * * verify2() was originally created by someone in Transvirtual Technologies. however, * it did almost nothing (only a shrivel of the stuff needed by pass 2... * specifically part 3 of of pass 2, which has been modified), * so questions regarding pass 2 should be sent to: * Rob Gonzalez <rob@kaffe.org> * * verify3() was also originally created by someone in Transvirtual, but it only * returned true :) Questions regarding this one can be sent to Rob as well. * * All other code in this file was added to assist the full versions of verify2() and * verify3(). */#include "config.h"#include "config-std.h"#include "constants.h"#include "classMethod.h"#include "baseClasses.h"#include "lookup.h"#include "exception.h"#include "errors.h"#include "jni.h"#include "debug.h"#include "utf8const.h"/* needed for pass 3 */#include "bytecode.h"#include "itypes.h"#include "soft.h"#include "verify.h"/* * Returns whether the given class is "trusted" (i.e. does not require verification). */staticboolisTrustedClass(Hjava_lang_Class* class) { /* recall (from main.c): -verifyremote (default) ==> verifyMode = 2 * -verify ==> verifyMode = 3 * -noverify ==> verifyMode = 0 */ return ((class->loader == 0 && (Kaffe_JavaVMArgs[0].verifyMode & 1) == 0) || (class->loader != 0 && (Kaffe_JavaVMArgs[0].verifyMode & 2) == 0));}/********************************************************************************* * Type Descriptor Parsing Methods *********************************************************************************/static const char* parseBaseTypeDescriptor(const char* sig);static const char* parseObjectTypeDescriptor(const char* sig);static const char* parseArrayTypeDescriptor(const char* sig);static const char* parseFieldTypeDescriptor(const char* sig);static bool parseMethodTypeDescriptor(const char* sig);/* * parses a BaseType descriptor (p.100 JVML Spec 2) * * BaseType: B | C | D | F | I | J | S | Z * * returns the sig pointing right after the BaseType * or NULL if an error occurred. */staticconst char*parseBaseTypeDescriptor(const char* sig){ switch(*sig) { case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': return sig + 1; default: break; } return NULL;}/* * parses an ObjectType descriptor (p.101 JVML Spec 2) * * ObjectType: L<classname>; * <classname> is a string made from unicode characters * * precondition: *sig == 'L' * * returns the sig pointing right after the ObjectType * or NULL if an error occurred. */staticconst char*parseObjectTypeDescriptor(const char* sig){ for (sig++; sig != '\0' && *sig != ';'; sig++); if (sig == '\0') return NULL; else return sig + 1;}/* parses an ArrayType descriptor (p.101 JVML Spec 2) * * ArrayType : [ComponentType * ComponentType: FieldType * * precondition: *sig == '[' * * returns the sig pointing right after the ArrayType * or NULL if an error occurred. */staticconst char*parseArrayTypeDescriptor(const char* sig){ while (*sig != '\0' && *sig == '[') sig++; if (*sig == '\0') return NULL; else return parseFieldTypeDescriptor(sig);}/* * parses a field type descriptor (i.e. its type signature). * * FieldType: * BaseType | ObjectType | ArrayType * * returns the signatures position immediately after the field's * type signature, or NULL if any error has occurred. */staticconst char*parseFieldTypeDescriptor(const char* sig){ if (sig == NULL) return NULL; else if (*sig == '[') return parseArrayTypeDescriptor(sig); else if (*sig == 'L') return parseObjectTypeDescriptor(sig); else return parseBaseTypeDescriptor(sig);}/* * parses a method type descriptor (i.e. its signature). (p.103 JVML Spec 2) * * MethodDescriptor: * ( ParameterDescriptor* ) ReturnDescriptor * ParameterDescriptor: * FieldType * ReturnDescriptor: * FieldType | V * * returns whether the descriptor is legal */staticboolparseMethodTypeDescriptor(const char* sig){ if (sig == NULL || *sig != '(') return false; DBG(VERIFY2, dprintf(" parsing method type descriptor: %s\n", sig); ); /* parse the type parameters */ for (sig++; sig && *sig != ')' && *sig != '\0'; sig = parseFieldTypeDescriptor(sig)) { DBG(VERIFY2, dprintf(" parameter sig: %s\n", sig); ); } if (sig == NULL || *sig == '\0') { DBG(VERIFY2, dprintf(" error: no ReturnDescriptor\n"); ); return false; } sig++; DBG(VERIFY2, dprintf(" ReturnDescriptor: %s\n", sig); ); if (*sig == 'V') { sig++; return *sig == '\0'; } if (parseFieldTypeDescriptor(sig) != NULL) return true; return false;}/********************************************************************************* * Pass 2 Verification *********************************************************************************/static bool checkField(Field*, errorInfo*);static bool checkConstructor(Method*, errorInfo*);static bool checkMethodStaticConstraints(Method*, errorInfo*);static bool checkAbstractMethod(Method*, errorInfo*);/* perhaps this should go in classMethod.[ch]... */static bool isMethodVoid(Method* method){ char* sig = (char*)method->parsed_sig->signature->data; int i = strlen(sig); return (i > 2) && (sig[i-2] == ')' && sig[i-1] == 'V');}/* * For debugging purposes. Prints out the value of the specified constant pool entry. */#if !(defined(NDEBUG) || !defined(KAFFE_VMDEBUG))const char* indent = " ";const char* indent2 = " ";staticuint32printConstantPoolEntry(const Hjava_lang_Class* class, uint32 idx){ const constants* pool = CLASS_CONSTANTS(class); switch (pool->tags[idx]) { case CONSTANT_Utf8: DBG(VERIFY2, dprintf(" UTF8: %s", CONST_UTF2CHAR(idx, pool)) ); break; case CONSTANT_Long: case CONSTANT_Double: idx++; case CONSTANT_Integer: case CONSTANT_Float: DBG(VERIFY2, dprintf(" NUMERICAL"); ); break; case CONSTANT_ResolvedString: case CONSTANT_ResolvedClass: DBG(VERIFY2, dprintf(" RESOLVED: %s", ((Hjava_lang_Class*)pool->data[idx])->name->data); ); break; case CONSTANT_Class: DBG(VERIFY2, dprintf(" UNRESOLVED CLASS: %s", CLASS_NAMED(idx, pool)); ); break; case CONSTANT_String: DBG(VERIFY2, dprintf(" STRING: %s", CONST_STRING_NAMED(idx, pool)); ); break; case CONSTANT_Fieldref: DBG(VERIFY2, dprintf(" FIELDREF: %s --type-- %s", FIELDREF_NAMED(idx, pool), FIELDREF_SIGD(idx, pool)); ); break; case CONSTANT_Methodref: DBG(VERIFY2, dprintf(" METHODREF: %s --type-- %s", METHODREF_NAMED(idx, pool), METHODREF_SIGD(idx, pool)); ); break; case CONSTANT_InterfaceMethodref: DBG(VERIFY2, dprintf(" INTERFACEMETHODREF: %s --type-- %s", INTERFACEMETHODREF_NAMED(idx, pool), INTERFACEMETHODREF_SIGD(idx, pool)); ); break; case CONSTANT_NameAndType: DBG(VERIFY2, dprintf(" NAMEANDTYPE: %s --and-- %s", NAMEANDTYPE_NAMED(idx, pool), NAMEANDTYPE_SIGD(idx, pool)); ); break; default: DBG(VERIFY2, dprintf(" *** UNRECOGNIZED CONSTANT POOL ENTRY in class %s *** ", CLASS_CNAME(class)); ); } return idx;}staticvoidprintConstantPool(const Hjava_lang_Class* class){ uint32 idx; const constants *pool = CLASS_CONSTANTS(class); DBG(VERIFY2, dprintf(" CONSTANT POOL FOR %s\n", class->name->data); ); for (idx = 1; idx < pool->size; idx++) { DBG(VERIFY2, dprintf(" %d", idx); ); idx = printConstantPoolEntry(class, idx); DBG(VERIFY2, dprintf("\n"); ); }}#endif /* !(defined(NDEBUG) || !defined(KAFFE_VMDEBUG)) *//* * Verify pass 2: Check the internal consistency of the class file * but do not check the bytecode. If at any point we find an error in the * class file format, we abort and throw a ClassFormatError or VerifyError * (depending on what the Sun Java VM would throw given the specific error). * * According to the JVML spec for Java 2, this should do the following: * 1. ensure that every class (except java/lang/Object) has a direct superclass * * 2. a. ensure that final classes are not subclassed * b. ensure that final methods are not overriden * * 3. ensure that constant pool satisfies the documented static constraints * - i.e. each CONSTANT_Class_info structure in constant pool contains in its * name_index item a valid constant pool index for a CONSTANT_Utf8_info structure, * etc. * * 4. ensure that all field references and method references in the constant pool have * (this is basically parsing type signatures for validity): * - valid names * - valid classes * - a valid type desriptor * * However, the JVML spec also specifies a bunch of other rules that make sense to be * checked here (i.e. interfaces must be abstract) */boolverify2(Hjava_lang_Class* class, errorInfo *einfo){ constants* pool; int idx; int tag; if (isTrustedClass(class)) return(true); DBG(VERIFY2, dprintf("\nPass 2 Verifying class %s\n", CLASS_CNAME(class)); ); #ifdef UNENFORCED_INTERFACE_RESTRICTIONS /* this is commented out because Sun's Java runtime environment doesn't enforce the restrictions * placed upon interfaces by the specification. * GJC also doesn't follow this! */ if (CLASS_IS_INTERFACE(class)) { /* JVML spec p. 96: if a class has ACC_INTERFACE flag set, it must also have * ( ACC_ABSTRACT set and may have ACC_PUBLIC set. it may not have any other flags * (in the given table) set. */ if (!CLASS_IS_ABSTRACT(class)) { postExceptionMessage(einfo, JAVA_LANG(ClassFormatError), "interfaces must have their abstract flag set (in interface \"%s\")", CLASS_CNAME(class)); return(false); } else if (CLASS_IS_FINAL(class)) { postExceptionMessage(einfo, JAVA_LANG(ClassFormatError), "interfaces cannot be final (in interface \"%s\")", CLASS_CNAME(class)); return(false); } }#endif /* UNENFORCED_INTERFACE_RESTRICTIONS */ { Field *fld; int lpc; fld = CLASS_FIELDS(class); for( lpc = 0; lpc < CLASS_NFIELDS(class); lpc++ ) { if( !checkField(&fld[lpc], einfo) ) return(false); } } /* java/lang/Object does not have a superclass, is not final, and is not an interface, * so we skip all of those checks */ if(strcmp(OBJECTCLASS, CLASS_CNAME(class))) { if (class->superclass == NULL) { /*********************************************************** * 1 - every class except java/lang/Object has a superclass ***********************************************************/ postExceptionMessage(einfo, JAVA_LANG(ClassFormatError), "class \"%s\" does not have a superclass", CLASS_CNAME(class)); return(false); } else if (CLASS_IS_FINAL(class->superclass)) { /********************************************************* * 2a: check that final classes are not inherited from *********************************************************/ postExceptionMessage(einfo, JAVA_LANG(VerifyError), "class \"%s\" cannot inherit from final class \"%s\"", CLASS_CNAME(class), CLASS_CNAME(class->superclass)); return(false); } else if (CLASS_IS_INTERFACE(class)) { /* we separate this from the rest of the method checking because the only requirement * of methods in an interface is that they be public, abstract, and nothing else. */ Method* method; int n; for (n = CLASS_NMETHODS(class), method = CLASS_METHODS(class); n > 0; --n, ++method) { /* if it's <clinit> (init_name) then it doesn't have to be public. * compilers often insert a <clinit> function to initialize an * interfaces public static fields, if there are any. */ if (strcmp(init_name->data, METHOD_NAMED(method))) { if (!METHOD_IS_PUBLIC(method)) { postExceptionMessage(einfo, JAVA_LANG(ClassFormatError), "interface methods must be declared public (method \"%s\" in interface \"%s\")", METHOD_NAMED(method), CLASS_CNAME(class)); return(false); } else if (!METHOD_IS_ABSTRACT(method)) { postExceptionMessage(einfo, JAVA_LANG(ClassFormatError), "interface methods must be declared abstract (method \"%s\" in interface \"%s\")", METHOD_NAMED(method), CLASS_CNAME(class)); return(false); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -