Commit message: None
CVE: CVE-2016-2416
CWE: CWE-264
Vulnerability description: libs/gui/BufferQueueConsumer.cpp in mediaserver 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 check for the android.permission.DUMP permission, which allows attackers to obtain sensitive information, and consequently bypass an unspecified protection mechanism, via a dump request, as demonstrated by obtaining Signature or SignatureOrSystem access, aka internal bug 27046057.

Project: Android

status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
 nsecs_t expectedPresent) {
    ATRACE_CALL();
 Mutex::Autolock lock(mCore->mMutex);

 int numAcquiredBuffers = 0;
 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
 if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
 ++numAcquiredBuffers;
 }
 }
 if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
        BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
                numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
 return INVALID_OPERATION;
 }

 if (mCore->mQueue.empty()) {
 return NO_BUFFER_AVAILABLE;
 }

 BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());

 if (expectedPresent != 0) {
 const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second


 while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
 const BufferItem& bufferItem(mCore->mQueue[1]);
 nsecs_t desiredPresent = bufferItem.mTimestamp;
 if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
                    desiredPresent > expectedPresent) {
                BQ_LOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%"
 PRId64 " (%" PRId64 ") now=%" PRId64,
                        desiredPresent, expectedPresent,
                        desiredPresent - expectedPresent,
                        systemTime(CLOCK_MONOTONIC));
 break;
 }

            BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
 " size=%zu",
                    desiredPresent, expectedPresent, mCore->mQueue.size());
 if (mCore->stillTracking(front)) {
                mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
 }
            mCore->mQueue.erase(front);
            front = mCore->mQueue.begin();
 }

 nsecs_t desiredPresent = front->mTimestamp;
 if (desiredPresent > expectedPresent &&
                desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) {
            BQ_LOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64
 " (%" PRId64 ") now=%" PRId64,
                    desiredPresent, expectedPresent,
                    desiredPresent - expectedPresent,
                    systemTime(CLOCK_MONOTONIC));
 return PRESENT_LATER;
 }

        BQ_LOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " "
 "(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent,
                desiredPresent - expectedPresent,
                systemTime(CLOCK_MONOTONIC));
 }

 int slot = front->mSlot;
 *outBuffer = *front;
    ATRACE_BUFFER_INDEX(slot);

    BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
            slot, front->mFrameNumber, front->mGraphicBuffer->handle);
 if (mCore->stillTracking(front)) {
        mSlots[slot].mAcquireCalled = true;
        mSlots[slot].mNeedsCleanupOnRelease = false;
        mSlots[slot].mBufferState = BufferSlot::ACQUIRED;
        mSlots[slot].mFence = Fence::NO_FENCE;
 }

 if (outBuffer->mAcquireCalled) {
        outBuffer->mGraphicBuffer = NULL;
 }

    mCore->mQueue.erase(front);

    mCore->mDequeueCondition.broadcast();

    ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());

 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::attachBuffer(int* outSlot,
 const sp<android::GraphicBuffer>& buffer) {
    ATRACE_CALL();

 if (outSlot == NULL) {
        BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
 return BAD_VALUE;
 } else if (buffer == NULL) {
        BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
 return BAD_VALUE;
 }

 Mutex::Autolock lock(mCore->mMutex);

 int numAcquiredBuffers = 0;
 int found = BufferQueueCore::INVALID_BUFFER_SLOT;
 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
 if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
 ++numAcquiredBuffers;
 } else if (mSlots[s].mBufferState == BufferSlot::FREE) {
 if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
                    mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
                found = s;
 }
 }
 }

 if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
        BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d "
 "(max %d)", numAcquiredBuffers,
                mCore->mMaxAcquiredBufferCount);
 return INVALID_OPERATION;
 }
 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
        BQ_LOGE("attachBuffer(P): could not find free buffer slot");
 return NO_MEMORY;
 }

 *outSlot = found;
    ATRACE_BUFFER_INDEX(*outSlot);
    BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot);

    mSlots[*outSlot].mGraphicBuffer = buffer;
    mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED;
    mSlots[*outSlot].mAttachedByConsumer = true;
    mSlots[*outSlot].mNeedsCleanupOnRelease = false;
    mSlots[*outSlot].mFence = Fence::NO_FENCE;
    mSlots[*outSlot].mFrameNumber = 0;

    mSlots[*outSlot].mAcquireCalled = false;

 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::connect(
 const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
    ATRACE_CALL();

 if (consumerListener == NULL) {
        BQ_LOGE("connect(C): consumerListener may not be NULL");
 return BAD_VALUE;
 }

    BQ_LOGV("connect(C): controlledByApp=%s",
            controlledByApp ? "true" : "false");

 Mutex::Autolock lock(mCore->mMutex);

 if (mCore->mIsAbandoned) {
        BQ_LOGE("connect(C): BufferQueue has been abandoned");
 return NO_INIT;
 }

    mCore->mConsumerListener = consumerListener;
    mCore->mConsumerControlledByApp = controlledByApp;

 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::disableAsyncBuffer() {
    ATRACE_CALL();

 Mutex::Autolock lock(mCore->mMutex);

 if (mCore->mConsumerListener != NULL) {
        BQ_LOGE("disableAsyncBuffer: consumer already connected");
 return INVALID_OPERATION;
 }

    BQ_LOGV("disableAsyncBuffer");
    mCore->mUseAsyncBuffer = false;
 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) {
    ATRACE_CALL();

 if (outSlotMask == NULL) {
        BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL");
 return BAD_VALUE;
 }

 Mutex::Autolock lock(mCore->mMutex);

 if (mCore->mIsAbandoned) {
        BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned");
 return NO_INIT;
 }

 uint64_t mask = 0;
 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
 if (!mSlots[s].mAcquireCalled) {
            mask |= (1ULL << s);
 }
 }

 BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
 while (current != mCore->mQueue.end()) {
 if (current->mAcquireCalled) {
            mask &= ~(1ULL << current->mSlot);
 }
 ++current;
 }

    BQ_LOGV("getReleasedBuffers: returning mask %#" PRIx64, mask);
 *outSlotMask = mask;
 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
 const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
 EGLSyncKHR eglFence) {
    ATRACE_CALL();
    ATRACE_BUFFER_INDEX(slot);

 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
            releaseFence == NULL) {
 return BAD_VALUE;
 }

    sp<IProducerListener> listener;
 { // Autolock scope
 Mutex::Autolock lock(mCore->mMutex);

 if (frameNumber != mSlots[slot].mFrameNumber) {
 return STALE_BUFFER_SLOT;
 }

 BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
 while (current != mCore->mQueue.end()) {
 if (current->mSlot == slot) {
                BQ_LOGE("releaseBuffer: buffer slot %d pending release is "
 "currently queued", slot);
 return BAD_VALUE;
 }
 ++current;
 }

 if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
            mSlots[slot].mEglDisplay = eglDisplay;
            mSlots[slot].mEglFence = eglFence;
            mSlots[slot].mFence = releaseFence;
            mSlots[slot].mBufferState = BufferSlot::FREE;
            listener = mCore->mConnectedProducerListener;
            BQ_LOGV("releaseBuffer: releasing slot %d", slot);
 } else if (mSlots[slot].mNeedsCleanupOnRelease) {
            BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
 "(state = %d)", slot, mSlots[slot].mBufferState);
            mSlots[slot].mNeedsCleanupOnRelease = false;
 return STALE_BUFFER_SLOT;
 } else {
            BQ_LOGV("releaseBuffer: attempted to release buffer slot %d "
 "but its state was %d", slot, mSlots[slot].mBufferState);
 return BAD_VALUE;
 }

        mCore->mDequeueCondition.broadcast();
 } // Autolock scope

 if (listener != NULL) {
        listener->onBufferReleased();
 }

 return NO_ERROR;
}

Project: Android

void BufferQueueConsumer::setConsumerName(const String8& name) {
    ATRACE_CALL();
    BQ_LOGV("setConsumerName: '%s'", name.string());
 Mutex::Autolock lock(mCore->mMutex);
    mCore->mConsumerName = name;
    mConsumerName = name;
}

Project: Android

status_t BufferQueueConsumer::setConsumerUsageBits(uint32_t usage) {
    ATRACE_CALL();
    BQ_LOGV("setConsumerUsageBits: %#x", usage);
 Mutex::Autolock lock(mCore->mMutex);
    mCore->mConsumerUsageBits = usage;
 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
    ATRACE_CALL();
    BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat);
 Mutex::Autolock lock(mCore->mMutex);
    mCore->mDefaultBufferFormat = defaultFormat;
 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
 uint32_t height) {
    ATRACE_CALL();

 if (width == 0 || height == 0) {
        BQ_LOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u "
 "height=%u)", width, height);
 return BAD_VALUE;
 }

    BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height);

 Mutex::Autolock lock(mCore->mMutex);
    mCore->mDefaultWidth = width;
    mCore->mDefaultHeight = height;
 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::setDefaultMaxBufferCount(int bufferCount) {
    ATRACE_CALL();
 Mutex::Autolock lock(mCore->mMutex);
 return mCore->setDefaultMaxBufferCountLocked(bufferCount);
}

Project: Android

status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
 int maxAcquiredBuffers) {
    ATRACE_CALL();

 if (maxAcquiredBuffers < 1 ||
            maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
        BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
                maxAcquiredBuffers);
 return BAD_VALUE;
 }

 Mutex::Autolock lock(mCore->mMutex);

 if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
        BQ_LOGE("setMaxAcquiredBufferCount: producer is already connected");
 return INVALID_OPERATION;
 }

    BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
    mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
 return NO_ERROR;
}

Project: Android

status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
    ATRACE_CALL();
    BQ_LOGV("setTransformHint: %#x", hint);
 Mutex::Autolock lock(mCore->mMutex);
    mCore->mTransformHint = hint;
 return NO_ERROR;
}

Project: Android

BufferQueueConsumer::~BufferQueueConsumer() {}