pwd/grp: fix pwd _r reentrancy, new tests, clean up
getpwnam_r() and getpwuid_r() clobber the storage used by getpwnam() and getpwuid(). This isn't likely to be a big issue, but since we do this right for the group functions, fix this as well as add a test. Both use more space in buf than is actually required, but well below their sysconf() suggested values, so we accept that to keep the code concise. Add tests for dealing with unaligned input buffers, particularly for getgrnam_r() and getgrgid_r(), as they require alignment but this wasn't being tested. Refactor common initialization code for both passwd and group state structs. Remove extraneous null pointer checks; the values they were testing were offsets of a previous pointer, so guaranteed to never actually be null. If the underlying pointer is actually null, we're beyond repair anyway, so accept that we'll crash. Test: pwd/grp unit tests Change-Id: I60c4d00e9ab3cf55daf8314c5029fd914025b696
This commit is contained in:
parent
84c29cc446
commit
c57c5bdb7f
|
@ -58,85 +58,31 @@ static GroupFile vendor_group("/vendor/etc/group", "vendor_");
|
|||
// functions to share state, but <grp.h> functions can't clobber <passwd.h>
|
||||
// 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) {
|
||||
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;
|
||||
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_;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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<char*>(__BIONIC_ALIGN(reinterpret_cast<uintptr_t>(buf), sizeof(uintptr_t)));
|
||||
if (p + sizeof(passwd_state_t) > buf + buflen) {
|
||||
return ERANGE;
|
||||
}
|
||||
passwd_state_t* state = reinterpret_cast<passwd_state_t*>(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);
|
||||
}
|
||||
|
|
|
@ -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 <typename T>
|
||||
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];
|
||||
|
|
Loading…
Reference in New Issue