Bug #19635
closederrno may be overwritten by rb_sprintf if it triggers GC
Description
Here is an excerpt in the trap
function in signal.c
oldfunc = ruby_signal(sig, func);
if (oldfunc == SIG_ERR) rb_sys_fail_str(rb_signo2signm(sig));
ruby_signal
tries to register a signal handling function. If it fails, it returns SIG_ERR
, and errno
is set to by sigaction
to indicate the error.
However, the snippet above calls rb_signo2signm(sig)
before calling rb_sys_fail_str
. rb_signo2signm
allocates a Ruby heap object using rb_sprintf
. The problem is, if this rb_sprintf
triggers GC, the garbage collector may perform may complex operations, including executing obj_free
, calling mmap
to allocate more memory from the OS, etc. They may overwrite the errno
.
So if GC is triggered in the very unfortunate time, the caller of the trap
function will receive an exception, but with the wrong reason. This may cause some tests, such as the test_trap_uncatchable_#{sig}
test cases in test_signal.rb
, to fail.
There are similar use cases in hash.c
, for example:
if (ret) rb_sys_fail_str(rb_sprintf("setenv(%s)", name));
The good practice is always loading from errno
immediately after calling functions that may set errno
, such as sigaction
and setenv
.