Ensure ruby_xfree
won't segfault if called after ruby_vm_destruct
#7663
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This commit modifies
ruby_sized_xfree
to handle whenruby_current_vm_ptr
(a.k.a.GET_VM()
) is NULL by callingruby_mimfree
to free the memory without using rb_objspace.[Bug #19580]
Rationale
The POSIX Threads API provides the ability to define a destructor to clean up thread-local storage at thread exit by using
pthread_key_create
to bind a storage key and an associated destructor function, andpthread_setspecific
to associate a value with that key. If a C extension is usingruby_xmalloc
andruby_xfree
to manage memory, then the destructor function will likely callruby_xfree
.There is a small window of time as a process is exiting -- after
ruby_vm_destruct
but before the process exits -- in which a thread may exit and the destructor function called, leading to a segfault.Real-world use case
The real-world scenario motivating this change is libxml2's pthread code which uses
pthread_key_create
to set up a destructor that is called at thread exit to clean up thread-local storage associated with the thread bypthread_setspecific
.Nokogiri has, since 2009, configured libxml2 to use
ruby_xmalloc
andruby_xfree
so that Ruby's memory management is aware of the allocated memory. As a result,ruby_xfree
is being called by the pthread destructor, which may lead to a segfault that looks like:I've reproduced this in Ruby 3.0, 3.1, 3.2, and master by using the following ruby code:
The timing is admittedly hard to reproduce, but Gitlab has seen this in their CI pipelines a few dozen times and it's made easier if the process lifetime is extended by an
atexit
-registered function.