Severity by source
CVSS:4.0/AV:L/AC:H/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Trigger is in-process API misuse after GC (AV:L, AC:H), needs the app's own code path (PR:L); only a crash results, so A:L and C:N/I:N.
Primary rating from Vendor (https://github.com/sparklemotion/sqlite3-ruby).
CVSS VectorVendor: https://github.com/sparklemotion/sqlite3-ruby
Lifecycle Timeline
4DescriptionCVE.org
Summary
Using Database#create_aggregate, #create_aggregate_handler, or Database#define_aggregator to define an aggregate function, and then using an open statement calling that function after the database has been explicitly closed will result in an invalid memory read and a segmentation fault.
Mitigation
Upgrade to sqlite3 gem v2.9.5 or later.
As a workaround, avoid using an aggregate function after closing the database.
Severity
The sqlite3-ruby maintainers assess this as Low severity. It is reliably triggered after GC when code is structured in a particular way. There is no known general exploit that could be used as a denial of service attack.
AnalysisAI
Use-after-free in the sqlite3 Ruby gem (published as both sqlite3-ruby and sqlite3) versions 2.1.0 through 2.9.4 lets an aggregate function be invoked through a still-open prepared statement after its Database object has been explicitly closed, producing an invalid memory read and a segmentation fault that crashes the Ruby process. Exploitation requires an in-process API-misuse pattern in the host application itself - local, high-complexity, low-privileged per the assessed CVSS:3.1 vector (AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L) - with no remote or network vector and no attacker-supplied input. No public exploit identified at time of analysis (EPSS 0.11%, CISA SSVC exploitation: none, Automatable: no); the maintainers classify it as Low severity, describing a reliably reproducible crash after garbage collection rather than a proven denial-of-service or code-execution primitive.
Technical ContextAI
The flaw is a use-after-free (CWE-416) inside the sqlite3-ruby native C extension (ext/sqlite3/database.c and lib/sqlite3/database.rb). Aggregate functions are registered through Database#create_aggregate, #create_aggregate_handler, or #define_aggregator, and their Ruby handler blocks/objects are retained on the Database object (the "-aggregators" instance variable). When the database is explicitly closed via #close or #discard, the extension released those references and set them to nil, and after a subsequent garbage-collection cycle the underlying aggregator callbacks were freed. A prepared statement that was already open and calls that aggregate still holds pointers to the SQLite aggregate context and callback, so a later Statement#step dereferences the freed aggregator, triggering an invalid memory read and segfault. The fix (PR #711, commit b24e1e6076528b7f95f99acf7a81c70d0004c726) removes the nil-ing of the aggregator reference on close/discard so the Ruby blocks are kept alive for the lifetime of the database object, and adds a regression test (test_step_on_statement_whose_database_was_closed_does_not_use_freed_aggregator). Affected packages per CPE data are pkg:rubygems/sqlite3-ruby and pkg:rubygems/sqlite3; this is a purely in-process Ruby/SQLite integration issue with no SQL injection, protocol, or network component.
RemediationAI
Upgrade to the vendor-released fixed version sqlite3 gem 2.9.5 or later (release: https://github.com/sparklemotion/sqlite3-ruby/releases/tag/v2.9.5), which retains aggregator references for the lifetime of the Database object; the fix is carried in PR https://github.com/sparklemotion/sqlite3-ruby/pull/711 and commit https://github.com/sparklemotion/sqlite3-ruby/commit/b24e1e6076528b7f95f99acf7a81c70d0004c726, with full details in advisory GHSA-j7fr-3v8c-3qc3. If an immediate upgrade is not possible, the maintainer-recommended workaround is to avoid invoking aggregate functions after the database has been closed - specifically, finalize or discard every prepared statement that calls an aggregate before calling Database#close or #discard, and never call Statement#step on a statement whose database has been closed; the trade-off is that this requires auditing application and library code for statement-lifecycle ordering, and any missed path can still crash the process. Because there is no remote vector, network-level controls are not applicable; where aggregate callbacks must survive close, keeping a local reference to the aggregator object can also prevent premature freeing, at the cost of a small memory leak.
Same weakness CWE-416 – Use After Free
View allSame technique Denial Of Service
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-49899
GHSA-j7fr-3v8c-3qc3