Skip to content

Thread synchronisation issue in SQLiteCompiledSQL.releaseReference() which causes a double free in SQLiteCompiledSQL native_finalize #632

Open
@aneeetha

Description

@aneeetha

pid: 0, tid: 8948 >>> com.example.redacted <<<

backtrace:
#00 pc 0x0000000000051ba8 /apex/com.android.runtime/lib64/bionic/libc.so (abort+164)
#1 pc 0x000000000004171c /apex/com.android.runtime/lib64/bionic/libc.so (scudo::die()+8)
#2 pc 0x0000000000041dc8 /apex/com.android.runtime/lib64/bionic/libc.so (scudo::ScopedErrorReport::~ScopedErrorReport()+32)
#3 pc 0x0000000000042128 /apex/com.android.runtime/lib64/bionic/libc.so (scudo::reportInvalidChunkState(scudo::AllocatorAction, void*)+116)
#4 pc 0x0000000000043928 /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::deallocate(void*, scudo::Chunk::Origin, unsigned long, unsigned long)+308)
#5 pc 0x00000000001d5e28 /data/app/~~brUaJPOp5HwTzGRwB57bMg==/com.example.redacted-b71M8dXHyQEN-c_xJiL7sA==/split_config.arm64_v8a.apk!libsqlcipher.so
#6 pc 0x0000000000140660 /data/app/~~brUaJPOp5HwTzGRwB57bMg==/com.example.redacted-b71M8dXHyQEN-c_xJiL7sA==/split_config.arm64_v8a.apk!libsqlcipher.so
#7 pc 0x00000000000ca640 /data/app/~~brUaJPOp5HwTzGRwB57bMg==/com.example.redacted-b71M8dXHyQEN-c_xJiL7sA==/split_config.arm64_v8a.apk!libsqlcipher.so
#8 pc 0x00000000000d3f44 /data/app/~~brUaJPOp5HwTzGRwB57bMg==/com.example.redacted-b71M8dXHyQEN-c_xJiL7sA==/split_config.arm64_v8a.apk!libsqlcipher.so (sqlite3_finalize+264)
#9 pc 0x000000000023961c /data/app/~~brUaJPOp5HwTzGRwB57bMg==/com.example.redacted-b71M8dXHyQEN-c_xJiL7sA==/split_config.arm64_v8a.apk!libsqlcipher.so
#10 pc 0x000000000016c1a4 /data/app/~~brUaJPOp5HwTzGRwB57bMg==/com.example.redacted-b71M8dXHyQEN-c_xJiL7sA==/oat/arm64/base.odex (art_jni_trampoline+116)
#11 pc 0x00000000005ef9ec /data/app/~~brUaJPOp5HwTzGRwB57bMg==/com.example.redacted-b71M8dXHyQEN-c_xJiL7sA==/oat/arm64/base.odex (net.sqlcipher.database.SQLiteCompiledSql.releaseSqlStatement+348)
#12 pc 0x00000000005ef618 /data/app/~~brUaJPOp5HwTzGRwB57bMg==/com.example.redacted-b71M8dXHyQEN-c_xJiL7sA==/oat/arm64/base.odex (net.sqlcipher.database.SQLiteCompiledSql.finalize+376)
#13 pc 0x00000000006382a8 /data/misc/apexdata/com.android.art/dalvik-cache/arm64/boot.oat (java.lang.Daemons$FinalizerDaemon.doFinalize+104)
#14 pc 0x0000000000638598 /data/misc/apexdata/com.android.art/dalvik-cache/arm64/boot.oat (java.lang.Daemons$FinalizerDaemon.runInternal+584)
#15 pc 0x0000000000604c14 /data/misc/apexdata/com.android.art/dalvik-cache/arm64/boot.oat (java.lang.Daemons$Daemon.run+180)
#16 pc 0x00000000003fe6b0 /data/misc/apexdata/com.android.art/dalvik-cache/arm64/boot.oat (java.lang.Thread.run+80)
#17 pc 0x000000000045836c /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+556)
#18 pc 0x00000000004841e4 /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
#19 pc 0x0000000000483eb0 /apex/com.android.art/lib64/libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValuesart::ArtMethod*(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+400)
#20 pc 0x00000000005cc668 /apex/com.android.art/lib64/libart.so (art::Thread::CreateCallback(void*)+1680)
#21 pc 0x00000000000b6668 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208)
#22 pc 0x00000000000532cc /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

In our application we often perform db operations. These operations are performed in a multi threaded environment, that we even perform db operations in one thread and close the cursor in another thread. We are facing an issue when SQLiteCompiledSql.releaseSqlStatement is called. This issue occurs when the SQL cache is completely full and the newly created SQLITE Statements are not stored in the cache. In this case when the SQLiteCursor.close() is invoked it will call SQLProgram.close() which again calls releaseRerference() which again invokes onAllReferencesReleased() which invokes releaseCompiledSQLIfnotinCache()

In releaseCompiledSQLIfnotinCache(), it invokes SQLiteCompiledSQL.releaseReference() where the releaseReference() method is not synchronised properly. so this might invoke native_finalize() double times which causes the error.

As per our analysis, we can see the problem if the SQLiteDatabase.close() is called in one thread and at the same time if any opened cursor is closed from an another thread there is a possibility that these two threads will reach to SqliteCompiledSQL.releaseReference() at the same time which can cause a double free in C++ layer.

When checking SQLiteDatabase.close() we can see that it invokes SQliteDatabase.closeClosable() and in that method all the SQLprograms stored in map are iterated and try to release the reference.

In short the issue here is

On two separate threads in a cache full scenario if one thread is trying to close the database which will iterate over all the SQLPrograms and invoke SQLiteCompiledSQL.releaseReference() and on the same time if another thread tries to close the cursor related to this SQLiteCompiledSQL there will be a scenario where two different threads can access the same SQLiteCompiledSQL.releaseReference() which will invoke a double free in C++ layer.

SQLCipher version: 4.5.3

Have added backtrace for reference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions