diff options
-rw-r--r-- | libc/Android.bp | 22 | ||||
-rw-r--r-- | libc/bionic/pthread_mutex.cpp | 22 | ||||
-rw-r--r-- | libc/dns/net/getaddrinfo.c | 10 | ||||
-rw-r--r-- | libc/dns/net/hosts_cache.c | 551 | ||||
-rw-r--r-- | libc/dns/net/hosts_cache.h | 23 | ||||
-rw-r--r-- | libc/dns/net/sethostent.c | 7 | ||||
-rw-r--r-- | libc/include/arpa/inet.h | 1 | ||||
-rw-r--r-- | libc/include/bits/in_addr.h | 3 | ||||
-rw-r--r-- | libc/include/inaddr.h | 36 | ||||
-rw-r--r-- | linker/Android.bp | 4 | ||||
-rw-r--r-- | linker/linker.cpp | 84 | ||||
-rw-r--r-- | linker/linker.h | 20 | ||||
-rw-r--r-- | linker/linker_main.cpp | 11 |
13 files changed, 782 insertions, 12 deletions
diff --git a/libc/Android.bp b/libc/Android.bp index d3271ae91..ce714054a 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -102,6 +102,18 @@ libc_scudo_product_variables = { }, } +libc32_scudo_product_variables = { + malloc_not_svelte_libc32: { + cflags: ["-DUSE_SCUDO"], + whole_static_libs: ["libscudo"], + exclude_static_libs: [ + "libjemalloc5", + "libc_jemalloc_wrapper", + ], + }, +} + + // Defaults for native allocator libs/includes to make it // easier to change. // To disable scudo for the non-svelte config remove the line: @@ -116,7 +128,14 @@ cc_defaults { "libc_jemalloc_wrapper", ], header_libs: ["gwp_asan_headers"], - product_variables: libc_scudo_product_variables, + multilib: { + lib64: { + product_variables: libc_scudo_product_variables, + }, + lib32: { + product_variables: libc32_scudo_product_variables, + } + }, } // Functions not implemented by jemalloc directly, or that need to @@ -1860,6 +1879,7 @@ cc_library { name: "libstdc++", static_ndk_lib: true, static_libs: ["libasync_safe"], + vendor_available: true, static: { system_shared_libs: [], diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp index a15e98149..ead3e632e 100644 --- a/libc/bionic/pthread_mutex.cpp +++ b/libc/bionic/pthread_mutex.cpp @@ -527,7 +527,8 @@ int pthread_mutex_init(pthread_mutex_t* mutex_interface, const pthread_mutexattr return EINVAL; } - if (((*attr & MUTEXATTR_PROTOCOL_MASK) >> MUTEXATTR_PROTOCOL_SHIFT) == PTHREAD_PRIO_INHERIT) { + if (((*attr & MUTEXATTR_PROTOCOL_MASK) >> MUTEXATTR_PROTOCOL_SHIFT) == PTHREAD_PRIO_INHERIT + && android_get_application_target_sdk_version() >= 28) { #if !defined(__LP64__) if (state & MUTEX_SHARED_MASK) { return EINVAL; @@ -791,17 +792,22 @@ static int MutexLockWithTimeout(pthread_mutex_internal_t* mutex, bool use_realti } // namespace NonPI -static inline __always_inline bool IsMutexDestroyed(uint16_t mutex_state) { - return mutex_state == 0xffff; -} - // Inlining this function in pthread_mutex_lock() adds the cost of stack frame instructions on // ARM64. So make it noinline. -static int __attribute__((noinline)) HandleUsingDestroyedMutex(pthread_mutex_t* mutex, - const char* function_name) { +static bool __attribute__((noinline)) IsMutexDestroyed(uint16_t mutex_state) { + // Checking for mutex destruction is a P-specific behavior. Bypass the + // check if the SDK version precedes P, so that no change in behavior + // that may cause crashes is introduced. if (android_get_application_target_sdk_version() >= 28) { - __fortify_fatal("%s called on a destroyed mutex (%p)", function_name, mutex); + return mutex_state == 0xffff; + } else { + return false; } +} + +static int __always_inline HandleUsingDestroyedMutex(pthread_mutex_t* mutex, + const char* function_name) { + __fortify_fatal("%s called on a destroyed mutex (%p)", function_name, mutex); return EBUSY; } diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c index d0c11d2b0..cc94b21e2 100644 --- a/libc/dns/net/getaddrinfo.c +++ b/libc/dns/net/getaddrinfo.c @@ -109,6 +109,8 @@ #include "nsswitch.h" #include "private/bionic_defs.h" +#include "hosts_cache.h" + typedef union sockaddr_union { struct sockaddr generic; struct sockaddr_in in; @@ -2125,6 +2127,14 @@ _files_getaddrinfo(void *rv, void *cb_data, va_list ap) name = va_arg(ap, char *); pai = va_arg(ap, struct addrinfo *); + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + int gai_error = hc_getaddrinfo(name, NULL, pai, &cur); + if (gai_error != EAI_SYSTEM) { + *((struct addrinfo **)rv) = sentinel.ai_next; + return (gai_error == 0 ? NS_SUCCESS : NS_NOTFOUND); + } + // fprintf(stderr, "_files_getaddrinfo() name = '%s'\n", name); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; diff --git a/libc/dns/net/hosts_cache.c b/libc/dns/net/hosts_cache.c new file mode 100644 index 000000000..5038986be --- /dev/null +++ b/libc/dns/net/hosts_cache.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <strings.h> +#include <sys/file.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <utime.h> +#include <pthread.h> + +#include <netinet/in6.h> +#include <arpa/inet.h> + +#include "hostent.h" +#include "resolv_private.h" + +#define MAX_ADDRLEN (INET6_ADDRSTRLEN - (1 + 5)) +#define MAX_HOSTLEN MAXHOSTNAMELEN + +#define ESTIMATED_LINELEN 32 +#define HCFILE_ALLOC_SIZE 256 + +/* From sethostent.c */ +#define ALIGNBYTES (sizeof(uintptr_t) - 1) +#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES) + +/* + * Host cache entry for hcfile.c_data. + * Offsets are into hcfile.h_data. + * Strings are *not* terminated by NULL, but by whitespace (isspace) or '#'. + * Use hstr* functions with these. + */ +struct hcent +{ + uint32_t addr; + uint32_t name; +}; + +/* + * Overall host cache file state. + */ +struct hcfile +{ + int h_fd; + struct stat h_st; + char *h_data; + + uint32_t c_alloc; + uint32_t c_len; + struct hcent *c_data; +}; +static struct hcfile hcfile; +static pthread_mutex_t hclock = PTHREAD_MUTEX_INITIALIZER; + +static size_t hstrlen(const char *s) +{ + const char *p = s; + while (*p && *p != '#' && !isspace(*p)) + ++p; + return p - s; +} + +static int hstrcmp(const char *a, const char *b) +{ + size_t alen = hstrlen(a); + size_t blen = hstrlen(b); + int res = strncmp(a, b, MIN(alen, blen)); + if (res == 0) + res = alen - blen; + return res; +} + +static char *hstrcpy(char *dest, const char *src) +{ + size_t len = hstrlen(src); + memcpy(dest, src, len); + dest[len] = '\0'; + return dest; +} + +static char *hstrdup(const char *s) +{ + size_t len = hstrlen(s); + char *dest = (char *)malloc(len + 1); + if (!dest) + return NULL; + memcpy(dest, s, len); + dest[len] = '\0'; + return dest; +} + +static int cmp_hcent_name(const void *a, const void *b) +{ + struct hcent *ea = (struct hcent *)a; + const char *na = hcfile.h_data + ea->name; + struct hcent *eb = (struct hcent *)b; + const char *nb = hcfile.h_data + eb->name; + + return hstrcmp(na, nb); +} + +static struct hcent *_hcfindname_exact(const char *name) +{ + size_t first, last, mid; + struct hcent *cur = NULL; + int cmp; + + if (hcfile.c_len == 0) + return NULL; + + first = 0; + last = hcfile.c_len - 1; + mid = (first + last) / 2; + while (first <= last) { + cur = hcfile.c_data + mid; + cmp = hstrcmp(hcfile.h_data + cur->name, name); + if (cmp == 0) + goto found; + if (cmp < 0) + first = mid + 1; + else { + if (mid > 0) + last = mid - 1; + else + return NULL; + } + mid = (first + last) / 2; + } + return NULL; + +found: + while (cur > hcfile.c_data) { + struct hcent *prev = cur - 1; + cmp = cmp_hcent_name(cur, prev); + if (cmp) + break; + cur = prev; + } + + return cur; +} + +static struct hcent *_hcfindname(const char *name) +{ + struct hcent *ent; + char namebuf[MAX_HOSTLEN]; + char *p; + char *dot; + + ent = _hcfindname_exact(name); + if (!ent && strlen(name) < sizeof(namebuf)) { + strcpy(namebuf, name); + p = namebuf; + do { + dot = strchr(p, '.'); + if (!dot) + break; + if (dot > p) { + *(dot - 1) = '*'; + ent = _hcfindname_exact(dot - 1); + } + p = dot + 1; + } + while (!ent); + } + + return ent; +} + +/* + * Find next name on line, if any. + * + * Assumes that line is terminated by LF. + */ +static const char *_hcnextname(const char *name) +{ + while (!isspace(*name)) { + if (*name == '#') + return NULL; + ++name; + } + while (isspace(*name)) { + if (*name == '\n') + return NULL; + ++name; + } + if (*name == '#') + return NULL; + return name; +} + +static int _hcfilemmap(void) +{ + struct stat st; + int h_fd; + char *h_addr; + const char *p, *pend; + uint32_t c_alloc; + + h_fd = open(_PATH_HOSTS, O_RDONLY); + if (h_fd < 0) + return -1; + if (flock(h_fd, LOCK_EX) != 0) { + close(h_fd); + return -1; + } + + if (hcfile.h_data) { + memset(&st, 0, sizeof(st)); + if (fstat(h_fd, &st) == 0) { + if (st.st_size == hcfile.h_st.st_size && + st.st_mtime == hcfile.h_st.st_mtime) { + flock(h_fd, LOCK_UN); + close(h_fd); + return 0; + } + } + free(hcfile.c_data); + munmap(hcfile.h_data, hcfile.h_st.st_size); + close(hcfile.h_fd); + memset(&hcfile, 0, sizeof(struct hcfile)); + } + + if (fstat(h_fd, &st) != 0) { + flock(h_fd, LOCK_UN); + close(h_fd); + return -1; + } + h_addr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, h_fd, 0); + if (h_addr == MAP_FAILED) { + flock(h_fd, LOCK_UN); + close(h_fd); + return -1; + } + + hcfile.h_fd = h_fd; + hcfile.h_st = st; + hcfile.h_data = h_addr; + + c_alloc = 0; + /* + * Do an initial allocation if the file is "large". Estimate + * 32 bytes per line and define "large" as more than half of + * the alloc growth size (256 entries). + */ + if (st.st_size >= ESTIMATED_LINELEN * HCFILE_ALLOC_SIZE / 2) { + c_alloc = st.st_size / ESTIMATED_LINELEN; + hcfile.c_data = malloc(c_alloc * sizeof(struct hcent)); + if (!hcfile.c_data) { + goto oom; + } + } + + p = (const char *)h_addr; + pend = p + st.st_size; + while (p < pend) { + const char *eol, *addr, *name; + size_t len; + addr = p; + eol = memchr(p, '\n', pend - p); + if (!eol) + break; + p = eol + 1; + if (*addr == '#' || *addr == '\n') + continue; + len = hstrlen(addr); + if (len > MAX_ADDRLEN) + continue; + name = addr + len; + while (name < eol && isspace(*name)) + ++name; + while (name < eol) { + len = hstrlen(name); + if (len == 0) + break; + if (len < MAX_HOSTLEN) { + struct hcent *ent; + if (c_alloc <= hcfile.c_len) { + struct hcent *c_data; + c_alloc += HCFILE_ALLOC_SIZE; + c_data = realloc(hcfile.c_data, c_alloc * sizeof(struct hcent)); + if (!c_data) { + goto oom; + } + hcfile.c_data = c_data; + } + ent = hcfile.c_data + hcfile.c_len; + ent->addr = addr - h_addr; + ent->name = name - h_addr; + ++hcfile.c_len; + } + name += len; + while (name < eol && isspace(*name)) + ++name; + } + } + + qsort(hcfile.c_data, hcfile.c_len, + sizeof(struct hcent), cmp_hcent_name); + + flock(h_fd, LOCK_UN); + + return 0; + +oom: + free(hcfile.c_data); + munmap(hcfile.h_data, hcfile.h_st.st_size); + flock(hcfile.h_fd, LOCK_UN); + close(hcfile.h_fd); + memset(&hcfile, 0, sizeof(struct hcfile)); + return -1; +} + +/* + * Caching version of getaddrinfo. + * + * If we find the requested host name in the cache, use getaddrinfo to + * populate the result for each address we find. + * + * Note glibc and bionic differ in the handling of ai_canonname. POSIX + * says that ai_canonname is only populated in the first result entry. + * glibc does this. bionic populates ai_canonname in all result entries. + * We choose the POSIX/glibc way here. + */ +int hc_getaddrinfo(const char *host, const char *service, + const struct addrinfo *hints, + struct addrinfo **result) +{ + int ret = 0; + struct hcent *ent, *cur; + struct addrinfo *ai; + struct addrinfo rhints; + struct addrinfo *last; + int canonname = 0; + int cmp; + + if (getenv("ANDROID_HOSTS_CACHE_DISABLE") != NULL) + return EAI_SYSTEM; + + /* Avoid needless work and recursion */ + if (hints && (hints->ai_flags & AI_NUMERICHOST)) + return EAI_SYSTEM; + if (!host) + return EAI_SYSTEM; + + pthread_mutex_lock(&hclock); + + if (_hcfilemmap() != 0) { + ret = EAI_SYSTEM; + goto out; + } + ent = _hcfindname(host); + if (!ent) { + ret = EAI_NONAME; + goto out; + } + + if (hints) { + canonname = (hints->ai_flags & AI_CANONNAME); + memcpy(&rhints, hints, sizeof(rhints)); + rhints.ai_flags &= ~AI_CANONNAME; + } + else { + memset(&rhints, 0, sizeof(rhints)); + } + rhints.ai_flags |= AI_NUMERICHOST; + + last = NULL; + cur = ent; + do { + char addrstr[MAX_ADDRLEN]; + struct addrinfo *res; + + hstrcpy(addrstr, hcfile.h_data + cur->addr); + + if (getaddrinfo(addrstr, service, &rhints, &res) == 0) { + if (!last) + (*result)->ai_next = res; + else + last->ai_next = res; + last = res; + while (last->ai_next) + last = last->ai_next; + } + + if(cur + 1 >= hcfile.c_data + hcfile.c_len) + break; + cmp = cmp_hcent_name(cur, cur + 1); + cur = cur + 1; + } + while (!cmp); + + if (last == NULL) { + /* This check is equivalent to (*result)->ai_next == NULL */ + ret = EAI_NODATA; + goto out; + } + + if (canonname) { + ai = (*result)->ai_next; + free(ai->ai_canonname); + ai->ai_canonname = hstrdup(hcfile.h_data + ent->name); + } + +out: + pthread_mutex_unlock(&hclock); + return ret; +} + +/* + * Caching version of gethtbyname. + * + * Note glibc and bionic differ in the handling of aliases. glibc returns + * all aliases for all entries, regardless of whether they match h_addrtype. + * bionic returns only the aliases for the first hosts entry. We return all + * aliases for all IPv4 entries. + * + * Additionally, if an alias is IPv6 and the primary name for an alias also + * has an IPv4 entry, glibc will return the IPv4 address(es), but bionic + * will not. Neither do we. + */ +int hc_gethtbyname(const char *host, int af, struct getnamaddr *info) +{ + int ret = NETDB_SUCCESS; + struct hcent *ent, *cur; + int cmp; + size_t addrlen; + unsigned int naliases = 0; + char *aliases[MAXALIASES]; + unsigned int naddrs = 0; + char *addr_ptrs[MAXADDRS]; + unsigned int n; + + if (getenv("ANDROID_HOSTS_CACHE_DISABLE") != NULL) + return NETDB_INTERNAL; + + switch (af) { + case AF_INET: addrlen = NS_INADDRSZ; break; + case AF_INET6: addrlen = NS_IN6ADDRSZ; break; + default: + return NETDB_INTERNAL; + } + + pthread_mutex_lock(&hclock); + + if (_hcfilemmap() != 0) { + ret = NETDB_INTERNAL; + goto out; + } + + ent = _hcfindname(host); + if (!ent) { + ret = HOST_NOT_FOUND; + goto out; + } + + cur = ent; + do { + char addr[16]; + char addrstr[MAX_ADDRLEN]; + char namestr[MAX_HOSTLEN]; + const char *name; + + hstrcpy(addrstr, hcfile.h_data + cur->addr); + if (inet_pton(af, addrstr, &addr) == 1) { + char *aligned; + /* First match is considered the official hostname */ + if (naddrs == 0) { + hstrcpy(namestr, hcfile.h_data + cur->name); + HENT_SCOPY(info->hp->h_name, namestr, info->buf, info->buflen); + } + for (name = hcfile.h_data + cur->name; name; name = _hcnextname(name)) { + if (!hstrcmp(name, host)) + continue; + hstrcpy(namestr, name); + HENT_SCOPY(aliases[naliases], namestr, info->buf, info->buflen); + ++naliases; + if (naliases >= MAXALIASES) + goto nospc; + } + aligned = (char *)ALIGN(info->buf); + if (info->buf != aligned) { + if ((ptrdiff_t)info->buflen < (aligned - info->buf)) + goto nospc; + info->buflen -= (aligned - info->buf); + info->buf = aligned; + } + HENT_COPY(addr_ptrs[naddrs], addr, addrlen, info->buf, info->buflen); + ++naddrs; + if (naddrs >= MAXADDRS) + goto nospc; + } + + if(cur + 1 >= hcfile.c_data + hcfile.c_len) + break; + cmp = cmp_hcent_name(cur, cur + 1); + cur = cur + 1; + } + while (!cmp); + + if (naddrs == 0) { + ret = HOST_NOT_FOUND; + goto out; + } + + addr_ptrs[naddrs++] = NULL; + aliases[naliases++] = NULL; + + /* hp->h_name already populated */ + HENT_ARRAY(info->hp->h_aliases, naliases, info->buf, info->buflen); + for (n = 0; n < naliases; ++n) { + info->hp->h_aliases[n] = aliases[n]; + } + info->hp->h_addrtype = af; + info->hp->h_length = addrlen; + HENT_ARRAY(info->hp->h_addr_list, naddrs, info->buf, info->buflen); + for (n = 0; n < naddrs; ++n) { + info->hp->h_addr_list[n] = addr_ptrs[n]; + } + +out: + pthread_mutex_unlock(&hclock); + *info->he = ret; + return ret; + +nospc: + ret = NETDB_INTERNAL; + goto out; +} diff --git a/libc/dns/net/hosts_cache.h b/libc/dns/net/hosts_cache.h new file mode 100644 index 000000000..fa5488f51 --- /dev/null +++ b/libc/dns/net/hosts_cache.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +struct getnamaddr; + +int hc_getaddrinfo(const char *host, const char *service, + const struct addrinfo *hints, + struct addrinfo **result); + +int hc_gethtbyname(const char *host, int af, struct getnamaddr *info); diff --git a/libc/dns/net/sethostent.c b/libc/dns/net/sethostent.c index a743a543e..cbf091167 100644 --- a/libc/dns/net/sethostent.c +++ b/libc/dns/net/sethostent.c @@ -55,6 +55,8 @@ __RCSID("$NetBSD: sethostent.c,v 1.20 2014/03/17 13:24:23 christos Exp $"); #include "hostent.h" #include "resolv_private.h" +#include "hosts_cache.h" + #define ALIGNBYTES (sizeof(uintptr_t) - 1) #define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES) @@ -112,6 +114,11 @@ _hf_gethtbyname(void *rv, void *cb_data, va_list ap) /* NOSTRICT skip string len */(void)va_arg(ap, int); af = va_arg(ap, int); + int rc = hc_gethtbyname(name, af, info); + if (rc != NETDB_INTERNAL) { + return (rc == NETDB_SUCCESS ? NS_SUCCESS : NS_NOTFOUND); + } + #if 0 { res_state res = __res_get_state(); diff --git a/libc/include/arpa/inet.h b/libc/include/arpa/inet.h index db054c9e1..7716b9445 100644 --- a/libc/include/arpa/inet.h +++ b/libc/include/arpa/inet.h @@ -33,6 +33,7 @@ #include <stdint.h> #include <sys/cdefs.h> #include <sys/types.h> +#include <inaddr.h> __BEGIN_DECLS diff --git a/libc/include/bits/in_addr.h b/libc/include/bits/in_addr.h index 30eb04b66..3e46dad2b 100644 --- a/libc/include/bits/in_addr.h +++ b/libc/include/bits/in_addr.h @@ -36,8 +36,7 @@ #include <sys/cdefs.h> #include <stdint.h> -/** An integral type representing an IPv4 address. */ -typedef uint32_t in_addr_t; +#include <inaddr.h> /** A structure representing an IPv4 address. */ struct in_addr { diff --git a/libc/include/inaddr.h b/libc/include/inaddr.h new file mode 100644 index 000000000..524addabf --- /dev/null +++ b/libc/include/inaddr.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef _INADDR_H_ +#define _INADDR_H_ + +#include <stdint.h> + +typedef uint32_t in_addr_t; + +#endif diff --git a/linker/Android.bp b/linker/Android.bp index f75088d60..582a9e41e 100644 --- a/linker/Android.bp +++ b/linker/Android.bp @@ -57,6 +57,10 @@ cc_object { // Configuration for the linker binary and any of its static libraries. cc_defaults { name: "linker_defaults", + defaults: [ + "process_sdk_version_overrides_defaults", + "shim_libs_defaults", + ], arch: { arm: { cflags: ["-D__work_around_b_24465209__"], diff --git a/linker/linker.cpp b/linker/linker.cpp index f24167722..c87118b9f 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -644,6 +644,68 @@ enum walk_action_result_t : uint32_t { kWalkSkip = 2 }; +#ifdef LD_SHIM_LIBS +// g_ld_all_shim_libs maintains the references to memory as it used +// in the soinfo structures and in the g_active_shim_libs list. + +static std::vector<ShimDescriptor> g_ld_all_shim_libs; + +// g_active_shim_libs are all shim libs that are still eligible +// to be loaded. We must remove a shim lib from the list before +// we load the library to avoid recursive loops (load shim libA +// for libB where libA also links against libB). +static linked_list_t<const ShimDescriptor> g_active_shim_libs; + +static void reset_g_active_shim_libs(void) { + g_active_shim_libs.clear(); + for (const auto& pair : g_ld_all_shim_libs) { + g_active_shim_libs.push_back(&pair); + } +} + +void parse_LD_SHIM_LIBS(const char* path) { + g_ld_all_shim_libs.clear(); + if (path != nullptr) { + for (const auto& pair : android::base::Split(path, ":")) { + std::vector<std::string> pieces = android::base::Split(pair, "|"); + if (pieces.size() != 2) continue; + // If the path can be resolved, resolve it + char buf[PATH_MAX]; + std::string resolved_path = pieces[0]; + if (access(pieces[0].c_str(), R_OK) != 0) { + if (errno == ENOENT) { + // no need to test for non-existing path. skip. + continue; + } + // If not accessible, don't call realpath as it will just cause + // SELinux denial spam. Use the path unresolved. + } else if (realpath(pieces[0].c_str(), buf) != nullptr) { + resolved_path = buf; + } + auto desc = std::pair<std::string, std::string>(resolved_path, pieces[1]); + g_ld_all_shim_libs.push_back(desc); + } + } + reset_g_active_shim_libs(); +} + +std::vector<const ShimDescriptor*> shim_matching_pairs(const char* path) { + std::vector<const ShimDescriptor*> matched_pairs; + + g_active_shim_libs.for_each([&](const ShimDescriptor* a_pair) { + if (a_pair->first == path) { + matched_pairs.push_back(a_pair); + } + }); + + g_active_shim_libs.remove_if([&](const ShimDescriptor* a_pair) { + return a_pair->first == path; + }); + + return matched_pairs; +} +#endif + // This function walks down the tree of soinfo dependencies // in breadth-first order and // * calls action(soinfo* si) for each node, and @@ -1279,6 +1341,12 @@ static bool load_library(android_namespace_t* ns, } #endif +#ifdef LD_SHIM_LIBS + for_each_matching_shim(realpath.c_str(), [&](const char* name) { + load_tasks->push_back(LoadTask::create(name, si, ns, task->get_readers_map())); + }); +#endif + for_each_dt_needed(task->get_elf_reader(), [&](const char* name) { LD_LOG(kLogDlopen, "load_library(ns=%s, task=%s): Adding DT_NEEDED task: %s", ns->get_name(), task->get_name(), name); @@ -2181,6 +2249,9 @@ void* do_dlopen(const char* name, int flags, } ProtectedDataGuard guard; +#ifdef LD_SHIM_LIBS + reset_g_active_shim_libs(); +#endif soinfo* si = find_library(ns, translated_name, flags, extinfo, caller); loading_trace.End(); @@ -3530,7 +3601,18 @@ std::vector<android_namespace_t*> init_default_namespaces(const char* executable } } - set_application_target_sdk_version(config->target_sdk_version()); + uint32_t target_sdk = config->target_sdk_version(); +#ifdef SDK_VERSION_OVERRIDES + for (const auto& entry : android::base::Split(SDK_VERSION_OVERRIDES, " ")) { + auto splitted = android::base::Split(entry, "="); + if (splitted.size() == 2 && splitted[0] == executable_path) { + target_sdk = static_cast<uint32_t>(std::stoul(splitted[1])); + break; + } + } + DEBUG("Target SDK for %s = %d", executable_path, target_sdk); +#endif + set_application_target_sdk_version(target_sdk); std::vector<android_namespace_t*> created_namespaces; created_namespaces.reserve(namespaces.size()); diff --git a/linker/linker.h b/linker/linker.h index 2da14041b..dce6c5e4c 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -42,6 +42,10 @@ #include "linker_logger.h" #include "linker_soinfo.h" +#ifdef LD_SHIM_LIBS +#include "linker_debug.h" +#endif + #include <string> #include <vector> @@ -77,6 +81,22 @@ soinfo* find_containing_library(const void* p); int open_executable(const char* path, off64_t* file_offset, std::string* realpath); +#ifdef LD_SHIM_LIBS +typedef std::pair<std::string, std::string> ShimDescriptor; +void parse_LD_SHIM_LIBS(const char* path); +std::vector<const ShimDescriptor*> shim_matching_pairs(const char* path); + +template<typename F> +void for_each_matching_shim(const char* path, F action) { + if (path == nullptr) return; + INFO("Finding shim libs for \"%s\"", path); + for (const auto& one_pair : shim_matching_pairs(path)) { + INFO("Injecting shim lib \"%s\" as needed for %s", one_pair->second.c_str(), path); + action(one_pair->second.c_str()); + } +} +#endif + void do_android_get_LD_LIBRARY_PATH(char*, size_t); void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); void* do_dlopen(const char* name, diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp index 98af54ac2..22c034b61 100644 --- a/linker/linker_main.cpp +++ b/linker/linker_main.cpp @@ -417,6 +417,11 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load parse_LD_LIBRARY_PATH(ldpath_env); parse_LD_PRELOAD(ldpreload_env); +#ifdef LD_SHIM_LIBS + // Read from TARGET_LD_SHIM_LIBS + parse_LD_SHIM_LIBS(LD_SHIM_LIBS); +#endif + std::vector<android_namespace_t*> namespaces = init_default_namespaces(exe_info.path.c_str()); if (!si->prelink_image()) __linker_cannot_link(g_argv[0]); @@ -442,6 +447,12 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load ++ld_preloads_count; } +#ifdef LD_SHIM_LIBS + for_each_matching_shim(si->get_realpath(), [&](const char* name) { + needed_library_name_list.push_back(name); + }); +#endif + for_each_dt_needed(si, [&](const char* name) { needed_library_name_list.push_back(name); }); |