Bug 512485 - kmymoney crashes with sqlcipher 4.7.0+
Summary: kmymoney crashes with sqlcipher 4.7.0+
Status: REPORTED
Alias: None
Product: kmymoney
Classification: Applications
Component: database (other bugs)
Version First Reported In: 5.2.1
Platform: Debian unstable Linux
: NOR crash
Target Milestone: ---
Assignee: KMyMoney Devel Mailing List
URL:
Keywords:
: 512525 (view as bug list)
Depends on:
Blocks:
 
Reported: 2025-11-22 19:24 UTC by Boyuan Yang
Modified: 2025-11-27 23:02 UTC (History)
4 users (show)

See Also:
Latest Commit:
Version Fixed/Implemented In:
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Boyuan Yang 2025-11-22 19:24:50 UTC
SUMMARY

Kmymoney is not compatible with sqlcipher v5.7.0+.

Downstream bug report: https://bugs.debian.org/1120685 , but the actual useful information is at https://bugs.debian.org/1121167 .

STEPS TO REPRODUCE
1. Build kmymoney with sqlcipher v5.7.0 or later, ideally v5.11.0.
2. Launch kmymoney.

OBSERVED RESULT

Segmentation fault


EXPECTED RESULT

The program launches successfully

SOFTWARE/OS VERSIONS
Linux/KDE Plasma: 6.5.3
KDE Plasma Version: 6.5.3
KDE Frameworks Version: 6.20
Qt Version: 6.9.2

ADDITIONAL INFORMATION

When we look at the related source code:

https://sources.debian.org/src/kmymoney/5.2.1-1/kmymoney/kmymoney.cpp#L1045

```cpp
#ifdef ENABLE_SQLCIPHER
    /* Issues:
     * 1) libsqlite3 loads implicitly before libsqlcipher
     *  thus making the second one loaded but non-functional,
     * 2) libsqlite3 gets linked into kmymoney target implicitly
     *  and it's not possible to unload or unlink it explicitly
     *
     * Solution:
     * Use e.g. dummy sqlite3_key call, so that libsqlcipher gets loaded implicitly before libsqlite3
     * thus making the first one functional.
     *
     * Additional info:
     * 1) loading libsqlcipher explicitly doesn't solve the issue,
     * 2) using sqlite3_key only in sqlstorage plugin doesn't solve the issue,
     * 3) in a separate, minimal test case, loading libsqlite3 explicitly
     *  with QLibrary::ExportExternalSymbolsHint makes libsqlcipher non-functional
    */
    sqlite3_key(nullptr, nullptr, 0);
#endif
```

The `sqlite3_key(nullptr, nullptr, 0);` call is problematic as it will likely result in an undefined behavior.

When we build the following minimal example C program:

```
-> % cat test_key_null.c
#include <stdio.h>
#include "/usr/include/sqlcipher/sqlite3.h"

//extern int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);

int main(void)
{
    printf("Calling sqlite3_key(NULL, NULL, 0)...\n");

    int rc = sqlite3_key(NULL, NULL, 0);

    printf("Return code: %d\n", rc);
    return 0;
```

...and build it with the following command line:

gcc test_key_null.c -o test_key_null $(pkg-config --cflags --libs sqlcipher) -I/usr/include -DSQLITE_HAS_CODEC

When building with sqlcipher 4.6.1:

-> % ./test_key_null
Calling sqlite3_key(NULL, NULL, 0)...
Return code: 1

When building with sqlcipher 4.11.0:

-> % ./test_key_null
Calling sqlite3_key(NULL, NULL, 0)...
[1]    2190 segmentation fault  ./test_key_null

As a result, I suggest no longer using sqlite3_key(NULL, NULL, 0) anymore.

FULL BACKTRACE

#0  sqlcipher_find_db_index (db=0x0, zDb=0x7ffff53ba14d "main") at ./sqlite3.c:111826
        db_index = <optimized out>
        pDb = <optimized out>
#1  0x00007ffff52e7c8d in sqlite3_key_v2 (db=0x0, zDb=0x7ffff53ba14d "main", pKey=0x0, nKey=0) at ./sqlite3.c:111850
        db_index = <optimized out>
        __func__ = "sqlite3_key_v2"
#2  0x0000555555644408 in KMyMoneyApp::KMyMoneyApp (this=0x555555ae6f50, parent=<optimized out>, __in_chrg=<optimized out>, __vtt_parm=<optimized out>)
    at ./kmymoney/kmymoney.cpp:1045
        frame = <optimized out>
        layout = <optimized out>
        viewActions = {d = 0x0}
        locale = {static DefaultTwoDigitBaseYear = 1900, d = {d = {ptr = 0x0}}}
        frame = <optimized out>
        layout = <optimized out>
        viewActions = <optimized out>
        locale = <optimized out>
        it = <optimized out>
        weekDay = <optimized out>
        __for_range = <optimized out>
        __for_begin = <optimized out>
        __for_end = <optimized out>
#3  0x0000555555618a56 in main (argc=<optimized out>, argv=<optimized out>) at ./kmymoney/main.cpp:232
        app = {<QGuiApplication> = {<QCoreApplication> = {<QObject> = {_vptr.QObject = 0x7ffff6baaa28 <vtable for QApplication+16>, static staticMetaObject = {d = {
                    superdata = {direct = 0x0},
                    stringdata = 0x7ffff58556d0 <_ZN7QObject32qt_staticMetaObjectStaticContentIN12_GLOBAL__N_125qt_meta_tag_ZN7QObjectE_tEEE.lto_priv.0+272>,
                    data = 0x7ffff58555c0 <_ZN7QObject32qt_staticMetaObjectStaticContentIN12_GLOBAL__N_125qt_meta_tag_ZN7QObjectE_tEEE.lto_priv.0>,
                    static_metacall = 0x7ffff55ffac0 <QObject::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>, relatedMetaObjects = 0x0,
                    metaTypes = 0x7ffff59de9c0 <_ZN7QObject36qt_staticMetaObjectRelocatingContentIN12_GLOBAL__N_125qt_meta_tag_ZN7QObjectE_tEEE.lto_priv.0>,
                    extradata = 0x0}}, d_ptr = {d = 0x55555594b540}}, static staticMetaObject = {d = {superdata = {direct = 0x5555558fcc20 <QObject::staticMetaObject>},
                  stringdata = 0x7ffff5854c88 <_ZN16QCoreApplication32qt_staticMetaObjectStaticContentIN12_GLOBAL__N_135qt_meta_tag_ZN16QCoreApplicationE_tEEE.lto_priv.0+392>, data = 0x7ffff5854b00 <_ZN16QCoreApplication32qt_staticMetaObjectStaticContentIN12_GLOBAL__N_135qt_meta_tag_ZN16QCoreApplicationE_tEEE.lto_priv.0>,
                  static_metacall = 0x7ffff55addf0 <QCoreApplication::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>, relatedMetaObjects = 0x0,
                  metaTypes = 0x7ffff59de5c0 <_ZN16QCoreApplication36qt_staticMetaObjectRelocatingContentIN12_GLOBAL__N_135qt_meta_tag_ZN16QCoreApplicationE_tEEE.lto_priv.0>,
                  extradata = 0x0}}, static self = 0x7fffffffd900}, static staticMetaObject = {d = {superdata = {
                  direct = 0x7ffff59de640 <QCoreApplication::staticMetaObject>},
                stringdata = 0x7ffff6108e50 <_ZN15QGuiApplication32qt_staticMetaObjectStaticContentIN12_GLOBAL__N_134qt_meta_tag_ZN15QGuiApplicationE_tEEE.lto_priv.0+752>,
                data = 0x7ffff6108b60 <_ZN15QGuiApplication32qt_staticMetaObjectStaticContentIN12_GLOBAL__N_134qt_meta_tag_ZN15QGuiApplicationE_tEEE.lto_priv.0>,
                static_metacall = 0x7ffff5c014a0 <QGuiApplication::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>, relatedMetaObjects = 0x0,
                metaTypes = 0x7ffff623c240 <_ZN15QGuiApplication36qt_staticMetaObjectRelocatingContentIN12_GLOBAL__N_134qt_meta_tag_ZN15QGuiApplicationE_tEEE.lto_priv.0>,
                extradata = 0x0}}}, static staticMetaObject = {d = {superdata = {direct = 0x7ffff623c7e0 <QGuiApplication::staticMetaObject>},
              stringdata = 0x7ffff6a40568 <_ZN12QApplication32qt_staticMetaObjectStaticContentIN12_GLOBAL__N_131qt_meta_tag_ZN12QApplicationE_tEEE.lto_priv.0+392>,
              data = 0x7ffff6a403e0 <_ZN12QApplication32qt_staticMetaObjectStaticContentIN12_GLOBAL__N_131qt_meta_tag_ZN12QApplicationE_tEEE.lto_priv.0>,
              static_metacall = 0x7ffff65bc900 <QApplication::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>, relatedMetaObjects = 0x0,
              metaTypes = 0x7ffff6b9a7c0 <_ZN12QApplication36qt_staticMetaObjectRelocatingContentIN12_GLOBAL__N_131qt_meta_tag_ZN12QApplicationE_tEEE.lto_priv.0>,
              extradata = 0x0}}}
        aboutData = {d = std::unique_ptr<KAboutDataPrivate> = {get() = 0x5555576de210}}
        fileUrls = {<QListSpecialMethods<QString>> = {<QListSpecialMethodsBase<QString>> = {<No data fields>}, <No data fields>}, d = {d = 0x0, ptr = 0x0, size = 0}}
        isNoCatchOption = false
        isNoFileOption = false
        file = <optimized out>
        fname = {d = {d = 0x555555ae6830, ptr = 0x0, size = 0}, static _empty = 0 u'\000'}
        url = {d = 0x555557653f70}
        rc = <optimized out>
Comment 1 Jack 2025-11-22 19:48:31 UTC
Are you sure about these version numbers?  I see 4.11.0 as the most recent release, from October.
Comment 2 Boyuan Yang 2025-11-22 19:49:56 UTC
(In reply to Jack from comment #1)
> Are you sure about these version numbers?  I see 4.11.0 as the most recent
> release, from October.

I am not sure about the exact version. I can bisect them if you find that necessary.
Comment 3 Boyuan Yang 2025-11-22 19:53:29 UTC
Maybe the issue would only occur after this commit:

https://github.com/sqlcipher/sqlcipher/commit/291dc9bb45bd608e9c3a6ec046619f9abb6b05f0

This commit explicitly mentions the guarding against "misuse" of the APIs, which looks pretty likely to be the cause of the current cause. This commit exists in sqlcipher 5.7.0 and all later versions.
Comment 4 Jack 2025-11-22 20:01:53 UTC
I am not questioning when the issue was created - just the released version numbers.  The most recent version I can find is 4.11, not 5.11, so I'm wondering if you really mean 4.7.0 and later, not 5.7.0.
Comment 5 Boyuan Yang 2025-11-22 20:03:37 UTC
(In reply to Jack from comment #4)
> I am not questioning when the issue was created - just the released version
> numbers.  The most recent version I can find is 4.11, not 5.11, so I'm
> wondering if you really mean 4.7.0 and later, not 5.7.0.

Sure. I just found there are version typos all around above in my comments.
When I said 5.7.0, I actually meant sqlcipher 4.7.0. Similarly it should be sqlcipher 4.11.0 not 5.11.0. Sorry for the confusion.
Comment 6 Boyuan Yang 2025-11-23 18:34:33 UTC
A related discussion has been opened on sqlcipher's issue list: https://github.com/sqlcipher/sqlcipher/issues/578
Comment 7 Jack 2025-11-23 19:52:48 UTC
*** Bug 512525 has been marked as a duplicate of this bug. ***
Comment 8 Jack 2025-11-27 23:02:10 UTC
*** Bug 512599 has been marked as a duplicate of this bug. ***