Merge changes from topic "memfd-support" am: 3133b695e0
am: b03663ebdf
Change-Id: Idf18cb0a8eb9327f75d28018e9f0940cc32ae962
This commit is contained in:
commit
e3b45e5c31
4 changed files with 246 additions and 4 deletions
|
|
@ -172,7 +172,10 @@ cc_library {
|
|||
}
|
||||
},
|
||||
|
||||
shared_libs: ["liblog"],
|
||||
shared_libs: [
|
||||
"liblog",
|
||||
"libbase",
|
||||
],
|
||||
header_libs: [
|
||||
"libbase_headers",
|
||||
"libcutils_headers",
|
||||
|
|
|
|||
|
|
@ -29,17 +29,34 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/ashmem.h>
|
||||
#include <linux/memfd.h>
|
||||
#include <log/log.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <log/log.h>
|
||||
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
|
||||
/* Will be added to UAPI once upstream change is merged */
|
||||
#define F_SEAL_FUTURE_WRITE 0x0010
|
||||
|
||||
/*
|
||||
* The minimum vendor API level at and after which it is safe to use memfd.
|
||||
* This is to facilitate deprecation of ashmem.
|
||||
*/
|
||||
#define MIN_MEMFD_VENDOR_API_LEVEL 29
|
||||
#define MIN_MEMFD_VENDOR_API_LEVEL_CHAR 'Q'
|
||||
|
||||
/* ashmem identity */
|
||||
static dev_t __ashmem_rdev;
|
||||
/*
|
||||
|
|
@ -72,6 +89,130 @@ openFdType initOpenAshmemFd() {
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* has_memfd_support() determines if the device can use memfd. memfd support
|
||||
* has been there for long time, but certain things in it may be missing. We
|
||||
* check for needed support in it. Also we check if the VNDK version of
|
||||
* libcutils being used is new enough, if its not, then we cannot use memfd
|
||||
* since the older copies may be using ashmem so we just use ashmem. Once all
|
||||
* Android devices that are getting updates are new enough (ex, they were
|
||||
* originally shipped with Android release > P), then we can just use memfd and
|
||||
* delete all ashmem code from libcutils (while preserving the interface).
|
||||
*
|
||||
* NOTE:
|
||||
* The sys.use_memfd property is set by default to false in Android
|
||||
* to temporarily disable memfd, till vendor and apps are ready for it.
|
||||
* The main issue: either apps or vendor processes can directly make ashmem
|
||||
* IOCTLs on FDs they receive by assuming they are ashmem, without going
|
||||
* through libcutils. Such fds could have very well be originally created with
|
||||
* libcutils hence they could be memfd. Thus the IOCTLs will break.
|
||||
*
|
||||
* Set default value of sys.use_memfd property to true once the issue is
|
||||
* resolved, so that the code can then self-detect if kernel support is present
|
||||
* on the device. The property can also set to true from adb shell, for
|
||||
* debugging.
|
||||
*/
|
||||
|
||||
static bool debug_log = false; /* set to true for verbose logging and other debug */
|
||||
static bool pin_deprecation_warn = true; /* Log the pin deprecation warning only once */
|
||||
|
||||
/* Determine if vendor processes would be ok with memfd in the system:
|
||||
*
|
||||
* If VNDK is using older libcutils, don't use memfd. This is so that the
|
||||
* same shared memory mechanism is used across binder transactions between
|
||||
* vendor partition processes and system partition processes.
|
||||
*/
|
||||
static bool check_vendor_memfd_allowed() {
|
||||
std::string vndk_version = android::base::GetProperty("ro.vndk.version", "");
|
||||
|
||||
if (vndk_version == "") {
|
||||
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
|
||||
vndk_version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* No issues if vendor is targetting current Dessert */
|
||||
if (vndk_version == "current") {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if VNDK version is a number and act on it */
|
||||
char* p;
|
||||
long int vers = strtol(vndk_version.c_str(), &p, 10);
|
||||
if (*p == 0) {
|
||||
if (vers < MIN_MEMFD_VENDOR_API_LEVEL) {
|
||||
ALOGI("memfd: device VNDK version (%s) is < Q so using ashmem.\n",
|
||||
vndk_version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If its not a number, assume string, but check if its a sane string */
|
||||
if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
|
||||
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
|
||||
vndk_version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tolower(vndk_version[0]) < tolower(MIN_MEMFD_VENDOR_API_LEVEL_CHAR)) {
|
||||
ALOGI("memfd: device is using VNDK version (%s) which is less than Q. Use ashmem only.\n",
|
||||
vndk_version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Determine if memfd can be supported. This is just one-time hardwork
|
||||
* which will be cached by the caller.
|
||||
*/
|
||||
static bool __has_memfd_support() {
|
||||
if (check_vendor_memfd_allowed() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Used to turn on/off the detection at runtime, in the future this
|
||||
* property will be removed once we switch everything over to ashmem.
|
||||
* Currently it is used only for debugging to switch the system over.
|
||||
*/
|
||||
if (!android::base::GetBoolProperty("sys.use_memfd", false)) {
|
||||
if (debug_log) {
|
||||
ALOGD("sys.use_memfd=false so memfd disabled\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if kernel support exists, otherwise fall back to ashmem */
|
||||
android::base::unique_fd fd(
|
||||
syscall(__NR_memfd_create, "test_android_memfd", MFD_ALLOW_SEALING));
|
||||
if (fd == -1) {
|
||||
ALOGE("memfd_create failed: %s, no memfd support.\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
|
||||
ALOGE("fcntl(F_ADD_SEALS) failed: %s, no memfd support.\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (debug_log) {
|
||||
ALOGD("memfd: device has memfd support, using it\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool has_memfd_support() {
|
||||
/* memfd_supported is the initial global per-process state of what is known
|
||||
* about memfd.
|
||||
*/
|
||||
static bool memfd_supported = __has_memfd_support();
|
||||
|
||||
return memfd_supported;
|
||||
}
|
||||
|
||||
/* logistics of getting file descriptor for ashmem */
|
||||
static int __ashmem_open_locked()
|
||||
{
|
||||
|
|
@ -177,11 +318,49 @@ static int __ashmem_check_failure(int fd, int result)
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool memfd_is_ashmem(int fd) {
|
||||
static bool fd_check_error_once = false;
|
||||
|
||||
if (__ashmem_is_ashmem(fd, 0) == 0) {
|
||||
if (!fd_check_error_once) {
|
||||
ALOGE("memfd: memfd expected but ashmem fd used - please use libcutils.\n");
|
||||
fd_check_error_once = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ashmem_valid(int fd)
|
||||
{
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return __ashmem_is_ashmem(fd, 0) >= 0;
|
||||
}
|
||||
|
||||
static int memfd_create_region(const char* name, size_t size) {
|
||||
android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING));
|
||||
|
||||
if (fd == -1) {
|
||||
ALOGE("memfd_create(%s, %zd) failed: %s\n", name, size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftruncate(fd, size) == -1) {
|
||||
ALOGE("ftruncate(%s, %zd) failed for memfd creation: %s\n", name, size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (debug_log) {
|
||||
ALOGE("memfd_create(%s, %zd) success. fd=%d\n", name, size, fd.get());
|
||||
}
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
/*
|
||||
* ashmem_create_region - creates a new ashmem region and returns the file
|
||||
* descriptor, or <0 on error
|
||||
|
|
@ -193,6 +372,10 @@ int ashmem_create_region(const char *name, size_t size)
|
|||
{
|
||||
int ret, save_errno;
|
||||
|
||||
if (has_memfd_support()) {
|
||||
return memfd_create_region(name ? name : "none", size);
|
||||
}
|
||||
|
||||
int fd = __ashmem_open();
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
|
|
@ -222,28 +405,78 @@ error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int memfd_set_prot_region(int fd, int prot) {
|
||||
/* Only proceed if an fd needs to be write-protected */
|
||||
if (prot & PROT_WRITE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
|
||||
ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE seal failed: %s\n", fd, prot,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ashmem_set_prot_region(int fd, int prot)
|
||||
{
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
return memfd_set_prot_region(fd, prot);
|
||||
}
|
||||
|
||||
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));
|
||||
}
|
||||
|
||||
int ashmem_pin_region(int fd, size_t offset, size_t len)
|
||||
{
|
||||
if (!pin_deprecation_warn || debug_log) {
|
||||
ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
|
||||
pin_deprecation_warn = true;
|
||||
}
|
||||
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: should LP64 reject too-large offset/len?
|
||||
ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
|
||||
|
||||
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin)));
|
||||
}
|
||||
|
||||
int ashmem_unpin_region(int fd, size_t offset, size_t len)
|
||||
{
|
||||
if (!pin_deprecation_warn || debug_log) {
|
||||
ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
|
||||
pin_deprecation_warn = true;
|
||||
}
|
||||
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: should LP64 reject too-large offset/len?
|
||||
ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
|
||||
|
||||
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin)));
|
||||
}
|
||||
|
||||
int ashmem_get_size_region(int fd)
|
||||
{
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
struct stat sb;
|
||||
|
||||
if (fstat(fd, &sb) == -1) {
|
||||
ALOGE("ashmem_get_size_region(%d): fstat failed: %s\n", fd, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (debug_log) {
|
||||
ALOGD("ashmem_get_size_region(%d): %d\n", fd, static_cast<int>(sb.st_size));
|
||||
}
|
||||
|
||||
return sb.st_size;
|
||||
}
|
||||
|
||||
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ cc_defaults {
|
|||
|
||||
header_libs: ["libpixelflinger_internal"],
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
"libpixelflinger",
|
||||
|
|
|
|||
|
|
@ -593,6 +593,11 @@ on post-fs-data
|
|||
# Set indication (checked by vold) that we have finished this action
|
||||
#setprop vold.post_fs_data_done 1
|
||||
|
||||
# sys.memfd_use set to false by default, which keeps it disabled
|
||||
# until it is confirmed that apps and vendor processes don't make
|
||||
# IOCTLs on ashmem fds any more.
|
||||
setprop sys.use_memfd false
|
||||
|
||||
# It is recommended to put unnecessary data/ initialization from post-fs-data
|
||||
# to start-zygote in device's init.rc to unblock zygote start.
|
||||
on zygote-start && property:ro.crypto.state=unencrypted
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue