]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-123290: fix reference leaks in the error-branches of `_cursesmodule.c` (#123953)
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>
Wed, 11 Sep 2024 15:49:33 +0000 (17:49 +0200)
committerGitHub <noreply@github.com>
Wed, 11 Sep 2024 15:49:33 +0000 (15:49 +0000)
Modules/_cursesmodule.c

index b5854e8c33f28a9b7767961052b66182ffa79aed..1d36b40d5236b8f23c5c731535494ad448f31e14 100644 (file)
@@ -716,9 +716,13 @@ PyCursesWindow_New(WINDOW *win, const char *encoding)
 static void
 PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
 {
-    if (wo->win != stdscr) delwin(wo->win);
-    if (wo->encoding != NULL)
+    if (wo->win != stdscr && wo->win != NULL) {
+        // silently ignore errors in delwin(3)
+        (void)delwin(wo->win);
+    }
+    if (wo->encoding != NULL) {
         PyMem_Free(wo->encoding);
+    }
     PyObject_Free(wo);
 }
 
@@ -3266,7 +3270,6 @@ _curses_initscr_impl(PyObject *module)
 /*[clinic end generated code: output=619fb68443810b7b input=514f4bce1821f6b5]*/
 {
     WINDOW *win;
-    PyCursesWindowObject *winobj;
 
     if (initialised) {
         wrefresh(stdscr);
@@ -3284,12 +3287,17 @@ _curses_initscr_impl(PyObject *module)
 
 /* This was moved from initcurses() because it core dumped on SGI,
    where they're not defined until you've called initscr() */
-#define SetDictInt(string,ch)                                           \
-    do {                                                                \
-        PyObject *o = PyLong_FromLong((long) (ch));                     \
-        if (o && PyDict_SetItemString(ModDict, string, o) == 0)     {   \
-            Py_DECREF(o);                                               \
-        }                                                               \
+#define SetDictInt(NAME, VALUE)                                 \
+    do {                                                        \
+        PyObject *value = PyLong_FromLong((long)(VALUE));       \
+        if (value == NULL) {                                    \
+            return NULL;                                        \
+        }                                                       \
+        int rc = PyDict_SetItemString(ModDict, (NAME), value);  \
+        Py_DECREF(value);                                       \
+        if (rc < 0) {                                           \
+            return NULL;                                        \
+        }                                                       \
     } while (0)
 
     /* Here are some graphic symbols you can use */
@@ -3361,8 +3369,12 @@ _curses_initscr_impl(PyObject *module)
 
     SetDictInt("LINES", LINES);
     SetDictInt("COLS", COLS);
+#undef SetDictInt
 
-    winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL);
+    PyCursesWindowObject *winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL);
+    if (winobj == NULL) {
+        return NULL;
+    }
     screen_encoding = winobj->encoding;
     return (PyObject *)winobj;
 }
@@ -3970,46 +3982,47 @@ _curses_qiflush_impl(PyObject *module, int flag)
 static int
 update_lines_cols(void)
 {
-    PyObject *o;
-    PyObject *m = PyImport_ImportModule("curses");
+    PyObject *exposed_module = NULL, *o = NULL;
 
-    if (!m)
-        return 0;
+    exposed_module = PyImport_ImportModule("curses");
+    if (exposed_module == NULL) {
+        goto error;
+    }
+    PyObject *exposed_module_dict = PyModule_GetDict(exposed_module); // borrowed
+    if (exposed_module_dict == NULL) {
+        goto error;
+    }
 
     o = PyLong_FromLong(LINES);
-    if (!o) {
-        Py_DECREF(m);
-        return 0;
+    if (o == NULL) {
+        goto error;
     }
-    if (PyObject_SetAttrString(m, "LINES", o)) {
-        Py_DECREF(m);
-        Py_DECREF(o);
-        return 0;
+    if (PyDict_SetItemString(exposed_module_dict, "LINES", o) < 0) {
+        goto error;
     }
-    if (PyDict_SetItemString(ModDict, "LINES", o)) {
-        Py_DECREF(m);
-        Py_DECREF(o);
-        return 0;
+    if (PyDict_SetItemString(ModDict, "LINES", o) < 0) {
+        goto error;
     }
     Py_DECREF(o);
+
     o = PyLong_FromLong(COLS);
-    if (!o) {
-        Py_DECREF(m);
-        return 0;
+    if (o == NULL) {
+        goto error;
     }
-    if (PyObject_SetAttrString(m, "COLS", o)) {
-        Py_DECREF(m);
-        Py_DECREF(o);
-        return 0;
+    if (PyDict_SetItemString(exposed_module_dict, "COLS", o) < 0) {
+        goto error;
     }
-    if (PyDict_SetItemString(ModDict, "COLS", o)) {
-        Py_DECREF(m);
-        Py_DECREF(o);
-        return 0;
+    if (PyDict_SetItemString(ModDict, "COLS", o) < 0) {
+        goto error;
     }
     Py_DECREF(o);
-    Py_DECREF(m);
+    Py_DECREF(exposed_module);
     return 1;
+
+error:
+    Py_XDECREF(o);
+    Py_DECREF(exposed_module);
+    return 0;
 }
 
 /*[clinic input]
@@ -4210,35 +4223,33 @@ static PyObject *
 _curses_start_color_impl(PyObject *module)
 /*[clinic end generated code: output=8b772b41d8090ede input=0ca0ecb2b77e1a12]*/
 {
-    int code;
-    PyObject *c, *cp;
-
     PyCursesInitialised;
 
-    code = start_color();
-    if (code != ERR) {
-        initialisedcolors = TRUE;
-        c = PyLong_FromLong((long) COLORS);
-        if (c == NULL)
-            return NULL;
-        if (PyDict_SetItemString(ModDict, "COLORS", c) < 0) {
-            Py_DECREF(c);
-            return NULL;
-        }
-        Py_DECREF(c);
-        cp = PyLong_FromLong((long) COLOR_PAIRS);
-        if (cp == NULL)
-            return NULL;
-        if (PyDict_SetItemString(ModDict, "COLOR_PAIRS", cp) < 0) {
-            Py_DECREF(cp);
-            return NULL;
-        }
-        Py_DECREF(cp);
-        Py_RETURN_NONE;
-    } else {
+    if (start_color() == ERR) {
         PyErr_SetString(PyCursesError, "start_color() returned ERR");
         return NULL;
     }
+
+    initialisedcolors = TRUE;
+
+#define DICT_ADD_INT_VALUE(NAME, VALUE)                         \
+    do {                                                        \
+        PyObject *value = PyLong_FromLong((long)(VALUE));       \
+        if (value == NULL) {                                    \
+            return NULL;                                        \
+        }                                                       \
+        int rc = PyDict_SetItemString(ModDict, (NAME), value);  \
+        Py_DECREF(value);                                       \
+        if (rc < 0) {                                           \
+            return NULL;                                        \
+        }                                                       \
+    } while (0)
+
+    DICT_ADD_INT_VALUE("COLORS", COLORS);
+    DICT_ADD_INT_VALUE("COLOR_PAIRS", COLOR_PAIRS);
+#undef DICT_ADD_INT_VALUE
+
+    Py_RETURN_NONE;
 }
 
 /*[clinic input]
@@ -4595,10 +4606,7 @@ static PyStructSequence_Desc ncurses_version_desc = {
 static PyObject *
 make_ncurses_version(PyTypeObject *type)
 {
-    PyObject *ncurses_version;
-    int pos = 0;
-
-    ncurses_version = PyStructSequence_New(type);
+    PyObject *ncurses_version = PyStructSequence_New(type);
     if (ncurses_version == NULL) {
         return NULL;
     }
@@ -4610,18 +4618,20 @@ make_ncurses_version(PyTypeObject *type)
         minor = NCURSES_VERSION_MINOR;
         patch = NCURSES_VERSION_PATCH;
     }
-#define SetIntItem(flag) \
-    PyStructSequence_SET_ITEM(ncurses_version, pos++, PyLong_FromLong(flag)); \
-    if (PyErr_Occurred()) { \
-        Py_CLEAR(ncurses_version); \
-        return NULL; \
-    }
-
-    SetIntItem(major)
-    SetIntItem(minor)
-    SetIntItem(patch)
-#undef SetIntItem
+#define SET_VERSION_COMPONENT(INDEX, VALUE)                     \
+    do {                                                        \
+        PyObject *o = PyLong_FromLong(VALUE);                   \
+        if (o == NULL) {                                        \
+            Py_DECREF(ncurses_version);                         \
+            return NULL;                                        \
+        }                                                       \
+        PyStructSequence_SET_ITEM(ncurses_version, INDEX, o);   \
+    } while (0)
 
+    SET_VERSION_COMPONENT(0, major);
+    SET_VERSION_COMPONENT(1, minor);
+    SET_VERSION_COMPONENT(2, patch);
+#undef SET_VERSION_COMPONENT
     return ncurses_version;
 }
 
@@ -4753,33 +4763,28 @@ curses_destructor(PyObject *op)
     PyMem_Free(ptr);
 }
 
-PyMODINIT_FUNC
-PyInit__curses(void)
+static int
+cursesmodule_exec(PyObject *module)
 {
-    PyObject *m, *d, *v, *c_api_object;
-
     /* Initialize object type */
-    if (PyType_Ready(&PyCursesWindow_Type) < 0)
-        return NULL;
-
-    /* Create the module and add the functions */
-    m = PyModule_Create(&_cursesmodule);
-    if (m == NULL)
-        return NULL;
-#ifdef Py_GIL_DISABLED
-    PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
-#endif
+    if (PyType_Ready(&PyCursesWindow_Type) < 0) {
+        return -1;
+    }
+    if (PyModule_AddType(module, &PyCursesWindow_Type) < 0) {
+        return -1;
+    }
 
     /* Add some symbolic constants to the module */
-    d = PyModule_GetDict(m);
-    if (d == NULL)
-        return NULL;
-    ModDict = d; /* For PyCurses_InitScr to use later */
+    PyObject *module_dict = PyModule_GetDict(module);
+    if (module_dict == NULL) {
+        return -1;
+    }
+    ModDict = module_dict; /* For PyCurses_InitScr to use later */
 
     void **PyCurses_API = PyMem_Calloc(PyCurses_API_pointers, sizeof(void *));
     if (PyCurses_API == NULL) {
         PyErr_NoMemory();
-        return NULL;
+        return -1;
     }
     /* Initialize the C API pointer array */
     PyCurses_API[0] = (void *)Py_NewRef(&PyCursesWindow_Type);
@@ -4788,28 +4793,45 @@ PyInit__curses(void)
     PyCurses_API[3] = (void *)func_PyCursesInitialisedColor;
 
     /* Add a capsule for the C API */
-    c_api_object = PyCapsule_New(PyCurses_API, PyCurses_CAPSULE_NAME,
-                                 curses_destructor);
+    PyObject *c_api_object = PyCapsule_New(PyCurses_API, PyCurses_CAPSULE_NAME,
+                                           curses_destructor);
     if (c_api_object == NULL) {
         Py_DECREF(PyCurses_API[0]);
         PyMem_Free(PyCurses_API);
-        return NULL;
-    }
-    if (PyDict_SetItemString(d, "_C_API", c_api_object) < 0) {
-        Py_DECREF(c_api_object);
-        return NULL;
+        return -1;
     }
+    int rc = PyDict_SetItemString(module_dict, "_C_API", c_api_object);
     Py_DECREF(c_api_object);
+    if (rc < 0) {
+        return -1;
+    }
 
     /* For exception curses.error */
     PyCursesError = PyErr_NewException("_curses.error", NULL, NULL);
-    PyDict_SetItemString(d, "error", PyCursesError);
+    if (PyCursesError == NULL) {
+        return -1;
+    }
+    rc = PyDict_SetItemString(module_dict, "error", PyCursesError);
+    Py_DECREF(PyCursesError);
+    if (rc < 0) {
+        return -1;
+    }
 
     /* Make the version available */
-    v = PyBytes_FromString(PyCursesVersion);
-    PyDict_SetItemString(d, "version", v);
-    PyDict_SetItemString(d, "__version__", v);
-    Py_DECREF(v);
+    PyObject *curses_version = PyBytes_FromString(PyCursesVersion);
+    if (curses_version == NULL) {
+        return -1;
+    }
+    rc = PyDict_SetItemString(module_dict, "version", curses_version);
+    if (rc < 0) {
+        Py_DECREF(curses_version);
+        return -1;
+    }
+    rc = PyDict_SetItemString(module_dict, "__version__", curses_version);
+    Py_CLEAR(curses_version);
+    if (rc < 0) {
+        return -1;
+    }
 
 #ifdef NCURSES_VERSION
     /* ncurses_version */
@@ -4817,17 +4839,33 @@ PyInit__curses(void)
     version_type = _PyStructSequence_NewType(&ncurses_version_desc,
                                              Py_TPFLAGS_DISALLOW_INSTANTIATION);
     if (version_type == NULL) {
-        return NULL;
+        return -1;
     }
-    v = make_ncurses_version(version_type);
+    PyObject *ncurses_version = make_ncurses_version(version_type);
     Py_DECREF(version_type);
-    if (v == NULL) {
-        return NULL;
+    if (ncurses_version == NULL) {
+        return -1;
+    }
+    rc = PyDict_SetItemString(module_dict, "ncurses_version", ncurses_version);
+    Py_CLEAR(ncurses_version);
+    if (rc < 0) {
+        return -1;
     }
-    PyDict_SetItemString(d, "ncurses_version", v);
-    Py_DECREF(v);
 #endif /* NCURSES_VERSION */
 
+#define SetDictInt(NAME, VALUE)                                     \
+    do {                                                            \
+        PyObject *value = PyLong_FromLong((long)(VALUE));           \
+        if (value == NULL) {                                        \
+            return -1;                                              \
+        }                                                           \
+        int rc = PyDict_SetItemString(module_dict, (NAME), value);  \
+        Py_DECREF(value);                                           \
+        if (rc < 0) {                                               \
+            return -1;                                              \
+        }                                                           \
+    } while (0)
+
     SetDictInt("ERR", ERR);
     SetDictInt("OK", OK);
 
@@ -4923,43 +4961,69 @@ PyInit__curses(void)
     SetDictInt("REPORT_MOUSE_POSITION",    REPORT_MOUSE_POSITION);
 #endif
     /* Now set everything up for KEY_ variables */
-    {
-        int key;
-        char *key_n;
-        char *key_n2;
-        for (key=KEY_MIN;key < KEY_MAX; key++) {
-            key_n = (char *)keyname(key);
-            if (key_n == NULL || strcmp(key_n,"UNKNOWN KEY")==0)
-                continue;
-            if (strncmp(key_n,"KEY_F(",6)==0) {
-                char *p1, *p2;
-                key_n2 = PyMem_Malloc(strlen(key_n)+1);
-                if (!key_n2) {
-                    PyErr_NoMemory();
-                    break;
-                }
-                p1 = key_n;
-                p2 = key_n2;
-                while (*p1) {
-                    if (*p1 != '(' && *p1 != ')') {
-                        *p2 = *p1;
-                        p2++;
-                    }
-                    p1++;
+    for (int keycode = KEY_MIN; keycode < KEY_MAX; keycode++) {
+        const char *key_name = keyname(keycode);
+        if (key_name == NULL || strcmp(key_name, "UNKNOWN KEY") == 0) {
+            continue;
+        }
+        if (strncmp(key_name, "KEY_F(", 6) == 0) {
+            char *fn_key_name = PyMem_Malloc(strlen(key_name) + 1);
+            if (!fn_key_name) {
+                PyErr_NoMemory();
+                return -1;
+            }
+            const char *p1 = key_name;
+            char *p2 = fn_key_name;
+            while (*p1) {
+                if (*p1 != '(' && *p1 != ')') {
+                    *p2 = *p1;
+                    p2++;
                 }
-                *p2 = (char)0;
-            } else
-                key_n2 = key_n;
-            SetDictInt(key_n2,key);
-            if (key_n2 != key_n)
-                PyMem_Free(key_n2);
+                p1++;
+            }
+            *p2 = (char)0;
+            PyObject *p_keycode = PyLong_FromLong((long)keycode);
+            if (p_keycode == NULL) {
+                PyMem_Free(fn_key_name);
+                return -1;
+            }
+            int rc = PyDict_SetItemString(module_dict, fn_key_name, p_keycode);
+            Py_DECREF(p_keycode);
+            PyMem_Free(fn_key_name);
+            if (rc < 0) {
+                return -1;
+            }
+        }
+        else {
+            SetDictInt(key_name, keycode);
         }
-        SetDictInt("KEY_MIN", KEY_MIN);
-        SetDictInt("KEY_MAX", KEY_MAX);
     }
+    SetDictInt("KEY_MIN", KEY_MIN);
+    SetDictInt("KEY_MAX", KEY_MAX);
+#undef SetDictInt
+    return 0;
+}
 
-    if (PyModule_AddType(m, &PyCursesWindow_Type) < 0) {
-        return NULL;
+PyMODINIT_FUNC
+PyInit__curses(void)
+{
+    // create the module
+    PyObject *mod = PyModule_Create(&_cursesmodule);
+    if (mod == NULL) {
+        goto error;
     }
-    return m;
+#ifdef Py_GIL_DISABLED
+    if (PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED) < 0) {
+        goto error;
+    }
+#endif
+    // populate the module
+    if (cursesmodule_exec(mod) < 0) {
+        goto error;
+    }
+    return mod;
+
+error:
+    Py_XDECREF(mod);
+    return NULL;
 }