summaryrefslogtreecommitdiffstats
path: root/libc/bionic/stubs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libc/bionic/stubs.cpp')
-rw-r--r--libc/bionic/stubs.cpp233
1 files changed, 109 insertions, 124 deletions
diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp
index b1e38be..b57aeda 100644
--- a/libc/bionic/stubs.cpp
+++ b/libc/bionic/stubs.cpp
@@ -35,25 +35,46 @@
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include "private/android_filesystem_config.h"
#include "private/ErrnoRestorer.h"
#include "private/libc_logging.h"
+#include "private/ThreadLocalBuffer.h"
-// 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_;
+// POSIX seems to envisage an implementation where the <pwd.h> functions are
+// implemented by brute-force searching with getpwent(3), and the <grp.h>
+// functions are implemented similarly with getgrent(3). This means that it's
+// okay for all the <grp.h> functions to share state, and all the <passwd.h>
+// functions to share state, but <grp.h> functions can't clobber <passwd.h>
+// functions' state and vice versa.
+
+struct group_state_t {
group group_;
char* group_members_[2];
- char app_name_buffer_[32];
char group_name_buffer_[32];
+};
+
+struct passwd_state_t {
+ passwd passwd_;
+ char name_buffer_[32];
char dir_buffer_[32];
char sh_buffer_[32];
};
+static ThreadLocalBuffer<group_state_t> g_group_tls_buffer;
+static ThreadLocalBuffer<passwd_state_t> g_passwd_tls_buffer;
+
+static group_state_t* __group_state() {
+ group_state_t* result = g_group_tls_buffer.get();
+ if (result != nullptr) {
+ memset(result, 0, sizeof(group_state_t));
+ result->group_.gr_mem = result->group_members_;
+ }
+ 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) {
@@ -91,7 +112,7 @@ static int do_getpw_r(int by_name, const char* name, uid_t uid,
// pw_passwd and pw_gecos are non-POSIX and unused (always NULL) in bionic.
dst->pw_passwd = NULL;
-#ifdef __LP64__
+#if defined(__LP64__)
dst->pw_gecos = NULL;
#endif
@@ -113,48 +134,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<stubs_state_t*>(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<stubs_state_t*>(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<stubs_state_t*>(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;
- }
- }
- }
- return s;
-}
-
-static passwd* android_iinfo_to_passwd(stubs_state_t* state,
+static passwd* android_iinfo_to_passwd(passwd_state_t* state,
const android_id_info* iinfo) {
+ snprintf(state->name_buffer_, sizeof(state->name_buffer_), "%s", iinfo->name);
snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/");
snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/system/bin/sh");
passwd* pw = &state->passwd_;
- pw->pw_name = (char*) iinfo->name;
+ pw->pw_name = state->name_buffer_;
pw->pw_uid = iinfo->aid;
pw->pw_gid = iinfo->aid;
pw->pw_dir = state->dir_buffer_;
@@ -162,16 +149,18 @@ static passwd* android_iinfo_to_passwd(stubs_state_t* state,
return pw;
}
-static group* android_iinfo_to_group(group* gr,
+static group* android_iinfo_to_group(group_state_t* state,
const android_id_info* iinfo) {
- gr->gr_name = (char*) iinfo->name;
+ 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_mem[1] = NULL;
return gr;
}
-static passwd* android_id_to_passwd(stubs_state_t* state, unsigned id) {
+static passwd* android_id_to_passwd(passwd_state_t* state, unsigned id) {
for (size_t n = 0; n < android_id_count; ++n) {
if (android_ids[n].aid == id) {
return android_iinfo_to_passwd(state, android_ids + n);
@@ -180,7 +169,7 @@ static passwd* android_id_to_passwd(stubs_state_t* state, unsigned id) {
return NULL;
}
-static passwd* android_name_to_passwd(stubs_state_t* state, const char* name) {
+static passwd* android_name_to_passwd(passwd_state_t* state, const char* name) {
for (size_t n = 0; n < android_id_count; ++n) {
if (!strcmp(android_ids[n].name, name)) {
return android_iinfo_to_passwd(state, android_ids + n);
@@ -189,37 +178,46 @@ static passwd* android_name_to_passwd(stubs_state_t* state, const char* name) {
return NULL;
}
-static group* android_id_to_group(group* gr, unsigned id) {
+static group* android_id_to_group(group_state_t* state, unsigned id) {
for (size_t n = 0; n < android_id_count; ++n) {
if (android_ids[n].aid == id) {
- return android_iinfo_to_group(gr, android_ids + n);
+ return android_iinfo_to_group(state, android_ids + n);
}
}
return NULL;
}
-static group* android_name_to_group(group* gr, const char* name) {
+static group* android_name_to_group(group_state_t* state, const char* name) {
for (size_t n = 0; n < android_id_count; ++n) {
if (!strcmp(android_ids[n].name, name)) {
- return android_iinfo_to_group(gr, android_ids + n);
+ return android_iinfo_to_group(state, android_ids + n);
}
}
return NULL;
}
// 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 id_t 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<char*>(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 +225,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;
@@ -238,6 +245,7 @@ static unsigned app_id_from_name(const char* name) {
appid = android_ids[n].aid;
// Move the end pointer to the null terminator.
end += strlen(android_ids[n].name) + 1;
+ break;
}
}
}
@@ -260,15 +268,14 @@ static unsigned app_id_from_name(const char* name) {
return 0;
}
- return (unsigned)(appid + userid*AID_USER);
+ return (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 +288,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.
@@ -293,20 +313,15 @@ static void print_app_name_from_uid(const uid_t uid, char* buffer, const int buf
// AID_ISOLATED_START to AID_USER-1 -> u0_i1234
// AID_USER+ -> u1_radio, u1_a1234, u2_i1234, etc.
// returns a passwd structure (sets errno to ENOENT on failure).
-static passwd* app_id_to_passwd(uid_t uid, stubs_state_t* state) {
- passwd* pw = &state->passwd_;
-
+static passwd* app_id_to_passwd(uid_t uid, passwd_state_t* state) {
if (uid < AID_APP) {
errno = ENOENT;
return NULL;
}
- 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_));
+ print_app_name_from_uid(uid, state->name_buffer_, sizeof(state->name_buffer_));
+ const uid_t appid = uid % AID_USER;
if (appid < AID_APP) {
snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/");
} else {
@@ -315,37 +330,34 @@ static passwd* app_id_to_passwd(uid_t uid, stubs_state_t* state) {
snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/system/bin/sh");
- pw->pw_name = state->app_name_buffer_;
+ 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;
}
// Translate a gid into the corresponding app_<gid>
// group structure (sets errno to ENOENT on failure).
-static group* app_id_to_group(gid_t gid, stubs_state_t* state) {
+static group* app_id_to_group(gid_t gid, group_state_t* state) {
if (gid < AID_APP) {
errno = ENOENT;
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;
}
-
passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function.
- stubs_state_t* state = __stubs_state();
+ passwd_state_t* state = g_passwd_tls_buffer.get();
if (state == NULL) {
return NULL;
}
@@ -358,7 +370,7 @@ passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function.
}
passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
- stubs_state_t* state = __stubs_state();
+ passwd_state_t* state = g_passwd_tls_buffer.get();
if (state == NULL) {
return NULL;
}
@@ -367,17 +379,17 @@ 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.
int getgrouplist(const char* /*user*/, gid_t group, gid_t* groups, int* ngroups) {
- if (*ngroups < 1) {
- *ngroups = 1;
- return -1;
- }
- groups[0] = group;
- return (*ngroups = 1);
+ if (*ngroups < 1) {
+ *ngroups = 1;
+ return -1;
+ }
+ groups[0] = group;
+ return (*ngroups = 1);
}
char* getlogin() { // NOLINT: implementing bad function.
@@ -386,30 +398,28 @@ char* getlogin() { // NOLINT: implementing bad function.
}
group* getgrgid(gid_t gid) { // NOLINT: implementing bad function.
- stubs_state_t* state = __stubs_state();
+ group_state_t* state = __group_state();
if (state == NULL) {
return NULL;
}
- group* gr = android_id_to_group(&state->group_, gid);
+ group* gr = android_id_to_group(state, gid);
if (gr != NULL) {
return gr;
}
-
return app_id_to_group(gid, state);
}
group* getgrnam(const char* name) { // NOLINT: implementing bad function.
- stubs_state_t* state = __stubs_state();
+ group_state_t* state = __group_state();
if (state == NULL) {
return NULL;
}
- if (android_name_to_group(&state->group_, name) != 0) {
+ if (android_name_to_group(state, 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.
@@ -432,31 +442,6 @@ protoent* getprotobynumber(int /*proto*/) {
return NULL;
}
-static void unimplemented_stub(const char* function) {
- const char* fmt = "%s(3) is not implemented on Android\n";
- __libc_format_log(ANDROID_LOG_WARN, "libc", fmt, function);
- fprintf(stderr, fmt, function);
-}
-
-#define UNIMPLEMENTED unimplemented_stub(__PRETTY_FUNCTION__)
-
-void endpwent() {
- UNIMPLEMENTED;
-}
-
-char* getusershell() {
- UNIMPLEMENTED;
- return NULL;
-}
-
-void setusershell() {
- UNIMPLEMENTED;
-}
-
-void endusershell() {
- UNIMPLEMENTED;
-}
-
// Portable code should use sysconf(_SC_PAGE_SIZE) directly instead.
int getpagesize() {
// We dont use sysconf(3) here because that drags in stdio, which makes static binaries fat.