gnucash stable: Bug 799213 - SIGSEGV caused by revising an auto completed transaction
John Ralls
jralls at code.gnucash.org
Thu Feb 29 18:53:17 EST 2024
Updated via https://github.com/Gnucash/gnucash/commit/8ebac5b5 (commit)
from https://github.com/Gnucash/gnucash/commit/d011f064 (commit)
commit 8ebac5b596898e3a820c96427d72c5c2486f5f25
Author: John Ralls <jralls at ceridwen.us>
Date: Thu Feb 29 14:29:40 2024 -0800
Bug 799213 - SIGSEGV caused by revising an auto completed transaction
Calling xaccSplitDestroy without also calling xaccSplitCommitEdit then
deleting the split list before calling xaccTransCommitEdit prevents
xaccSplitCommitEdit from being called on the supposedly deleted
splits. Not only does this leak them it leaves them in the book
potentially with a dangling parent pointer.
diff --git a/libgnucash/engine/Transaction.cpp b/libgnucash/engine/Transaction.cpp
index 4dc9afd30f..efb088f269 100644
--- a/libgnucash/engine/Transaction.cpp
+++ b/libgnucash/engine/Transaction.cpp
@@ -25,6 +25,7 @@
\********************************************************************/
#include "qofinstance.h"
+#include <__nullptr>
#include <config.h>
#include <platform.h>
@@ -754,10 +755,7 @@ xaccTransCopyFromClipBoard(const Transaction *from_trans, Transaction *to_trans,
change_accounts = from_acc && GNC_IS_ACCOUNT(to_acc) && from_acc != to_acc;
xaccTransBeginEdit(to_trans);
- FOR_EACH_SPLIT(to_trans, xaccSplitDestroy(s));
- g_list_free(to_trans->splits);
- to_trans->splits = NULL;
-
+ xaccTransClearSplits(to_trans);
xaccTransSetCurrency(to_trans, xaccTransGetCurrency(from_trans));
xaccTransSetDescription(to_trans, xaccTransGetDescription(from_trans));
@@ -1489,7 +1487,6 @@ static void
do_destroy (QofInstance* inst)
{
Transaction *trans{GNC_TRANSACTION (inst)};
- SplitList *node;
gboolean shutting_down = qof_book_shutting_down(qof_instance_get_book(trans));
/* If there are capital-gains transactions associated with this,
@@ -1503,32 +1500,10 @@ do_destroy (QofInstance* inst)
xaccTransWriteLog (trans, 'D');
qof_event_gen (&trans->inst, QOF_EVENT_DESTROY, NULL);
-
- /* We only own the splits that still think they belong to us. This is done
- in 2 steps. In the first, the splits are marked as being destroyed, but they
- are not destroyed yet. In the second, the destruction is committed which will
- do the actual destruction. If both steps are done for a split before they are
- done for the next split, then a split will still be on the split list after it
- has been freed. This can cause other parts of the code (e.g. in xaccSplitDestroy())
- to reference the split after it has been freed. */
- for (node = trans->splits; node; node = node->next)
- {
- Split *s = GNC_SPLIT(node->data);
- if (s && s->parent == trans)
- {
- xaccSplitDestroy(s);
- }
- }
- for (node = trans->splits; node; node = node->next)
- {
- Split *s = GNC_SPLIT(node->data);
- if (s && s->parent == trans)
- {
- xaccSplitCommitEdit(s);
- }
- }
- g_list_free (trans->splits);
- trans->splits = NULL;
+ /* xaccFreeTransaction will also clean up the splits but without
+ * emitting GNC_EVENT_ITEM_REMOVED.
+ */
+ xaccTransClearSplits(trans);
xaccFreeTransaction (trans);
}
@@ -2232,9 +2207,32 @@ void
xaccTransClearSplits(Transaction* trans)
{
xaccTransBeginEdit(trans);
- FOR_EACH_SPLIT(trans, xaccSplitDestroy(s));
+ /* We only own the splits that still think they belong to us. This is done
+ in 2 steps. In the first, the splits are marked as being destroyed, but they
+ are not destroyed yet. In the second, the destruction is committed which will
+ do the actual destruction. If both steps are done for a split before they are
+ done for the next split, then a split will still be on the split list after it
+ has been freed. This can cause other parts of the code (e.g. in xaccSplitDestroy())
+ to reference the split after it has been freed. */
+ for (auto node = trans->splits; node; node = node->next)
+ {
+ auto s = GNC_SPLIT(node->data);
+ if (s && s->parent == trans)
+ {
+ xaccSplitDestroy(s);
+ }
+ }
+ for (auto node = trans->splits; node; node = node->next)
+ {
+ auto s = GNC_SPLIT(node->data);
+ if (s && s->parent == trans)
+ {
+ xaccSplitCommitEdit(s);
+ }
+ }
g_list_free (trans->splits);
- trans->splits = NULL;
+ trans->splits = nullptr;
+
xaccTransCommitEdit(trans);
}
Summary of changes:
libgnucash/engine/Transaction.cpp | 64 +++++++++++++++++++--------------------
1 file changed, 31 insertions(+), 33 deletions(-)
More information about the gnucash-changes
mailing list