core/vm/sp_vm_function.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 26 17:41:02 2007 +0000 (2007-08-26)
changeset 1369 bd9ccced1b1a
parent 1339 02e8f2c0f8af
child 1908 318e0a01aef9
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 <stdio.h>
    33 #include <string.h>
    34 #include "sp_vm_function.h"
    35 
    36 /********************
    37 * FUNCTION CALLING *
    38 ********************/
    39 
    40 void CFunction::Set(uint32_t code_addr, IPluginContext *plugin, funcid_t id)
    41 {
    42 	m_codeaddr = code_addr;
    43 	m_pContext = plugin;
    44 	m_curparam = 0;
    45 	m_errorstate = SP_ERROR_NONE;
    46 	m_Invalid = false;
    47 	m_pCtx = plugin ? plugin->GetContext() : NULL;
    48 	m_FnId = id;
    49 }
    50 
    51 bool CFunction::IsRunnable()
    52 {
    53 	return ((m_pCtx->flags & SPFLAG_PLUGIN_PAUSED) != SPFLAG_PLUGIN_PAUSED);
    54 }
    55 
    56 int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
    57 {
    58 	if (!IsRunnable())
    59 	{
    60 		return SP_ERROR_NOT_RUNNABLE;
    61 	}
    62 
    63 	while (num_params--)
    64 	{
    65 		m_pContext->PushCell(params[num_params]);
    66 	}
    67 
    68 	return m_pContext->Execute(m_codeaddr, result);
    69 }
    70 
    71 IPluginContext *CFunction::GetParentContext()
    72 {
    73 	return m_pContext;
    74 }
    75 
    76 CFunction::CFunction(uint32_t code_addr, IPluginContext *plugin, funcid_t id) : 
    77 	m_codeaddr(code_addr), m_pContext(plugin), m_curparam(0), 
    78 	m_errorstate(SP_ERROR_NONE), m_FnId(id)
    79 {
    80 	m_Invalid = false;
    81 	if (plugin)
    82 	{
    83 		m_pCtx = plugin->GetContext();
    84 	}
    85 }
    86 
    87 int CFunction::PushCell(cell_t cell)
    88 {
    89 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
    90 	{
    91 		return SetError(SP_ERROR_PARAMS_MAX);
    92 	}
    93 
    94 	m_info[m_curparam].marked = false;
    95 	m_params[m_curparam] = cell;
    96 	m_curparam++;
    97 
    98 	return SP_ERROR_NONE;
    99 }
   100 
   101 int CFunction::PushCellByRef(cell_t *cell, int flags)
   102 {
   103 	return PushArray(cell, 1, flags);
   104 }
   105 
   106 int CFunction::PushFloat(float number)
   107 {
   108 	cell_t val = *(cell_t *)&number;
   109 
   110 	return PushCell(val);
   111 }
   112 
   113 int CFunction::PushFloatByRef(float *number, int flags)
   114 {
   115 	return PushCellByRef((cell_t *)number, flags);
   116 }
   117 
   118 int CFunction::PushArray(cell_t *inarray, unsigned int cells, int copyback)
   119 {
   120 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   121 	{
   122 		return SetError(SP_ERROR_PARAMS_MAX);
   123 	}
   124 
   125 	ParamInfo *info = &m_info[m_curparam];
   126 
   127 	info->flags = inarray ? copyback : 0;
   128 	info->marked = true;
   129 	info->size = cells;
   130 	info->str.is_sz = false;
   131 	info->orig_addr = inarray;
   132 
   133 	m_curparam++;
   134 
   135 	return SP_ERROR_NONE;
   136 }
   137 
   138 int CFunction::PushString(const char *string)
   139 {
   140 	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
   141 }
   142 
   143 int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
   144 {
   145 	return _PushString(buffer, sz_flags, cp_flags, length);
   146 }
   147 
   148 int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
   149 {
   150 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   151 	{
   152 		return SetError(SP_ERROR_PARAMS_MAX);
   153 	}
   154 
   155 	ParamInfo *info = &m_info[m_curparam];
   156 
   157 	info->marked = true;
   158 	info->orig_addr = (cell_t *)string;
   159 	info->flags = cp_flags;
   160 	info->size = len;
   161 	info->str.sz_flags = sz_flags;
   162 	info->str.is_sz = true;
   163 
   164 	m_curparam++;
   165 
   166 	return SP_ERROR_NONE;
   167 }
   168 
   169 void CFunction::Cancel()
   170 {
   171 	if (!m_curparam)
   172 	{
   173 		return;
   174 	}
   175 
   176 	m_errorstate = SP_ERROR_NONE;
   177 	m_curparam = 0;
   178 }
   179 
   180 int CFunction::Execute(cell_t *result)
   181 {
   182 	int err = SP_ERROR_NONE;
   183 	if (!IsRunnable())
   184 	{
   185 		m_errorstate = SP_ERROR_NOT_RUNNABLE;
   186 	}
   187 	if (m_errorstate != SP_ERROR_NONE)
   188 	{
   189 		err = m_errorstate;
   190 		Cancel();
   191 		return err;
   192 	}
   193 
   194 	//This is for re-entrancy!
   195 	cell_t temp_params[SP_MAX_EXEC_PARAMS];
   196 	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
   197 	unsigned int numparams = m_curparam;
   198 	unsigned int i;
   199 	bool docopies = true;
   200 
   201 	if (numparams)
   202 	{
   203 		//Save the info locally, then reset it for re-entrant calls.
   204 		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
   205 	}
   206 	m_curparam = 0;
   207 
   208 	/* Browse the parameters and build arrays */
   209 	for (i=0; i<numparams; i++)
   210 	{
   211 		/* Is this marked as an array? */
   212 		if (temp_info[i].marked)
   213 		{
   214 			if (!temp_info[i].str.is_sz)
   215 			{
   216 				/* Allocate a normal/generic array */
   217 				if ((err=m_pContext->HeapAlloc(temp_info[i].size, 
   218 											   &(temp_info[i].local_addr),
   219 											   &(temp_info[i].phys_addr)))
   220 					!= SP_ERROR_NONE)
   221 				{
   222 					break;
   223 				}
   224 				if (temp_info[i].orig_addr)
   225 				{
   226 					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
   227 				}
   228 			} else {
   229 				/* Calculate cells required for the string */
   230 				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
   231 
   232 				/* Allocate the buffer */
   233 				if ((err=m_pContext->HeapAlloc(cells,
   234 												&(temp_info[i].local_addr),
   235 												&(temp_info[i].phys_addr)))
   236 					!= SP_ERROR_NONE)
   237 				{
   238 					break;
   239 				}
   240 				/* Copy original string if necessary */
   241 				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
   242 				{
   243 					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
   244 					{
   245 						if ((err=m_pContext->StringToLocalUTF8(temp_info[i].local_addr, 
   246 																temp_info[i].size, 
   247 																(const char *)temp_info[i].orig_addr,
   248 																NULL))
   249 							!= SP_ERROR_NONE)
   250 						{
   251 							break;
   252 						}
   253 					} else {
   254 						if ((err=m_pContext->StringToLocal(temp_info[i].local_addr,
   255 															temp_info[i].size,
   256 															(const char *)temp_info[i].orig_addr))
   257 							!= SP_ERROR_NONE)
   258 						{
   259 							break;
   260 						}
   261 					}
   262 				}
   263 			} /* End array/string calculation */
   264 			/* Update the pushed parameter with the byref local address */
   265 			temp_params[i] = temp_info[i].local_addr;
   266 		} else {
   267 			/* Just copy the value normally */
   268 			temp_params[i] = m_params[i];
   269 		}
   270 	}
   271 
   272 	/* Make the call if we can */
   273 	if (err == SP_ERROR_NONE)
   274 	{
   275 		if ((err = CallFunction(temp_params, numparams, result)) != SP_ERROR_NONE)
   276 		{
   277 			docopies = false;
   278 		}
   279 	} else {
   280 		docopies = false;
   281 	}
   282 
   283 	/* i should be equal to the last valid parameter + 1 */
   284 	while (i--)
   285 	{
   286 		if (!temp_info[i].marked)
   287 		{
   288 			continue;
   289 		}
   290 		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
   291 		{
   292 			if (temp_info[i].orig_addr)
   293 			{
   294 				if (temp_info[i].str.is_sz)
   295 				{
   296 					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
   297 				} else {
   298 					if (temp_info[i].size == 1)
   299 					{
   300 						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
   301 					} else {
   302 						memcpy(temp_info[i].orig_addr, 
   303 								temp_info[i].phys_addr, 
   304 								temp_info[i].size * sizeof(cell_t));
   305 					}
   306 				}
   307 			}
   308 		}
   309 		if ((err=m_pContext->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
   310 		{
   311 			return err;
   312 		}
   313 	}
   314 
   315 	return err;
   316 }
   317 
   318 funcid_t CFunction::GetFunctionID()
   319 {
   320 	return m_FnId;
   321 }