Commit message: None
CVE: CVE-2016-3826
CWE: CWE-20
Vulnerability description: services/audioflinger/Effects.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-08-01 does not validate the reply size for an AudioFlinger effect command, which allows attackers to gain privileges via a crafted application, aka internal bug 29251553.

Project: Android

AudioFlinger::EffectChain::EffectChain(ThreadBase *thread,
 int sessionId)
 : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
      mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
      mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX), mForceVolume(false)
{
    mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
 if (thread == NULL) {
 return;
 }
    mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) /
                                    thread->frameCount();
}

Project: Android

AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
 const sp<AudioFlinger::Client>& client,
 const sp<IEffectClient>& effectClient,
 int32_t priority)
 : BnEffect(),
    mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL),
    mPriority(priority), mHasControl(false), mEnabled(false), mDestroyed(false)
{
    ALOGV("constructor %p", this);

 if (client == 0) {
 return;
 }
 int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
    mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset);
 if (mCblkMemory == 0 ||
 (mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer())) == NULL) {
        ALOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE +
 sizeof(effect_param_cblk_t));
        mCblkMemory.clear();
 return;
 }
 new(mCblk) effect_param_cblk_t();
    mBuffer = (uint8_t *)mCblk + bufOffset;
}

Project: Android

AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
 const wp<AudioFlinger::EffectChain>& chain,
 effect_descriptor_t *desc,
 int id,
 int sessionId)
 : mPinned(sessionId > AUDIO_SESSION_OUTPUT_MIX),
      mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
      mDescriptor(*desc),
      mEffectInterface(NULL),
      mStatus(NO_INIT), mState(IDLE),
      mSuspended(false),
      mAudioFlinger(thread->mAudioFlinger)
{
    ALOGV("Constructor %p", this);
 int lStatus;

    mStatus = EffectCreate(&desc->uuid, sessionId, thread->id(), &mEffectInterface);

 if (mStatus != NO_ERROR) {
 return;
 }
    lStatus = init();
 if (lStatus < 0) {
        mStatus = lStatus;
 goto Error;
 }

    ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
 return;
Error:
 EffectRelease(mEffectInterface);
    mEffectInterface = NULL;
    ALOGV("Constructor Error %d", mStatus);
}

Project: Android

status_t AudioFlinger::EffectChain::addEffect_l(const sp<EffectModule>& effect)
{
 effect_descriptor_t desc = effect->desc();
 uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;

 Mutex::Autolock _l(mLock);
    effect->setChain(this);
    sp<ThreadBase> thread = mThread.promote();
 if (thread == 0) {
 return NO_INIT;
 }
    effect->setThread(thread);

 if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
        mEffects.insertAt(effect, 0);

 size_t numSamples = thread->frameCount();
 int32_t *buffer = new int32_t[numSamples];
        memset(buffer, 0, numSamples * sizeof(int32_t));
        effect->setInBuffer((int16_t *)buffer);
        effect->setOutBuffer(mInBuffer);
 } else {

 size_t size = mEffects.size();
 size_t idx_insert = size;
 ssize_t idx_insert_first = -1;
 ssize_t idx_insert_last = -1;

 for (size_t i = 0; i < size; i++) {
 effect_descriptor_t d = mEffects[i]->desc();
 uint32_t iMode = d.flags & EFFECT_FLAG_TYPE_MASK;
 uint32_t iPref = d.flags & EFFECT_FLAG_INSERT_MASK;
 if (iMode == EFFECT_FLAG_TYPE_INSERT) {
 if (insertPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
                    iPref == EFFECT_FLAG_INSERT_EXCLUSIVE) {
                    ALOGW("addEffect_l() could not insert effect %s: exclusive conflict with %s",
                            desc.name, d.name);
 return INVALID_OPERATION;
 }
 if (idx_insert == size) {
                    idx_insert = i;
 }
 if (iPref == EFFECT_FLAG_INSERT_FIRST) {
                    idx_insert_first = i;
 }
 if (iPref == EFFECT_FLAG_INSERT_LAST &&
                    idx_insert_last == -1) {
                    idx_insert_last = i;
 }
 }
 }

 if (insertPref == EFFECT_FLAG_INSERT_LAST) {
 if (idx_insert_last != -1) {
                idx_insert = idx_insert_last;
 } else {
                idx_insert = size;
 }
 } else {
 if (idx_insert_first != -1) {
                idx_insert = idx_insert_first + 1;
 }
 }

        effect->setInBuffer(mInBuffer);

 if (idx_insert == size) {
 if (idx_insert != 0) {
                mEffects[idx_insert-1]->setOutBuffer(mInBuffer);
                mEffects[idx_insert-1]->configure();
 }
            effect->setOutBuffer(mOutBuffer);
 } else {
            effect->setOutBuffer(mInBuffer);
 }
        mEffects.insertAt(effect, idx_insert);

        ALOGV("addEffect_l() effect %p, added in chain %p at rank %d", effect.get(), this,
                idx_insert);
 }
    effect->configure();
 return NO_ERROR;
}

Project: Android

status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle)
{
 status_t status;

 Mutex::Autolock _l(mLock);
 int priority = handle->priority();
 size_t size = mHandles.size();
 EffectHandle *controlHandle = NULL;
 size_t i;
 for (i = 0; i < size; i++) {
 EffectHandle *h = mHandles[i];
 if (h == NULL || h->destroyed_l()) {
 continue;
 }
 if (controlHandle == NULL) {
            controlHandle = h;
 }
 if (h->priority() <= priority) {
 break;
 }
 }
 if (i == 0) {
 bool enabled = false;
 if (controlHandle != NULL) {
            enabled = controlHandle->enabled();
            controlHandle->setControl(false/*hasControl*/, true /*signal*/, enabled /*enabled*/);
 }
        handle->setControl(true /*hasControl*/, false /*signal*/, enabled /*enabled*/);
        status = NO_ERROR;
 } else {
        status = ALREADY_EXISTS;
 }
    ALOGV("addHandle() %p added handle %p in position %d", this, handle, i);
    mHandles.insertAt(handle, i);
 return status;
}

Project: Android

status_t AudioFlinger::EffectModule::configure()
{
 status_t status;
    sp<ThreadBase> thread;
 uint32_t size;
 audio_channel_mask_t channelMask;

 if (mEffectInterface == NULL) {
        status = NO_INIT;
 goto exit;
 }

    thread = mThread.promote();
 if (thread == 0) {
        status = DEAD_OBJECT;
 goto exit;
 }

    channelMask = thread->channelMask();
    mConfig.outputCfg.channels = channelMask;

 if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
        mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
 } else {
        mConfig.inputCfg.channels = channelMask;
 if (channelMask == AUDIO_CHANNEL_OUT_MONO) {
            mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
            mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
            ALOGV("Overriding effect input and output as STEREO");
 }
 }

    mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    mConfig.inputCfg.samplingRate = thread->sampleRate();
    mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
    mConfig.inputCfg.bufferProvider.cookie = NULL;
    mConfig.inputCfg.bufferProvider.getBuffer = NULL;
    mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
    mConfig.outputCfg.bufferProvider.cookie = NULL;
    mConfig.outputCfg.bufferProvider.getBuffer = NULL;
    mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
    mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
 if (mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
        mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
 } else {
        mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
 }
    mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
    mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
    mConfig.inputCfg.buffer.frameCount = thread->frameCount();
    mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;

    ALOGV("configure() %p thread %p buffer %p framecount %d",
 this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);

 status_t cmdStatus;
    size = sizeof(int);
    status = (*mEffectInterface)->command(mEffectInterface,
                                                   EFFECT_CMD_SET_CONFIG,
 sizeof(effect_config_t),
 &mConfig,
 &size,
 &cmdStatus);
 if (status == 0) {
        status = cmdStatus;
 }

 if (status == 0 &&
 (memcmp(&mDescriptor.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0)) {
 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
 effect_param_t *p = (effect_param_t *)buf32;

        p->psize = sizeof(uint32_t);
        p->vsize = sizeof(uint32_t);
        size = sizeof(int);
 *(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;

 uint32_t latency = 0;
 PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
 if (pbt != NULL) {
            latency = pbt->latency_l();
 }

 *((int32_t *)p->data + 1)= latency;
 (*mEffectInterface)->command(mEffectInterface,
                                     EFFECT_CMD_SET_PARAM,
 sizeof(effect_param_t) + 8,
 &buf32,
 &size,
 &cmdStatus);
 }

    mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
 (1000 * mConfig.outputCfg.buffer.frameCount);

exit:
    mStatus = status;
 return status;
}

Project: Android

String8 effectFlagsToString(uint32_t flags) {
 String8 s;

    s.append("conn. mode: ");
 switch (flags & EFFECT_FLAG_TYPE_MASK) {
 case EFFECT_FLAG_TYPE_INSERT: s.append("insert"); break;
 case EFFECT_FLAG_TYPE_AUXILIARY: s.append("auxiliary"); break;
 case EFFECT_FLAG_TYPE_REPLACE: s.append("replace"); break;
 case EFFECT_FLAG_TYPE_PRE_PROC: s.append("preproc"); break;
 case EFFECT_FLAG_TYPE_POST_PROC: s.append("postproc"); break;
 default: s.append("unknown/reserved"); break;
 }
    s.append(", ");

    s.append("insert pref: ");
 switch (flags & EFFECT_FLAG_INSERT_MASK) {
 case EFFECT_FLAG_INSERT_ANY: s.append("any"); break;
 case EFFECT_FLAG_INSERT_FIRST: s.append("first"); break;
 case EFFECT_FLAG_INSERT_LAST: s.append("last"); break;
 case EFFECT_FLAG_INSERT_EXCLUSIVE: s.append("exclusive"); break;
 default: s.append("unknown/reserved"); break;
 }
    s.append(", ");

    s.append("volume mgmt: ");
 switch (flags & EFFECT_FLAG_VOLUME_MASK) {
 case EFFECT_FLAG_VOLUME_NONE: s.append("none"); break;
 case EFFECT_FLAG_VOLUME_CTRL: s.append("implements control"); break;
 case EFFECT_FLAG_VOLUME_IND: s.append("requires indication"); break;
 default: s.append("unknown/reserved"); break;
 }
    s.append(", ");

 uint32_t devind = flags & EFFECT_FLAG_DEVICE_MASK;
 if (devind) {
        s.append("device indication: ");
 switch (devind) {
 case EFFECT_FLAG_DEVICE_IND: s.append("requires updates"); break;
 default: s.append("unknown/reserved"); break;
 }
        s.append(", ");
 }

    s.append("input mode: ");
 switch (flags & EFFECT_FLAG_INPUT_MASK) {
 case EFFECT_FLAG_INPUT_DIRECT: s.append("direct"); break;
 case EFFECT_FLAG_INPUT_PROVIDER: s.append("provider"); break;
 case EFFECT_FLAG_INPUT_BOTH: s.append("direct+provider"); break;
 default: s.append("not set"); break;
 }
    s.append(", ");

    s.append("output mode: ");
 switch (flags & EFFECT_FLAG_OUTPUT_MASK) {
 case EFFECT_FLAG_OUTPUT_DIRECT: s.append("direct"); break;
 case EFFECT_FLAG_OUTPUT_PROVIDER: s.append("provider"); break;
 case EFFECT_FLAG_OUTPUT_BOTH: s.append("direct+provider"); break;
 default: s.append("not set"); break;
 }
    s.append(", ");

 uint32_t accel = flags & EFFECT_FLAG_HW_ACC_MASK;
 if (accel) {
        s.append("hardware acceleration: ");
 switch (accel) {
 case EFFECT_FLAG_HW_ACC_SIMPLE: s.append("non-tunneled"); break;
 case EFFECT_FLAG_HW_ACC_TUNNEL: s.append("tunneled"); break;
 default: s.append("unknown/reserved"); break;
 }
        s.append(", ");
 }

 uint32_t modeind = flags & EFFECT_FLAG_AUDIO_MODE_MASK;
 if (modeind) {
        s.append("mode indication: ");
 switch (modeind) {
 case EFFECT_FLAG_AUDIO_MODE_IND: s.append("required"); break;
 default: s.append("unknown/reserved"); break;
 }
        s.append(", ");
 }

 uint32_t srcind = flags & EFFECT_FLAG_AUDIO_SOURCE_MASK;
 if (srcind) {
        s.append("source indication: ");
 switch (srcind) {
 case EFFECT_FLAG_AUDIO_SOURCE_IND: s.append("required"); break;
 default: s.append("unknown/reserved"); break;
 }
        s.append(", ");
 }

 if (flags & EFFECT_FLAG_OFFLOAD_MASK) {
        s.append("offloadable, ");
 }

 int len = s.length();
 if (s.length() > 2) {
 char *str = s.lockBuffer(len);
        s.unlockBuffer(len - 2);
 }
 return s;
}

Project: Android

size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
{
 Mutex::Autolock _l(mLock);
 size_t size = mEffects.size();
 uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;

 for (size_t i = 0; i < size; i++) {
 if (effect == mEffects[i]) {
 if (mEffects[i]->state() == EffectModule::ACTIVE ||
                    mEffects[i]->state() == EffectModule::STOPPING) {
                mEffects[i]->stop();
 }
 if (type == EFFECT_FLAG_TYPE_AUXILIARY) {
 delete[] effect->inBuffer();
 } else {
 if (i == size - 1 && i != 0) {
                    mEffects[i - 1]->setOutBuffer(mOutBuffer);
                    mEffects[i - 1]->configure();
 }
 }
            mEffects.removeAt(i);
            ALOGV("removeEffect_l() effect %p, removed from chain %p at rank %d", effect.get(),
 this, i);
 break;
 }
 }

 return mEffects.size();
}

Project: Android

size_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle)
{
 Mutex::Autolock _l(mLock);
 size_t size = mHandles.size();
 size_t i;
 for (i = 0; i < size; i++) {
 if (mHandles[i] == handle) {
 break;
 }
 }
 if (i == size) {
 return size;
 }
    ALOGV("removeHandle() %p removed handle %p in position %d", this, handle, i);

    mHandles.removeAt(i);
 if (i == 0) {
 EffectHandle *h = controlHandle_l();
 if (h != NULL) {
            h->setControl(true /*hasControl*/, true /*signal*/ , handle->enabled() /*enabled*/);
 }
 }

 if (mHandles.size() == 0 && !mPinned) {
        mState = DESTROYED;
 }

 return mHandles.size();
}

Project: Android

status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
{
 Mutex::Autolock _l(mLock);
 if (mStatus != NO_ERROR) {
 return mStatus;
 }
 status_t status = NO_ERROR;
 if (isProcessEnabled() &&
 ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
 (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) {
 status_t cmdStatus;
 uint32_t volume[2];
 uint32_t *pVolume = NULL;
 uint32_t size = sizeof(volume);
        volume[0] = *left;
        volume[1] = *right;
 if (controller) {
            pVolume = volume;
 }
        status = (*mEffectInterface)->command(mEffectInterface,
                                              EFFECT_CMD_SET_VOLUME,
                                              size,
                                              volume,
 &size,
                                              pVolume);
 if (controller && status == NO_ERROR && size == sizeof(volume)) {
 *left = volume[0];
 *right = volume[1];
 }
 }
 return status;
}