
We had two copies of the backtrace code, and two copies of the libcorkscrew /proc/pid/maps code. This patch gets us down to one. We also had hacks so we could log in the malloc debugging code. This patch pulls the non-allocating "printf" code out of the dynamic linker so everyone can share. This patch also makes the leak diagnostics easier to read, and makes it possible to paste them directly into the 'stack' tool (by using relative PCs). This patch also fixes the stdio standard stream leak that was causing a leak warning every time tf_daemon ran. Bug: 7291287 Change-Id: I66e4083ac2c5606c8d2737cb45c8ac8a32c7cfe8
476 lines
14 KiB
C++
476 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <grp.h>
|
|
#include <mntent.h>
|
|
#include <netdb.h>
|
|
#include <private/android_filesystem_config.h>
|
|
#include <private/debug_format.h>
|
|
#include <private/logd.h>
|
|
#include <pthread.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.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_;
|
|
group group_;
|
|
char* group_members_[2];
|
|
char app_name_buffer_[32];
|
|
char group_name_buffer_[32];
|
|
char dir_buffer_[32];
|
|
char sh_buffer_[32];
|
|
};
|
|
|
|
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.
|
|
int old_errno = errno;
|
|
*result = NULL;
|
|
|
|
// 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 == NULL) {
|
|
int rc = (errno == ENOENT) ? 0 : errno;
|
|
errno = old_errno;
|
|
return rc;
|
|
}
|
|
|
|
// 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) {
|
|
errno = old_errno;
|
|
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 is non-POSIX and unused (always NULL) in bionic.
|
|
// pw_gecos is non-POSIX and missing in bionic.
|
|
dst->pw_passwd = NULL;
|
|
|
|
// Copy the integral fields.
|
|
dst->pw_gid = src->pw_gid;
|
|
dst->pw_uid = src->pw_uid;
|
|
|
|
*result = dst;
|
|
errno = old_errno;
|
|
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, 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,
|
|
const android_id_info* iinfo) {
|
|
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_uid = iinfo->aid;
|
|
pw->pw_gid = iinfo->aid;
|
|
pw->pw_dir = state->dir_buffer_;
|
|
pw->pw_shell = state->sh_buffer_;
|
|
return pw;
|
|
}
|
|
|
|
static group* android_iinfo_to_group(group* gr,
|
|
const android_id_info* iinfo) {
|
|
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;
|
|
}
|
|
|
|
static passwd* android_id_to_passwd(stubs_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);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static passwd* android_name_to_passwd(stubs_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);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static group* android_id_to_group(group* gr, 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 NULL;
|
|
}
|
|
|
|
static group* android_name_to_group(group* gr, 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 NULL;
|
|
}
|
|
|
|
// Translate a user/group name to the corresponding user/group id.
|
|
// 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])) {
|
|
errno = ENOENT;
|
|
return 0;
|
|
}
|
|
|
|
char* end;
|
|
unsigned long userid = strtoul(name+1, &end, 10);
|
|
if (end[0] != '_' || end[1] == 0) {
|
|
errno = ENOENT;
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
} 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;
|
|
} else {
|
|
for (size_t n = 0; n < android_id_count; n++) {
|
|
if (!strcmp(android_ids[n].name, end + 1)) {
|
|
appid = android_ids[n].aid;
|
|
// Move the end pointer to the null terminator.
|
|
end += strlen(android_ids[n].name) + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check that the entire string was consumed by one of the 3 cases above.
|
|
if (end[0] != 0) {
|
|
errno = ENOENT;
|
|
return 0;
|
|
}
|
|
|
|
// Check that user id won't overflow.
|
|
if (userid > 1000) {
|
|
errno = ENOENT;
|
|
return 0;
|
|
}
|
|
|
|
// Check that app id is within range.
|
|
if (appid >= AID_USER) {
|
|
errno = ENOENT;
|
|
return 0;
|
|
}
|
|
|
|
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) {
|
|
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) {
|
|
snprintf(buffer, bufferlen, "u%u_%s", userid, android_ids[n].name);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
snprintf(buffer, bufferlen, "u%u_a%u", userid, appid - AID_APP);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// Translate a uid into the corresponding name.
|
|
// 0 to AID_APP-1 -> "system", "radio", etc.
|
|
// AID_APP to AID_ISOLATED_START-1 -> u0_a1234
|
|
// 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_;
|
|
|
|
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_));
|
|
|
|
if (appid < AID_APP) {
|
|
snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/");
|
|
} else {
|
|
snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/data");
|
|
}
|
|
|
|
snprintf(state->sh_buffer_, sizeof(state->sh_buffer_), "/system/bin/sh");
|
|
|
|
pw->pw_name = state->app_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) {
|
|
if (gid < AID_APP) {
|
|
errno = ENOENT;
|
|
return NULL;
|
|
}
|
|
|
|
print_app_name_from_uid(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();
|
|
if (state == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
passwd* pw = android_id_to_passwd(state, uid);
|
|
if (pw != NULL) {
|
|
return pw;
|
|
}
|
|
return app_id_to_passwd(uid, state);
|
|
}
|
|
|
|
passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
|
|
stubs_state_t* state = __stubs_state();
|
|
if (state == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
passwd* pw = android_name_to_passwd(state, login);
|
|
if (pw != NULL) {
|
|
return pw;
|
|
}
|
|
return app_id_to_passwd(app_id_from_name(login), 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);
|
|
}
|
|
|
|
char* getlogin() { // NOLINT: implementing bad function.
|
|
passwd *pw = getpwuid(getuid()); // NOLINT: implementing bad function in terms of bad function.
|
|
return (pw != NULL) ? pw->pw_name : NULL;
|
|
}
|
|
|
|
group* getgrgid(gid_t gid) { // NOLINT: implementing bad function.
|
|
stubs_state_t* state = __stubs_state();
|
|
if (state == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
group* gr = android_id_to_group(&state->group_, 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();
|
|
if (state == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (android_name_to_group(&state->group_, name) != 0) {
|
|
return &state->group_;
|
|
}
|
|
|
|
return app_id_to_group(app_id_from_name(name), state);
|
|
}
|
|
|
|
// We don't have an /etc/networks, so all inputs return NULL.
|
|
netent* getnetbyname(const char* /*name*/) {
|
|
return NULL;
|
|
}
|
|
|
|
// We don't have an /etc/networks, so all inputs return NULL.
|
|
netent* getnetbyaddr(uint32_t /*net*/, int /*type*/) {
|
|
return NULL;
|
|
}
|
|
|
|
// We don't have an /etc/protocols, so all inputs return NULL.
|
|
protoent* getprotobyname(const char* /*name*/) {
|
|
return NULL;
|
|
}
|
|
|
|
// We don't have an /etc/protocols, so all inputs return NULL.
|
|
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;
|
|
}
|
|
|
|
mntent* getmntent(FILE* /*f*/) {
|
|
UNIMPLEMENTED;
|
|
return NULL;
|
|
}
|
|
|
|
char* ttyname(int /*fd*/) { // NOLINT: implementing bad function.
|
|
UNIMPLEMENTED;
|
|
return NULL;
|
|
}
|
|
|
|
int ttyname_r(int /*fd*/, char* /*buf*/, size_t /*buflen*/) {
|
|
UNIMPLEMENTED;
|
|
return -ERANGE;
|
|
}
|
|
|
|
char* getusershell() {
|
|
UNIMPLEMENTED;
|
|
return NULL;
|
|
}
|
|
|
|
void setusershell() {
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
void endusershell() {
|
|
UNIMPLEMENTED;
|
|
}
|