From 352b15c1b7d6e1dbaa1efa1c7739beecaf0e29f7 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 17 Dec 2006 09:56:45 +0000 Subject: [PATCH] Added Handle cloning and reference counting in preparation for IShareSys Made the internal code a bit more flexible and improved access security structures --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40227 --- core/interfaces/IHandleSys.h | 50 +++++-- core/systems/HandleSys.cpp | 254 ++++++++++++++++++++++++++--------- core/systems/HandleSys.h | 39 ++++-- core/systems/PluginSys.cpp | 25 ++-- 4 files changed, 270 insertions(+), 98 deletions(-) diff --git a/core/interfaces/IHandleSys.h b/core/interfaces/IHandleSys.h index 2621ba6d4..08d0d71ea 100644 --- a/core/interfaces/IHandleSys.h +++ b/core/interfaces/IHandleSys.h @@ -36,29 +36,39 @@ namespace SourceMod HandleError_Freed, /* The handle has been freed */ HandleError_Index, /* generic internal indexing error */ HandleError_Access, /* No access permitted to free this handle */ + HandleError_Limit, /* The limited number of handles has been reached */ }; - struct HandleAccess + enum HandleAccessRight { - HandleAccess() : canRead(true), canDelete(true), canInherit(true), canCreate(true) - { - } - bool canCreate; /* Instances can be created by other objects (this makes it searchable) */ - bool canRead; /* Handles can be read by other objects */ - bool canDelete; /* Handles can be deleted by other objects */ - bool canInherit; /* Handle type can be inherited */ + HandleAccess_Create, /* TYPE: Instances can be created by other objects (this makes it searchable) */ + HandleAccess_Read, /* HANDLES: Can be read by other objects */ + HandleAccess_Delete, /* HANDLES: Can be deleted by other objects */ + HandleAccess_Inherit, /* TYPE: Can be inherited by new types */ + HandleAccess_Clone, /* HANDLES: Can be cloned */ + /* ------------- */ + HandleAccess_TOTAL, /* Total number of access rights */ }; struct HandleSecurity { - IdentityToken_t owner; /* Owner of the handle */ - HandleAccess all; /* Access permissions of everyone */ + HandleSecurity() + { + owner = 0; + access[HandleAccess_Create] = true; + access[HandleAccess_Read] = true; + access[HandleAccess_Delete] = true; + access[HandleAccess_Inherit] = true; + access[HandleAccess_Clone] = true; + } + IdentityToken_t owner; /* Owner of the handle */ + bool access[HandleAccess_TOTAL]; /* World access rights */ }; class IHandleTypeDispatch { public: - virtual unsigned int GetInterfaceVersion() + virtual unsigned int GetDispatchVersion() { return SMINTERFACE_HANDLESYSTEM_VERSION; } @@ -174,13 +184,27 @@ namespace SourceMod IdentityToken_t ident) =0; /** - * @brief Destroys a handle. + * @brief Frees the memory associated with a handle and calls any destructors. + * NOTE: This function will decrement the internal reference counter. It will + * only perform any further action if the counter hits 0. * * @param type Handle_t identifier to destroy. * @param ident Identity token, for destroying secure handles (0 for none). * @return A HandleError error code. */ - virtual HandleError DestroyHandle(Handle_t handle, IdentityToken_t ident) =0; + virtual HandleError FreeHandle(Handle_t handle, IdentityToken_t ident) =0; + + /** + * @brief Clones a handle by adding to its internal reference count. Its data, + * type, and security permissions remain the same. + * + * @param handle Handle to duplicate. Any non-free handle target is valid. + * @param newhandle If non-NULL, stores the duplicated handle in the pointer. + * @param source New source of cloned handle. + * @param ident Security token, if needed. + * @return A HandleError error code. + */ + virtual HandleError CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t source, IdentityToken_t ident) =0; /** * @brief Retrieves the contents of a handle. diff --git a/core/systems/HandleSys.cpp b/core/systems/HandleSys.cpp index 78c32af13..35ef4a62d 100644 --- a/core/systems/HandleSys.cpp +++ b/core/systems/HandleSys.cpp @@ -1,5 +1,6 @@ #include "HandleSys.h" #include "PluginSys.h" +#include HandleSystem g_HandleSys; @@ -56,7 +57,7 @@ HandleType_t HandleSystem::CreateTypeEx(const char *name, } if (parent >= HANDLESYS_TYPEARRAY_SIZE || m_Types[parent].dispatch != NULL - || m_Types[parent].sec.all.canInherit == false) + || m_Types[parent].sec.access[HandleAccess_Inherit] == false) { return 0; } @@ -68,12 +69,13 @@ HandleType_t HandleSystem::CreateTypeEx(const char *name, { security = &m_Types[parent].sec; } else { - static HandleSecurity def_h = {0, HandleAccess() }; + static HandleSecurity def_h; security = &def_h; } } - if (security->all.canCreate && sm_trie_retrieve(m_TypeLookup, name, NULL)) + if (security->access[HandleAccess_Create] + && sm_trie_retrieve(m_TypeLookup, name, NULL)) { return 0; } @@ -158,6 +160,54 @@ bool HandleSystem::FindHandleType(const char *name, HandleType_t *type) return true; } +HandleError HandleSystem::MakePrimHandle(HandleType_t type, + QHandle **in_pHandle, + unsigned int *in_index, + Handle_t *in_handle) +{ + unsigned int handle; + if (m_FreeHandles == 0) + { + if (m_HandleTail >= HANDLESYS_MAX_HANDLES) + { + return HandleError_Limit;; + } + handle = ++m_HandleTail; + } else { + handle = m_Handles[m_FreeHandles--].freeID; + } + + QHandle *pHandle = &m_Handles[handle]; + + assert(pHandle->set == false); + + if (++m_HSerial >= HANDLESYS_MAX_SERIALS) + { + m_HSerial = 1; + } + + /* Set essential information */ + pHandle->set = true; + pHandle->refcount = 1; + pHandle->type = type; + pHandle->serial = m_HSerial; + + /* Create the hash value */ + Handle_t hash = pHandle->serial; + hash <<= 16; + hash |= handle; + + /* Add a reference count to the type */ + m_Types[type].opened++; + + /* Output */ + *in_pHandle = pHandle; + *in_index = handle; + *in_handle = hash; + + return HandleError_None; +} + Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t source, IdentityToken_t ident) { if (!type @@ -169,44 +219,27 @@ Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityTok /* Check the security of this handle */ QHandleType *pType = &m_Types[type]; - if (!pType->sec.all.canCreate + if (!pType->sec.access[HandleAccess_Create] && pType->sec.owner != ident) { return 0; } - unsigned int handle = 0; - if (m_FreeHandles == 0) + unsigned int index; + Handle_t handle; + QHandle *pHandle; + HandleError err; + + if ((err=MakePrimHandle(type, &pHandle, &index, &handle)) != HandleError_None) { - if (m_HandleTail >= HANDLESYS_MAX_HANDLES) - { - return 0; - } - handle = ++m_HandleTail; - } else { - handle = m_Handles[m_FreeHandles--].freeID; + return 0; } - QHandle *pHandle = &m_Handles[handle]; - - pHandle->type = type; pHandle->source = source; - pHandle->set = true; pHandle->object = object; + pHandle->clone = 0; - if (++m_HSerial >= HANDLESYS_MAX_SERIALS) - { - m_HSerial = 1; - } - pHandle->serial = m_HSerial; - - Handle_t hash = pHandle->serial; - hash <<= 16; - hash |= handle; - - pType->opened++; - - return hash; + return handle; } Handle_t HandleSystem::CreateScriptHandle(HandleType_t type, @@ -219,7 +252,11 @@ Handle_t HandleSystem::CreateScriptHandle(HandleType_t type, return CreateHandle(type, object, pPlugin->GetIdentity(), ident); } -HandleError HandleSystem::DestroyHandle(Handle_t handle, IdentityToken_t ident) +HandleError HandleSystem::GetHandle(Handle_t handle, + IdentityToken_t ident, + QHandle **in_pHandle, + unsigned int *in_index, + HandleAccessRight access) { unsigned int serial = (handle >> 16); unsigned int index = (handle & HANDLESYS_HANDLE_MASK); @@ -232,8 +269,8 @@ HandleError HandleSystem::DestroyHandle(Handle_t handle, IdentityToken_t ident) QHandle *pHandle = &m_Handles[index]; QHandleType *pType = &m_Types[pHandle->type]; - if (!pType->sec.all.canDelete - && pType->sec.owner != ident) + if ((access != HandleAccess_TOTAL) + && (!pType->sec.access[access] && pType->sec.owner != ident)) { return HandleError_Access; } @@ -246,10 +283,80 @@ HandleError HandleSystem::DestroyHandle(Handle_t handle, IdentityToken_t ident) return HandleError_Changed; } - pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object); - pType->opened--; - pHandle->set = false; - m_Handles[++m_FreeTypes].freeID = index; + *in_pHandle = pHandle; + *in_index = index; + + return HandleError_None; +} + +HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *out_newhandle, IdentityToken_t source, IdentityToken_t ident) +{ + HandleError err; + QHandle *pHandle; + unsigned int index; + + if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Clone)) != HandleError_None) + { + return err; + } + + QHandleType *pType = &m_Types[pHandle->type]; + + /* Get a new Handle ID */ + unsigned int new_index; + QHandle *pNewHandle; + Handle_t new_handle; + + if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle)) != HandleError_None) + { + return err; + } + + pNewHandle->clone = index; + pNewHandle->source = source; + + if (out_newhandle) + { + *out_newhandle = new_handle; + } + + return HandleError_None; +} + +HandleError HandleSystem::FreeHandle(Handle_t handle, IdentityToken_t ident) +{ + unsigned int index; + QHandle *pHandle; + HandleError err; + + if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Delete)) != HandleError_None) + { + return err; + } + + QHandleType *pType = &m_Types[pHandle->type]; + + bool dofree = false; + if (pHandle->clone) + { + /* If we're a clone, there's not a lot to do. */ + if (FreeHandle(pHandle->clone, ident) != HandleError_None) + { + assert(false); + } + dofree = true; + } else { + if (--pHandle->refcount == 0) + { + dofree = true; + pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object); + } + } + + if (dofree) + { + ReleasePrimHandle(index); + } return HandleError_None; } @@ -264,24 +371,13 @@ HandleError HandleSystem::ReadHandle(Handle_t handle, IdentityToken_t ident, void **object) { - unsigned int serial = (handle >> 16); - unsigned int index = (handle & HANDLESYS_HANDLE_MASK); + unsigned int index; + QHandle *pHandle; + HandleError err; - if (index == 0 || index == 0xFFFF) + if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Read)) != HandleError_None) { - return HandleError_Index; - } - - QHandle *pHandle = &m_Handles[index]; - - /* Do sanity checks first */ - if (!pHandle->set) - { - return HandleError_Freed; - } - if (pHandle->serial != serial) - { - return HandleError_Changed; + return err; } /* Check the type inheritance */ @@ -299,22 +395,28 @@ HandleError HandleSystem::ReadHandle(Handle_t handle, } } - /* Now do security checks */ - QHandleType *pType = &m_Types[pHandle->type]; - if (!pType->sec.all.canRead - && pType->sec.owner != ident) - { - return HandleError_Access; - } - if (object) { + /* if we're a clone, the rules change - object is ONLY in our reference */ + if (pHandle->clone) + { + pHandle = &m_Handles[pHandle->clone]; + } *object = pHandle->object; } return HandleError_None; } +void HandleSystem::ReleasePrimHandle(unsigned int index) +{ + QHandle *pHandle = &m_Handles[index]; + + pHandle->set = false; + m_Types[pHandle->type].opened--; + m_Handles[++m_FreeHandles].freeID = index; +} + bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t ident) { if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE) @@ -358,17 +460,37 @@ bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t ident) if (pType->opened) { QHandle *pHandle; - for (unsigned int i=1; i<=HANDLESYS_MAX_HANDLES; i++) + for (unsigned int i=1; iset || pHandle->type != type) { continue; } - dispatch->OnHandleDestroy(type, pHandle->object); - pHandle->set = false; - m_Handles[++m_FreeHandles].freeID = i; - if (--pType->opened == 0) + if (pHandle->clone) + { + /* Get parent */ + QHandle *pOther = &m_Handles[pHandle->clone]; + if (--pOther->refcount == 0) + { + /* Free! */ + dispatch->OnHandleDestroy(type, pOther->object); + ReleasePrimHandle(pHandle->clone); + } + /* Unlink ourselves since we don't have a reference count */ + ReleasePrimHandle(i); + } else { + /* If it's not a clone, we still have to check the reference count. + * Either way, we'll be destroyed eventually because the handle types do not change. + */ + if (--pHandle->refcount == 0) + { + /* Free! */ + dispatch->OnHandleDestroy(type, pHandle->object); + ReleasePrimHandle(i); + } + } + if (pType->opened == 0) { break; } diff --git a/core/systems/HandleSys.h b/core/systems/HandleSys.h index 84a78b55b..755de11fa 100644 --- a/core/systems/HandleSys.h +++ b/core/systems/HandleSys.h @@ -7,7 +7,7 @@ #include "sourcemod.h" #include "sm_memtable.h" -#define HANDLESYS_MAX_HANDLES (1<<16) +#define HANDLESYS_MAX_HANDLES (1<<14) #define HANDLESYS_MAX_TYPES (1<<9) #define HANDLESYS_MAX_SUBTYPES 0xF #define HANDLESYS_SUBTYPE_MASK 0xF @@ -18,12 +18,14 @@ struct QHandle { - HandleType_t type; - void *object; - unsigned int freeID; - IdentityToken_t source; - bool set; - unsigned int serial; + HandleType_t type; /* Handle type */ + void *object; /* Unmaintained object pointer */ + unsigned int freeID; /* ID of a free handle in the free handle chain */ + IdentityToken_t source; /* Identity of object which owns this */ + unsigned int serial; /* Serial no. for sanity checking */ + unsigned int refcount; /* Reference count for safe destruction */ + Handle_t clone; /* If non-zero, this is our cloned parent */ + bool set; /* Whether or not this handle is set */ }; struct QHandleType @@ -56,8 +58,29 @@ public: //IHandleSystem IdentityToken_t source, IdentityToken_t ident); Handle_t CreateScriptHandle(HandleType_t type, void *object, sp_context_t *ctx, IdentityToken_t ident); - HandleError DestroyHandle(Handle_t handle, IdentityToken_t ident); + HandleError FreeHandle(Handle_t handle, IdentityToken_t ident); + HandleError CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t source, IdentityToken_t ident); HandleError ReadHandle(Handle_t handle, HandleType_t type, IdentityToken_t ident, void **object); +private: + /** + * Decodes a handle with sanity and security checking. + */ + HandleError GetHandle(Handle_t handle, + IdentityToken_t ident, + QHandle **pHandle, + unsigned int *index, + HandleAccessRight access); + + /** + * Creates a basic handle and sets its reference count to 1. + * Does not do any type or security checking. + */ + HandleError MakePrimHandle(HandleType_t type, QHandle **pHandle, unsigned int *index, HandleType_t *handle); + + /** + * Frees a primitive handle. Does no object freeing, only reference count and bookkeepping. + */ + void ReleasePrimHandle(unsigned int index); private: QHandle *m_Handles; QHandleType *m_Types; diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index f03afe533..ce10f0963 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -54,12 +54,6 @@ CPlugin::~CPlugin() m_pub_funcs = NULL; } - if (m_plugin) - { - g_pSourcePawn->FreeFromMemory(m_plugin); - m_plugin = NULL; - } - if (m_priv_funcs) { for (unsigned int i=0; iFreeFromMemory(m_plugin); + m_plugin = NULL; + } + + g_HandleSys.FreeHandle(m_handle, g_PluginType); } CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength) @@ -328,7 +330,7 @@ void CPlugin::Call_OnPluginInit() } /* :TODO: push our own handle */ - pFunction->PushCell(0); + pFunction->PushCell(m_handle); if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) { /* :TODO: log into debugger instead */ @@ -360,7 +362,7 @@ bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength) return true; } - pFunction->PushCell(0); //:TODO: handle to ourself + pFunction->PushCell(m_handle); pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0); pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK); pFunction->PushCell(maxlength); @@ -1093,9 +1095,10 @@ void CPluginManager::OnSourceModAllInitialized() HandleSecurity sec; sec.owner = 1; /* :TODO: implement ShareSys */ - sec.all.canCreate = false; - sec.all.canDelete = false; - sec.all.canInherit = false; + sec.access[HandleAccess_Create] = false; + sec.access[HandleAccess_Delete] = false; + sec.access[HandleAccess_Inherit] = false; + sec.access[HandleAccess_Clone] = false; g_PluginType = g_HandleSys.CreateTypeEx("IPlugin", this, 0, &sec); }