Пример #1
0
int _ILVerify(ILCoder *coder, unsigned char **start, ILMethod *method,
			  ILMethodCode *code, int unsafeAllowed, ILExecThread *thread)
{
	TempAllocator allocator;
	ILCoderExceptions coderExceptions;
	ILCoderExceptionBlock *coderException;
	ILCoderExceptionBlock *currentCoderException;
	int numHandlers;
	int extraCodeLen;
	unsigned long *jumpMask;
	unsigned char *pc;
	ILUInt32 len;
	int result;
	unsigned opcode;
	ILUInt32 insnSize;
	int isStatic, isSynchronized;
	int insnType;
	ILUInt32 offset = 0;
	ILEngineStackItem *stack;
	ILUInt32 stackSize;
#ifdef IL_VERIFY_DEBUG
	const ILOpcodeInfo *insn = 0;
	#define	MAIN_OPCODE_TABLE	ILMainOpcodeTable
	#define	PREFIX_OPCODE_TABLE	ILPrefixOpcodeTable
#else
	const ILOpcodeSmallInfo *insn = 0;
	#define	MAIN_OPCODE_TABLE	ILMainOpcodeSmallTable
	#define	PREFIX_OPCODE_TABLE	ILPrefixOpcodeSmallTable
#endif
	ILType *signature;
	ILType *type;
	ILUInt32 numArgs;
	ILUInt32 numLocals;
	ILType *localVars;
	int lastWasJump;
	ILException *exceptions;
	ILException *exception;
	int hasRethrow;
	int tryInlineType;
	int coderFlags;
	unsigned int tryInlineOpcode;
	unsigned char *tryInlinePc;
	ILUInt32 optimizationLevel;
	ILBool lastInsnWasPrefix;
	ILCoderPrefixInfo prefixInfo;
#ifdef IL_CONFIG_DEBUG_LINES
	int haveDebug = ILDebugPresent(ILProgramItem_Image(method));
#else
	int haveDebug = 0;
#endif

	/* Include local variables that are required by the include files */
#define IL_VERIFY_LOCALS
#include "verify_var.c"
#include "verify_const.c"
#include "verify_arith.c"
#include "verify_conv.c"
#include "verify_stack.c"
#include "verify_ptr.c"
#include "verify_obj.c"
#include "verify_call.c"
#include "verify_branch.c"
#include "verify_except.c"
#include "verify_ann.c"
#undef IL_VERIFY_LOCALS

	/* Get the exception list */
	if(!ILMethodGetExceptions(method, code, &exceptions))
	{
		return 0;
	}

	/* Clear the exception management structure */
	ILMemZero(&coderExceptions, sizeof(ILCoderExceptions));
	/*
	 * Initialize the size of the additional code generated for
	 * synchronization.
	 */
	extraCodeLen = 0;
	/* And set the last label to the code length */
	coderExceptions.lastLabel = code->codeLen;
	
	/* Initialize the memory allocator that is used for temporary
	   allocation during bytecode verification */
	ILMemZero(allocator.buffer, sizeof(allocator.buffer));
	allocator.posn = 0;
	allocator.overflow = 0;

	coderFlags = ILCoderGetFlags(coder);
	optimizationLevel = ILCoderGetOptimizationLevel(coder);
	isStatic = ILMethod_IsStatic(method);
	isSynchronized = ILMethod_IsSynchronized(method);

	result = 0;
	if(exceptions || isSynchronized)
	{
		numHandlers = 0;
		exception = exceptions;
		while(exception)
		{
			++numHandlers;
			exception = exception->next;
		}
		if(isSynchronized)
		{
			/* We'll need an extra try and fault block for synchronization */
			++numHandlers;
		}
		/*
		 * Allocate memory for the exception infos.
		 * There might be create 3 coder exception blocks for one
		 * IL exception.
		 * So we allocate memory for the worst case here.
		 */
		coderExceptions.blocks = ILCalloc(sizeof(ILCoderExceptionBlock),
										  numHandlers * 3);
		if(!coderExceptions.blocks)
		{
			return 0;
		}

		/* Now setup the exception structure */
		exception = exceptions;
		while(exception)
		{
			switch(_ILCoderAddExceptionBlock(&coderExceptions, method,
											 exception))
			{
				case IL_CODER_BRANCH_ERR:
				{
					VERIFY_BRANCH_ERROR();
				}
				break;

				case IL_CODER_TYPE_ERR:
				{
					VERIFY_TYPE_ERROR();
				}
				break;
			}
			exception = exception->next;
		}
		/*
		 * Now check if all exception block limits are in the code.
		 */
		len = code->codeLen;
		coderException = coderExceptions.firstBlock;
		if(coderException)
		{
			/*
			 * Check the start offset of the first exception block in the
			 * lowest list.
			 */
			if(coderException->startOffset > len)
			{
				VERIFY_BRANCH_ERROR();
			}
			/*
			 * Look for the last exception block in the lowest list.
			 */
			while(coderException->nextNested)
			{
				coderException = coderException->nextNested;
			}
			/*
			 * Check the end offset of the last exception block in the
			 * lowest list.
			 * All other exceprion blocks end at or before this offset.
			 */
			if(coderException->endOffset > len)
			{
				VERIFY_BRANCH_ERROR();
			}
		}
		if(isSynchronized)
		{
			/*
			 * Wrap the whole function in a try block with a fault handler.
			 */
			ILException tempException;

			tempException.flags = IL_META_EXCEPTION_FAULT;
			tempException.tryOffset = 0;
			tempException.tryLength = len;
			tempException.handlerOffset = len;
			tempException.handlerLength = 1;
			tempException.extraArg = 0;
			tempException.userData = 0;
			tempException.ptrUserData = 0;
			tempException.next = 0;

			switch(_ILCoderAddExceptionBlock(&coderExceptions, method,
											 &tempException))
			{
				case IL_CODER_BRANCH_ERR:
				{
					VERIFY_BRANCH_ERROR();
				}
				break;

				case IL_CODER_TYPE_ERR:
				{
					VERIFY_TYPE_ERROR();
				}
				break;
			}
			extraCodeLen = 2;
		}
	}

restart:
	result = 0;
	labelList = 0;
	hasRethrow = 0;

	/* Reset the prefix information */
	ILMemZero(&prefixInfo, sizeof(ILCoderPrefixInfo));

	/* Allocate the jump target mask */
	jumpMask = (unsigned long *)TempAllocate
					(&allocator, BYTES_FOR_MASK(code->codeLen + extraCodeLen));
	if(!jumpMask)
	{
		VERIFY_MEMORY_ERROR();
	}

	/* Scan the code looking for all jump targets, and validating
	   that all instructions are more or less valid */
	pc = code->code;
	len = code->codeLen;
	while(len > 0)
	{
		/* Mark this position in the jump mask as an instruction start */
		MarkInsnStart(jumpMask, (ILUInt32)(pc - (unsigned char *)(code->code)));

		/* Fetch the instruction size and type */
		opcode = (unsigned)(pc[0]);
		if(opcode != IL_OP_PREFIX)
		{
			/* Regular opcode */
			insnSize = (ILUInt32)(MAIN_OPCODE_TABLE[opcode].size);
			if(len < insnSize)
			{
				VERIFY_TRUNCATED();
			}
			insnType = MAIN_OPCODE_TABLE[opcode].args;
		}
		else
		{
			/* Prefixed opcode */
			if(len < 2)
			{
				VERIFY_TRUNCATED();
			}
			opcode = (unsigned)(pc[1]);
			insnSize = (ILUInt32)(PREFIX_OPCODE_TABLE[opcode].size);
			if(len < insnSize)
			{
				VERIFY_TRUNCATED();
			}
			insnType = PREFIX_OPCODE_TABLE[opcode].args;
			if(opcode == IL_PREFIX_OP_RETHROW)
			{
				hasRethrow = 1;
			}
			opcode += IL_OP_PREFIX;
		}

		/* Determine how to handle this type of instruction */
		switch(insnType)
		{
			case IL_OPCODE_ARGS_SHORT_JUMP:
			{
				/* 8-bit jump offset */
				offset = (ILUInt32)((pc + insnSize) -
										(unsigned char *)(code->code)) +
						 (ILUInt32)(ILInt32)(ILInt8)(pc[1]);
				if(offset >= code->codeLen)
				{
					VERIFY_BRANCH_ERROR();
				}
				MarkJumpTarget(jumpMask, offset);
			}
			break;

			case IL_OPCODE_ARGS_LONG_JUMP:
			{
				/* 32-bit jump offset */
				offset = (ILUInt32)((pc + insnSize) -
										(unsigned char *)(code->code)) +
						 (ILUInt32)(IL_READ_INT32(pc + 1));
				if(offset >= code->codeLen)
				{
					VERIFY_BRANCH_ERROR();
				}
				MarkJumpTarget(jumpMask, offset);
			}
			break;

			case IL_OPCODE_ARGS_SWITCH:
			{
				/* Switch statement */
				if(len < 5)
				{
					VERIFY_TRUNCATED();
				}
				numArgs = IL_READ_UINT32(pc + 1);
				insnSize = 5 + numArgs * 4;
				if(numArgs >= 0x20000000 || len < insnSize)
				{
					VERIFY_TRUNCATED();
				}
				while(numArgs > 0)
				{
					--numArgs;
					offset = (ILUInt32)((pc + insnSize) -
											(unsigned char *)(code->code)) +
							 (ILUInt32)(IL_READ_INT32(pc + 5 + numArgs * 4));
					if(offset >= code->codeLen)
					{
						VERIFY_BRANCH_ERROR();
					}
					MarkJumpTarget(jumpMask, offset);
				}
			}
			break;

			case IL_OPCODE_ARGS_ANN_DATA:
			{
				/* Variable-length annotation data */
				if(opcode == IL_OP_ANN_DATA_S)
				{
					if(len < 2)
					{
						VERIFY_TRUNCATED();
					}
					insnSize = (((ILUInt32)(pc[1])) & 0xFF) + 2;
					if(len < insnSize)
					{
						VERIFY_TRUNCATED();
					}
				}
				else
				{
					if(len < 6)
					{
						VERIFY_TRUNCATED();
					}
					insnSize = (IL_READ_UINT32(pc + 2) + 6);
					if(len < insnSize)
					{
						VERIFY_TRUNCATED();
					}
				}
			}
			break;

			case IL_OPCODE_ARGS_ANN_PHI:
			{
				/* Variable-length annotation data */
				if(len < 3)
				{
					VERIFY_TRUNCATED();
				}
				insnSize = ((ILUInt32)IL_READ_UINT16(pc + 1)) * 2 + 3;
				if(len < insnSize)
				{
					VERIFY_TRUNCATED();
				}
			}
			break;

			case IL_OPCODE_ARGS_INVALID:
			{
				VERIFY_INSN_ERROR();
			}
			break;

			default: break;
		}

		/* Advance to the next instruction */
		pc += insnSize;
		len -= insnSize;
	}

	/* Mark the start and end of exception blocks as special jump targets */
	numHandlers = 0;
	while(numHandlers < coderExceptions.numBlocks)
	{
		coderException = &(coderExceptions.blocks[numHandlers]);
		MarkJumpTarget(jumpMask, coderException->startOffset);
		MarkSpecialJumpTarget(jumpMask, coderException->startOffset);
		MarkJumpTarget(jumpMask, coderException->endOffset);
		MarkSpecialJumpTarget(jumpMask, coderException->endOffset);
		switch(coderException->flags)
		{
			case IL_CODER_HANDLER_TYPE_TRY:
			{
				/* Nothing to do here */
			}
			break;

			case IL_CODER_HANDLER_TYPE_CATCH:
			{
				/* This is a typed catch block */
				classInfo = coderException->un.handlerBlock.exceptionClass;
				/*
				 * This block will be called with an object of the given
				 * type on the stack.
				 */
				SET_TARGET_STACK(coderException->startOffset, classInfo);
			}
			break;

			case IL_CODER_HANDLER_TYPE_FINALLY:
			case IL_CODER_HANDLER_TYPE_FAULT:
			{
				/* This is a finally or fault clause */
				/* The clause will be called with nothing on the stack */
				SET_TARGET_STACK_EMPTY(coderException->startOffset);
			}
			break;

			case IL_CODER_HANDLER_TYPE_FILTER:
			case IL_CODER_HANDLER_TYPE_FILTEREDCATCH:
			{
				/* This is an exception filter or the corresponding catch block */
				/* 
				 * The block will be called with an object on the stack,
				 * so record that in the label list for later
				 */
				classInfo = ILClassResolveSystem(ILProgramItem_Image(method),
												 0, "Object", "System");
				if(!classInfo)
				{
					/* Ran out of memory trying to create "System.Object" */
					VERIFY_MEMORY_ERROR();
				}
				SET_TARGET_STACK(coderException->startOffset, classInfo);
			}
			break;
		}
		++numHandlers;
	}

	/* Make sure that all jump targets are instruction starts */
	len = code->codeLen;
	while(len > 0)
	{
		--len;
		if(IsJumpTarget(jumpMask, len) && !IsInsnStart(jumpMask, len))
		{
			VERIFY_BRANCH_ERROR();
		}
	}

	/* Create the stack.  We need two extra "slop" items to allow for
	   stack expansion during object construction.  See "verify_call.c"
	   for further details */
	stack = (ILEngineStackItem *)TempAllocate
				(&allocator, sizeof(ILEngineStackItem) * (code->maxStack + 2));
	if(!stack)
	{
		VERIFY_MEMORY_ERROR();
	}
	stackSize = 0;

	/* Get the method signature, plus the number of arguments and locals */
	signature = ILMethod_Signature(method);
	numArgs = ILTypeNumParams(signature);
	if(ILType_HasThis(signature))
	{
		/* Account for the "this" argument */
		++numArgs;
	}
	if(code->localVarSig)
	{
		localVars = ILStandAloneSigGetType(code->localVarSig);
		numLocals = ILTypeNumLocals(localVars);
		if(ILTypeNeedsInstantiation(localVars) &&
		   ILMember_IsGenericInstance(method))
		{
			ILType *classTypeArgs = ILMethodGetClassTypeArguments(method);
			ILType *methodTypeArgs = ILMethodGetMethodTypeArguments(method);

			localVars = ILTypeInstantiate(ILImageToContext(ILProgramItem_Image(method)),
										  localVars,
										  classTypeArgs, 
										  methodTypeArgs);
		}
	}
	else
	{
		localVars = 0;
		numLocals = 0;
	}

	/* Set up the coder to process the method */
	if(!ILCoderSetup(coder, start, method, code, &coderExceptions, hasRethrow))
	{
		VERIFY_MEMORY_ERROR();
	}

	if((coderFlags & IL_CODER_FLAG_METHOD_PROFILE) != 0)
	{
		ILCoderProfileStart(coder);
	}

	/* Verify the code */
	pc = code->code;
	len = code->codeLen;
	lastWasJump = 0;

	/* If the method is synchronized then generate the Monitor.Enter call */
	if (isSynchronized)
	{
		PUSH_SYNC_OBJECT();
		ILCoderCallInlineable(coder, IL_INLINEMETHOD_MONITOR_ENTER, 0, 0);
	}

	lastInsnWasPrefix = 0;
	while(len > 0)
	{
		/* Fetch the instruction information block */
		offset = (ILUInt32)(pc - (unsigned char *)(code->code));
		opcode = pc[0];
		if(opcode != IL_OP_PREFIX)
		{
			insn = &(MAIN_OPCODE_TABLE[opcode]);
		}
		else
		{
			opcode = pc[1];
			insn = &(PREFIX_OPCODE_TABLE[opcode]);
			opcode += IL_OP_PREFIX;
		}
		insnSize = (ILUInt32)(insn->size);

		/* Is this a jump target? */
		if(IsJumpTarget(jumpMask, offset))
		{
			/* Validate the stack information */
			VALIDATE_TARGET_STACK(offset);

			/* Notify the coder of a label at this position */
		#ifdef IL_USE_JIT
			ILCoderStackRefresh(coder, stack, stackSize);
			ILCoderLabel(coder, offset);
		#else
			ILCoderLabel(coder, offset);
			ILCoderStackRefresh(coder, stack, stackSize);
		#endif
		}
		else if(lastWasJump)
		{
			/* An instruction just after an opcode that jumps to
			   somewhere else in the flow of control.  As this
			   isn't a jump target, we assume that the stack
			   must be empty at this point.  The validate code
			   will ensure that this is checked */
			VALIDATE_TARGET_STACK(offset);

			/* Reset the coder's notion of the stack to empty */
			ILCoderStackRefresh(coder, stack, stackSize);
		}

		/* Mark this offset if the method has debug information */
		if(haveDebug)
		{
			ILCoderMarkBytecode(coder, offset);
		}

		/* Validate the stack height changes */
		if(stackSize < ((ILUInt32)(insn->popped)) ||
		   (stackSize - ((ILUInt32)(insn->popped)) + ((ILUInt32)(insn->pushed)))
				> code->maxStack)
		{
			VERIFY_STACK_ERROR();
		}

		/*
		 * Check if all prefix flags are zero, otherwise an invalid prefix
		 * was used for the last instruction.
		 */
		if(!lastInsnWasPrefix)
		{
			if((prefixInfo.prefixFlags != 0) || (prefixInfo.noFlags != 0))
			{
				VERIFY_PREFIX_ERROR();
			}
		}

		/* Verify the instruction */
		lastWasJump = 0;
		lastInsnWasPrefix = 0;
		switch(opcode)
		{
			case IL_OP_NOP:   break;

			/* IL breakpoints are ignored - the coder inserts its
			   own breakpoint handlers where required */
			case IL_OP_BREAK: break;

#define	IL_VERIFY_CODE
#include "verify_var.c"
#include "verify_const.c"
#include "verify_arith.c"
#include "verify_conv.c"
#include "verify_stack.c"
#include "verify_ptr.c"
#include "verify_obj.c"
#include "verify_branch.c"
#include "verify_call.c"
#include "verify_except.c"
#include "verify_ann.c"
#undef IL_VERIFY_CODE

		}

		/* Advance to the next instruction */
		pc += insnSize;
		len -= insnSize;
	}

	/* If the last instruction was not a jump, then the code
	   may fall off the end of the method */
	if(!lastWasJump)
	{
		VERIFY_INSN_ERROR();
	}

	/*
	 * Generate the code for the fault block for synchronization.
	 */
	if(isSynchronized)
	{
		coderException = _ILCoderFindExceptionBlock(&coderExceptions, code->codeLen);
		/*
		 * This check is for catching bugs.
		 */
		if(!coderException ||
		   ((coderException->flags & IL_CODER_HANDLER_TYPE_FINALLY) == 0))
		{
			VERIFY_BRANCH_ERROR();
		}
		/*
		 * Insert the start label for the fault handler.
		 */
		ILCoderLabel(coder, code->codeLen);

		/*
		 * Call the Monitor.Exit method.
		 */
		PUSH_SYNC_OBJECT();
		ILCoderCallInlineable(coder, IL_INLINEMETHOD_MONITOR_EXIT, 0, 0);
		/*
		 * Leave the fault block.
		 */
		ILCoderRetFromFinally(coder);
		/*
		 * Insert the end label for the fault handler.
		 */
		ILCoderLabel(coder, code->codeLen + 1);
	}
	
	/* Mark the end of the method */
	ILCoderMarkEnd(coder);

	/* Output the exception handler table, if necessary */
	if(coderExceptions.numBlocks > 0)
	{
		ILCoderOutputExceptionTable(coder, &coderExceptions);
	}

	/* Finish processing using the coder */
	result = ILCoderFinish(coder);

	/* Do we need to restart due to cache exhaustion in the coder? */
	if(result == IL_CODER_END_RESTART)
	{
		TempAllocatorDestroy(&allocator);
		/* Reinitialize the memory allocator that is used for temporary
		   allocation during bytecode verification */
		ILMemZero(allocator.buffer, sizeof(allocator.buffer));
		allocator.posn = 0;
		allocator.overflow = 0;

		/*
		 * Reset the userdata in the exception blocks.
		 */
		numHandlers = 0;
		while(numHandlers < coderExceptions.numBlocks)
		{
			coderException = &(coderExceptions.blocks[numHandlers]);
			coderException->userData = 0;
			coderException->ptrUserData = 0;
			++numHandlers;
		}
		
		goto restart;
	}
#ifdef IL_VERIFY_DEBUG
	else if(result == IL_CODER_END_TOO_BIG)
	{
		ILMethodCode code;
		ILMethodGetCode(method,&code);
		fprintf(stderr,
			"%s::%s - method is too big to be converted (%d%s bytes)\n",
							ILClass_Name(ILMethod_Owner(method)),
							ILMethod_Name(method),
							code.codeLen,
							(code.moreSections != 0) ? "+" : "");
	}
#endif
	result = (result == IL_CODER_END_OK);

	/* Clean up and exit */
cleanup:
	TempAllocatorDestroy(&allocator);
	if(exceptions)
	{
		ILMethodFreeExceptions(exceptions);
	}
	if(coderExceptions.blocks)
	{
		ILFree(coderExceptions.blocks);
	}

	return result;
}
Пример #2
0
/*
 * Output a table of exception matching directives.
 * Each table entry specifies a region of code for the
 * directive.  Whenever an exception occurs in this
 * region, the method will jump to the instructions
 * contained in the table entry.  These instructions
 * will typically call "finally" handlers, and then
 * attempt to match the exception against the rules.
 */
static void OutputExceptionTable(ILCoder *coder, ILMethod *method,
								 ILException *exceptions, int hasRethrow)
{
	ILUInt32 offset;
	ILUInt32 end;
	int isStatic;
	ILException *exception;
	ILClass *classInfo;
	
	/* Process all regions in the method */
	offset = 0;
	for(;;)
	{
		/* Find the end of the region that starts at "offset" */
		end = IL_MAX_UINT32;
		exception = exceptions;
		while(exception != 0)
		{
			if(offset < exception->tryOffset)
			{
				/* We are in the code before this exception region */
				if(end > exception->tryOffset)
				{
					end = exception->tryOffset;
				}
			}
			else if(offset >= exception->tryOffset &&
			        offset < (exception->tryOffset + exception->tryLength))
			{
				/* We are in code in the middle of this exception region */
				if(end > (exception->tryOffset + exception->tryLength))
				{
					end = exception->tryOffset + exception->tryLength;
				}
			}
			exception = exception->next;
		}
		if(end == IL_MAX_UINT32)
		{
			break;
		}

		/* Output the region information to the table */
		ILCoderTryHandlerStart(coder, offset, end);

		/* Output exception matching code for this region */
		exception = exceptions;
		while(exception != 0)
		{
			if(offset >= exception->tryOffset &&
			   offset < (exception->tryOffset + exception->tryLength))
			{
				if((exception->flags & (IL_META_EXCEPTION_FINALLY |
										IL_META_EXCEPTION_FAULT)) != 0)
				{
					/* Call a "finally" or "fault" clause */
					ILCoderFinally(coder, exception, exception->handlerOffset);					
				}
				else if((exception->flags & IL_META_EXCEPTION_FILTER) == 0)
				{
					/* Match against a "catch" clause */
					classInfo = ILProgramItemToClass
						((ILProgramItem *)ILImageTokenInfo
							(ILProgramItem_Image(method), exception->extraArg));

					ILCoderCatch(coder, exception, classInfo, hasRethrow);
				}
				else
				{
					/* TODO: handle a "filter" clause */
				}
			}
			exception = exception->next;
		}

		/* If execution falls off the end of the matching code,
		   then throw the exception to the calling method */
		ILCoderThrow(coder, 0);

		/* Mark the end of the handler */
		ILCoderTryHandlerEnd(coder);

		/* Advance to the next region within the code */
		offset = end;
	}

	/* If execution gets here, then there were no applicable catch blocks,
	   so we always throw the exception to the calling method */
	ILCoderTryHandlerStart(coder, 0, IL_MAX_UINT32);
	
	if (ILMethod_IsSynchronized(method))
	{
		/* Exit the sync lock before throwing to the calling method */
		isStatic = ILMethod_IsStatic(method);
		PUSH_SYNC_OBJECT();
		ILCoderCallInlineable(coder, IL_INLINEMETHOD_MONITOR_EXIT, 0);
	}

	ILCoderThrow(coder, 0);
	ILCoderTryHandlerEnd(coder);
}