Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7ade4f4c authored by Atneya Nair's avatar Atneya Nair
Browse files

Add explicit memfd support to MemoryHeapBase

The default behavior of MemoryHeapBase is to request memory from
ashmem in libcutils. This library is being used to migrate from
ashmem to memfd, but on supported devices, memfd is still debug
only in order to sanitize expectations of callees of the library.

As such, we add a flag to explicitly request memfd files, as well
as to opt into file sealing of memfd files.

Note: the calling process will have to modify selinux permissions
to allow tmpfs access to the domains accessing the shared buffer.
This is more secure than ashmem as granular acess can be specified.
See audioserver_tmpfs in system/selinux for examples.

Test: Expected mappings recieved in Audioflinger for flag combos.
      atest binderMemoryHeapBaseUnitTest
Change-Id: I893f7846f4d16b8ccc7f407fb957dccd9ebe62d7
parent 148b13fc
Loading
Loading
Loading
Loading
+56 −8
Original line number Diff line number Diff line
@@ -18,10 +18,13 @@

#include <errno.h>
#include <fcntl.h>
#include <linux/memfd.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

@@ -34,6 +37,24 @@ namespace android {

// ---------------------------------------------------------------------------

#ifdef __BIONIC__
static int memfd_create_region(const char* name, size_t size) {
    int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING);
    if (fd == -1) {
        ALOGE("%s: memfd_create(%s, %zd) failed: %s\n", __func__, name, size, strerror(errno));
        return -1;
    }

    if (ftruncate(fd, size) == -1) {
        ALOGE("%s, ftruncate(%s, %zd) failed for memfd creation: %s\n", __func__, name, size,
              strerror(errno));
        close(fd);
        return -1;
    }
    return fd;
}
#endif

MemoryHeapBase::MemoryHeapBase()
    : mFD(-1), mSize(0), mBase(MAP_FAILED),
      mDevice(nullptr), mNeedUnmap(false), mOffset(0)
@@ -46,14 +67,35 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
{
    const size_t pagesize = getpagesize();
    size = ((size + pagesize - 1) & ~(pagesize - 1));
    int fd = ashmem_create_region(name == nullptr ? "MemoryHeapBase" : name, size);
    ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
    if (fd >= 0) {
        if (mapfd(fd, true, size) == NO_ERROR) {
            if (flags & READ_ONLY) {
                ashmem_set_prot_region(fd, PROT_READ);
    int fd = -1;
    if (mFlags & FORCE_MEMFD) {
#ifdef __BIONIC__
        ALOGV("MemoryHeapBase: Attempting to force MemFD");
        fd = memfd_create_region(name ? name : "MemoryHeapBase", size);
        if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
        const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) |
                ((mFlags & MEMFD_ALLOW_SEALING) ? 0 : F_SEAL_SEAL);
        if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) {
            ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error  %s", name,
                  SEAL_FLAGS, strerror(errno));
            munmap(mBase, mSize);
            mBase = nullptr;
            mSize = 0;
            close(fd);
        }
        return;
#else
        mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING);
#endif
    }
    if (mFlags & MEMFD_ALLOW_SEALING) {
      LOG_ALWAYS_FATAL("Invalid Flags. MEMFD_ALLOW_SEALING only valid with FORCE_MEMFD.");
    }
    fd = ashmem_create_region(name ? name : "MemoryHeapBase", size);
    ALOGE_IF(fd < 0, "MemoryHeapBase: error creating ashmem region: %s", strerror(errno));
    if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
    if (mFlags & READ_ONLY) {
        ashmem_set_prot_region(fd, PROT_READ);
    }
}

@@ -61,6 +103,9 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags)
    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
      mDevice(nullptr), mNeedUnmap(false), mOffset(0)
{
    if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) {
        LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor");
    }
    int open_flags = O_RDWR;
    if (flags & NO_CACHING)
        open_flags |= O_SYNC;
@@ -80,6 +125,9 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset
    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
      mDevice(nullptr), mNeedUnmap(false), mOffset(0)
{
    if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) {
        LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor");
    }
    const size_t pagesize = getpagesize();
    size = ((size + pagesize-1) & ~(pagesize-1));
    mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), false, size, offset);
+15 −1
Original line number Diff line number Diff line
@@ -34,7 +34,21 @@ public:
        // memory won't be mapped locally, but will be mapped in the remote
        // process.
        DONT_MAP_LOCALLY = 0x00000100,
        NO_CACHING = 0x00000200
        NO_CACHING = 0x00000200,
        // Bypass ashmem-libcutils to create a memfd shared region.
        // Ashmem-libcutils will eventually migrate to memfd.
        // Memfd has security benefits and supports file sealing.
        // Calling process will need to modify selinux permissions to
        // open access to tmpfs files. See audioserver for examples.
        // This is only valid for size constructor.
        // For host compilation targets, memfd is stubbed in favor of /tmp
        // files so sealing is not enforced.
        FORCE_MEMFD = 0x00000400,
        // Default opt-out of sealing behavior in memfd to avoid potential DOS.
        // Clients of shared files can seal at anytime via syscall, leading to
        // TOC/TOU issues if additional seals prevent access from the creating
        // process. Alternatively, seccomp fcntl().
        MEMFD_ALLOW_SEALING = 0x00000800
    };

    /*
+1 −0
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ cc_test {
        "binderParcelUnitTest.cpp",
        "binderBinderUnitTest.cpp",
        "binderStatusUnitTest.cpp",
        "binderMemoryHeapBaseUnitTest.cpp",
    ],
    shared_libs: [
        "libbinder",
+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source 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 <binder/MemoryHeapBase.h>
#include <cutils/ashmem.h>
#include <fcntl.h>

#include <gtest/gtest.h>
using namespace android;
#ifdef __BIONIC__
TEST(MemoryHeapBase, ForceMemfdRespected) {
    auto mHeap = sp<MemoryHeapBase>::make(10, MemoryHeapBase::FORCE_MEMFD, "Test mapping");
    int fd = mHeap->getHeapID();
    EXPECT_NE(fd, -1);
    EXPECT_FALSE(ashmem_valid(fd));
    EXPECT_NE(fcntl(fd, F_GET_SEALS), -1);
}

TEST(MemoryHeapBase, MemfdSealed) {
    auto mHeap = sp<MemoryHeapBase>::make(8192,
                                          MemoryHeapBase::FORCE_MEMFD,
                                          "Test mapping");
    int fd = mHeap->getHeapID();
    EXPECT_NE(fd, -1);
    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL);
}

TEST(MemoryHeapBase, MemfdUnsealed) {
    auto mHeap = sp<MemoryHeapBase>::make(8192,
                                          MemoryHeapBase::FORCE_MEMFD |
                                          MemoryHeapBase::MEMFD_ALLOW_SEALING,
                                          "Test mapping");
    int fd = mHeap->getHeapID();
    EXPECT_NE(fd, -1);
    EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0);
}

TEST(MemoryHeapBase, MemfdSealedProtected) {
    auto mHeap = sp<MemoryHeapBase>::make(8192,
                                          MemoryHeapBase::FORCE_MEMFD |
                                          MemoryHeapBase::READ_ONLY,
                                          "Test mapping");
    int fd = mHeap->getHeapID();
    EXPECT_NE(fd, -1);
    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
}

TEST(MemoryHeapBase, MemfdUnsealedProtected) {
    auto mHeap = sp<MemoryHeapBase>::make(8192,
                                          MemoryHeapBase::FORCE_MEMFD |
                                          MemoryHeapBase::READ_ONLY |
                                          MemoryHeapBase::MEMFD_ALLOW_SEALING,
                                          "Test mapping");
    int fd = mHeap->getHeapID();
    EXPECT_NE(fd, -1);
    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE);
}

#else
TEST(MemoryHeapBase, HostMemfdExpected) {
    auto mHeap = sp<MemoryHeapBase>::make(8192,
                                          MemoryHeapBase::READ_ONLY,
                                          "Test mapping");
    int fd = mHeap->getHeapID();
    void* ptr = mHeap->getBase();
    EXPECT_NE(ptr, MAP_FAILED);
    EXPECT_TRUE(ashmem_valid(fd));
    EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY);
}

TEST(MemoryHeapBase,HostMemfdException) {
    auto mHeap = sp<MemoryHeapBase>::make(8192,
                                          MemoryHeapBase::FORCE_MEMFD |
                                          MemoryHeapBase::READ_ONLY |
                                          MemoryHeapBase::MEMFD_ALLOW_SEALING,
                                          "Test mapping");
    int fd = mHeap->getHeapID();
    void* ptr = mHeap->getBase();
    EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY);
    EXPECT_TRUE(ashmem_valid(fd));
    EXPECT_NE(ptr, MAP_FAILED);
}

#endif