Commit message: None
CVE: CVE-2016-2449
CWE: CWE-264
Vulnerability description: services/camera/libcameraservice/device3/Camera3Device.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-05-01 does not validate template IDs, which allows attackers to gain privileges via a crafted application, as demonstrated by obtaining Signature or SignatureOrSystem access, aka internal bug 27568958.

Project: Android

status_t Camera3Device::captureList(const List<const CameraMetadata> &requests,
 int64_t *lastFrameNumber) {
    ATRACE_CALL();

 return submitRequestsHelper(requests, /*repeating*/false, lastFrameNumber);
}

Project: Android

status_t Camera3Device::RequestThread::clear(
 NotificationListener *listener,
 /*out*/int64_t *lastFrameNumber) {
 Mutex::Autolock l(mRequestLock);
    ALOGV("RequestThread::%s:", __FUNCTION__);

    mRepeatingRequests.clear();

 if (listener != NULL) {
 for (RequestList::iterator it = mRequestQueue.begin();
                 it != mRequestQueue.end(); ++it) {
 if ((*it)->mInputStream != NULL) {
 camera3_stream_buffer_t inputBuffer;
 status_t res = (*it)->mInputStream->getInputBuffer(&inputBuffer);
 if (res != OK) {
                    ALOGW("%s: %d: couldn't get input buffer while clearing the request "
 "list: %s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
 } else {
                    res = (*it)->mInputStream->returnInputBuffer(inputBuffer);
 if (res != OK) {
                        ALOGE("%s: %d: couldn't return input buffer while clearing the request "
 "list: %s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
 }
 }
 }
 (*it)->mResultExtras.frameNumber = mFrameNumber++;
            listener->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
 (*it)->mResultExtras);
 }
 }
    mRequestQueue.clear();
    mTriggerMap.clear();
 if (lastFrameNumber != NULL) {
 *lastFrameNumber = mRepeatingLastFrameNumber;
 }
    mRepeatingLastFrameNumber = NO_IN_FLIGHT_REPEATING_FRAMES;
 return OK;
}

Project: Android

bool Camera3Device::insert3AResult(CameraMetadata& result, int32_t tag,
 const T* value, uint32_t frameNumber) {
 if (result.update(tag, value, 1) != NO_ERROR) {
        mResultQueue.erase(--mResultQueue.end(), mResultQueue.end());
        SET_ERR("Frame %d: Failed to set %s in partial metadata",
                frameNumber, get_camera_metadata_tag_name(tag));
 return false;
 }
 return true;
}

Project: Android

void Camera3Device::internalUpdateStatusLocked(Status status) {
    mStatus = status;
    mRecentStatusUpdates.add(mStatus);
    mStatusChanged.broadcast();
}

Project: Android

bool Camera3Device::isOpaqueInputSizeSupported(uint32_t width, uint32_t height) {
 for (uint32_t i = 0; i < mSupportedOpaqueInputSizes.size(); i++) {
 Size size = mSupportedOpaqueInputSizes[i];
 if (size.width == width && size.height == height) {
 return true;
 }
 }

 return false;
}

Project: Android

bool Camera3Device::RequestThread::isStreamPending(
        sp<Camera3StreamInterface>& stream) {
 Mutex::Autolock l(mRequestLock);

 for (const auto& nextRequest : mNextRequests) {
 if (!nextRequest.submitted) {
 for (const auto& s : nextRequest.captureRequest->mOutputStreams) {
 if (stream == s) return true;
 }
 if (stream == nextRequest.captureRequest->mInputStream) return true;
 }
 }

 for (const auto& request : mRequestQueue) {
 for (const auto& s : request->mOutputStreams) {
 if (stream == s) return true;
 }
 if (stream == request->mInputStream) return true;
 }

 for (const auto& request : mRepeatingRequests) {
 for (const auto& s : request->mOutputStreams) {
 if (stream == s) return true;
 }
 if (stream == request->mInputStream) return true;
 }

 return false;
}

Project: Android

void Camera3Device::RequestThread::setNotificationListener(
 NotificationListener *listener) {
 Mutex::Autolock l(mRequestLock);
    mListener = listener;
}

Project: Android

bool Camera3Device::RequestThread::threadLoop() {
    ATRACE_CALL();
 status_t res;

 if (waitIfPaused()) {
 return true;
 }

    waitForNextRequestBatch();
 if (mNextRequests.size() == 0) {
 return true;
 }

 int latestRequestId;
 camera_metadata_entry_t requestIdEntry = mNextRequests[mNextRequests.size() - 1].
            captureRequest->mSettings.find(ANDROID_REQUEST_ID);
 if (requestIdEntry.count > 0) {
        latestRequestId = requestIdEntry.data.i32[0];
 } else {
        ALOGW("%s: Did not have android.request.id set in the request.", __FUNCTION__);
        latestRequestId = NAME_NOT_FOUND;
 }

    res = prepareHalRequests();
 if (res == TIMED_OUT) {
        cleanUpFailedRequests(/*sendRequestError*/ true);
 return true;
 } else if (res != OK) {
        cleanUpFailedRequests(/*sendRequestError*/ false);
 return false;
 }

 {
 Mutex::Autolock al(mLatestRequestMutex);

        mLatestRequestId = latestRequestId;
        mLatestRequestSignal.signal();
 }

 bool useFlushLock = mNextRequests.size() > 1;

 if (useFlushLock) {
        mFlushLock.lock();
 }

    ALOGVV("%s: %d: submitting %d requests in a batch.", __FUNCTION__, __LINE__,
            mNextRequests.size());
 for (auto& nextRequest : mNextRequests) {
        ATRACE_ASYNC_BEGIN("frame capture", nextRequest.halRequest.frame_number);
        ATRACE_BEGIN("camera3->process_capture_request");
        res = mHal3Device->ops->process_capture_request(mHal3Device, &nextRequest.halRequest);
        ATRACE_END();

 if (res != OK) {
            SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
 " device: %s (%d)", nextRequest.halRequest.frame_number, strerror(-res),
                    res);
            cleanUpFailedRequests(/*sendRequestError*/ false);
 if (useFlushLock) {
                mFlushLock.unlock();
 }
 return false;
 }

        nextRequest.submitted = true;

 if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
 Mutex::Autolock al(mLatestRequestMutex);

 camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
            mLatestRequest.acquire(cloned);
 }

 if (nextRequest.halRequest.settings != NULL) {
            nextRequest.captureRequest->mSettings.unlock(nextRequest.halRequest.settings);
 }

        res = removeTriggers(mPrevRequest);
 if (res != OK) {
            SET_ERR("RequestThread: Unable to remove triggers "
 "(capture request %d, HAL device: %s (%d)",
                  nextRequest.halRequest.frame_number, strerror(-res), res);
            cleanUpFailedRequests(/*sendRequestError*/ false);
 if (useFlushLock) {
                mFlushLock.unlock();
 }
 return false;
 }
 }

 if (useFlushLock) {
        mFlushLock.unlock();
 }

 {
 Mutex::Autolock l(mRequestLock);
        mNextRequests.clear();
 }

 return true;
}

Project: Android

status_t Camera3Device::tryRemoveDummyStreamLocked() {
    ATRACE_CALL();
 status_t res;

 if (mDummyStreamId == NO_STREAM) return OK;
 if (mOutputStreams.size() == 1) return OK;

    ALOGV("%s: Camera %d: Removing the dummy stream", __FUNCTION__, mId);


    sp<Camera3StreamInterface> deletedStream;
 ssize_t outputStreamIdx = mOutputStreams.indexOfKey(mDummyStreamId);
 if (outputStreamIdx == NAME_NOT_FOUND) {
        SET_ERR_L("Dummy stream %d does not appear to exist", mDummyStreamId);
 return INVALID_OPERATION;
 }

    deletedStream = mOutputStreams.editValueAt(outputStreamIdx);
    mOutputStreams.removeItemsAt(outputStreamIdx);

    res = deletedStream->disconnect();
 if (res != OK) {
        SET_ERR_L("Can't disconnect deleted dummy stream %d", mDummyStreamId);
 }
    mDeletedStreams.add(deletedStream);
    mDummyStreamId = NO_STREAM;

 return res;
}

Project: Android

status_t Camera3Device::waitUntilStateThenRelock(bool active, nsecs_t timeout) {
 status_t res = OK;

 size_t startIndex = 0;
 if (mStatusWaiters == 0) {
        mRecentStatusUpdates.clear();
 } else {
        startIndex = mRecentStatusUpdates.size();
 }

    mStatusWaiters++;

 bool stateSeen = false;
 do {
 if (active == (mStatus == STATUS_ACTIVE)) {
 break;
 }

        res = mStatusChanged.waitRelative(mLock, timeout);
 if (res != OK) break;

        LOG_ALWAYS_FATAL_IF(startIndex > mRecentStatusUpdates.size(),
 "%s: Skipping status updates in Camera3Device, may result in deadlock.",
                __FUNCTION__);

 for (size_t i = startIndex; i < mRecentStatusUpdates.size(); i++) {
 if (active == (mRecentStatusUpdates[i] == STATUS_ACTIVE) ) {
                stateSeen = true;
 break;
 }
 }
 } while (!stateSeen);

    mStatusWaiters--;

 return res;
}

Project: Android

bool Camera3Device::willNotify3A() {
 return false;
}