[channels,rdpdr] fix possible surface leak

* Ensure DeleteSurface is called before CreateSurface: Under certain
  conditions it is possible that there already exists a surface with the
  same id as the new CreateSurface PDU. Delete the already existing
  instance in that case before creating a new one.
* Simplify surface cleanup on shutdown: Use HashTable_Foreach
This commit is contained in:
akallabeth 2022-12-15 12:57:13 +01:00 committed by Martin Fleisz
parent df4c076946
commit a2329281a4

View File

@ -45,22 +45,18 @@
#define TAG CHANNELS_TAG("rdpgfx.client")
static void free_surfaces(RdpgfxClientContext* context, wHashTable* SurfaceTable)
{
UINT error = 0;
ULONG_PTR* pKeys = NULL;
size_t count;
size_t index;
count = HashTable_GetKeys(SurfaceTable, &pKeys);
for (index = 0; index < count; index++)
static BOOL delete_surface(const void* key, void* value, void* arg)
{
const UINT16 id = (UINT16)(key);
RdpgfxClientContext* context = arg;
RDPGFX_DELETE_SURFACE_PDU pdu = { 0 };
pdu.surfaceId = ((UINT16)pKeys[index]) - 1;
WINPR_UNUSED(value);
pdu.surfaceId = id - 1;
if (context)
{
UINT error = CHANNEL_RC_OK;
IFCALLRET(context->DeleteSurface, error, context, &pdu);
if (error)
@ -68,9 +64,12 @@ static void free_surfaces(RdpgfxClientContext* context, wHashTable* SurfaceTable
WLog_ERR(TAG, "context->DeleteSurface failed with error %" PRIu32 "", error);
}
}
return TRUE;
}
free(pKeys);
static void free_surfaces(RdpgfxClientContext* context, wHashTable* SurfaceTable)
{
HashTable_Foreach(SurfaceTable, delete_surface, context);
}
static void evict_cache_slots(RdpgfxClientContext* context, UINT16 MaxCacheSlots, void** CacheSlots)
@ -1065,6 +1064,13 @@ static UINT rdpgfx_recv_create_surface_pdu(GENERIC_CHANNEL_CALLBACK* callback, w
if (context)
{
/* create surface PDU sometimes happens for surface ID that are already in use and have not
* been removed yet. Ensure that there is no surface with the new ID by trying to remove it
* manually.
*/
RDPGFX_DELETE_SURFACE_PDU deletePdu = { pdu.surfaceId };
IFCALL(context->DeleteSurface, context, &deletePdu);
IFCALLRET(context->CreateSurface, error, context, &pdu);
if (error)