Commit message: None
CVE: CVE-2016-0846
CWE: CWE-264
Vulnerability description: libs/binder/IMemory.cpp in the IMemory Native Interface in Android 4.x before 4.4.4, 5.0.x before 5.0.2, 5.1.x before 5.1.1, and 6.x before 2016-04-01 does not properly consider the heap size, which allows attackers to gain privileges via a crafted application, as demonstrated by obtaining Signature or SignatureOrSystem access, aka internal bug 26877992.

Project: Android

BnMemory::BnMemory() {
}

Project: Android

BnMemoryHeap::BnMemoryHeap() {
}

Project: Android

BpMemory::BpMemory(const sp<IBinder>& impl)
 : BpInterface<IMemory>(impl), mOffset(0), mSize(0)
{
}

Project: Android

BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl)
 : BpInterface<IMemoryHeap>(impl),
        mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mOffset(0), mRealHeap(false)
{
}

Project: Android

HeapCache::HeapCache()
 : DeathRecipient()
{
}

Project: Android

void BpMemoryHeap::assertMapped() const
{
 if (mHeapId == -1) {
        sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());
        sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
        heap->assertReallyMapped();
 if (heap->mBase != MAP_FAILED) {
 Mutex::Autolock _l(mLock);
 if (mHeapId == -1) {
                mBase   = heap->mBase;
                mSize   = heap->mSize;
                mOffset = heap->mOffset;
                android_atomic_write( dup( heap->mHeapId ), &mHeapId );
 }
 } else {
            free_heap(binder);
 }
 }
}

Project: Android

void BpMemoryHeap::assertReallyMapped() const
{
 if (mHeapId == -1) {


 Parcel data, reply;
        data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
 status_t err = remote()->transact(HEAP_ID, data, &reply);
 int parcel_fd = reply.readFileDescriptor();
 ssize_t size = reply.readInt32();
 uint32_t flags = reply.readInt32();
 uint32_t offset = reply.readInt32();

        ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)",
                asBinder().get(), parcel_fd, size, err, strerror(-err));

 int fd = dup( parcel_fd );
        ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
                parcel_fd, size, err, strerror(errno));

 int access = PROT_READ;
 if (!(flags & READ_ONLY)) {
            access |= PROT_WRITE;
 }

 Mutex::Autolock _l(mLock);
 if (mHeapId == -1) {
            mRealHeap = true;
            mBase = mmap(0, size, access, MAP_SHARED, fd, offset);
 if (mBase == MAP_FAILED) {
                ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)",
                        asBinder().get(), size, fd, strerror(errno));
                close(fd);
 } else {
                mSize = size;
                mFlags = flags;
                mOffset = offset;
                android_atomic_write(fd, &mHeapId);
 }
 }
 }
}

Project: Android

 static inline void dump_heaps() {
        gHeapCache->dump_heaps();
 }

Project: Android

void HeapCache::dump_heaps()
{
 Mutex::Autolock _l(mHeapCacheLock);
 int c = mHeapCache.size();
 for (int i=0 ; i<c ; i++) {
 const heap_info_t& info = mHeapCache.valueAt(i);
 BpMemoryHeap const* h(static_cast<BpMemoryHeap const *>(info.heap.get()));
        ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)",
                mHeapCache.keyAt(i).unsafe_get(),
                info.heap.get(), info.count,
                h->mHeapId, h->mBase, h->mSize);
 }
}

Project: Android

 static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) {
 return gHeapCache->find_heap(binder);
 }

Project: Android

sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)
{
 Mutex::Autolock _l(mHeapCacheLock);
 ssize_t i = mHeapCache.indexOfKey(binder);
 if (i>=0) {
 heap_info_t& info = mHeapCache.editValueAt(i);
        ALOGD_IF(VERBOSE,
 "found binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
                binder.get(), info.heap.get(),
 static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
 static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
                info.count);
        android_atomic_inc(&info.count);
 return info.heap;
 } else {
 heap_info_t info;
        info.heap = interface_cast<IMemoryHeap>(binder);
        info.count = 1;
        mHeapCache.add(binder, info);
 return info.heap;
 }
}

Project: Android

 static inline void free_heap(const sp<IBinder>& binder) {
        gHeapCache->free_heap(binder);
 }

Project: Android

void HeapCache::free_heap(const sp<IBinder>& binder) {
    free_heap( wp<IBinder>(binder) );
}

Project: Android

void HeapCache::free_heap(const wp<IBinder>& binder)
{
    sp<IMemoryHeap> rel;
 {
 Mutex::Autolock _l(mHeapCacheLock);
 ssize_t i = mHeapCache.indexOfKey(binder);
 if (i>=0) {
 heap_info_t& info(mHeapCache.editValueAt(i));
 int32_t c = android_atomic_dec(&info.count);
 if (c == 1) {
                ALOGD_IF(VERBOSE,
 "removing binder=%p, heap=%p, size=%zu, fd=%d, count=%d",
                        binder.unsafe_get(), info.heap.get(),
 static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
 static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
                        info.count);
                rel = mHeapCache.valueAt(i).heap;
                mHeapCache.removeItemsAt(i);
 }
 } else {
            ALOGE("free_heap binder=%p not found!!!", binder.unsafe_get());
 }
 }
}

Project: Android

void* BpMemoryHeap::getBase() const {
    assertMapped();
 return mBase;
}

Project: Android

uint32_t BpMemoryHeap::getFlags() const {
    assertMapped();
 return mFlags;
}

Project: Android

int BpMemoryHeap::getHeapID() const {
    assertMapped();
 return mHeapId;
}

Project: Android

uint32_t BpMemoryHeap::getOffset() const {
    assertMapped();
 return mOffset;
}

Project: Android

 static inline sp<IMemoryHeap> get_heap(const sp<IBinder>& binder) {
 return gHeapCache->get_heap(binder);
 }

Project: Android

sp<IMemoryHeap> HeapCache::get_heap(const sp<IBinder>& binder)
{
    sp<IMemoryHeap> realHeap;
 Mutex::Autolock _l(mHeapCacheLock);
 ssize_t i = mHeapCache.indexOfKey(binder);
 if (i>=0)   realHeap = mHeapCache.valueAt(i).heap;
 else        realHeap = interface_cast<IMemoryHeap>(binder);
 return realHeap;
}

Project: Android

ssize_t IMemory::offset() const {
 ssize_t offset;
    getMemory(&offset);
 return offset;
}

Project: Android

status_t BnMemory::onTransact(
 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
 switch(code) {
 case GET_MEMORY: {
            CHECK_INTERFACE(IMemory, data, reply);
 ssize_t offset;
 size_t size;
            reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() );
            reply->writeInt32(offset);
            reply->writeInt32(size);
 return NO_ERROR;
 } break;
 default:
 return BBinder::onTransact(code, data, reply, flags);
 }
}

Project: Android

status_t BnMemoryHeap::onTransact(
 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
 switch(code) {
 case HEAP_ID: {
            CHECK_INTERFACE(IMemoryHeap, data, reply);
            reply->writeFileDescriptor(getHeapID());
            reply->writeInt32(getSize());
            reply->writeInt32(getFlags());
            reply->writeInt32(getOffset());
 return NO_ERROR;
 } break;
 default:
 return BBinder::onTransact(code, data, reply, flags);
 }
}

Project: Android

void* IMemory::pointer() const {
 ssize_t offset;
    sp<IMemoryHeap> heap = getMemory(&offset);
 void* const base = heap!=0 ? heap->base() : MAP_FAILED;
 if (base == MAP_FAILED)
 return 0;
 return static_cast<char*>(base) + offset;
}

Project: Android

size_t IMemory::size() const {
 size_t size;
    getMemory(NULL, &size);
 return size;
}

Project: Android

BnMemory::~BnMemory() {
}

Project: Android

BnMemoryHeap::~BnMemoryHeap() {
}

Project: Android

BpMemory::~BpMemory()
{
}

Project: Android

BpMemoryHeap::~BpMemoryHeap() {
 if (mHeapId != -1) {
        close(mHeapId);
 if (mRealHeap) {
 if (mBase != MAP_FAILED) {
                sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();

 if (VERBOSE) {
                    ALOGD("UNMAPPING binder=%p, heap=%p, size=%zu, fd=%d",
                            binder.get(), this, mSize, mHeapId);
 CallStack stack(LOG_TAG);
 }

                munmap(mBase, mSize);
 }
 } else {
            sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();
            free_heap(binder);
 }
 }
}

Project: Android

HeapCache::~HeapCache()
{
}