1diff --git a/third_party/ashmem/ashmem-dev.c b/third_party/ashmem/ashmem-dev.c 2index 52b3f47eeae0..25a33cdcd0c8 100644 3--- a/third_party/ashmem/ashmem-dev.c 4+++ b/third_party/ashmem/ashmem-dev.c 5@@ -14,23 +14,115 @@ 6 * limitations under the License. 7 */ 8 9-/* 10- * Implementation of the user-space ashmem API for devices, which have our 11- * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version, 12- * used by the simulator. 13- */ 14+#include "ashmem.h" 15 16+#include <dlfcn.h> 17+#include <errno.h> 18 #include <unistd.h> 19+#include <stdlib.h> 20 #include <string.h> 21+#include <sys/mman.h> 22 #include <sys/types.h> 23 #include <sys/stat.h> 24 #include <sys/ioctl.h> 25+#include <sys/stat.h> /* for fdstat() */ 26 #include <fcntl.h> 27 28 #include <linux/ashmem.h> 29-#include "ashmem.h" 30+#include <sys/system_properties.h> 31 32-#define ASHMEM_DEVICE "/dev/ashmem" 33+#define ASHMEM_DEVICE "/dev/ashmem" 34+ 35+/* Technical note regarding reading system properties. 36+ * 37+ * Try to use the new __system_property_read_callback API that appeared in 38+ * Android O / API level 26 when available. Otherwise use the deprecated 39+ * __system_property_get function. 40+ * 41+ * For more technical details from an NDK maintainer, see: 42+ * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17 43+ */ 44+ 45+/* Weak symbol import */ 46+void __system_property_read_callback( 47+ const prop_info* info, 48+ void (*callback)( 49+ void* cookie, const char* name, const char* value, uint32_t serial), 50+ void* cookie) __attribute__((weak)); 51+ 52+/* Callback used with __system_property_read_callback. */ 53+static void prop_read_int(void* cookie, 54+ const char* name, 55+ const char* value, 56+ uint32_t serial) { 57+ *(int *)cookie = atoi(value); 58+ (void)name; 59+ (void)serial; 60+} 61+ 62+static int system_property_get_int(const char* name) { 63+ int result = 0; 64+ if (__system_property_read_callback) { 65+ const prop_info* info = __system_property_find(name); 66+ if (info) 67+ __system_property_read_callback(info, &prop_read_int, &result); 68+ } else { 69+ char value[PROP_VALUE_MAX] = {}; 70+ if (__system_property_get(name, value) >= 1) 71+ result = atoi(value); 72+ } 73+ return result; 74+} 75+ 76+static int device_api_level() { 77+ static int s_api_level = -1; 78+ if (s_api_level < 0) 79+ s_api_level = system_property_get_int("ro.build.version.sdk"); 80+ return s_api_level; 81+} 82+ 83+typedef enum { 84+ ASHMEM_STATUS_INIT, 85+ ASHMEM_STATUS_NOT_SUPPORTED, 86+ ASHMEM_STATUS_SUPPORTED, 87+} AshmemStatus; 88+ 89+static AshmemStatus s_ashmem_status = ASHMEM_STATUS_INIT; 90+static dev_t s_ashmem_dev; 91+ 92+/* Return the dev_t of a given file path, or 0 if not available, */ 93+static dev_t ashmem_find_dev(const char* path) { 94+ struct stat st; 95+ dev_t result = 0; 96+ if (stat(path, &st) == 0 && S_ISCHR(st.st_mode)) 97+ result = st.st_dev; 98+ return result; 99+} 100+ 101+static AshmemStatus ashmem_get_status(void) { 102+ /* NOTE: No need to make this thread-safe, assuming that 103+ * all threads will find the same value. */ 104+ if (s_ashmem_status != ASHMEM_STATUS_INIT) 105+ return s_ashmem_status; 106+ 107+ s_ashmem_dev = ashmem_find_dev(ASHMEM_DEVICE); 108+ s_ashmem_status = (s_ashmem_dev == 0) ? ASHMEM_STATUS_NOT_SUPPORTED 109+ : ASHMEM_STATUS_SUPPORTED; 110+ return s_ashmem_status; 111+} 112+ 113+/* Returns true iff the ashmem device ioctl should be used for a given fd. 114+ * NOTE: Try not to use fstat() when possible to avoid performance issues. */ 115+static int ashmem_dev_fd_check(int fd) { 116+ if (device_api_level() <= __ANDROID_API_O_MR1__) 117+ return 1; 118+ if (ashmem_get_status() == ASHMEM_STATUS_SUPPORTED) { 119+ struct stat st; 120+ return (fstat(fd, &st) == 0 && S_ISCHR(st.st_mode) && 121+ st.st_dev != 0 && st.st_dev == s_ashmem_dev); 122+ } 123+ return 0; 124+} 125 126 /* 127 * ashmem_create_region - creates a new ashmem region and returns the file 128@@ -39,67 +131,133 @@ 129 * `name' is an optional label to give the region (visible in /proc/pid/maps) 130 * `size' is the size of the region, in page-aligned bytes 131 */ 132-int ashmem_create_region(const char *name, size_t size) 133-{ 134- int fd, ret; 135+static int ashmem_dev_create_region(const char *name, size_t size) { 136+ int fd = open(ASHMEM_DEVICE, O_RDWR); 137+ if (fd < 0) 138+ return fd; 139 140- fd = open(ASHMEM_DEVICE, O_RDWR); 141- if (fd < 0) 142- return fd; 143+ int ret; 144+ if (name) { 145+ char buf[ASHMEM_NAME_LEN]; 146+ strlcpy(buf, name, sizeof(buf)); 147+ ret = ioctl(fd, ASHMEM_SET_NAME, buf); 148+ if (ret < 0) 149+ goto error; 150+ } 151+ ret = ioctl(fd, ASHMEM_SET_SIZE, size); 152+ if (ret < 0) 153+ goto error; 154 155- if (name) { 156- char buf[ASHMEM_NAME_LEN]; 157+ return fd; 158 159- strlcpy(buf, name, sizeof(buf)); 160- ret = ioctl(fd, ASHMEM_SET_NAME, buf); 161- if (ret < 0) 162- goto error; 163- } 164+error: 165+ close(fd); 166+ return ret; 167+} 168 169- ret = ioctl(fd, ASHMEM_SET_SIZE, size); 170- if (ret < 0) 171- goto error; 172+static int ashmem_dev_set_prot_region(int fd, int prot) { 173+ return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); 174+} 175 176- return fd; 177+static int ashmem_dev_get_prot_region(int fd) { 178+ return ioctl(fd, ASHMEM_GET_PROT_MASK); 179+} 180 181-error: 182- close(fd); 183- return ret; 184+static int ashmem_dev_pin_region(int fd, size_t offset, size_t len) { 185+ struct ashmem_pin pin = { offset, len }; 186+ return ioctl(fd, ASHMEM_PIN, &pin); 187 } 188 189-int ashmem_set_prot_region(int fd, int prot) 190-{ 191- return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); 192+static int ashmem_dev_unpin_region(int fd, size_t offset, size_t len) { 193+ struct ashmem_pin pin = { offset, len }; 194+ return ioctl(fd, ASHMEM_UNPIN, &pin); 195 } 196 197-int ashmem_get_prot_region(int fd) 198-{ 199- return ioctl(fd, ASHMEM_GET_PROT_MASK); 200+static size_t ashmem_dev_get_size_region(int fd) { 201+ return ioctl(fd, ASHMEM_GET_SIZE, NULL); 202 } 203 204-int ashmem_pin_region(int fd, size_t offset, size_t len) 205-{ 206- struct ashmem_pin pin = { offset, len }; 207- return ioctl(fd, ASHMEM_PIN, &pin); 208+// Starting with API level 26, the following functions from 209+// libandroid.so should be used to create shared memory regions. 210+typedef int(*ASharedMemory_createFunc)(const char*, size_t); 211+typedef size_t(*ASharedMemory_getSizeFunc)(int fd); 212+typedef int(*ASharedMemory_setProtFunc)(int fd, int prot); 213+ 214+// Function pointers to shared memory functions. 215+typedef struct { 216+ ASharedMemory_createFunc create; 217+ ASharedMemory_getSizeFunc getSize; 218+ ASharedMemory_setProtFunc setProt; 219+} ASharedMemoryFuncs; 220+ 221+const ASharedMemoryFuncs* ashmem_get_funcs() { 222+ static ASharedMemoryFuncs s_ashmem_funcs = {}; 223+ ASharedMemoryFuncs* funcs = &s_ashmem_funcs; 224+ if (funcs->create == NULL) { 225+ if (device_api_level() >= __ANDROID_API_O__) { 226+ /* Leaked intentionally! */ 227+ void* lib = dlopen("libandroid.so", RTLD_NOW); 228+ funcs->create = (ASharedMemory_createFunc) 229+ dlsym(lib, "ASharedMemory_create"); 230+ funcs->getSize = (ASharedMemory_getSizeFunc) 231+ dlsym(lib, "ASharedMemory_getSize"); 232+ funcs->setProt = (ASharedMemory_setProtFunc) 233+ dlsym(lib, "ASharedMemory_setProt"); 234+ } else { 235+ funcs->create = &ashmem_dev_create_region; 236+ funcs->getSize = &ashmem_dev_get_size_region; 237+ funcs->setProt = &ashmem_dev_set_prot_region; 238+ } 239+ } 240+ return funcs; 241 } 242 243-int ashmem_unpin_region(int fd, size_t offset, size_t len) 244-{ 245- struct ashmem_pin pin = { offset, len }; 246- return ioctl(fd, ASHMEM_UNPIN, &pin); 247+int ashmem_create_region(const char* name, size_t size) { 248+ return ashmem_get_funcs()->create(name, size); 249 } 250 251-int ashmem_get_size_region(int fd) 252-{ 253- return ioctl(fd, ASHMEM_GET_SIZE, NULL); 254+int ashmem_set_prot_region(int fd, int prot) { 255+ return ashmem_get_funcs()->setProt(fd, prot); 256 } 257 258-int ashmem_purge_all(void) 259-{ 260- const int fd = open(ASHMEM_DEVICE, O_RDWR); 261- if (fd < 0) 262- return fd; 263- const int ret = ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0); 264- close(fd); 265- return ret; 266+int ashmem_get_prot_region(int fd) { 267+ if (ashmem_dev_fd_check(fd)) 268+ return ashmem_dev_get_prot_region(fd); 269+ /* There are only two practical values to return here: either 270+ * PROT_READ|PROT_WRITE or just PROT_READ, so try to determine 271+ * the flags by trying to mmap() the region read-write first. 272+ */ 273+ int result = PROT_READ; 274+ const size_t page_size = (size_t)sysconf(_SC_PAGESIZE); 275+ void* m = mmap(NULL, page_size, PROT_READ|PROT_WRITE, 276+ MAP_PRIVATE, fd, 0); 277+ if (m != MAP_FAILED) { 278+ munmap(m, page_size); 279+ result = PROT_READ|PROT_WRITE; 280+ } 281+ return result; 282+} 283+ 284+int ashmem_pin_region(int fd, size_t offset, size_t len) { 285+ if (ashmem_dev_fd_check(fd)) 286+ return ashmem_dev_pin_region(fd, offset, len); 287+ return ASHMEM_NOT_PURGED; 288+} 289+ 290+int ashmem_unpin_region(int fd, size_t offset, size_t len) { 291+ if (ashmem_dev_fd_check(fd)) 292+ return ashmem_dev_unpin_region(fd, offset, len); 293+ /* NOTE: It is not possible to use madvise() here because it requires a 294+ * memory address. This could be done in the caller though, instead of 295+ * this function. */ 296+ return 0; 297+} 298+ 299+int ashmem_get_size_region(int fd) { 300+ /* NOTE: Original API returns an int. Avoid breaking it. */ 301+ return (int)ashmem_get_funcs()->getSize(fd); 302+} 303+ 304+int ashmem_device_is_supported(void) { 305+ return ashmem_get_status() == ASHMEM_STATUS_SUPPORTED; 306 } 307diff --git a/third_party/ashmem/ashmem.h b/third_party/ashmem/ashmem.h 308index d8afccbd2a6e..f3675c98b19a 100644 309--- a/third_party/ashmem/ashmem.h 310+++ b/third_party/ashmem/ashmem.h 311@@ -16,13 +16,20 @@ 312 extern "C" { 313 #endif 314 315+/* Returns true if the ashmem device is supported on this device. 316+ * Not that even if the device is not supported, 317+ * ashmem_{create,set_prot,get_prot,get_size}_region() will still work 318+ * because they will use the ASharedMemory functions from libandroid.so 319+ * instead. But ashmem_{pin,unpin}_region() will be no-ops. 320+ */ 321+int ashmem_device_is_supported(void); 322+ 323 int ashmem_create_region(const char *name, size_t size); 324 int ashmem_set_prot_region(int fd, int prot); 325 int ashmem_get_prot_region(int fd); 326 int ashmem_pin_region(int fd, size_t offset, size_t len); 327 int ashmem_unpin_region(int fd, size_t offset, size_t len); 328 int ashmem_get_size_region(int fd); 329-int ashmem_purge_all(void); 330 331 #ifdef __cplusplus 332 } 333