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

Commit 4bf81b14 authored by Mathias Agopian's avatar Mathias Agopian Committed by Android (Google) Code Review
Browse files

Merge "PermissionCache caches permission checks"

parents 88643bce 99b49840
Loading
Loading
Loading
Loading
+79 −0
Original line number Diff line number Diff line
@@ -20,46 +20,57 @@
#include <stdint.h>
#include <unistd.h>

#include <utils/SortedVector.h>
#include <utils/String16.h>
#include <utils/threads.h>
#include <utils/Singleton.h>

namespace android {
// ---------------------------------------------------------------------------

/*
 * Permission caches the result of the permission check for the given
 * permission name and the provided uid/pid. It also handles a few
 * known cases efficiently (caller is in the same process or is root).
 * The package manager does something similar but lives in dalvik world
 * and is therefore extremely slow to access.
 * PermissionCache caches permission checks for a given uid.
 *
 * Currently the cache is not updated when there is a permission change,
 * for instance when an application is uninstalled.
 *
 * IMPORTANT: for the reason stated above, only system permissions are safe
 * to cache. This restriction may be lifted at a later time.
 *
 */

class Permission
{
public:
            Permission(char const* name);
            Permission(const String16& name);
            Permission(const Permission& rhs);
    virtual ~Permission();
class PermissionCache : Singleton<PermissionCache> {
    struct Entry {
        String16    name;
        uid_t       uid;
        bool        granted;
        inline bool operator < (const Entry& e) const {
            return (uid == e.uid) ? (name < e.name) : (uid < e.uid);
        }
    };
    mutable Mutex mLock;
    // we pool all the permission names we see, as many permissions checks
    // will have identical names
    SortedVector< String16 > mPermissionNamesPool;
    // this is our cache per say. it stores pooled names.
    SortedVector< Entry > mCache;

    bool operator < (const Permission& rhs) const;
    // free the whole cache, but keep the permission name pool
    void purge();

    // checks the current binder call's caller has access to this permission
    bool checkCalling() const;
    status_t check(bool* granted,
            const String16& permission, uid_t uid) const;

    // checks the specified pid/uid has access to this permission
    bool check(pid_t pid, uid_t uid) const;
    void cache(const String16& permission, uid_t uid, bool granted);

protected:
    virtual bool doCheckPermission(pid_t pid, uid_t uid) const;
public:
    PermissionCache();

private:
    Permission& operator = (const Permission& rhs) const;
    const String16 mPermissionName;
    mutable SortedVector<uid_t> mGranted;
    const pid_t mPid;
    mutable Mutex mLock;
    static bool checkCallingPermission(const String16& permission);

    static bool checkCallingPermission(const String16& permission,
                                int32_t* outPid, int32_t* outUid);

    static bool checkPermission(const String16& permission,
            pid_t pid, uid_t uid);
};

// ---------------------------------------------------------------------------
+1 −1
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ sources := \
    MemoryHeapBase.cpp \
    MemoryHeapPmem.cpp \
    Parcel.cpp \
    Permission.cpp \
    PermissionCache.cpp \
    ProcessState.cpp \
    Static.cpp

+113 −0
Original line number Diff line number Diff line
@@ -14,72 +14,97 @@
 * limitations under the License.
 */

#define LOG_TAG "PermissionCache"

#include <stdint.h>
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Permission.h>
#include <binder/PermissionCache.h>
#include <utils/String8.h>

namespace android {
// ---------------------------------------------------------------------------

Permission::Permission(char const* name)
    : mPermissionName(name), mPid(getpid())
{
// ----------------------------------------------------------------------------

ANDROID_SINGLETON_STATIC_INSTANCE(PermissionCache) ;

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

PermissionCache::PermissionCache() {
}

Permission::Permission(const String16& name)
    : mPermissionName(name), mPid(getpid())
{
status_t PermissionCache::check(bool* granted,
        const String16& permission, uid_t uid) const {
    Mutex::Autolock _l(mLock);
    Entry e;
    e.name = permission;
    e.uid  = uid;
    ssize_t index = mCache.indexOf(e);
    if (index >= 0) {
        *granted = mCache.itemAt(index).granted;
        return NO_ERROR;
    }
    return NAME_NOT_FOUND;
}

Permission::Permission(const Permission& rhs)
    : mPermissionName(rhs.mPermissionName),
    mGranted(rhs.mGranted),
    mPid(rhs.mPid)
{
void PermissionCache::cache(const String16& permission,
        uid_t uid, bool granted) {
    Mutex::Autolock _l(mLock);
    Entry e;
    ssize_t index = mPermissionNamesPool.indexOf(permission);
    if (index > 0) {
        e.name = mPermissionNamesPool.itemAt(index);
    } else {
        mPermissionNamesPool.add(permission);
        e.name = permission;
    }
    // note, we don't need to store the pid, which is not actually used in
    // permission checks
    e.uid  = uid;
    e.granted = granted;
    index = mCache.indexOf(e);
    if (index < 0) {
        mCache.add(e);
    }
}

Permission::~Permission()
{
void PermissionCache::purge() {
    Mutex::Autolock _l(mLock);
    mCache.clear();
}

bool Permission::operator < (const Permission& rhs) const
{
    return mPermissionName < rhs.mPermissionName;
bool PermissionCache::checkCallingPermission(const String16& permission) {
    return PermissionCache::checkCallingPermission(permission, NULL, NULL);
}

bool Permission::checkCalling() const
{
bool PermissionCache::checkCallingPermission(
        const String16& permission, int32_t* outPid, int32_t* outUid) {
    IPCThreadState* ipcState = IPCThreadState::self();
    pid_t pid = ipcState->getCallingPid();
    uid_t uid = ipcState->getCallingUid();
    return doCheckPermission(pid, uid);
}

bool Permission::check(pid_t pid, uid_t uid) const
{
    return doCheckPermission(pid, uid);
    if (outPid) *outPid = pid;
    if (outUid) *outUid = uid;
    return PermissionCache::checkPermission(permission, pid, uid);
}

bool Permission::doCheckPermission(pid_t pid, uid_t uid) const
{
    if ((uid == 0) || (pid == mPid)) {
bool PermissionCache::checkPermission(
        const String16& permission, pid_t pid, uid_t uid) {
    if ((uid == 0) || (pid == getpid())) {
        // root and ourselves is always okay
        return true;
    } else {
        // see if we already granted this permission for this uid
        Mutex::Autolock _l(mLock);
        if (mGranted.indexOf(uid) >= 0)
            return true;
    }

    bool granted = checkPermission(mPermissionName, pid, uid);
    if (granted) {
        Mutex::Autolock _l(mLock);
        // no need to check again, the old item will be replaced if it is
        // already there.
        mGranted.add(uid);
    PermissionCache& pc(PermissionCache::getInstance());
    bool granted = false;
    if (pc.check(&granted, permission, uid) != NO_ERROR) {
        nsecs_t t = -systemTime();
        granted = android::checkPermission(permission, pid, uid);
        t += systemTime();
        LOGD("checking %s for uid=%d => %s (%d us)",
                String8(permission).string(), uid,
                granted?"granted":"denied", (int)ns2us(t));
        pc.cache(permission, uid, granted);
    }
    return granted;
}
+16 −10
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryHeapBase.h>
#include <binder/PermissionCache.h>

#include <utils/String8.h>
#include <utils/String16.h>
@@ -67,6 +68,13 @@
namespace android {
// ---------------------------------------------------------------------------

const String16 sHardwareTest("android.permission.HARDWARE_TEST");
const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");

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

SurfaceFlinger::SurfaceFlinger()
    :   BnSurfaceComposer(), Thread(false),
        mTransactionFlags(0),
@@ -74,10 +82,6 @@ SurfaceFlinger::SurfaceFlinger()
        mResizeTransationPending(false),
        mLayersRemoved(false),
        mBootTime(systemTime()),
        mHardwareTest("android.permission.HARDWARE_TEST"),
        mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
        mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
        mDump("android.permission.DUMP"),
        mVisibleRegionsDirty(false),
        mHwWorkListDirty(false),
        mDeferReleaseConsole(false),
@@ -1464,7 +1468,8 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
    const size_t SIZE = 4096;
    char buffer[SIZE];
    String8 result;
    if (!mDump.checkCalling()) {

    if (!PermissionCache::checkCallingPermission(sDump)) {
        snprintf(buffer, SIZE, "Permission Denial: "
                "can't dump SurfaceFlinger from pid=%d, uid=%d\n",
                IPCThreadState::self()->getCallingPid(),
@@ -1596,7 +1601,8 @@ status_t SurfaceFlinger::onTransact(
            IPCThreadState* ipc = IPCThreadState::self();
            const int pid = ipc->getCallingPid();
            const int uid = ipc->getCallingUid();
            if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) {
            if ((uid != AID_GRAPHICS) &&
                    !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
                LOGE("Permission Denial: "
                        "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
                return PERMISSION_DENIED;
@@ -1609,7 +1615,8 @@ status_t SurfaceFlinger::onTransact(
            IPCThreadState* ipc = IPCThreadState::self();
            const int pid = ipc->getCallingPid();
            const int uid = ipc->getCallingUid();
            if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
            if ((uid != AID_GRAPHICS) &&
                    !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
                LOGE("Permission Denial: "
                        "can't read framebuffer pid=%d, uid=%d", pid, uid);
                return PERMISSION_DENIED;
@@ -1621,7 +1628,7 @@ status_t SurfaceFlinger::onTransact(
    status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
    if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
        CHECK_INTERFACE(ISurfaceComposer, data, reply);
        if (UNLIKELY(!mHardwareTest.checkCalling())) {
        if (UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) {
            IPCThreadState* ipc = IPCThreadState::self();
            const int pid = ipc->getCallingPid();
            const int uid = ipc->getCallingUid();
@@ -2404,8 +2411,7 @@ status_t Client::onTransact(
     const int self_pid = getpid();
     if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) {
         // we're called from a different process, do the real check
         if (!checkCallingPermission(
                 String16("android.permission.ACCESS_SURFACE_FLINGER")))
         if (!PermissionCache::checkCallingPermission(sAccessSurfaceFlinger))
         {
             LOGE("Permission Denial: "
                     "can't openGlobalTransaction pid=%d, uid=%d", pid, uid);
+6 −11
Original line number Diff line number Diff line
@@ -20,21 +20,20 @@
#include <stdint.h>
#include <sys/types.h>

#include <utils/SortedVector.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/threads.h>

#include <binder/IMemory.h>
#include <binder/Permission.h>
#include <binder/BinderService.h>
#include <binder/IMemory.h>

#include <ui/PixelFormat.h>
#include <surfaceflinger/IGraphicBufferAlloc.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/ISurfaceComposerClient.h>
#include <surfaceflinger/IGraphicBufferAlloc.h>

#include "Barrier.h"
#include "Layer.h"
@@ -353,10 +352,6 @@ private:
                surface_flinger_cblk_t*     mServerCblk;
                GLuint                      mWormholeTexName;
                nsecs_t                     mBootTime;
                Permission                  mHardwareTest;
                Permission                  mAccessSurfaceFlinger;
                Permission                  mReadFramebuffer;
                Permission                  mDump;

                // Can only accessed from the main thread, these members
                // don't need synchronization
Loading