fix: use NullPool for SQLCipher engine to prevent segfault (#22273)
The SQLCipher engine used a dummy sqlite:// URL with a creator function, which caused SQLAlchemy to auto-select SingletonThreadPool. This pool non-deterministically closes in-use connections when thread count exceeds pool_size (default 5), leading to use-after-free segfaults (exit code 139) in the native sqlcipher3 C library during multi-threaded operations like user signup. Now defaults to NullPool (each operation creates/closes its own connection) for maximum safety with the native C extension. Also respects the DATABASE_POOL_SIZE setting: if explicitly set >0, QueuePool is used with the configured pool parameters, matching the behavior of other DB paths. Fixes #22258
This commit is contained in:
@@ -102,11 +102,30 @@ if SQLALCHEMY_DATABASE_URL.startswith("sqlite+sqlcipher://"):
|
|||||||
conn.execute(f"PRAGMA key = '{database_password}'")
|
conn.execute(f"PRAGMA key = '{database_password}'")
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
engine = create_engine(
|
# The dummy "sqlite://" URL would cause SQLAlchemy to auto-select
|
||||||
"sqlite://", # Dummy URL since we're using creator
|
# SingletonThreadPool, which non-deterministically closes in-use
|
||||||
creator=create_sqlcipher_connection,
|
# connections when thread count exceeds pool_size, leading to segfaults
|
||||||
echo=False,
|
# in the native sqlcipher3 C library. Use NullPool by default for safety,
|
||||||
)
|
# or QueuePool if DATABASE_POOL_SIZE is explicitly configured.
|
||||||
|
if isinstance(DATABASE_POOL_SIZE, int) and DATABASE_POOL_SIZE > 0:
|
||||||
|
engine = create_engine(
|
||||||
|
"sqlite://",
|
||||||
|
creator=create_sqlcipher_connection,
|
||||||
|
pool_size=DATABASE_POOL_SIZE,
|
||||||
|
max_overflow=DATABASE_POOL_MAX_OVERFLOW,
|
||||||
|
pool_timeout=DATABASE_POOL_TIMEOUT,
|
||||||
|
pool_recycle=DATABASE_POOL_RECYCLE,
|
||||||
|
pool_pre_ping=True,
|
||||||
|
poolclass=QueuePool,
|
||||||
|
echo=False,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
engine = create_engine(
|
||||||
|
"sqlite://",
|
||||||
|
creator=create_sqlcipher_connection,
|
||||||
|
poolclass=NullPool,
|
||||||
|
echo=False,
|
||||||
|
)
|
||||||
|
|
||||||
log.info("Connected to encrypted SQLite database using SQLCipher")
|
log.info("Connected to encrypted SQLite database using SQLCipher")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user