Commit message: None
CVE: CVE-2016-3913
CWE: CWE-264
Vulnerability description: media/libmediaplayerservice/MediaPlayerService.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, 6.x before 2016-10-01, and 7.0 before 2016-10-01 does not validate a certain static_cast operation, which allows attackers to gain privileges via a crafted application, aka internal bug 30204103.

Project: Android

ssize_t MediaPlayerService::AudioOutput::channelCount() const
{
 Mutex::Autolock lock(mLock);
 if (mTrack == 0) return NO_INIT;
 return mTrack->channelCount();
}

Project: Android

void MediaPlayerService::Client::disconnectNativeWindow() {
 if (mConnectedWindow != NULL) {
 status_t err = native_window_api_disconnect(mConnectedWindow.get(),
                NATIVE_WINDOW_API_MEDIA);

 if (err != OK) {
            ALOGW("native_window_api_disconnect returned an error: %s (%d)",
                    strerror(-err), err);
 }
 }
    mConnectedWindow.clear();
}

Project: Android

status_t MediaPlayerService::Client::getRetransmitEndpoint(
 struct sockaddr_in* endpoint)
{
 if (NULL == endpoint)
 return BAD_VALUE;

    sp<MediaPlayerBase> p = getPlayer();

 if (p != NULL)
 return p->getRetransmitEndpoint(endpoint);

 if (!mRetransmitEndpointValid)
 return NO_INIT;

 *endpoint = mRetransmitEndpoint;

 return NO_ERROR;
}

Project: Android

audio_session_t MediaPlayerService::AudioOutput::getSessionId() const
{
 Mutex::Autolock lock(mLock);
 return mSessionId;
}

Project: Android

void MediaPlayerService::Client::notify(
 void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
 Client* client = static_cast<Client*>(cookie);
 if (client == NULL) {
 return;
 }

    sp<IMediaPlayerClient> c;
 {
 Mutex::Autolock l(client->mLock);
        c = client->mClient;
 if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) {
 if (client->mAudioOutput != NULL)
                client->mAudioOutput->switchToNextOutput();
            client->mNextClient->start();
 if (client->mNextClient->mClient != NULL) {
                client->mNextClient->mClient->notify(
                        MEDIA_INFO, MEDIA_INFO_STARTED_AS_NEXT, 0, obj);
 }
 }
 }

 if (MEDIA_INFO == msg &&
        MEDIA_INFO_METADATA_UPDATE == ext1) {
 const media::Metadata::Type metadata_type = ext2;

 if(client->shouldDropMetadata(metadata_type)) {
 return;
 }

        client->addNewMetadataUpdate(metadata_type);
 }

 if (c != NULL) {
        ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
        c->notify(msg, ext1, ext2, obj);
 }
}

Project: Android

status_t MediaPlayerService::AudioOutput::open(
 uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
 audio_format_t format, int bufferCount,
 AudioCallback cb, void *cookie,
 audio_output_flags_t flags,
 const audio_offload_info_t *offloadInfo,
 bool doNotReconnect,
 uint32_t suggestedFrameCount)
{
    ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
                format, bufferCount, mSessionId, flags);

 if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
 ((cb == NULL) || (offloadInfo == NULL))) {
 return BAD_VALUE;
 }

 size_t frameCount;
 if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
        frameCount = 0; // AudioTrack will get frame count from AudioFlinger
 } else {
 uint32_t afSampleRate;
 size_t afFrameCount;
 if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
 return NO_INIT;
 }
 if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
 return NO_INIT;
 }
 const size_t framesPerBuffer =
 (unsigned long long)sampleRate * afFrameCount / afSampleRate;

 if (bufferCount == 0) {
            bufferCount = (suggestedFrameCount + framesPerBuffer - 1) / framesPerBuffer;
 }
 if (bufferCount != 0 && bufferCount < mMinBufferCount) {
            ALOGV("bufferCount (%d) increased to %d", bufferCount, mMinBufferCount);
            bufferCount = mMinBufferCount;
 }
        frameCount = bufferCount * framesPerBuffer;
 }

 if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
        channelMask = audio_channel_out_mask_from_count(channelCount);
 if (0 == channelMask) {
            ALOGE("open() error, can\'t derive mask for %d audio channels", channelCount);
 return NO_INIT;
 }
 }

 Mutex::Autolock lock(mLock);
    mCallback = cb;
    mCallbackCookie = cookie;

 bool reuse = false;
 bool bothOffloaded = false;

 if (mRecycledTrack != 0) {
        bothOffloaded = (flags & mRecycledTrack->getFlags()
 & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0;

        reuse = true;

 if ((mCallbackData == NULL && mCallback != NULL) ||
 (mCallbackData != NULL && mCallback == NULL)) {
            ALOGV("can't chain callback and write");
            reuse = false;
 } else if ((mRecycledTrack->getSampleRate() != sampleRate) ||
 (mRecycledTrack->channelCount() != (uint32_t)channelCount) ) {
            ALOGV("samplerate, channelcount differ: %u/%u Hz, %u/%d ch",
                  mRecycledTrack->getSampleRate(), sampleRate,
                  mRecycledTrack->channelCount(), channelCount);
            reuse = false;
 } else if (flags != mFlags) {
            ALOGV("output flags differ %08x/%08x", flags, mFlags);
            reuse = false;
 } else if (mRecycledTrack->format() != format) {
            reuse = false;
 }
 } else {
        ALOGV("no track available to recycle");
 }

    ALOGV_IF(bothOffloaded, "both tracks offloaded");

 if (bothOffloaded && !reuse) {
        ALOGV("both offloaded and not recycling");
        deleteRecycledTrack_l();
 }

    sp<AudioTrack> t;
 CallbackData *newcbd = NULL;


 if (!(reuse && bothOffloaded)) {
        ALOGV("creating new AudioTrack");

 if (mCallback != NULL) {
            newcbd = new CallbackData(this);
            t = new AudioTrack(
                    mStreamType,
                    sampleRate,
                    format,
                    channelMask,
                    frameCount,
                    flags,
 CallbackWrapper,
                    newcbd,
 0, // notification frames
                    mSessionId,
 AudioTrack::TRANSFER_CALLBACK,
                    offloadInfo,
                    mUid,
                    mPid,
                    mAttributes,
                    doNotReconnect);
 } else {
 const float targetSpeed =
                    std::min(std::max(mPlaybackRate.mSpeed, 1.0f), kMaxRequiredSpeed);
            ALOGW_IF(targetSpeed != mPlaybackRate.mSpeed,
 "track target speed:%f clamped from playback speed:%f",
                    targetSpeed, mPlaybackRate.mSpeed);
            t = new AudioTrack(
                    mStreamType,
                    sampleRate,
                    format,
                    channelMask,
                    frameCount,
                    flags,
                    NULL, // callback
                    NULL, // user data
 0, // notification frames
                    mSessionId,
 AudioTrack::TRANSFER_DEFAULT,
                    NULL, // offload info
                    mUid,
                    mPid,
                    mAttributes,
                    doNotReconnect,
                    targetSpeed);
 }

 if ((t == 0) || (t->initCheck() != NO_ERROR)) {
            ALOGE("Unable to create audio track");
 delete newcbd;
 return NO_INIT;
 } else {
            mStreamType = t->streamType();
 }
 }

 if (reuse) {
        CHECK(mRecycledTrack != NULL);

 if (!bothOffloaded) {
 if (mRecycledTrack->frameCount() != t->frameCount()) {
                ALOGV("framecount differs: %zu/%zu frames",
                      mRecycledTrack->frameCount(), t->frameCount());
                reuse = false;
 }
 }

 if (reuse) {
            ALOGV("chaining to next output and recycling track");
            close_l();
            mTrack = mRecycledTrack;
            mRecycledTrack.clear();
 if (mCallbackData != NULL) {
                mCallbackData->setOutput(this);
 }
 delete newcbd;
 return OK;
 }
 }

 if (!bothOffloaded) {
        deleteRecycledTrack_l();
 }

    CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));

    mCallbackData = newcbd;
    ALOGV("setVolume");
    t->setVolume(mLeftVolume, mRightVolume);

    mSampleRateHz = sampleRate;
    mFlags = flags;
    mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
    mFrameSize = t->frameSize();
    mTrack = t;

 status_t res = NO_ERROR;
 if ((t->getFlags() & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
        res = t->setPlaybackRate(mPlaybackRate);
 if (res == NO_ERROR) {
            t->setAuxEffectSendLevel(mSendLevel);
            res = t->attachAuxEffect(mAuxEffectId);
 }
 }
    ALOGV("open() DONE status %d", res);
 return res;
}

Project: Android

status_t MediaPlayerService::Client::prepareAsync()
{
    ALOGV("[%d] prepareAsync", mConnId);
    sp<MediaPlayerBase> p = getPlayer();
 if (p == 0) return UNKNOWN_ERROR;
 status_t ret = p->prepareAsync();
#if CALLBACK_ANTAGONIZER
    ALOGD("start Antagonizer");
 if (ret == NO_ERROR) mAntagonizer->start();
#endif
 return ret;
}

Project: Android

void MediaPlayerService::Client::setDataSource_post(
 const sp<MediaPlayerBase>& p,
 status_t status)
{
    ALOGV(" setDataSource");
    mStatus = status;
 if (mStatus != OK) {
        ALOGE("  error: %d", mStatus);
 return;
 }

 if (mRetransmitEndpointValid) {
        mStatus = p->setRetransmitEndpoint(&mRetransmitEndpoint);
 if (mStatus != NO_ERROR) {
            ALOGE("setRetransmitEndpoint error: %d", mStatus);
 }
 }

 if (mStatus == OK) {
        mPlayer = p;
 }
}

Project: Android

status_t MediaPlayerService::Client::setPlaybackSettings(const AudioPlaybackRate& rate)
{
    ALOGV("[%d] setPlaybackSettings(%f, %f, %d, %d)",
            mConnId, rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
    sp<MediaPlayerBase> p = getPlayer();
 if (p == 0) return UNKNOWN_ERROR;
 return p->setPlaybackSettings(rate);
}

Project: Android

void MediaPlayerService::AudioOutput::stop()
{
    ALOGV("stop");
 Mutex::Autolock lock(mLock);
 if (mTrack != 0) mTrack->stop();
}