Skip to content

Fix segfault when accessing fields/field_types on freed result#1427

Open
jonathan-biskycreative wants to merge 1 commit into
brianmario:masterfrom
jonathan-biskycreative:fix-fields-segfault-on-freed-result
Open

Fix segfault when accessing fields/field_types on freed result#1427
jonathan-biskycreative wants to merge 1 commit into
brianmario:masterfrom
jonathan-biskycreative:fix-fields-segfault-on-freed-result

Conversation

@jonathan-biskycreative
Copy link
Copy Markdown

@jonathan-biskycreative jonathan-biskycreative commented Mar 25, 2026

Summary

Fixes segfault when accessing .fields or .field_types on a Result object after the internal result has been freed. This is particularly common with 0-row query results.

Problem

A segfault occurs in the following sequence:

  1. Query returns 0 rows
  2. Internal row caching iterates (0 iterations), never populating wrapper->fields
  3. Result is freed after iteration completes (0 processed == 0 total)
  4. .fields is called, wrapper->fields is Qnil
  5. mysql_num_fields(wrapper->result) accesses freed memory
  6. SEGFAULT

Fix

This PR includes two improvements:

1. Eager field population (primary fix)

Fields are now populated immediately when a result is created, before any iteration can free the result:

// In rb_mysql_result_to_obj
if (r != NULL && !wrapper->is_streaming) {
  rb_mysql_result_fetch_fields(obj);
}

This prevents the problematic scenario from occurring at all.

2. Safety check (fallback)

The resultFreed check (which already exists in other functions like rb_mysql_result_each_) is now added to the fields accessor functions:

if (wrapper->resultFreed) {
  rb_raise(cMysql2Error, "Result set has already been freed");
}

If an edge case still occurs, this raises a catchable Ruby exception instead of crashing the process.

Testing

This fix has been tested in a production Rails 8.0 application where the segfault was occurring ~9 times per month during queries that return 0 rows. With the eager field population, the issue no longer occurs.

Fixes #1426

@jonathan-biskycreative jonathan-biskycreative force-pushed the fix-fields-segfault-on-freed-result branch from 3b3cd2c to a8b1f7c Compare March 26, 2026 03:12
Add resultFreed check to rb_mysql_result_fetch_fields and
rb_mysql_result_fetch_field_types before accessing wrapper->result.

Additionally, eagerly populate fields when a result is created (for
non-streaming results) to prevent the issue from occurring at all.
This ensures fields are fetched before any iteration can free the
result.

The segfault occurs when:
1. A query returns 0 rows
2. Internal row caching iterates (0 iterations), never populating wrapper->fields
3. Result is freed after iteration completes
4. .fields is called, wrapper->fields is Qnil
5. mysql_num_fields(wrapper->result) accesses freed memory -> SEGFAULT

This check already exists in other functions (e.g., rb_mysql_result_each_)
but was missing in the fields accessor functions.

Based on fix for brianmario#1426
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Segfault in rb_mysql_result_fetch_fields when accessing fields after result freed (0-row results)

1 participant