From cc8b2146ca7baba7bd8dbcd23fd0f9516bb51338 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 16 Aug 2020 12:40:19 +0200
Subject: Avoided a crash at exit with incorrect Python plugins.

---
 plugins/pychrysalide/pychrysa.c | 53 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 52 insertions(+), 1 deletion(-)

diff --git a/plugins/pychrysalide/pychrysa.c b/plugins/pychrysalide/pychrysa.c
index 29b0ee8..8e6cfbc 100644
--- a/plugins/pychrysalide/pychrysa.c
+++ b/plugins/pychrysalide/pychrysa.c
@@ -951,6 +951,16 @@ void log_pychrysalide_exception(const char *prefix, ...)
 
         PyErr_Fetch(&err_type, &err_value, &err_traceback);
 
+        PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+        if (err_traceback == NULL)
+        {
+            err_traceback = Py_None;
+            Py_INCREF(err_traceback);
+        }
+
+        PyException_SetTraceback(err_value, err_traceback);
+
         if (err_value == NULL)
             msg = stradd(msg, _(": no extra information is provided..."));
 
@@ -963,11 +973,52 @@ void log_pychrysalide_exception(const char *prefix, ...)
             msg = stradd(msg, err_msg);
 
             Py_DECREF(err_string);
-            Py_DECREF(err_value);
 
         }
 
+        /**
+         * Bien que la documentation précise que la fonction PyErr_Fetch()
+         * transfère la propritété des éléments retournés, la pratique
+         * montre que le programme plante à la terminaison en cas d'exception.
+         *
+         * C'est par exemple le cas quand un greffon Python ne peut se lancer
+         * correctement ; l'exception est alors levée à partir de la fonction
+         * g_python_plugin_new() et le plantage intervient en sortie d'exécution,
+         * au moment de la libération de l'extension Python :
+         *
+         *    ==14939== Jump to the invalid address stated on the next line
+         *    ==14939==    at 0x1A8FCBC9: ???
+         *    ==14939==    by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3)
+         *    ==14939==    by 0x610F834: on_plugin_ref_toggle (pglist.c:370)
+         *    ==14939==    by 0x610F31A: exit_all_plugins (pglist.c:153)
+         *    ==14939==    by 0x10AD19: main (main.c:440)
+         *    ==14939==  Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd
+         *
+         * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un
+         * appel à PyErr_PrintEx(0) ne change rien.
+         *
+         * La seule différence de l'instruction set_sys_last_vars réside en quelques
+         * lignes dans le code de l'interpréteur Python :
+         *
+         *    if (set_sys_last_vars) {
+         *        _PySys_SetObjectId(&PyId_last_type, exception);
+         *        _PySys_SetObjectId(&PyId_last_value, v);
+         *        _PySys_SetObjectId(&PyId_last_traceback, tb);
+         *    }
+         *
+         * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ?
+         * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence
+         * à ces éléments.
+         *
+         * On reproduit ici le comportement du code correcteur avec PySys_SetObject().
+         */
+
+        PySys_SetObject("last_type", err_type);
+        PySys_SetObject("last_value", err_value);
+        PySys_SetObject("last_traceback", err_traceback);
+
         Py_XDECREF(err_traceback);
+        Py_XDECREF(err_value);
         Py_XDECREF(err_type);
 
         log_plugin_simple_message(LMT_ERROR, msg);
-- 
cgit v0.11.2-87-g4458