diff --git a/libc/bionic/grp_pwd.cpp b/libc/bionic/grp_pwd.cpp index c6e09a287..bd5c27dd1 100644 --- a/libc/bionic/grp_pwd.cpp +++ b/libc/bionic/grp_pwd.cpp @@ -58,85 +58,31 @@ static GroupFile vendor_group("/vendor/etc/group", "vendor_"); // functions to share state, but functions can't clobber // functions' state and vice versa. #include "bionic/pthread_internal.h" -static group_state_t* get_group_tls_buffer() { - return &__get_bionic_tls().group; -} - -static passwd_state_t* get_passwd_tls_buffer() { - return &__get_bionic_tls().passwd; -} static void init_group_state(group_state_t* state) { memset(state, 0, sizeof(group_state_t) - sizeof(state->getgrent_idx)); + state->group_.gr_name = state->group_name_buffer_; state->group_.gr_mem = state->group_members_; + state->group_.gr_mem[0] = state->group_.gr_name; } -static group_state_t* __group_state() { - group_state_t* result = get_group_tls_buffer(); - if (result != nullptr) { - init_group_state(result); - } +static group_state_t* get_group_tls_buffer() { + auto result = &__get_bionic_tls().group; + init_group_state(result); return result; } -static int do_getpw_r(int by_name, const char* name, uid_t uid, - passwd* dst, char* buf, size_t byte_count, - passwd** result) { - // getpwnam_r and getpwuid_r don't modify errno, but library calls we - // make might. - ErrnoRestorer errno_restorer; - *result = nullptr; - - // Our implementation of getpwnam(3) and getpwuid(3) use thread-local - // storage, so we can call them as long as we copy everything out - // before returning. - const passwd* src = by_name ? getpwnam(name) : getpwuid(uid); // NOLINT: see above. - - // POSIX allows failure to find a match to be considered a non-error. - // Reporting success (0) but with *result NULL is glibc's behavior. - if (src == nullptr) { - return (errno == ENOENT) ? 0 : errno; - } - - // Work out where our strings will go in 'buf', and whether we've got - // enough space. - size_t required_byte_count = 0; - dst->pw_name = buf; - required_byte_count += strlen(src->pw_name) + 1; - dst->pw_dir = buf + required_byte_count; - required_byte_count += strlen(src->pw_dir) + 1; - dst->pw_shell = buf + required_byte_count; - required_byte_count += strlen(src->pw_shell) + 1; - if (byte_count < required_byte_count) { - return ERANGE; - } - - // Copy the strings. - snprintf(buf, byte_count, "%s%c%s%c%s", src->pw_name, 0, src->pw_dir, 0, src->pw_shell); - - // pw_passwd and pw_gecos are non-POSIX and unused (always NULL) in bionic. - // Note: On LP32, we define pw_gecos to pw_passwd since they're both NULL. - dst->pw_passwd = nullptr; -#if defined(__LP64__) - dst->pw_gecos = nullptr; -#endif - - // Copy the integral fields. - dst->pw_gid = src->pw_gid; - dst->pw_uid = src->pw_uid; - - *result = dst; - return 0; +static void init_passwd_state(passwd_state_t* state) { + memset(state, 0, sizeof(passwd_state_t) - sizeof(state->getpwent_idx)); + state->passwd_.pw_name = state->name_buffer_; + state->passwd_.pw_dir = state->dir_buffer_; + state->passwd_.pw_shell = state->sh_buffer_; } -int getpwnam_r(const char* name, passwd* pwd, - char* buf, size_t byte_count, passwd** result) { - return do_getpw_r(1, name, -1, pwd, buf, byte_count, result); -} - -int getpwuid_r(uid_t uid, passwd* pwd, - char* buf, size_t byte_count, passwd** result) { - return do_getpw_r(0, nullptr, uid, pwd, buf, byte_count, result); +static passwd_state_t* get_passwd_tls_buffer() { + auto result = &__get_bionic_tls().passwd; + init_passwd_state(result); + return result; } static passwd* android_iinfo_to_passwd(passwd_state_t* state, @@ -146,11 +92,8 @@ static passwd* android_iinfo_to_passwd(passwd_state_t* state, snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/system/bin/sh"); passwd* pw = &state->passwd_; - pw->pw_name = state->name_buffer_; pw->pw_uid = iinfo->aid; pw->pw_gid = iinfo->aid; - pw->pw_dir = state->dir_buffer_; - pw->pw_shell = state->sh_buffer_; return pw; } @@ -159,9 +102,7 @@ static group* android_iinfo_to_group(group_state_t* state, snprintf(state->group_name_buffer_, sizeof(state->group_name_buffer_), "%s", iinfo->name); group* gr = &state->group_; - gr->gr_name = state->group_name_buffer_; - gr->gr_gid = iinfo->aid; - gr->gr_mem[0] = gr->gr_name; + gr->gr_gid = iinfo->aid; return gr; } @@ -435,9 +376,6 @@ static passwd* oem_id_to_passwd(uid_t uid, passwd_state_t* state) { snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/vendor/bin/sh"); passwd* pw = &state->passwd_; - pw->pw_name = state->name_buffer_; - pw->pw_dir = state->dir_buffer_; - pw->pw_shell = state->sh_buffer_; pw->pw_uid = uid; pw->pw_gid = uid; return pw; @@ -456,9 +394,7 @@ static group* oem_id_to_group(gid_t gid, group_state_t* state) { "oem_%u", gid); group* gr = &state->group_; - gr->gr_name = state->group_name_buffer_; - gr->gr_gid = gid; - gr->gr_mem[0] = gr->gr_name; + gr->gr_gid = gid; return gr; } @@ -486,9 +422,6 @@ static passwd* app_id_to_passwd(uid_t uid, passwd_state_t* state) { snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/system/bin/sh"); passwd* pw = &state->passwd_; - pw->pw_name = state->name_buffer_; - pw->pw_dir = state->dir_buffer_; - pw->pw_shell = state->sh_buffer_; pw->pw_uid = uid; pw->pw_gid = uid; return pw; @@ -505,18 +438,11 @@ static group* app_id_to_group(gid_t gid, group_state_t* state) { print_app_name_from_gid(gid, state->group_name_buffer_, sizeof(state->group_name_buffer_)); group* gr = &state->group_; - gr->gr_name = state->group_name_buffer_; - gr->gr_gid = gid; - gr->gr_mem[0] = gr->gr_name; + gr->gr_gid = gid; return gr; } -passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function. - passwd_state_t* state = get_passwd_tls_buffer(); - if (state == nullptr) { - return nullptr; - } - +passwd* getpwuid_internal(uid_t uid, passwd_state_t* state) { if (auto* android_id_info = find_android_id_info(uid); android_id_info != nullptr) { return android_iinfo_to_passwd(state, android_id_info); } @@ -529,12 +455,12 @@ passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function. return app_id_to_passwd(uid, state); } -passwd* getpwnam(const char* login) { // NOLINT: implementing bad function. +passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function. passwd_state_t* state = get_passwd_tls_buffer(); - if (state == nullptr) { - return nullptr; - } + return getpwuid_internal(uid, state); +} +passwd* getpwnam_internal(const char* login, passwd_state_t* state) { if (auto* android_id_info = find_android_id_info(login); android_id_info != nullptr) { return android_iinfo_to_passwd(state, android_id_info); } @@ -553,6 +479,39 @@ passwd* getpwnam(const char* login) { // NOLINT: implementing bad function. return app_id_to_passwd(app_id_from_name(login, false), state); } +passwd* getpwnam(const char* login) { // NOLINT: implementing bad function. + passwd_state_t* state = get_passwd_tls_buffer(); + return getpwnam_internal(login, state); +} + +static int getpasswd_r(bool by_name, const char* name, uid_t uid, struct passwd* pwd, char* buf, + size_t buflen, struct passwd** result) { + ErrnoRestorer errno_restorer; + *result = nullptr; + char* p = + reinterpret_cast(__BIONIC_ALIGN(reinterpret_cast(buf), sizeof(uintptr_t))); + if (p + sizeof(passwd_state_t) > buf + buflen) { + return ERANGE; + } + passwd_state_t* state = reinterpret_cast(p); + init_passwd_state(state); + passwd* retval = (by_name ? getpwnam_internal(name, state) : getpwuid_internal(uid, state)); + if (retval != nullptr) { + *pwd = *retval; + *result = pwd; + return 0; + } + return errno; +} + +int getpwnam_r(const char* name, passwd* pwd, char* buf, size_t byte_count, passwd** result) { + return getpasswd_r(true, name, -1, pwd, buf, byte_count, result); +} + +int getpwuid_r(uid_t uid, passwd* pwd, char* buf, size_t byte_count, passwd** result) { + return getpasswd_r(false, nullptr, uid, pwd, buf, byte_count, result); +} + // All users are in just one group, the one passed in. int getgrouplist(const char* /*user*/, gid_t group, gid_t* groups, int* ngroups) { if (*ngroups < 1) { @@ -590,9 +549,6 @@ void endpwent() { passwd* getpwent() { passwd_state_t* state = get_passwd_tls_buffer(); - if (state == nullptr) { - return nullptr; - } if (state->getpwent_idx < 0) { return nullptr; } @@ -643,10 +599,7 @@ static group* getgrgid_internal(gid_t gid, group_state_t* state) { } group* getgrgid(gid_t gid) { // NOLINT: implementing bad function. - group_state_t* state = __group_state(); - if (state == nullptr) { - return nullptr; - } + group_state_t* state = get_group_tls_buffer(); return getgrgid_internal(gid, state); } @@ -670,10 +623,7 @@ static group* getgrnam_internal(const char* name, group_state_t* state) { } group* getgrnam(const char* name) { // NOLINT: implementing bad function. - group_state_t* state = __group_state(); - if (state == nullptr) { - return nullptr; - } + group_state_t* state = get_group_tls_buffer(); return getgrnam_internal(name, state); } @@ -719,9 +669,6 @@ void endgrent() { group* getgrent() { group_state_t* state = get_group_tls_buffer(); - if (state == nullptr) { - return nullptr; - } if (state->getgrent_idx < 0) { return nullptr; } @@ -729,7 +676,6 @@ group* getgrent() { size_t start = 0; ssize_t end = android_id_count; if (state->getgrent_idx < end) { - init_group_state(state); return android_iinfo_to_group(state, android_ids + state->getgrent_idx++); } @@ -737,7 +683,6 @@ group* getgrent() { end += AID_OEM_RESERVED_END - AID_OEM_RESERVED_START + 1; if (state->getgrent_idx < end) { - init_group_state(state); return oem_id_to_group( state->getgrent_idx++ - start + AID_OEM_RESERVED_START, state); } @@ -746,7 +691,6 @@ group* getgrent() { end += AID_OEM_RESERVED_2_END - AID_OEM_RESERVED_2_START + 1; if (state->getgrent_idx < end) { - init_group_state(state); return oem_id_to_group( state->getgrent_idx++ - start + AID_OEM_RESERVED_2_START, state); } diff --git a/tests/grp_pwd_test.cpp b/tests/grp_pwd_test.cpp index b46839bf4..ebc357c4b 100644 --- a/tests/grp_pwd_test.cpp +++ b/tests/grp_pwd_test.cpp @@ -217,6 +217,89 @@ TEST(pwd, getpwnam_app_id_u1_a40000) { TEST(pwd, getpwnam_app_id_u1_i0) { check_get_passwd("u1_i0", 190000, TYPE_APP); } + +TEST(pwd, getpwnam_r_alignment) { +#if defined(__BIONIC__) + passwd pwd_storage; + alignas(16) char buf[512]; + passwd* pwd; + int result = getpwnam_r("root", &pwd_storage, buf + 1, sizeof(buf) - 1, &pwd); + ASSERT_EQ(0, result); + check_passwd(pwd, "root", 0, TYPE_SYSTEM, true); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} + +TEST(pwd, getpwuid_r_alignment) { +#if defined(__BIONIC__) + passwd pwd_storage; + alignas(16) char buf[512]; + passwd* pwd; + int result = getpwuid_r(0, &pwd_storage, buf + 1, sizeof(buf) - 1, &pwd); + ASSERT_EQ(0, result); + check_passwd(pwd, "root", 0, TYPE_SYSTEM, true); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} + +TEST(pwd, getpwnam_r_reentrancy) { +#if defined(__BIONIC__) + passwd pwd_storage[2]; + char buf[2][512]; + passwd* pwd[3]; + int result = getpwnam_r("root", &pwd_storage[0], buf[0], sizeof(buf[0]), &pwd[0]); + ASSERT_EQ(0, result); + check_passwd(pwd[0], "root", 0, TYPE_SYSTEM, true); + pwd[1] = getpwnam("system"); + ASSERT_NE(nullptr, pwd[1]); + check_passwd(pwd[1], "system", 1000, TYPE_SYSTEM, true); + result = getpwnam_r("radio", &pwd_storage[1], buf[1], sizeof(buf[1]), &pwd[2]); + ASSERT_EQ(0, result); + check_passwd(pwd[2], "radio", 1001, TYPE_SYSTEM, true); + check_passwd(pwd[0], "root", 0, TYPE_SYSTEM, true); + check_passwd(pwd[1], "system", 1000, TYPE_SYSTEM, true); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} + +TEST(pwd, getpwuid_r_reentrancy) { +#if defined(__BIONIC__) + passwd pwd_storage[2]; + char buf[2][512]; + passwd* pwd[3]; + int result = getpwuid_r(0, &pwd_storage[0], buf[0], sizeof(buf[0]), &pwd[0]); + ASSERT_EQ(0, result); + check_passwd(pwd[0], "root", 0, TYPE_SYSTEM, true); + pwd[1] = getpwuid(1000); + ASSERT_NE(nullptr, pwd[1]); + check_passwd(pwd[1], "system", 1000, TYPE_SYSTEM, true); + result = getpwuid_r(1001, &pwd_storage[1], buf[1], sizeof(buf[1]), &pwd[2]); + ASSERT_EQ(0, result); + check_passwd(pwd[2], "radio", 1001, TYPE_SYSTEM, true); + check_passwd(pwd[0], "root", 0, TYPE_SYSTEM, true); + check_passwd(pwd[1], "system", 1000, TYPE_SYSTEM, true); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} + +TEST(pwd, getpwnam_r_large_enough_suggested_buffer_size) { +#if defined(__BIONIC__) + long size = sysconf(_SC_GETPW_R_SIZE_MAX); + ASSERT_GT(size, 0); + char buf[size]; + passwd pwd_storage; + passwd* pwd; + ASSERT_EQ(0, getpwnam_r("root", &pwd_storage, buf, size, &pwd)); + check_passwd(pwd, "root", 0, TYPE_SYSTEM, true); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} + #if defined(__BIONIC__) template static void expect_ids(const T& ids) { @@ -477,6 +560,32 @@ TEST(grp, getgrnam_app_id_u1_i0) { check_get_group("u1_i0", 190000); } +TEST(grp, getgrnam_r_alignment) { +#if defined(__BIONIC__) + group grp_storage; + alignas(16) char buf[512]; + group* grp; + int result = getgrnam_r("root", &grp_storage, buf + 1, sizeof(buf) - 1, &grp); + ASSERT_EQ(0, result); + check_group(grp, "root", 0); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} + +TEST(grp, getgrgid_r_alignment) { +#if defined(__BIONIC__) + group grp_storage; + alignas(16) char buf[512]; + group* grp; + int result = getgrgid_r(0, &grp_storage, buf + 1, sizeof(buf) - 1, &grp); + ASSERT_EQ(0, result); + check_group(grp, "root", 0); +#else + GTEST_SKIP() << "bionic-only test"; +#endif +} + TEST(grp, getgrnam_r_reentrancy) { #if defined(__BIONIC__) group grp_storage[2];