The vulnerability, GHSA-mwm8-39rw-8826, describes a use-after-free issue in sqlite3-ruby when defining aggregate functions with two or more arguments and evaluating them over TEXT or BLOB column values. The core problem lies in the garbage collection mechanism interacting with the argument conversion process.
The provided commit cc5ac0cdf05af8148f4363410da0ba32508d9c97 directly addresses this vulnerability. The commit message explicitly states: "Root the aggregate argument array so GC cannot free live values (GHSA-mwm8-39rw-8826)" and details that rb_sqlite3_aggregator_step was converting arguments into an xcalloc'd VALUE array that was not a GC root. This allowed sqlite3val2rb to trigger a GC that could free objects already stored in the array, leading to use-after-free or segmentation faults.
The patch modifies the rb_sqlite3_aggregator_step function in ext/sqlite3/aggregator.c. Specifically, it changes the memory allocation for the params array from xcalloc to ALLOCV_N and the deallocation from xfree to ALLOCV_END. ALLOCV_N is a Ruby-specific allocation macro that ensures the allocated memory is properly registered with the garbage collector, preventing premature collection of the Ruby objects stored within the array.
Therefore, the rb_sqlite3_aggregator_step function is identified as the vulnerable function because it contained the logic that, in its unpatched state, failed to correctly manage the memory of Ruby objects passed as arguments to aggregate functions, leading to the use-after-free vulnerability. When an application uses Database#create_aggregate, #create_aggregate_handler, or Database#define_aggregator with two or more arguments and processes TEXT or BLOB values, the rb_sqlite3_aggregator_step function would be called. During the conversion of arguments within this function, a garbage collection event could occur, leading to the premature freeing of previously converted arguments, resulting in a use-after-free condition when the aggregate's step method attempts to access these freed objects. This would manifest as an incorrect object being passed to the step method or a process crash.