core/vm/sp_vm_engine.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 26 17:41:02 2007 +0000 (2007-08-26)
changeset 1369 bd9ccced1b1a
parent 1330 ab94316bb52b
child 1894 e6efc8ffa47a
permissions -rw-r--r--
fixed amb834 - command callbacks could potentially occur on invalid clients in very rare cases
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourcePawn
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <malloc.h>
    33 #include <string.h>
    34 #include <assert.h>
    35 #include "sp_file_headers.h"
    36 #include "sp_vm_types.h"
    37 #include "sp_vm_engine.h"
    38 #include "zlib/zlib.h"
    39 #include "sp_vm_basecontext.h"
    40 
    41 #if defined WIN32
    42  #define WIN32_LEAN_AND_MEAN
    43  #include <windows.h>
    44 #elif defined __GNUC__
    45  #include <sys/mman.h>
    46 #endif
    47 
    48 #define INVALID_CIP			0xFFFFFFFF
    49 
    50 using namespace SourcePawn;
    51 
    52 #define ERROR_MESSAGE_MAX		25
    53 static const char *g_ErrorMsgTable[] = 
    54 {
    55 	NULL,
    56 	"Unrecognizable file format",
    57 	"Decompressor was not found",
    58 	"Not enough space on the heap",
    59 	"Invalid parameter or parameter type",
    60 	"Invalid plugin address",
    61 	"Object or index not found",
    62 	"Invalid index or index not found",
    63 	"Not enough space on the stack",
    64 	"Debug section not found or debug not enabled",
    65 	"Invalid instruction",
    66 	"Invalid memory access",
    67 	"Stack went below stack boundary",
    68 	"Heap went below heap boundary",
    69 	"Divide by zero",
    70 	"Array index is out of bounds",
    71 	"Instruction contained invalid parameter",
    72 	"Stack memory leaked by native",
    73 	"Heap memory leaked by native",
    74 	"Dynamic array is too big",
    75 	"Tracker stack is out of bounds",
    76 	"Native is not bound",
    77 	"Maximum number of parameters reached",
    78 	"Native detected error",
    79 	"Plugin not runnable",
    80 	"Call was aborted",
    81 };
    82 
    83 const char *GetSourcePawnErrorMessage(int error)
    84 {
    85 	if (error < 1 || error > ERROR_MESSAGE_MAX)
    86 	{
    87 		return NULL;
    88 	}
    89 
    90 	return g_ErrorMsgTable[error];
    91 }
    92 
    93 SourcePawnEngine::SourcePawnEngine()
    94 {
    95 	m_pDebugHook = NULL;
    96 	m_CallStack = NULL;
    97 	m_FreedCalls = NULL;
    98 	m_CurChain = 0;
    99 #if 0
   100 	m_pFreeFuncs = NULL;
   101 #endif
   102 }
   103 
   104 SourcePawnEngine::~SourcePawnEngine()
   105 {
   106 	TracedCall *pTemp;
   107 	while (m_FreedCalls)
   108 	{
   109 		pTemp = m_FreedCalls->next;
   110 		delete m_FreedCalls;
   111 		m_FreedCalls = pTemp;
   112 	}
   113 
   114 #if 0
   115 	CFunction *pNext;
   116 	while (m_pFreeFuncs)
   117 	{
   118 		pNext = m_pFreeFuncs->m_pNext;
   119 		delete m_pFreeFuncs;
   120 		m_pFreeFuncs = pNext;
   121 	}
   122 #endif
   123 }
   124 
   125 void *SourcePawnEngine::ExecAlloc(size_t size)
   126 {
   127 #if defined WIN32
   128 	return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
   129 #elif defined __GNUC__
   130 	void *base = memalign(sysconf(_SC_PAGESIZE), size);
   131 	if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
   132 	{
   133 		free(base);
   134 		return NULL;
   135 	}
   136 	return base;
   137 #endif
   138 }
   139 
   140 void SourcePawnEngine::ExecFree(void *address)
   141 {
   142 #if defined WIN32
   143 	VirtualFree(address, 0, MEM_RELEASE);
   144 #elif defined __GNUC__
   145 	free(address);
   146 #endif
   147 }
   148 
   149 void *SourcePawnEngine::BaseAlloc(size_t size)
   150 {
   151 	return malloc(size);
   152 }
   153 
   154 void SourcePawnEngine::BaseFree(void *memory)
   155 {
   156 	free(memory);
   157 }
   158 
   159 IPluginContext *SourcePawnEngine::CreateBaseContext(sp_context_t *ctx)
   160 {
   161 	return new BaseContext(ctx);
   162 }
   163 
   164 void SourcePawnEngine::FreeBaseContext(IPluginContext *ctx)
   165 {
   166 	delete ctx;
   167 }
   168 
   169 sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err)
   170 {
   171 	char *nameptr;
   172 	uint8_t sectnum = 0;
   173 	sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
   174 
   175 	memset(plugin, 0, sizeof(sp_plugin_t));
   176 
   177 	plugin->base = base;
   178 
   179 	while (sectnum < hdr->sections)
   180 	{
   181 		nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
   182 
   183 		if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
   184 		{
   185 			sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
   186 			plugin->pcode = base + secptr->dataoffs + cod->code;
   187 			plugin->pcode_size = cod->codesize;
   188 			plugin->flags = cod->flags;
   189 		}
   190 		else if (!(plugin->data) && !strcmp(nameptr, ".data"))
   191 		{
   192 			sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
   193 			plugin->data = base + secptr->dataoffs + dat->data;
   194 			plugin->data_size = dat->datasize;
   195 			plugin->memory = dat->memsize;
   196 		}
   197 		else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics"))
   198 		{
   199 			plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t);
   200 			plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs);
   201 		}
   202 		else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars"))
   203 		{
   204 			plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t);
   205 			plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
   206 		}
   207 		else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives"))
   208 		{
   209 			plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t);
   210 			plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs);
   211 		}
   212 		else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names"))
   213 		{
   214 			plugin->info.stringbase = (const char *)(base + secptr->dataoffs);
   215 		}
   216 		else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
   217 		{
   218 			plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
   219 		}
   220 		else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
   221 		{
   222 			plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
   223 		}
   224 		else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
   225 		{
   226 			plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
   227 		}
   228 		else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
   229 		{
   230 			sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
   231 			plugin->debug.files_num = inf->num_files;
   232 			plugin->debug.lines_num = inf->num_lines;
   233 			plugin->debug.syms_num = inf->num_syms;
   234 		}
   235 		else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
   236 		{
   237 			plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
   238 		}
   239 
   240 		secptr++;
   241 		sectnum++;
   242 	}
   243 
   244 	if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase))
   245 	{
   246 		goto return_error;
   247 	}
   248 
   249 	if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols)))
   250 	{
   251 		goto return_error;
   252 	}
   253 
   254 	if (err)
   255 	{
   256 		*err = SP_ERROR_NONE;
   257 	}
   258 
   259 	return plugin;
   260 
   261 return_error:
   262 	if (err)
   263 	{
   264 		*err = SP_ERROR_FILE_FORMAT;
   265 	}
   266 
   267 	return NULL;
   268 }
   269 
   270 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
   271 {
   272 	sp_file_hdr_t hdr;
   273 	sp_plugin_t *plugin;
   274 	uint8_t *base;
   275 	int z_result;
   276 	int error;
   277 
   278 	if (!fp)
   279 	{
   280 		error = SP_ERROR_NOT_FOUND;
   281 		goto return_error;
   282 	}
   283 
   284 	/* Rewind for safety */
   285 	rewind(fp);
   286 	fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
   287 
   288 	if (hdr.magic != SPFILE_MAGIC)
   289 	{
   290 		error = SP_ERROR_FILE_FORMAT;
   291 		goto return_error;
   292 	}
   293 
   294 	switch (hdr.compression)
   295 	{
   296 	case SPFILE_COMPRESSION_GZ:
   297 		{
   298 			uint32_t uncompsize = hdr.imagesize - hdr.dataoffs;
   299 			uint32_t compsize = hdr.disksize - hdr.dataoffs;
   300 			uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t);
   301 			uLongf destlen = uncompsize;
   302 
   303 			char *tempbuf = (char *)malloc(compsize);
   304 			void *uncompdata = malloc(uncompsize);
   305 			void *sectheader = malloc(sectsize);
   306 
   307 			fread(sectheader, sectsize, 1, fp);
   308 			fread(tempbuf, compsize, 1, fp);
   309 
   310 			z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
   311 			free(tempbuf);
   312 			if (z_result != Z_OK)
   313 			{
   314 				free(sectheader);
   315 				free(uncompdata);
   316 				error = SP_ERROR_DECOMPRESSOR;
   317 				goto return_error;
   318 			}
   319 
   320 			base = (uint8_t *)malloc(hdr.imagesize);
   321 			memcpy(base, &hdr, sizeof(sp_file_hdr_t));
   322 			memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize);
   323 			free(sectheader);
   324 			memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
   325 			free(uncompdata);
   326 			break;
   327 		}
   328 	case SPFILE_COMPRESSION_NONE:
   329 		{
   330 			base = (uint8_t *)malloc(hdr.imagesize);
   331 			rewind(fp);
   332 			fread(base, hdr.imagesize, 1, fp);
   333 			break;
   334 		}
   335 	default:
   336 		{
   337 			error = SP_ERROR_DECOMPRESSOR;
   338 			goto return_error;
   339 		}
   340 	}
   341 
   342 	plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
   343 	if (!_ReadPlugin(&hdr, base, plugin, err))
   344 	{
   345 		free(plugin);
   346 		free(base);
   347 		return NULL;
   348 	}
   349 
   350 	plugin->allocflags = 0;
   351 
   352 	return plugin;
   353 
   354 return_error:
   355 	if (err)
   356 	{
   357 		*err = error;
   358 	}
   359 	return NULL;
   360 }
   361 
   362 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
   363 {
   364 	sp_file_hdr_t hdr;
   365 	uint8_t noptr = 0;
   366 
   367 	memcpy(&hdr, base, sizeof(sp_file_hdr_t));
   368 
   369 	if (!plugin)
   370 	{
   371 		plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
   372 		noptr = 1;
   373 	}
   374 
   375 	if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err))
   376 	{
   377 		if (noptr)
   378 		{
   379 			free(plugin);
   380 		}
   381 		return NULL;
   382 	}
   383 
   384 	if (!noptr)
   385 	{
   386 		plugin->allocflags |= SP_FA_SELF_EXTERNAL;
   387 	}
   388 	plugin->allocflags |= SP_FA_BASE_EXTERNAL;
   389 
   390 	return plugin;
   391 }
   392 
   393 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
   394 {
   395 	if (!(plugin->allocflags & SP_FA_BASE_EXTERNAL))
   396 	{
   397 		free(plugin->base);
   398 		plugin->base = NULL;
   399 	}
   400 	if (!(plugin->allocflags & SP_FA_SELF_EXTERNAL))
   401 	{
   402 		free(plugin);
   403 	}
   404 
   405 	return SP_ERROR_NONE;
   406 }
   407 
   408 #if 0
   409 void SourcePawnEngine::ReleaseFunctionToPool(CFunction *func)
   410 {
   411 	if (!func)
   412 	{
   413 		return;
   414 	}
   415 	func->Cancel();
   416 	func->m_pNext = m_pFreeFuncs;
   417 	m_pFreeFuncs = func;
   418 }
   419 
   420 CFunction *SourcePawnEngine::GetFunctionFromPool(funcid_t f, IPluginContext *plugin)
   421 {
   422 	if (!m_pFreeFuncs)
   423 	{
   424 		return new CFunction(f, plugin);
   425 	} else {
   426 		CFunction *pFunc = m_pFreeFuncs;
   427 		m_pFreeFuncs = m_pFreeFuncs->m_pNext;
   428 		pFunc->Set(f, plugin);
   429 		return pFunc;
   430 	}
   431 }
   432 #endif
   433 
   434 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
   435 {
   436 	IDebugListener *old = m_pDebugHook;
   437 
   438 	m_pDebugHook = pListener;
   439 
   440 	return old;
   441 }
   442 
   443 unsigned int SourcePawnEngine::GetContextCallCount()
   444 {
   445 	if (!m_CallStack)
   446 	{
   447 		return 0;
   448 	}
   449 
   450 	return m_CallStack->chain;
   451 }
   452 
   453 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
   454 {
   455 	TracedCall *pCall;
   456 
   457 	if (!m_FreedCalls)
   458 	{
   459 		pCall = new TracedCall;
   460 	} else {
   461 		/* Unlink the head node from the free list */
   462 		pCall = m_FreedCalls;
   463 		m_FreedCalls = m_FreedCalls->next;
   464 	}
   465 
   466 	/* Link as the head node into the call stack */
   467 	pCall->next = m_CallStack;
   468 
   469 	if (new_chain)
   470 	{
   471 		pCall->chain = ++m_CurChain;
   472 	} else {
   473 		pCall->chain = m_CurChain;
   474 	}
   475 
   476 	m_CallStack = pCall;
   477 
   478 	return pCall;
   479 }
   480 
   481 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
   482 {
   483 	/* Check if this is the top of the call stack */
   484 	if (pCall == m_CallStack)
   485 	{
   486 		m_CallStack = m_CallStack->next;
   487 	}
   488 
   489 	/* Add this to our linked list of freed calls */
   490 	if (!m_FreedCalls)
   491 	{
   492 		m_FreedCalls = pCall;
   493 		m_FreedCalls->next = NULL;
   494 	} else {
   495 		pCall->next = m_FreedCalls;
   496 		m_FreedCalls = pCall;
   497 	}
   498 }
   499 
   500 void SourcePawnEngine::PushTracer(sp_context_t *ctx)
   501 {
   502 	TracedCall *pCall = MakeTracedCall(true);
   503 
   504 	pCall->cip = INVALID_CIP;
   505 	pCall->ctx = ctx;
   506 	pCall->frm = INVALID_CIP;
   507 }
   508 
   509 void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
   510 {
   511 	assert(m_CallStack != NULL);
   512 	assert(m_CallStack->ctx == ctx);
   513 	assert(m_CallStack->chain == m_CurChain);
   514 
   515 	if (m_CallStack->cip == INVALID_CIP)
   516 	{
   517 		/* We aren't logging anything yet, so begin the trace */
   518 		m_CallStack->cip = codeip;
   519 		m_CallStack->frm = frame;
   520 	} else {
   521 		if (m_CallStack->frm > frame)
   522 		{
   523 			/* The last frame has moved down the stack, 
   524 			 * so we have to push a new call onto our list.
   525 			 */
   526 			TracedCall *pCall = MakeTracedCall(false);
   527 			pCall->ctx = ctx;
   528 			pCall->frm = frame;
   529 		} else if (m_CallStack->frm < frame) {
   530 			/* The last frame has moved up the stack,
   531 			 * so we have to pop the call from our list.
   532 			 */
   533 			FreeTracedCall(m_CallStack);
   534 		}
   535 		/* no matter where we are, update the cip */
   536 		m_CallStack->cip = codeip;
   537 	}
   538 }
   539 
   540 void SourcePawnEngine::PopTracer(int error, const char *msg)
   541 {
   542 	assert(m_CallStack != NULL);
   543 
   544 	if (error != SP_ERROR_NONE && m_pDebugHook)
   545 	{
   546 		uint32_t native = INVALID_CIP;
   547 
   548 		if (m_CallStack->ctx->n_err)
   549 		{
   550 			native = m_CallStack->ctx->n_idx;
   551 		}
   552 
   553 		CContextTrace trace(m_CallStack, error, msg, native);
   554 		m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
   555 	}
   556 
   557 	/* Now pop the error chain  */
   558 	while (m_CallStack && m_CallStack->chain == m_CurChain)
   559 	{
   560 		FreeTracedCall(m_CallStack);
   561 	}
   562 
   563 	m_CurChain--;
   564 }
   565 
   566 unsigned int SourcePawnEngine::GetEngineAPIVersion()
   567 {
   568 	return SOURCEPAWN_ENGINE_API_VERSION;
   569 }
   570 
   571 CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) : 
   572  m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native)
   573 {
   574 }
   575 
   576 bool CContextTrace::DebugInfoAvailable()
   577 {
   578 	return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
   579 }
   580 
   581 const char *CContextTrace::GetCustomErrorString()
   582 {
   583 	return m_pMsg;
   584 }
   585 
   586 int CContextTrace::GetErrorCode()
   587 {
   588 	return m_Error;
   589 }
   590 
   591 const char *CContextTrace::GetErrorString()
   592 {
   593 	if (m_Error > ERROR_MESSAGE_MAX || 
   594 		m_Error < 1)
   595 	{
   596 		return "Invalid error code";
   597 	} else {
   598 		return g_ErrorMsgTable[m_Error];
   599 	}
   600 }
   601 
   602 void CContextTrace::ResetTrace()
   603 {
   604 	m_pIterator = m_pStart;
   605 }
   606 
   607 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
   608 {
   609 	if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
   610 	{
   611 		return false;
   612 	}
   613 
   614 	if (m_pIterator->cip == INVALID_CIP)
   615 	{
   616 		return false;
   617 	}
   618 
   619 	IPluginContext *pContext = m_pIterator->ctx->context;
   620 	IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
   621 
   622 	if (!pInfo)
   623 	{
   624 		return false;
   625 	}
   626 
   627 	if (!trace)
   628 	{
   629 		m_pIterator = m_pIterator->next;
   630 		return true;
   631 	}
   632 
   633 	if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
   634 	{
   635 		trace->filename = NULL;
   636 	}
   637 
   638 	if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
   639 	{
   640 		trace->function = NULL;
   641 	}
   642 
   643 	if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
   644 	{
   645 		trace->line = 0;
   646 	}
   647 
   648 	m_pIterator = m_pIterator->next;
   649 
   650 	return true;
   651 }
   652 
   653 const char *CContextTrace::GetLastNative(uint32_t *index)
   654 {
   655 	if (m_Native == INVALID_CIP)
   656 	{
   657 		return NULL;
   658 	}
   659 
   660 	sp_native_t *native;
   661 	if (m_pIterator->ctx->context->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)
   662 	{
   663 		return NULL;
   664 	}
   665 
   666 	if (index)
   667 	{
   668 		*index = m_Native;
   669 	}
   670 
   671 	return native->name;
   672 }