I am experiencing crashes with SQLCipher 4.5.6 on a Debian 12-based OS using Shared-Cache mode when trying to attach a DB concurrently from multiple threads within our application.
It seems like multiple threads are trying to access the same cipher_ctx object instance that is unfortunately freed by one thread, which results in a use-after-free crash.
My issue is similar to this ticket.
Is Shared-Cache mode supported by SQLCipher? Is it our responsibility to synchronize open/attach operations to encrypted databases? I believe I understand that, once a single connection is established to a database, that connection can be used by multiple threads to read/write to the database, given the correct thread safety compiler flags.
Can you confirm if this is an issue within SQLCipher OR this is a known limitation that should be handled by the integrating application OR this should be possible, but we are using the API incorrectly?
I added a thread ID to the sqlcipher debug logs which highlights the use-after-free really nicely.
I can email the full log if needed (new users cannot attach zip files to tickets).
Details
SQLCipher is compiled into a static library with given compiler flags:
-DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_MAX_ATTACHED=125 -DSQLITE_ENABLE_MEMORY_MANAGEMENT -DSQLITE_THREADSAFE=2 -DSQLITE_ENABLE_MEMSYS5=1 -DSQLITE_DEFAULT_PAGE_SIZE=8192 -DSQLITE_HAS_CODEC=1 -DSQLITE_TEMP_STORE=2
Callstack
#2 <signal handler called>
#3 0x00007f82084cf850 in sqlcipher_codec_ctx_set_error (error=1, ctx=0x7f81dc27ddc8) at sqlite3.c:108407
#4 sqlite3Codec (iCtx=0x7f81dc27ddc8, data=<optimized out>, pgno=1, mode=3) at sqlite3.c:41565
#5 0x00007f82084912fb in readDbPage (pPg=pPg@entry=0x7f81dc290ae0) at sqlite3.c:59744
#6 0x00007f82084f2b17 in getPageNormal (pPager=0x7f81dc289dc8, pgno=1, ppPage=0x7f81fd147780, flags=<optimized out>) at sqlite3.c:62299
#7 0x00007f82084f705a in sqlite3PagerGet (flags=0, ppPage=0x7f81fd147780, pgno=1, pPager=<optimized out>) at sqlite3.c:62428
#8 btreeGetPage (flags=0, ppPage=<synthetic pointer>, pgno=1, pBt=0x7f81c82723d8) at sqlite3.c:6889
#9 lockBtree (pBt=0x7f81c82723d8) at sqlite3.c:7833
#10 btreeBeginTrans (p=p@entry=0x7f81dc285bf8, wrflag=wrflag@entry=0, pSchemaVersion=pSchemaVersion@entry=0x0) at sqlite3.c:8228
#11 0x00007f8208543fc4 in sqlite3BtreeBeginTrans (pSchemaVersion=<optimized out>, wrflag=<optimized out>, p=<optimized out>) at sqlite3.c:73857
#12 sqlite3InitOne (db=db@entry=0x7f81dc001558, iDb=iDb@entry=2, pzErrMsg=pzErrMsg@entry=0x7f81fd147950, mFlags=mFlags@entry=0) at sqlite3.c:14863
#13 0x00007f82085447f8 in sqlite3Init (db=db@entry=0x7f81dc001558, pzErrMsg=pzErrMsg@entry=0x7f81fd147950) at sqlite3.c:146136
#14 0x00007f8208545834 in attachFunc (context=0x7f81dc00d068, NotUsed=<optimized out>, argv=0x7f81dc00d098) at sqlite3.c:124796
#15 0x00007f820855b072 in sqlite3VdbeExec (p=p@entry=0x7f81dc00a7b8) at sqlite3.c:101251
#16 0x00007f820853ce80 in sqlite3Step (p=0x7f81dc00a7b8) at sqlite3.c:90720
#17 sqlite3_step (pStmt=pStmt@entry=0x7f81dc00a7b8) at sqlite3.c:25245
#18 0x00007f8208540178 in sqlite3_exec (db=<optimized out>, zSql=<optimized out>, xCallback=<optimized out>, pArg=<optimized out>, pzErrMsg=<optimized out>) at sqlite3.c:140223
#19 0x00007f82083f5ce6 in sqlite3_blocking_exec (db=0x7f81dc001558, sql=0x7f81dc26a400 "ATTACH DATABASE 'MyDatabase.db3' AS 'MyDatabase' KEY 'SQLCipherTestPassword123456';", callback=callback@entry=0x0, arg=arg@entry=0x0, errmsg=errmsg@entry=0x7f81fd147e28, pCommand=pCommand@entry=0x0, isReadContext=true)
Logs right before the crash: