Oct 3, 2026·7 min read·2 visits
A Use-After-Free flaw in the sqlite3-ruby native extension allows arbitrary memory corruption or application crash (SIGSEGV) when executing custom SQLite aggregates with multiple arguments under high memory allocation rates.
A Use-After-Free (UAF) vulnerability exists in the sqlite3-ruby native C extension when marshaling arguments for user-defined SQLite aggregate functions with multiple arguments. Due to temporary heap-allocated argument arrays not being registered with the Ruby Garbage Collector, active objects can be prematurely reclaimed, resulting in memory corruption or process-level crashes.
The sqlite3-ruby gem allows Ruby applications to interface directly with the SQLite database engine. A critical native extension component exposes an API to register custom, Ruby-defined aggregate functions with the SQLite engine. These aggregate functions compile individual rows of input into aggregated results by executing custom callback functions at each processing step.\n\nThe vulnerability is classified under CWE-416 as a Use-After-Free (UAF) condition. It manifests inside the native C extension layer during the execution of user-defined aggregate functions that accept multiple arguments. Specifically, when an aggregate function with an arity of two or more is processed, the data conversion layer fails to properly track memory references, exposing active Ruby objects to premature destruction by the Ruby Garbage Collector.\n\nA remote or local attacker who can influence the input arguments sent to custom, multi-argument SQLite aggregate functions can trigger this vulnerability. The resultant premature garbage collection leads to memory corruption, variable state misalignment, or immediate process termination via segmentation faults.
When SQLite processes custom aggregate calculations on multi-column datasets, it invokes the registered C extension function rb_sqlite3_aggregator_step for each row. This function receives an array of raw SQLite pointers (sqlite3_value **argv) representing the row's values. Before executing the developer-defined Ruby step handler, the native code must marshal each raw SQLite structure into an equivalent Ruby object (VALUE).\n\nTo hold these marshaled Ruby object references, the C extension allocates a temporary buffer on the heap. In affected versions of the gem, this buffer is allocated dynamically via the standard Ruby C-API allocator xcalloc: params = xcalloc((size_t)argc, sizeof(VALUE)). Because this temporary array is allocated directly on the heap and its pointer resides only within a raw C variable, the Ruby Garbage Collector (GC) is unaware of its existence.\n\nThe conversion of raw SQLite values into Ruby objects is handled by sqlite3val2rb. For values like complex strings, integers, or floats, this function allocates heap space inside the Ruby virtual machine. If an allocation triggers a GC run during an iteration of the argument loop (for example, while converting argv[1]), the GC sweeps the heap. Since the previously converted Ruby objects stored in the params buffer (such as params[0]) have no active references on the C execution stack or in registered GC roots, they are marked as unreachable and immediately reallocated or swept.\n\nThis flaw specifically requires custom aggregate functions with multiple arguments (arity >= 2). Single-argument functions (argc == 1) utilize a stack-allocated variable (one_param) to store the converted value. Because the C execution stack is conservatively scanned by Ruby's GC, the single argument remains marked as active. When multiple arguments are converted, the heap-allocated array is used instead, bypassing this safety mechanism.
Understanding the layout of memory before and after the application of the patch highlights how Ruby's GC scanner behaves. Below is a comparative review of the native C extension code in ext/sqlite3/aggregator.c.\n\nclike\n// VULNERABLE CODE BLOCK\nif (argc > 1) {\n // Allocation occurs on raw heap.\n // The GC is not aware of VALUE elements held in 'params'.\n params = xcalloc((size_t)argc, sizeof(VALUE));\n for (i = 0; i < argc; i++) {\n // sqlite3val2rb may trigger GC during execution.\n // This sweeps previously converted elements in 'params'.\n params[i] = sqlite3val2rb(argv[i]);\n }\n}\n\n\nIn the vulnerable execution path, the array params holds pointers to Ruby objects. However, since the array itself is allocated via xcalloc, it resides outside the scope of both the stack scanner and the VM's registered object tables. Consequently, the individual elements are vulnerable to sweep cycles if any subsequent call to sqlite3val2rb triggers memory compaction or collection.\n\nclike\n// PATCHED CODE BLOCK\nif (argc > 1) {\n /* ALLOCV memory is conservatively marked, so stored VALUEs survive a\n * GC raised by a later sqlite3val2rb call. */\n params = ALLOCV_N(VALUE, params_handle, argc);\n for (i = 0; i < argc; i++) {\n params[i] = sqlite3val2rb(argv[i]);\n }\n}\n\n\nThe remediation replaces the raw allocator with Ruby's managed ALLOCV_N API. If argc is small, ALLOCV_N uses alloca to reserve space on the C stack. The stack scanner detects these references and preserves the stored objects. For large parameters, ALLOCV_N creates a tracked VM wrapper object, storing its reference in the stack-allocated variable params_handle. This ensures that even heap-allocated structures are successfully anchored and reachable during garbage collection cycles.
To successfully trigger the Use-After-Free condition, the application must define a custom aggregate function taking two or more parameters. Furthermore, the dataset queried must contain strings or numerical values large enough to force memory allocations during the conversion loop, thereby increasing the likelihood of GC intervention.\n\nmermaid\ngraph LR\n subgraph SQLite Native Layer\n A["SQL Query Execution"] --> B["rb_sqlite3_aggregator_step()"]\n end\n subgraph Heap Allocation (Unregistered)\n B --> C["xcalloc() Array (params)"]\n end\n subgraph Conversion Loop\n C --> D["Convert argv[0] -> params[0]"]\n D --> E["Convert argv[1] (Triggers GC)"]\n end\n subgraph GC Action\n E --> F["GC sweeps params[0] as unreachable"]\n end\n subgraph Execution Phase\n F --> G["step() invoked with freed pointer"]\n G --> H["Memory Corruption / SIGSEGV"]\n end\n\n\nIn a test or staging environment, this race condition can be reliably reproduced by setting the global garbage collector stress flag (GC.stress = true). Under normal operating workloads, Ruby 3.4+ environments are particularly prone to this crash. Because Ruby 3.4 enforces system allocations for strings exceeding 640 bytes, standard datasets trigger enough allocation events to exhaust memory pools, forcing the GC to run dynamically.\n\nThe consequences of executing the aggregate step with a dangling pointer range from silent data corruption to full remote shell execution. If the memory previously occupied by params[0] is overwritten with another Ruby object structure (such as a string or array) before the step block evaluates the argument, the custom code processes forged or out-of-bounds data structures, bypasses type checks, or crashes the host interpreter with a segmentation fault.
The security impact of a Use-After-Free vulnerability in a native Ruby database driver is substantial. In server configurations where the application performs analytical processing on user-supplied inputs, an attacker can manipulate the input values to craft a specific memory alignment. By forcing the GC to reclaim and subsequently replace the structure of a parameter, attackers may achieve arbitrary write primitives within the host process context.\n\nIf successfully exploited, this can lead to remote code execution (RCE) with the permissions of the web service or system daemon running the Ruby interpreter. Even in environments where precise memory layout control is difficult to achieve, the vulnerability causes widespread Denial of Service (DoS) due to the frequency of process-level crashes (SIGSEGV) in unpatched implementations.\n\nThis vulnerability carries a calculated CVSS v3.1 score of 8.1 (High) when assessed in a remote execution context, and 7.0 (High) when assessed in a local context. The vulnerability requires no prior user interaction or authorization privileges to trigger, although it does require that the target application has implemented multi-argument custom aggregate calculations.
The only comprehensive remediation is to update the sqlite3 gem to version 2.9.6 or higher. The patch incorporates the managed memory wrappers that prevent premature sweeping.\n\nOrganizations that cannot immediately apply the library update must implement application-level workarounds. If possible, avoid registering custom aggregate functions that require two or more arguments. Instead, refactor the application to handle multi-column aggregations in Ruby memory after executing simpler database queries, or concatenate parameters into a single argument before passing them to the SQLite query interface.\n\nAdditionally, in production environments where updating native dependencies is delayed, operators can temporarily disable deep memory sweeping operations or limit the execution of heavy data processing jobs to isolated child processes. This ensures that any unexpected interpreter crashes do not affect the main application runtime or disrupt client connections.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
sqlite3 (Ruby gem) sparklemotion | < 2.9.6 | 2.9.6 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-416 |
| Attack Vector | Local / Remote (depending on application exposure) |
| CVSS Score | 8.1 |
| Exploit Status | PoC available in test suite |
| Impact | Memory Corruption / Denial of Service / Code Execution |
| Affected Function | rb_sqlite3_aggregator_step |
| Remediation | Upgrade to sqlite3 >= 2.9.6 |
The software uses an allocated resource after it has been freed, leading to potential instability, memory corruption, or control flow hijack.
A critical vulnerability exists in the praxis-proxy library where the omission of default limits on HTTP/2 server options allows remote attackers to trigger a Denial of Service (DoS) using an HPACK compression bomb and flow-control window stalls. This vulnerability is cataloged as GHSA-cjcg-cxmh-9wcr.
An unauthenticated remote denial of service vulnerability exists in @fastify/busboy versions 3.1.0 through 3.2.0. The vulnerability is caused by an integer wrap-around in the Boyer-Moore-Horspool algorithm implementation inside the sbmh submodule when initializing skip distances. When processing a specific boundary of 252 bytes, the parser triggers an infinite loop, stalling the single-threaded Node.js event loop and exhausting CPU resources.
A critical remote, unauthenticated Denial of Service (DoS) vulnerability in @fastify/busboy (<= 3.2.0) allows attackers to crash the Node.js process. By submitting a crafted multipart/form-data request with a header key matching an inherited property of Object.prototype (like __proto__ or constructor), the internal HeaderParser triggers a synchronous TypeError.
SiYuan is an open-source personal knowledge management system. Its Model Context Protocol (MCP) implementation within the asset.upload tool contains a path-traversal and workspace boundary bypass flaw. This allows remote AI models—acting on behalf of attackers via malicious prompts or documents—to import and read sensitive host-system files, such as private keys and system configurations, through absolute path inputs.
An Server-Side Request Forgery (SSRF) vulnerability via DNS-Rebinding Time-of-Check to Time-of-Use (TOCTOU) has been discovered in SiYuan (思源笔记), an open-source personal knowledge management system. The flaw exists within the AI Agent tools http_request (util.HTTPRequest) and web_fetch (util.WebFetch) of the SiYuan Kernel, allowing unauthenticated remote attackers to bypass SSRF validation and access private internal services or cloud metadata endpoints.
An uncontrolled resource consumption vulnerability (CWE-1333 / CWE-400) exists in probe-image-size versions prior to 7.4.0. The SVG parser utilizes an unanchored, inefficient regular expression to find the SVG root tag, leading to catastrophic backtracking when handling malformed payloads. This blocks the single-threaded Node.js event loop, resulting in a complete denial of service.