Release GVL for get{pwnam,pwuid,grgid,grnam}_r calls in process.c

Do not release GVL around get{pwuid,pwnam,grgid,grnam} calls,
as doing so is not thread-safe.  Another C extension could have
a concurrent call, and derefencing the returned pointer from
these calls could result in a segfault.

Have rb_home_dir_of call rb_getpwdirnam_for_login if available,
so it can use getpwnam_r and release GVL in a thread-safe manner.

This is related to GVL releasing work in [Bug #20587].
This commit is contained in:
Jeremy Evans 2024-07-18 12:08:06 -07:00
parent b10500b72b
commit ad761ad2d0
Notes: git 2024-09-12 14:24:21 +00:00
2 changed files with 99 additions and 49 deletions

23
file.c
View file

@ -3690,25 +3690,20 @@ copy_home_path(VALUE result, const char *dir)
return result;
}
#ifdef HAVE_PWD_H
static void *
nogvl_getpwnam(void *login)
{
return (void *)getpwnam((const char *)login);
}
#endif
VALUE
rb_home_dir_of(VALUE user, VALUE result)
{
#ifdef HAVE_PWD_H
struct passwd *pwPtr;
VALUE dirname = rb_getpwdirnam_for_login(user);
if (dirname == Qnil) {
rb_raise(rb_eArgError, "user %"PRIsVALUE" doesn't exist", user);
}
const char *dir = RSTRING_PTR(dirname);
#else
extern char *getlogin(void);
const char *pwPtr = 0;
const char *login;
# define endpwent() ((void)0)
#endif
const char *dir, *username = RSTRING_PTR(user);
rb_encoding *enc = rb_enc_get(user);
#if defined _WIN32
@ -3720,21 +3715,13 @@ rb_home_dir_of(VALUE user, VALUE result)
dir = username = RSTRING_PTR(rb_str_conv_enc(user, enc, fsenc));
}
#ifdef HAVE_PWD_H
pwPtr = (struct passwd *)IO_WITHOUT_GVL(nogvl_getpwnam, (void *)username);
#else
if ((login = getlogin()) && strcasecmp(username, login) == 0)
dir = pwPtr = getenv("HOME");
#endif
if (!pwPtr) {
endpwent();
rb_raise(rb_eArgError, "user %"PRIsVALUE" doesn't exist", user);
}
#ifdef HAVE_PWD_H
dir = pwPtr->pw_dir;
#endif
copy_home_path(result, dir);
endpwent();
return result;
}