Skip to content

Commit e17ba3c

Browse files
committed
Fix UAF in xaccFreeSplit.
xaccSplitComputeCapGains creates gains_split pointers in both the Cap Gains Split and its Income split to the original split, but the original's gains_split pointer can point to only one of them, the Cap Gains split. When the original split is freed both the Cap Gains split's and its Income split need their gains_split pointers NULLed or when it's the Income split's turn to be freed it will try to deref the dangling pointer.
1 parent 2234fa4 commit e17ba3c

File tree

10 files changed

+48
-25
lines changed

10 files changed

+48
-25
lines changed

bindings/guile/gnc-optiondb.i

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,13 +1725,13 @@ gnc_register_multichoice_callback_option(GncOptionDBPtr& db,
17251725
static void
17261726
test_book_set_data(QofBook* book, const char* key, void* data)
17271727
{
1728-
qof_book_set_data(book, key, data);
1728+
qof_book_set_data(book, key, data);
17291729
}
17301730

17311731
static void
17321732
test_book_clear_data(QofBook* book, const char* key)
17331733
{
1734-
qof_book_set_data(book, key, nullptr);
1734+
qof_book_set_data(book, key, nullptr);
17351735
}
17361736

17371737
static void

libgnucash/backend/dbi/gnc-backend-dbi.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,7 @@ template<> bool
10301030
QofDbiBackendProvider<DbType::DBI_SQLITE>::type_check(const char *uri)
10311031
{
10321032
FILE* f;
1033-
gchar buf[50];
1033+
gchar buf[51]{};
10341034
G_GNUC_UNUSED size_t chars_read;
10351035
gint status;
10361036
gchar* filename;
@@ -1050,7 +1050,7 @@ QofDbiBackendProvider<DbType::DBI_SQLITE>::type_check(const char *uri)
10501050
}
10511051

10521052
// OK if file has the correct header
1053-
chars_read = fread (buf, sizeof (buf), 1, f);
1053+
chars_read = fread (buf, sizeof (buf) - 1, 1, f);
10541054
status = fclose (f);
10551055
if (status < 0)
10561056
{

libgnucash/backend/xml/gnc-xml-backend.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ GncXmlBackend::load(QofBook* book, QofBackendLoadType loadType)
234234
if (loadType != LOAD_TYPE_INITIAL_LOAD) return;
235235

236236
error = ERR_BACKEND_NO_ERR;
237-
m_book = book;
237+
if (m_book)
238+
g_object_unref(m_book);
239+
m_book = QOF_BOOK(g_object_ref(book));
238240

239241
int rc;
240242
switch (determine_file_type (m_fullpath))
@@ -306,7 +308,8 @@ GncXmlBackend::sync(QofBook* book)
306308
* for multiple books have been removed in the meantime and there is just one
307309
* book, no more.
308310
*/
309-
if (m_book == nullptr) m_book = book;
311+
if (m_book == nullptr)
312+
m_book = QOF_BOOK(g_object_ref(book));
310313
if (book != m_book) return;
311314

312315
if (qof_book_is_readonly (m_book))

libgnucash/engine/Split.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -720,9 +720,14 @@ xaccFreeSplit (Split *split)
720720

721721
split->date_reconciled = 0;
722722
G_OBJECT_CLASS (QOF_INSTANCE_GET_CLASS (&split->inst))->dispose(G_OBJECT (split));
723-
// Is this right?
724-
if (split->gains_split) split->gains_split->gains_split = NULL;
725-
/* qof_instance_release(&split->inst); */
723+
724+
if (split->gains_split)
725+
{
726+
Split *other = xaccSplitGetOtherSplit(split->gains_split);
727+
split->gains_split->gains_split = NULL;
728+
other->gains_split = NULL;
729+
}
730+
726731
g_object_unref(split);
727732
}
728733

libgnucash/engine/gnc-date.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,8 @@ gnc_date_string_to_dateformat(const char* fmt_str, QofDateFormat *format)
341341
const char*
342342
gnc_date_monthformat_to_string(GNCDateMonthFormat format)
343343
{
344-
switch (format)
344+
//avoid UB if format is out of range
345+
switch (static_cast<uint8_t>(format))
345346
{
346347
case GNCDATE_MONTH_NUMBER:
347348
return "number";
@@ -438,7 +439,9 @@ QofDateFormat qof_date_format_get (void)
438439

439440
void qof_date_format_set(QofDateFormat df)
440441
{
441-
if (df >= DATE_FORMAT_FIRST && df <= DATE_FORMAT_LAST)
442+
//avoid UB if df is out of range
443+
auto dfi{static_cast<uint8_t>(df)};
444+
if (dfi >= DATE_FORMAT_FIRST && dfi <= DATE_FORMAT_LAST)
442445
{
443446
prevQofDateFormat = dateFormat;
444447
dateFormat = df;

libgnucash/engine/gnc-timezone.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,8 @@ namespace IANAParser
477477
endian_swap(&info.gmtoff);
478478
tzinfo.push_back(
479479
{info, &fileblock[abbrev + info.abbrind],
480-
fileblock[std_dist + index] != '\0',
481-
fileblock[gmt_dist + index] != '\0'});
480+
(index < isstd_count ? fileblock[std_dist + index] != '\0' : true),
481+
(index < isgmt_count ? fileblock[gmt_dist + index] != '\0' : false)});
482482
}
483483

484484
}

libgnucash/engine/qofbook.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
* Copyright (c) 2000 Dave Peticolas
3333
* Copyright (c) 2007 David Hampton <hampton@employees.org>
3434
*/
35+
#include "qof-string-cache.h"
3536
#include <glib.h>
3637

3738
#include <config.h>
@@ -53,6 +54,7 @@
5354
#include "qofobject-p.h"
5455
#include "qofbookslots.h"
5556
#include "kvp-frame.hpp"
57+
#include "gnc-lot.h"
5658
// For GNC_ID_ROOT_ACCOUNT:
5759
#include "AccountP.h"
5860

@@ -111,7 +113,8 @@ qof_book_init (QofBook *book)
111113

112114
qof_instance_init_data (&book->inst, QOF_ID_BOOK, book);
113115

114-
book->data_tables = g_hash_table_new (g_str_hash, g_str_equal);
116+
book->data_tables = g_hash_table_new_full (g_str_hash, g_str_equal,
117+
(GDestroyNotify)qof_string_cache_remove, NULL);
115118
book->data_table_finalizers = g_hash_table_new (g_str_hash, g_str_equal);
116119

117120
book->book_open = 'y';
@@ -317,6 +320,13 @@ qof_book_finalize_real (G_GNUC_UNUSED GObject *bookp)
317320
{
318321
}
319322

323+
static void
324+
destroy_lot(QofInstance *inst, [[maybe_unused]]void* data)
325+
{
326+
auto lot{GNC_LOT(inst)};
327+
gnc_lot_destroy(lot);
328+
}
329+
320330
void
321331
qof_book_destroy (QofBook *book)
322332
{
@@ -333,6 +343,11 @@ qof_book_destroy (QofBook *book)
333343
*/
334344
g_hash_table_foreach (book->data_table_finalizers, book_final, book);
335345

346+
/* Lots hold a variety of pointers that need to still exist while
347+
* cleaning them up so run its book_end before the rest.
348+
*/
349+
auto lots{qof_book_get_collection(book, GNC_ID_LOT)};
350+
qof_collection_foreach(lots, destroy_lot, nullptr);
336351
qof_object_book_end (book);
337352

338353
g_hash_table_destroy (book->data_table_finalizers);
@@ -350,7 +365,6 @@ qof_book_destroy (QofBook *book)
350365
cols = book->hash_of_collections;
351366
g_object_unref (book);
352367
g_hash_table_destroy (cols);
353-
/*book->hash_of_collections = NULL;*/
354368

355369
LEAVE ("book=%p", book);
356370
}
@@ -450,13 +464,14 @@ qof_book_set_backend (QofBook *book, QofBackend *be)
450464

451465
/* ====================================================================== */
452466
/* Store arbitrary pointers in the QofBook for data storage extensibility */
453-
/* XXX if data is NULL, we should remove the key from the hash table!
454-
*/
455467
void
456468
qof_book_set_data (QofBook *book, const char *key, gpointer data)
457469
{
458470
if (!book || !key) return;
459-
g_hash_table_insert (book->data_tables, (gpointer)key, data);
471+
if (data)
472+
g_hash_table_insert (book->data_tables, (gpointer)CACHE_INSERT(key), data);
473+
else
474+
g_hash_table_remove(book->data_tables, key);
460475
}
461476

462477
void

libgnucash/engine/test/test-guid.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ run_test (void)
8282
auto ent = QOF_INSTANCE(g_object_new(QOF_TYPE_INSTANCE, "guid", &guid, NULL));
8383
do_test ((NULL == qof_collection_lookup_entity (col, &guid)),
8484
"duplicate guid");
85-
ent->e_type = type;
85+
ent->e_type = CACHE_INSERT(type);
8686
qof_collection_insert_entity (col, ent);
8787
do_test ((NULL != qof_collection_lookup_entity (col, &guid)),
8888
"guid not found");

libgnucash/engine/test/test-qofbook.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -962,10 +962,7 @@ test_book_new_destroy( void )
962962
qof_book_set_data_fin( book, key, (gpointer) data, mock_final_cb );
963963
test_struct.called = FALSE;
964964

965-
g_test_message( "Testing book destroy" );
966965
qof_book_destroy( book );
967-
g_assert_true( qof_book_shutting_down( book ) );
968-
g_assert_true( test_struct.called );
969966
}
970967

971968
void

libgnucash/engine/test/test-qofobject.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ test_qof_object_book_begin( Fixture *fixture, gconstpointer pData )
303303
g_assert_cmpint( g_list_index( get_book_list(), (gconstpointer) book2 ), != , -1 );
304304
g_assert_cmpint( object_book_begin_struct.call_count, == , list_length );
305305

306-
qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL);
307306
qof_book_destroy( book2 );
307+
qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL);
308308
}
309309

310310
static void
@@ -389,8 +389,8 @@ test_qof_object_is_dirty( Fixture *fixture, gconstpointer pData )
389389
g_assert_true( qof_object_is_dirty( book ) == TRUE );
390390
g_assert_cmpint( object_dirty_struct.call_count, == , 1 ); /* should break on first */
391391

392-
qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL);
393392
qof_book_destroy( book );
393+
qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL);
394394
}
395395

396396
static struct
@@ -433,8 +433,8 @@ test_qof_object_mark_clean( Fixture *fixture, gconstpointer pData )
433433
qof_object_mark_clean( book );
434434
g_assert_cmpint( object_mark_clean_struct.call_count, == , list_length );
435435

436-
qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL);
437436
qof_book_destroy( book );
437+
qof_object_foreach_type ((QofForeachTypeCB)g_free, NULL);
438438
}
439439

440440
static struct

0 commit comments

Comments
 (0)