Use rb_ensure instead of guard objects for Dir.pwd

This replaces GC-based buffer guards (rb_imemo_tmpbuf,
TypedData_Wrap_Struct) with rb_ensure to clean up malloc/xmalloc memory
when an exception occurs.
This commit is contained in:
John Hawthorn 2026-02-19 15:09:42 -08:00
parent 06c8b1abfa
commit 126b657bd1
Notes: git 2026-02-20 01:40:12 +00:00
2 changed files with 37 additions and 33 deletions

34
dir.c
View File

@ -1586,23 +1586,29 @@ dir_chdir(VALUE dir)
}
#ifndef _WIN32
static VALUE
getcwd_to_str(VALUE arg)
{
const char *path = (const char *)arg;
#ifdef __APPLE__
return rb_str_normalize_ospath(path, strlen(path));
#else
return rb_str_new2(path);
#endif
}
static VALUE
getcwd_xfree(VALUE arg)
{
xfree((void *)arg);
return Qnil;
}
VALUE
rb_dir_getwd_ospath(void)
{
char *path;
VALUE cwd;
VALUE path_guard;
path_guard = rb_imemo_tmpbuf_new();
path = ruby_getcwd();
rb_imemo_tmpbuf_set_ptr(path_guard, path);
#ifdef __APPLE__
cwd = rb_str_normalize_ospath(path, strlen(path));
#else
cwd = rb_str_new2(path);
#endif
rb_free_tmp_buffer(&path_guard);
return cwd;
char *path = ruby_getcwd();
return rb_ensure(getcwd_to_str, (VALUE)path, getcwd_xfree, (VALUE)path);
}
#endif

36
util.c
View File

@ -529,45 +529,43 @@ ruby_strdup(const char *str)
char *
ruby_getcwd(void)
{
VALUE guard = rb_imemo_tmpbuf_new();
int size = 200;
char *buf = xmalloc(size);
while (!getcwd(buf, size)) {
int e = errno;
if (e != ERANGE) {
rb_free_tmp_buffer(&guard);
xfree(buf);
rb_syserr_fail(e, "getcwd");
}
size *= 2;
rb_imemo_tmpbuf_set_ptr(guard, buf);
buf = xrealloc(buf, size);
xfree(buf);
buf = xmalloc(size);
}
rb_imemo_tmpbuf_set_ptr(guard, NULL);
return buf;
}
# else
static const rb_data_type_t getcwd_buffer_guard_type = {
.wrap_struct_name = "ruby_getcwd_guard",
.function = {
.dfree = free // not xfree.
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
static VALUE
getcwd_strdup(VALUE arg)
{
return (VALUE)ruby_strdup((const char *)arg);
}
static VALUE
getcwd_free(VALUE arg)
{
free((void *)arg);
return Qnil;
}
char *
ruby_getcwd(void)
{
VALUE guard = TypedData_Wrap_Struct((VALUE)0, &getcwd_buffer_guard_type, NULL);
char *buf, *cwd = getcwd(NULL, 0);
RTYPEDDATA_DATA(guard) = cwd;
char *cwd = getcwd(NULL, 0);
if (!cwd) rb_sys_fail("getcwd");
buf = ruby_strdup(cwd); /* allocate by xmalloc */
free(cwd);
RTYPEDDATA_DATA(RB_GC_GUARD(guard)) = NULL;
return buf;
return (char *)rb_ensure(getcwd_strdup, (VALUE)cwd, getcwd_free, (VALUE)cwd);
}
# endif