SDL: Fix #9279: Memory leak in pen name handling

From 615c7efd98e010da16f156300e70a57f324e0ed5 Mon Sep 17 00:00:00 2001
From: Christoph Reichenbach <[EMAIL REDACTED]>
Date: Mon, 18 Mar 2024 08:40:23 +0000
Subject: [PATCH] Fix #9279: Memory leak in pen name handling

- Fixes a leak in pen name allocation that would trigger
  for both X11 and Wayland for some non-pen input devices
  when new devices are added/removed.
- SDL_PenQuit() now deallocates and resets the table of known pens
- testautomation_pen.c now uses PenInit and PenQuit as setup and
  teardown, respectively

testautomation_pen.c was already triggering the leak, and it is
visible with --trackmem, so no new tests are added.
---
 src/events/SDL_pen.c      | 14 +++++++++++++-
 test/testautomation_pen.c | 22 +++++++++++++++++-----
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/src/events/SDL_pen.c b/src/events/SDL_pen.c
index e2eb4e10cf973..d0b02a56310fd 100644
--- a/src/events/SDL_pen.c
+++ b/src/events/SDL_pen.c
@@ -269,7 +269,7 @@ static void pen_sort(void)
               pen_compare);
     pen_handler.sorted = SDL_TRUE;
 }
-
+#include<stdio.h>
 SDL_Pen *SDL_PenModifyBegin(Uint32 instance_id)
 {
     SDL_PenID id = { 0 };
@@ -357,6 +357,7 @@ void SDL_PenModifyEnd(SDL_Pen *pen, SDL_bool attach)
             attach = SDL_FALSE;
         } else {
             pen_handler.pens_known -= 1;
+            SDL_free(pen->name);
             SDL_memset(pen, 0, sizeof(SDL_Pen));
             SDL_UNLOCK_PENS();
             return;
@@ -828,6 +829,8 @@ void SDL_PenInit(void)
 
 void SDL_PenQuit(void)
 {
+    unsigned int i;
+
     SDL_DelHintCallback(SDL_HINT_PEN_NOT_MOUSE,
                         SDL_PenUpdateHint, &pen_mouse_emulation_mode);
 
@@ -837,6 +840,15 @@ void SDL_PenQuit(void)
     SDL_DestroyMutex(SDL_pen_access_lock);
     SDL_pen_access_lock = NULL;
 #endif
+
+    if (pen_handler.pens) {
+        for (i = 0; i < pen_handler.pens_known; ++i) {
+            SDL_free(pen_handler.pens[i].name);
+        }
+        SDL_free(pen_handler.pens);
+        /* Reset static pen information */
+        SDL_memset(&pen_handler, 0, sizeof(pen_handler));
+    }
 }
 
 SDL_bool SDL_PenPerformHitTest(void)
diff --git a/test/testautomation_pen.c b/test/testautomation_pen.c
index 7bbc5aa370ac0..445a298dff3ca 100644
--- a/test/testautomation_pen.c
+++ b/test/testautomation_pen.c
@@ -1872,9 +1872,21 @@ pen_memoryLayout(void *arg)
     return TEST_COMPLETED;
 }
 
+/* ================= Test Setup and Teardown ================== */
+
+static void
+pen_test_setup(void *arg) {
+    SDL_PenInit();
+}
+
+static void
+pen_test_teardown(void *arg) {
+    SDL_PenQuit();
+}
+
 /* ================= Test References ================== */
 
-/* Mouse test cases */
+/* Pen test cases */
 static const SDLTest_TestCaseReference penTest1 = { (SDLTest_TestCaseFp)pen_iteration, "pen_iteration", "Iterate over all pens with SDL_PenIDForIndex", TEST_ENABLED };
 
 static const SDLTest_TestCaseReference penTest2 = { (SDLTest_TestCaseFp)pen_hotplugging, "pen_hotplugging", "Hotplug pens and validate their status, including SDL_PenConnected", TEST_ENABLED };
@@ -1893,17 +1905,17 @@ static const SDLTest_TestCaseReference penTest8 = { (SDLTest_TestCaseFp)pen_mous
 
 static const SDLTest_TestCaseReference penTest9 = { (SDLTest_TestCaseFp)pen_memoryLayout, "pen_memoryLayout", "Check that all pen events have compatible layout (required by SDL_pen.c)", TEST_ENABLED };
 
-/* Sequence of Mouse test cases */
+/* Sequence of Pen test cases */
 static const SDLTest_TestCaseReference *penTests[] = {
     &penTest1, &penTest2, &penTest3, &penTest4, &penTest5, &penTest6, &penTest7, &penTest8, &penTest9, NULL
 };
 
-/* Mouse test suite (global) */
+/* Pen test suite (global) */
 SDLTest_TestSuiteReference penTestSuite = {
     "Pen",
-    NULL,
+    (SDLTest_TestCaseSetUpFp)pen_test_setup,
     penTests,
-    NULL
+    (SDLTest_TestCaseTearDownFp)pen_test_teardown
 };
 
 #else