diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp index b1e38be8f..88e5ac5ed 100644 --- a/libc/bionic/stubs.cpp +++ b/libc/bionic/stubs.cpp @@ -40,10 +40,10 @@ #include "private/android_filesystem_config.h" #include "private/ErrnoRestorer.h" #include "private/libc_logging.h" +#include "private/ThreadLocalBuffer.h" + +GLOBAL_INIT_THREAD_LOCAL_BUFFER(stubs); -// Thread-specific state for the non-reentrant functions. -static pthread_once_t stubs_once = PTHREAD_ONCE_INIT; -static pthread_key_t stubs_key; struct stubs_state_t { passwd passwd_; group group_; @@ -113,39 +113,14 @@ int getpwuid_r(uid_t uid, passwd* pwd, return do_getpw_r(0, NULL, uid, pwd, buf, byte_count, result); } -static stubs_state_t* stubs_state_alloc() { - stubs_state_t* s = static_cast(calloc(1, sizeof(*s))); - if (s != NULL) { - s->group_.gr_mem = s->group_members_; - } - return s; -} - -static void stubs_state_free(void* ptr) { - stubs_state_t* state = static_cast(ptr); - free(state); -} - -static void __stubs_key_init() { - pthread_key_create(&stubs_key, stubs_state_free); -} - static stubs_state_t* __stubs_state() { - pthread_once(&stubs_once, __stubs_key_init); - stubs_state_t* s = static_cast(pthread_getspecific(stubs_key)); - if (s == NULL) { - s = stubs_state_alloc(); - if (s == NULL) { - errno = ENOMEM; // Just in case. - } else { - if (pthread_setspecific(stubs_key, s) != 0) { - stubs_state_free(s); - errno = ENOMEM; - s = NULL; - } - } + LOCAL_INIT_THREAD_LOCAL_BUFFER(stubs_state_t*, stubs, sizeof(stubs_state_t)); + + if (stubs_tls_buffer != NULL) { + memset(stubs_tls_buffer, 0, sizeof(stubs_state_t)); + stubs_tls_buffer->group_.gr_mem = stubs_tls_buffer->group_members_; } - return s; + return stubs_tls_buffer; } static passwd* android_iinfo_to_passwd(stubs_state_t* state, @@ -167,7 +142,6 @@ static group* android_iinfo_to_group(group* gr, gr->gr_name = (char*) iinfo->name; gr->gr_gid = iinfo->aid; gr->gr_mem[0] = gr->gr_name; - gr->gr_mem[1] = NULL; return gr; } @@ -208,18 +182,27 @@ static group* android_name_to_group(group* gr, const char* name) { } // Translate a user/group name to the corresponding user/group id. +// all_a1234 -> 0 * AID_USER + AID_SHARED_GID_START + 1234 (group name only) // u0_a1234 -> 0 * AID_USER + AID_APP + 1234 // u2_i1000 -> 2 * AID_USER + AID_ISOLATED_START + 1000 // u1_system -> 1 * AID_USER + android_ids['system'] // returns 0 and sets errno to ENOENT in case of error -static unsigned app_id_from_name(const char* name) { - if (name[0] != 'u' || !isdigit(name[1])) { +static unsigned app_id_from_name(const char* name, bool is_group) { + char* end; + unsigned long userid; + bool is_shared_gid = false; + + if (is_group && name[0] == 'a' && name[1] == 'l' && name[2] == 'l') { + end = const_cast(name+3); + userid = 0; + is_shared_gid = true; + } else if (name[0] == 'u' && isdigit(name[1])) { + userid = strtoul(name+1, &end, 10); + } else { errno = ENOENT; return 0; } - char* end; - unsigned long userid = strtoul(name+1, &end, 10); if (end[0] != '_' || end[1] == 0) { errno = ENOENT; return 0; @@ -227,8 +210,17 @@ static unsigned app_id_from_name(const char* name) { unsigned long appid = 0; if (end[1] == 'a' && isdigit(end[2])) { - // end will point to \0 if the strtoul below succeeds. - appid = strtoul(end+2, &end, 10) + AID_APP; + if (is_shared_gid) { + // end will point to \0 if the strtoul below succeeds. + appid = strtoul(end+2, &end, 10) + AID_SHARED_GID_START; + if (appid > AID_SHARED_GID_END) { + errno = ENOENT; + return 0; + } + } else { + // end will point to \0 if the strtoul below succeeds. + appid = strtoul(end+2, &end, 10) + AID_APP; + } } else if (end[1] == 'i' && isdigit(end[2])) { // end will point to \0 if the strtoul below succeeds. appid = strtoul(end+2, &end, 10) + AID_ISOLATED_START; @@ -263,12 +255,11 @@ static unsigned app_id_from_name(const char* name) { return (unsigned)(appid + userid*AID_USER); } -static void print_app_name_from_appid_userid(const uid_t appid, - const uid_t userid, char* buffer, const int bufferlen) { +static void print_app_name_from_uid(const uid_t uid, char* buffer, const int bufferlen) { + const uid_t appid = uid % AID_USER; + const uid_t userid = uid / AID_USER; if (appid >= AID_ISOLATED_START) { snprintf(buffer, bufferlen, "u%u_i%u", userid, appid - AID_ISOLATED_START); - } else if (userid == 0 && appid >= AID_SHARED_GID_START) { - snprintf(buffer, bufferlen, "all_a%u", appid - AID_SHARED_GID_START); } else if (appid < AID_APP) { for (size_t n = 0; n < android_id_count; n++) { if (android_ids[n].aid == appid) { @@ -281,10 +272,23 @@ static void print_app_name_from_appid_userid(const uid_t appid, } } -static void print_app_name_from_uid(const uid_t uid, char* buffer, const int bufferlen) { - const uid_t appid = uid % AID_USER; - const uid_t userid = uid / AID_USER; - return print_app_name_from_appid_userid(appid, userid, buffer, bufferlen); +static void print_app_name_from_gid(const gid_t gid, char* buffer, const int bufferlen) { + const uid_t appid = gid % AID_USER; + const uid_t userid = gid / AID_USER; + if (appid >= AID_ISOLATED_START) { + snprintf(buffer, bufferlen, "u%u_i%u", userid, appid - AID_ISOLATED_START); + } else if (userid == 0 && appid >= AID_SHARED_GID_START && appid <= AID_SHARED_GID_END) { + snprintf(buffer, bufferlen, "all_a%u", appid - AID_SHARED_GID_START); + } else if (appid < AID_APP) { + for (size_t n = 0; n < android_id_count; n++) { + if (android_ids[n].aid == appid) { + snprintf(buffer, bufferlen, "u%u_%s", userid, android_ids[n].name); + return; + } + } + } else { + snprintf(buffer, bufferlen, "u%u_a%u", userid, appid - AID_APP); + } } // Translate a uid into the corresponding name. @@ -301,12 +305,9 @@ static passwd* app_id_to_passwd(uid_t uid, stubs_state_t* state) { return NULL; } + print_app_name_from_uid(uid, state->app_name_buffer_, sizeof(state->app_name_buffer_)); + const uid_t appid = uid % AID_USER; - const uid_t userid = uid / AID_USER; - - print_app_name_from_appid_userid(appid, userid, state->app_name_buffer_, - sizeof(state->app_name_buffer_)); - if (appid < AID_APP) { snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/"); } else { @@ -332,14 +333,13 @@ static group* app_id_to_group(gid_t gid, stubs_state_t* state) { return NULL; } - print_app_name_from_uid(gid, state->group_name_buffer_, - sizeof(state->group_name_buffer_)); + 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_mem[1] = NULL; + return gr; } @@ -367,7 +367,7 @@ passwd* getpwnam(const char* login) { // NOLINT: implementing bad function. if (pw != NULL) { return pw; } - return app_id_to_passwd(app_id_from_name(login), state); + return app_id_to_passwd(app_id_from_name(login, false), state); } // All users are in just one group, the one passed in. @@ -395,7 +395,6 @@ group* getgrgid(gid_t gid) { // NOLINT: implementing bad function. if (gr != NULL) { return gr; } - return app_id_to_group(gid, state); } @@ -408,8 +407,7 @@ group* getgrnam(const char* name) { // NOLINT: implementing bad function. if (android_name_to_group(&state->group_, name) != 0) { return &state->group_; } - - return app_id_to_group(app_id_from_name(name), state); + return app_id_to_group(app_id_from_name(name, true), state); } // We don't have an /etc/networks, so all inputs return NULL. diff --git a/tests/stubs_test.cpp b/tests/stubs_test.cpp index 9b0c2317d..89d67cbb8 100644 --- a/tests/stubs_test.cpp +++ b/tests/stubs_test.cpp @@ -16,98 +16,246 @@ #include -#include -#include +// Below are the header files we want to test. +#include #include + #include #include +#include +#include #include -#if defined(__BIONIC__) -#define CHECK_GETPWNAM_FOR(username, uid, uid_type) \ - SCOPED_TRACE(username); \ - ASSERT_NO_FATAL_FAILURE(check_getpwnam(username, uid, uid_type)); - -typedef enum { +enum uid_type_t { TYPE_SYSTEM, TYPE_APP -} uid_type_t; +}; -static void check_getpwnam(const char* username, uid_t uid, uid_type_t uid_type) { - errno = 0; - passwd* pwd = getpwuid(uid); +#if defined(__BIONIC__) + +static void check_passwd(const passwd* pwd, const char* username, uid_t uid, uid_type_t uid_type) { ASSERT_TRUE(pwd != NULL); - ASSERT_EQ(0, errno); - EXPECT_STREQ(username, pwd->pw_name); - EXPECT_EQ(uid, pwd->pw_uid); - EXPECT_EQ(uid, pwd->pw_gid); + ASSERT_STREQ(username, pwd->pw_name); + ASSERT_EQ(uid, pwd->pw_uid); + ASSERT_EQ(uid, pwd->pw_gid); ASSERT_EQ(NULL, pwd->pw_passwd); #ifdef __LP64__ ASSERT_EQ(NULL, pwd->pw_gecos); #endif if (uid_type == TYPE_SYSTEM) { - EXPECT_STREQ("/", pwd->pw_dir); - } else if (uid_type == TYPE_APP) { - EXPECT_STREQ("/data", pwd->pw_dir); + ASSERT_STREQ("/", pwd->pw_dir); + } else { + ASSERT_STREQ("/data", pwd->pw_dir); } - - EXPECT_STREQ("/system/bin/sh", pwd->pw_shell); + ASSERT_STREQ("/system/bin/sh", pwd->pw_shell); } -#else -#define CHECK_GETPWNAM_FOR(username, uid, uid_type) \ - GTEST_LOG_(INFO) << "This test does nothing.\n"; + +static void check_getpwuid(const char* username, uid_t uid, uid_type_t uid_type) { + errno = 0; + passwd* pwd = getpwuid(uid); + ASSERT_EQ(0, errno); + SCOPED_TRACE("getpwuid"); + check_passwd(pwd, username, uid, uid_type); +} + +static void check_getpwnam(const char* username, uid_t uid, uid_type_t uid_type) { + errno = 0; + passwd* pwd = getpwnam(username); + ASSERT_EQ(0, errno); + SCOPED_TRACE("getpwnam"); + check_passwd(pwd, username, uid, uid_type); +} + +static void check_getpwuid_r(const char* username, uid_t uid, uid_type_t uid_type) { + passwd pwd_storage; + char buf[512]; + int result; + + errno = 0; + passwd* pwd = NULL; + result = getpwuid_r(uid, &pwd_storage, buf, sizeof(buf), &pwd); + ASSERT_EQ(0, result); + ASSERT_EQ(0, errno); + SCOPED_TRACE("getpwuid_r"); + check_passwd(pwd, username, uid, uid_type); +} + +static void check_getpwnam_r(const char* username, uid_t uid, uid_type_t uid_type) { + passwd pwd_storage; + char buf[512]; + int result; + + errno = 0; + passwd* pwd = NULL; + result = getpwnam_r(username, &pwd_storage, buf, sizeof(buf), &pwd); + ASSERT_EQ(0, result); + ASSERT_EQ(0, errno); + SCOPED_TRACE("getpwnam_r"); + check_passwd(pwd, username, uid, uid_type); +} + +static void check_get_passwd(const char* username, uid_t uid, uid_type_t uid_type) { + check_getpwuid(username, uid, uid_type); + check_getpwnam(username, uid, uid_type); + check_getpwuid_r(username, uid, uid_type); + check_getpwnam_r(username, uid, uid_type); +} + +#else // !defined(__BIONIC__) + +static void check_get_passwd(const char* /* username */, uid_t /* uid */, uid_type_t /* uid_type */) { + GTEST_LOG_(INFO) << "This test is about uid/username translation for Android, which does nothing on libc other than bionic.\n"; +} + #endif TEST(getpwnam, system_id_root) { - CHECK_GETPWNAM_FOR("root", 0, TYPE_SYSTEM); + check_get_passwd("root", 0, TYPE_SYSTEM); } TEST(getpwnam, system_id_system) { - CHECK_GETPWNAM_FOR("system", 1000, TYPE_SYSTEM); + check_get_passwd("system", 1000, TYPE_SYSTEM); } TEST(getpwnam, app_id_radio) { - CHECK_GETPWNAM_FOR("radio", 1001, TYPE_SYSTEM); + check_get_passwd("radio", 1001, TYPE_SYSTEM); } TEST(getpwnam, app_id_nobody) { - CHECK_GETPWNAM_FOR("nobody", 9999, TYPE_SYSTEM); -} - -TEST(getpwnam, app_id_all_a0) { - CHECK_GETPWNAM_FOR("all_a0", 50000, TYPE_APP); -} - -TEST(getpwnam, app_id_u1_a40000) { - CHECK_GETPWNAM_FOR("u1_a40000", 150000, TYPE_APP); + check_get_passwd("nobody", 9999, TYPE_SYSTEM); } TEST(getpwnam, app_id_u0_a0) { - CHECK_GETPWNAM_FOR("u0_a0", 10000, TYPE_APP); + check_get_passwd("u0_a0", 10000, TYPE_APP); } TEST(getpwnam, app_id_u0_a1234) { - CHECK_GETPWNAM_FOR("u0_a1234", 11234, TYPE_APP); + check_get_passwd("u0_a1234", 11234, TYPE_APP); } -TEST(getpwnam, app_id_u0_a9999) { - CHECK_GETPWNAM_FOR("u0_a9999", 19999, TYPE_APP); +// Test the difference between uid and shared gid. +TEST(getpwnam, app_id_u0_a49999) { + check_get_passwd("u0_a49999", 59999, TYPE_APP); +} + +TEST(getpwnam, app_id_u0_i1) { + check_get_passwd("u0_i1", 99001, TYPE_APP); } -// nonsensical, but expected TEST(getpwnam, app_id_u1_root) { - CHECK_GETPWNAM_FOR("u1_root", 100000, TYPE_SYSTEM); + check_get_passwd("u1_root", 100000, TYPE_SYSTEM); } TEST(getpwnam, app_id_u1_radio) { - CHECK_GETPWNAM_FOR("u1_radio", 101001, TYPE_SYSTEM); + check_get_passwd("u1_radio", 101001, TYPE_SYSTEM); } TEST(getpwnam, app_id_u1_a0) { - CHECK_GETPWNAM_FOR("u1_a0", 110000, TYPE_APP); + check_get_passwd("u1_a0", 110000, TYPE_APP); +} + +TEST(getpwnam, app_id_u1_a40000) { + check_get_passwd("u1_a40000", 150000, TYPE_APP); } TEST(getpwnam, app_id_u1_i0) { - CHECK_GETPWNAM_FOR("u1_i0", 199000, TYPE_APP); + check_get_passwd("u1_i0", 199000, TYPE_APP); +} + +#if defined(__BIONIC__) + +static void check_group(const group* grp, const char* group_name, gid_t gid) { + ASSERT_TRUE(grp != NULL); + ASSERT_STREQ(group_name, grp->gr_name); + ASSERT_EQ(gid, grp->gr_gid); + ASSERT_TRUE(grp->gr_mem != NULL); + ASSERT_STREQ(group_name, grp->gr_mem[0]); + ASSERT_TRUE(grp->gr_mem[1] == NULL); +} + +static void check_getgrgid(const char* group_name, gid_t gid) { + errno = 0; + group* grp = getgrgid(gid); + ASSERT_EQ(0, errno); + SCOPED_TRACE("getgrgid"); + check_group(grp, group_name, gid); +} + +static void check_getgrnam(const char* group_name, gid_t gid) { + errno = 0; + group* grp = getgrnam(group_name); + ASSERT_EQ(0, errno); + SCOPED_TRACE("getgrnam"); + check_group(grp, group_name, gid); +} + +static void check_get_group(const char* group_name, gid_t gid) { + check_getgrgid(group_name, gid); + check_getgrnam(group_name, gid); +} + +#else // !defined(__BIONIC__) + +static void check_get_group(const char* /* group_name */, gid_t /* gid */) { + GTEST_LOG_(INFO) << "This test is about gid/group_name translation for Android, which does nothing on libc other than bionic.\n"; +} + +#endif + +TEST(getgrnam, system_id_root) { + check_get_group("root", 0); +} + +TEST(getgrnam, system_id_system) { + check_get_group("system", 1000); +} + +TEST(getgrnam, app_id_radio) { + check_get_group("radio", 1001); +} + +TEST(getgrnam, app_id_nobody) { + check_get_group("nobody", 9999); +} + +TEST(getgrnam, app_id_u0_a0) { + check_get_group("u0_a0", 10000); +} + +TEST(getgrnam, app_id_u0_a1234) { + check_get_group("u0_a1234", 11234); +} + +TEST(getgrnam, app_id_u0_a9999) { + check_get_group("u0_a9999", 19999); +} + +// Test the difference between uid and shared gid. +TEST(getgrnam, app_id_all_a9999) { + check_get_group("all_a9999", 59999); +} + +TEST(getgrnam, app_id_u0_i1) { + check_get_group("u0_i1", 99001); +} + +TEST(getgrnam, app_id_u1_root) { + check_get_group("u1_root", 100000); +} + +TEST(getgrnam, app_id_u1_radio) { + check_get_group("u1_radio", 101001); +} + +TEST(getgrnam, app_id_u1_a0) { + check_get_group("u1_a0", 110000); +} + +TEST(getgrnam, app_id_u1_a40000) { + check_get_group("u1_a40000", 150000); +} + +TEST(getgrnam, app_id_u1_i0) { + check_get_group("u1_i0", 199000); }