From 70e6a300b291f53ec6bbb5b8566ac994a3605c14 Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Tue, 17 May 2022 13:37:06 -0600 Subject: [PATCH] Fix a memory leak in libGLdispatch In __glDispatchMakeCurrent, it allocates a __GLdispatchThreadStatePrivate struct, which would normally get freed in __glDispatchLoseCurrent. However, if the library gets unloaded while another thread still has a current context, then __glDispatchLoseCurrent never gets called, and so that memory leaks. Add the __GLdispatchThreadStatePrivate to a linked list so that it can free any remaining structs in __glDispatchFini. Fixes https://gitlab.freedesktop.org/glvnd/libglvnd/-/issues/230. --- src/GLdispatch/GLdispatch.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/GLdispatch/GLdispatch.c b/src/GLdispatch/GLdispatch.c index 8e4bfdc..9bf7b2a 100644 --- a/src/GLdispatch/GLdispatch.c +++ b/src/GLdispatch/GLdispatch.c @@ -42,6 +42,13 @@ */ static struct glvnd_list currentDispatchList; +/** + * A linked list of __GLdispatchThreadStatePrivate structs that we've + * allocated. This is used to make sure that we clean up if we get unloaded + * while a thread is still current. + */ +static struct glvnd_list threadStatePrivateList; + /* * Number of clients using GLdispatch. */ @@ -64,6 +71,8 @@ typedef struct __GLdispatchThreadStatePrivateRec { /// The current (high-level) __GLdispatch table __GLdispatchTable *dispatch; + + struct glvnd_list entry; } __GLdispatchThreadStatePrivate; /* @@ -178,6 +187,7 @@ void __glDispatchInit(void) glvnd_list_init(&extProcList); glvnd_list_init(¤tDispatchList); glvnd_list_init(&dispatchStubList); + glvnd_list_init(&threadStatePrivateList); // Register GLdispatch's static entrypoints for rewriting localDispatchStubId = RegisterStubCallbacks(stub_get_patch_callbacks()); @@ -588,8 +598,7 @@ PUBLIC GLboolean __glDispatchMakeCurrent(__GLdispatchThreadState *threadState, DispatchCurrentRef(dispatch); numCurrentContexts++; - - UnlockDispatch(); + glvnd_list_add(&priv->entry, &threadStatePrivateList); /* * Update the API state with the new values. @@ -599,6 +608,8 @@ PUBLIC GLboolean __glDispatchMakeCurrent(__GLdispatchThreadState *threadState, priv->threadState = threadState; threadState->priv = priv; + UnlockDispatch(); + /* * Set the current state in TLS. */ @@ -624,6 +635,7 @@ static void LoseCurrentInternal(__GLdispatchThreadState *curThreadState, DispatchCurrentUnref(curThreadState->priv->dispatch); } + glvnd_list_del(&curThreadState->priv->entry); free(curThreadState->priv); curThreadState->priv = NULL; } @@ -722,6 +734,14 @@ void __glDispatchFini(void) clientRefcount--; if (clientRefcount == 0) { + while (!glvnd_list_is_empty(&threadStatePrivateList)) + { + __GLdispatchThreadStatePrivate *priv = glvnd_list_first_entry(&threadStatePrivateList, + __GLdispatchThreadStatePrivate, entry); + glvnd_list_del(&priv->entry); + free(priv); + } + /* This frees the dispatchStubList */ UnregisterAllStubCallbacks();