chromium/third_party/ashmem/patches/0002-Use-ASharedMemory-functions-when-possible.patch

diff --git a/third_party/ashmem/ashmem-dev.c b/third_party/ashmem/ashmem-dev.c
index 52b3f47eeae0..25a33cdcd0c8 100644
--- a/third_party/ashmem/ashmem-dev.c
+++ b/third_party/ashmem/ashmem-dev.c
@@ -14,23 +14,115 @@
  * limitations under the License.
  */
 
-/*
- * Implementation of the user-space ashmem API for devices, which have our
- * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
- * used by the simulator.
- */
+#include "ashmem.h"
 
+#include <dlfcn.h>
+#include <errno.h>
 #include <unistd.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
+#include <sys/stat.h>  /* for fdstat() */
 #include <fcntl.h>
 
 #include <linux/ashmem.h>
-#include "ashmem.h"
+#include <sys/system_properties.h>
 
-#define ASHMEM_DEVICE	"/dev/ashmem"
+#define ASHMEM_DEVICE  "/dev/ashmem"
+
+/* Technical note regarding reading system properties.
+ *
+ * Try to use the new __system_property_read_callback API that appeared in
+ * Android O / API level 26 when available. Otherwise use the deprecated
+ * __system_property_get function.
+ *
+ * For more technical details from an NDK maintainer, see:
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17
+ */
+
+/* Weak symbol import */
+void __system_property_read_callback(
+    const prop_info* info,
+    void (*callback)(
+        void* cookie, const char* name, const char* value, uint32_t serial),
+    void* cookie) __attribute__((weak));
+
+/* Callback used with __system_property_read_callback. */
+static void prop_read_int(void* cookie,
+                          const char* name,
+                          const char* value,
+                          uint32_t serial) {
+  *(int *)cookie = atoi(value);
+  (void)name;
+  (void)serial;
+}
+
+static int system_property_get_int(const char* name) {
+  int result = 0;
+  if (__system_property_read_callback) {
+    const prop_info* info = __system_property_find(name);
+    if (info)
+      __system_property_read_callback(info, &prop_read_int, &result);
+  } else {
+    char value[PROP_VALUE_MAX] = {};
+    if (__system_property_get(name, value) >= 1)
+      result = atoi(value);
+  }
+  return result;
+}
+
+static int device_api_level() {
+  static int s_api_level = -1;
+  if (s_api_level < 0)
+    s_api_level = system_property_get_int("ro.build.version.sdk");
+  return s_api_level;
+}
+
+typedef enum {
+  ASHMEM_STATUS_INIT,
+  ASHMEM_STATUS_NOT_SUPPORTED,
+  ASHMEM_STATUS_SUPPORTED,
+} AshmemStatus;
+
+static AshmemStatus s_ashmem_status = ASHMEM_STATUS_INIT;
+static dev_t s_ashmem_dev;
+
+/* Return the dev_t of a given file path, or 0 if not available, */
+static dev_t ashmem_find_dev(const char* path) {
+  struct stat st;
+  dev_t result = 0;
+  if (stat(path, &st) == 0 && S_ISCHR(st.st_mode))
+    result = st.st_dev;
+  return result;
+}
+
+static AshmemStatus ashmem_get_status(void) {
+  /* NOTE: No need to make this thread-safe, assuming that
+   * all threads will find the same value. */
+  if (s_ashmem_status != ASHMEM_STATUS_INIT)
+    return s_ashmem_status;
+
+  s_ashmem_dev = ashmem_find_dev(ASHMEM_DEVICE);
+  s_ashmem_status = (s_ashmem_dev == 0) ? ASHMEM_STATUS_NOT_SUPPORTED
+                                        : ASHMEM_STATUS_SUPPORTED;
+  return s_ashmem_status;
+}
+
+/* Returns true iff the ashmem device ioctl should be used for a given fd.
+ * NOTE: Try not to use fstat() when possible to avoid performance issues. */
+static int ashmem_dev_fd_check(int fd) {
+  if (device_api_level() <= __ANDROID_API_O_MR1__)
+    return 1;
+  if (ashmem_get_status() == ASHMEM_STATUS_SUPPORTED) {
+    struct stat st;
+    return (fstat(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
+            st.st_dev != 0 && st.st_dev == s_ashmem_dev);
+  }
+  return 0;
+}
 
 /*
  * ashmem_create_region - creates a new ashmem region and returns the file
@@ -39,67 +131,133 @@
  * `name' is an optional label to give the region (visible in /proc/pid/maps)
  * `size' is the size of the region, in page-aligned bytes
  */
-int ashmem_create_region(const char *name, size_t size)
-{
-	int fd, ret;
+static int ashmem_dev_create_region(const char *name, size_t size) {
+  int fd = open(ASHMEM_DEVICE, O_RDWR);
+  if (fd < 0)
+    return fd;
 
-	fd = open(ASHMEM_DEVICE, O_RDWR);
-	if (fd < 0)
-		return fd;
+  int ret;
+  if (name) {
+    char buf[ASHMEM_NAME_LEN];
+    strlcpy(buf, name, sizeof(buf));
+    ret = ioctl(fd, ASHMEM_SET_NAME, buf);
+    if (ret < 0)
+      goto error;
+  }
+  ret = ioctl(fd, ASHMEM_SET_SIZE, size);
+  if (ret < 0)
+    goto error;
 
-	if (name) {
-		char buf[ASHMEM_NAME_LEN];
+  return fd;
 
-		strlcpy(buf, name, sizeof(buf));
-		ret = ioctl(fd, ASHMEM_SET_NAME, buf);
-		if (ret < 0)
-			goto error;
-	}
+error:
+  close(fd);
+  return ret;
+}
 
-	ret = ioctl(fd, ASHMEM_SET_SIZE, size);
-	if (ret < 0)
-		goto error;
+static int ashmem_dev_set_prot_region(int fd, int prot) {
+  return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+}
 
-	return fd;
+static int ashmem_dev_get_prot_region(int fd) {
+  return ioctl(fd, ASHMEM_GET_PROT_MASK);
+}
 
-error:
-	close(fd);
-	return ret;
+static int ashmem_dev_pin_region(int fd, size_t offset, size_t len) {
+  struct ashmem_pin pin = { offset, len };
+  return ioctl(fd, ASHMEM_PIN, &pin);
 }
 
-int ashmem_set_prot_region(int fd, int prot)
-{
-	return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+static int ashmem_dev_unpin_region(int fd, size_t offset, size_t len) {
+  struct ashmem_pin pin = { offset, len };
+  return ioctl(fd, ASHMEM_UNPIN, &pin);
 }
 
-int ashmem_get_prot_region(int fd)
-{
-	return ioctl(fd, ASHMEM_GET_PROT_MASK);
+static size_t ashmem_dev_get_size_region(int fd) {
+  return ioctl(fd, ASHMEM_GET_SIZE, NULL);
 }
 
-int ashmem_pin_region(int fd, size_t offset, size_t len)
-{
-	struct ashmem_pin pin = { offset, len };
-	return ioctl(fd, ASHMEM_PIN, &pin);
+// Starting with API level 26, the following functions from
+// libandroid.so should be used to create shared memory regions.
+typedef int(*ASharedMemory_createFunc)(const char*, size_t);
+typedef size_t(*ASharedMemory_getSizeFunc)(int fd);
+typedef int(*ASharedMemory_setProtFunc)(int fd, int prot);
+
+// Function pointers to shared memory functions.
+typedef struct {
+  ASharedMemory_createFunc create;
+  ASharedMemory_getSizeFunc getSize;
+  ASharedMemory_setProtFunc setProt;
+} ASharedMemoryFuncs;
+
+const ASharedMemoryFuncs* ashmem_get_funcs() {
+  static ASharedMemoryFuncs s_ashmem_funcs = {};
+  ASharedMemoryFuncs* funcs = &s_ashmem_funcs;
+  if (funcs->create == NULL) {
+    if (device_api_level() >= __ANDROID_API_O__) {
+      /* Leaked intentionally! */
+      void* lib = dlopen("libandroid.so", RTLD_NOW);
+      funcs->create = (ASharedMemory_createFunc)
+          dlsym(lib, "ASharedMemory_create");
+      funcs->getSize = (ASharedMemory_getSizeFunc)
+          dlsym(lib, "ASharedMemory_getSize");
+      funcs->setProt = (ASharedMemory_setProtFunc)
+          dlsym(lib, "ASharedMemory_setProt");
+    } else {
+      funcs->create = &ashmem_dev_create_region;
+      funcs->getSize = &ashmem_dev_get_size_region;
+      funcs->setProt = &ashmem_dev_set_prot_region;
+    }
+  }
+  return funcs;
 }
 
-int ashmem_unpin_region(int fd, size_t offset, size_t len)
-{
-	struct ashmem_pin pin = { offset, len };
-	return ioctl(fd, ASHMEM_UNPIN, &pin);
+int ashmem_create_region(const char* name, size_t size) {
+  return ashmem_get_funcs()->create(name, size);
 }
 
-int ashmem_get_size_region(int fd)
-{
-  return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+int ashmem_set_prot_region(int fd, int prot) {
+  return ashmem_get_funcs()->setProt(fd, prot);
 }
 
-int ashmem_purge_all(void)
-{
-  const int fd = open(ASHMEM_DEVICE, O_RDWR);
-  if (fd < 0)
-    return fd;
-  const int ret = ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0);
-  close(fd);
-  return ret;
+int ashmem_get_prot_region(int fd) {
+  if (ashmem_dev_fd_check(fd))
+    return ashmem_dev_get_prot_region(fd);
+  /* There are only two practical values to return here: either
+   * PROT_READ|PROT_WRITE or just PROT_READ, so try to determine
+   * the flags by trying to mmap() the region read-write first.
+   */
+  int result = PROT_READ;
+  const size_t page_size = (size_t)sysconf(_SC_PAGESIZE);
+  void* m = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
+                 MAP_PRIVATE, fd, 0);
+  if (m != MAP_FAILED) {
+    munmap(m, page_size);
+    result = PROT_READ|PROT_WRITE;
+  }
+  return result;
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len) {
+ if (ashmem_dev_fd_check(fd))
+   return ashmem_dev_pin_region(fd, offset, len);
+  return ASHMEM_NOT_PURGED;
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len) {
+  if (ashmem_dev_fd_check(fd))
+    return ashmem_dev_unpin_region(fd, offset, len);
+  /* NOTE: It is not possible to use madvise() here because it requires a
+   * memory address. This could be done in the caller though, instead of
+   * this function. */
+  return 0;
+}
+
+int ashmem_get_size_region(int fd) {
+  /* NOTE: Original API returns an int. Avoid breaking it. */
+  return (int)ashmem_get_funcs()->getSize(fd);
+}
+
+int ashmem_device_is_supported(void) {
+  return ashmem_get_status() == ASHMEM_STATUS_SUPPORTED;
 }
diff --git a/third_party/ashmem/ashmem.h b/third_party/ashmem/ashmem.h
index d8afccbd2a6e..f3675c98b19a 100644
--- a/third_party/ashmem/ashmem.h
+++ b/third_party/ashmem/ashmem.h
@@ -16,13 +16,20 @@
 extern "C" {
 #endif
 
+/* Returns true if the ashmem device is supported on this device.
+ * Not that even if the device is not supported,
+ * ashmem_{create,set_prot,get_prot,get_size}_region() will still work
+ * because they will use the ASharedMemory functions from libandroid.so
+ * instead. But ashmem_{pin,unpin}_region() will be no-ops.
+ */
+int ashmem_device_is_supported(void);
+
 int ashmem_create_region(const char *name, size_t size);
 int ashmem_set_prot_region(int fd, int prot);
 int ashmem_get_prot_region(int fd);
 int ashmem_pin_region(int fd, size_t offset, size_t len);
 int ashmem_unpin_region(int fd, size_t offset, size_t len);
 int ashmem_get_size_region(int fd);
-int ashmem_purge_all(void);
 
 #ifdef __cplusplus
 }