Commit message: None
CVE: CVE-2016-2440
CWE: CWE-264
Vulnerability description: libs/binder/IPCThreadState.cpp in Binder 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 mishandles object references, which allows attackers to gain privileges via a crafted application, aka internal bug 27252896.

Project: Android

IPCThreadState::IPCThreadState()
 : mProcess(ProcessState::self()),
      mMyThreadId(gettid()),
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0)
{
    pthread_setspecific(gTLS, this);
    clearCaller();
    mIn.setDataCapacity(256);
    mOut.setDataCapacity(256);
}

Project: Android

void IPCThreadState::clearCaller()
{
    mCallingPid = getpid();
    mCallingUid = getuid();
}

Project: Android

int64_t IPCThreadState::clearCallingIdentity()
{
 int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
    clearCaller();
 return token;
}

Project: Android

status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy)
{
    mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION);
    mOut.writeInt32((int32_t)handle);
    mOut.writePointer((uintptr_t)proxy);
 return NO_ERROR;
}

Project: Android

void IPCThreadState::decStrongHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle);
    mOut.writeInt32(BC_RELEASE);
    mOut.writeInt32(handle);
}

Project: Android

void IPCThreadState::decWeakHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle);
    mOut.writeInt32(BC_DECREFS);
    mOut.writeInt32(handle);
}

Project: Android

void IPCThreadState::disableBackgroundScheduling(bool disable)
{
    gDisableBackgroundScheduling = disable;
}

Project: Android

void IPCThreadState::flushCommands()
{
 if (mProcess->mDriverFD <= 0)
 return;
    talkWithDriver(false);
}

Project: Android

void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
 size_t /*dataSize*/,
 const binder_size_t* /*objects*/,
 size_t /*objectsSize*/, void* /*cookie*/)
{
    IF_LOG_COMMANDS() {
        alog << "Writing BC_FREE_BUFFER for " << data << endl;
 }
    ALOG_ASSERT(data != NULL, "Called with NULL data");
 if (parcel != NULL) parcel->closeFileDescriptors();
 IPCThreadState* state = self();
    state->mOut.writeInt32(BC_FREE_BUFFER);
    state->mOut.writePointer((uintptr_t)data);
}

Project: Android

status_t IPCThreadState::getAndExecuteCommand()
{
 status_t result;
 int32_t cmd;

    result = talkWithDriver();
 if (result >= NO_ERROR) {
 size_t IN = mIn.dataAvail();
 if (IN < sizeof(int32_t)) return result;
        cmd = mIn.readInt32();
        IF_LOG_COMMANDS() {
            alog << "Processing top-level Command: "
 << getReturnString(cmd) << endl;
 }

        pthread_mutex_lock(&mProcess->mThreadCountLock);
        mProcess->mExecutingThreadsCount++;
        pthread_mutex_unlock(&mProcess->mThreadCountLock);

        result = executeCommand(cmd);

        pthread_mutex_lock(&mProcess->mThreadCountLock);
        mProcess->mExecutingThreadsCount--;
        pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
        pthread_mutex_unlock(&mProcess->mThreadCountLock);

        set_sched_policy(mMyThreadId, SP_FOREGROUND);
 }

 return result;
}

Project: Android

pid_t IPCThreadState::getCallingPid() const
{
 return mCallingPid;
}

Project: Android

int32_t IPCThreadState::getLastTransactionBinderFlags() const
{
 return mLastTransactionBinderFlags;
}

Project: Android

static const char* getReturnString(size_t idx)
{
 if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0]))
 return kReturnStrings[idx];
 else
 return "unknown";
}

Project: Android

int32_t IPCThreadState::getStrictModePolicy() const
{
 return mStrictModePolicy;
}

Project: Android

status_t IPCThreadState::handlePolledCommands()
{
 status_t result;

 do {
        result = getAndExecuteCommand();
 } while (mIn.dataPosition() < mIn.dataSize());

    processPendingDerefs();
    flushCommands();
 return result;
}

Project: Android

void IPCThreadState::incStrongHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle);
    mOut.writeInt32(BC_ACQUIRE);
    mOut.writeInt32(handle);
}

Project: Android

void IPCThreadState::incWeakHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
    mOut.writeInt32(BC_INCREFS);
    mOut.writeInt32(handle);
}

Project: Android

void IPCThreadState::joinThreadPool(bool isMain)
{
    LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());

    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
 
    set_sched_policy(mMyThreadId, SP_FOREGROUND);
 
 status_t result;
 do {
        processPendingDerefs();
        result = getAndExecuteCommand();

 if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
            ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
                  mProcess->mDriverFD, result);
            abort();
 }
 
 if(result == TIMED_OUT && !isMain) {
 break;
 }
 } while (result != -ECONNREFUSED && result != -EBADF);

    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
 (void*)pthread_self(), getpid(), (void*)result);
 
    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}

Project: Android

static const void* printBinderTransactionData(TextOutput& out, const void* data)
{
 const binder_transaction_data* btd =
 (const binder_transaction_data*)data;
 if (btd->target.handle < 1024) {
 /* want to print descriptors in decimal; guess based on value */
        out << "target.desc=" << btd->target.handle;
 } else {
        out << "target.ptr=" << btd->target.ptr;
 }
    out << " (cookie " << btd->cookie << ")" << endl
 << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(long)btd->flags << endl
 << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
 << " bytes)" << endl
 << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
 << " bytes)";
 return btd+1;
}

Project: Android

static const void* printReturnCommand(TextOutput& out, const void* _cmd)
{
 static const size_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
 const int32_t* cmd = (const int32_t*)_cmd;
 uint32_t code = (uint32_t)*cmd++;
 size_t cmdIndex = code & 0xff;
 if (code == BR_ERROR) {
        out << "BR_ERROR: " << (void*)(long)(*cmd++) << endl;
 return cmd;
 } else if (cmdIndex >= N) {
        out << "Unknown reply: " << code << endl;
 return cmd;
 }
    out << kReturnStrings[cmdIndex];
 
 switch (code) {
 case BR_TRANSACTION:
 case BR_REPLY: {
            out << ": " << indent;
            cmd = (const int32_t *)printBinderTransactionData(out, cmd);
            out << dedent;
 } break;
 
 case BR_ACQUIRE_RESULT: {
 const int32_t res = *cmd++;
            out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)");
 } break;
 
 case BR_INCREFS:
 case BR_ACQUIRE:
 case BR_RELEASE:
 case BR_DECREFS: {
 const int32_t b = *cmd++;
 const int32_t c = *cmd++;
            out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")";
 } break;
 
 case BR_ATTEMPT_ACQUIRE: {
 const int32_t p = *cmd++;
 const int32_t b = *cmd++;
 const int32_t c = *cmd++;
            out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c
 << "), pri=" << p;
 } break;

 case BR_DEAD_BINDER:
 case BR_CLEAR_DEATH_NOTIFICATION_DONE: {
 const int32_t c = *cmd++;
            out << ": death cookie " << (void*)(long)c;
 } break;

 default:
 break;
 }
 
    out << endl;
 return cmd;
}

Project: Android

sp<ProcessState> IPCThreadState::process()
{
 return mProcess;
}

Project: Android

void IPCThreadState::processPendingDerefs()
{
 if (mIn.dataPosition() >= mIn.dataSize()) {
 size_t numPending = mPendingWeakDerefs.size();
 if (numPending > 0) {
 for (size_t i = 0; i < numPending; i++) {
 RefBase::weakref_type* refs = mPendingWeakDerefs[i];
                refs->decWeak(mProcess.get());
 }
            mPendingWeakDerefs.clear();
 }

        numPending = mPendingStrongDerefs.size();
 if (numPending > 0) {
 for (size_t i = 0; i < numPending; i++) {
 BBinder* obj = mPendingStrongDerefs[i];
                obj->decStrong(mProcess.get());
 }
            mPendingStrongDerefs.clear();
 }
 }
}

Project: Android

status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
{
    mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
    mOut.writeInt32((int32_t)handle);
    mOut.writePointer((uintptr_t)proxy);
 return NO_ERROR;
}

Project: Android

void IPCThreadState::restoreCallingIdentity(int64_t token)
{
    mCallingUid = (int)(token>>32);
    mCallingPid = (int)token;
}

Project: Android

IPCThreadState* IPCThreadState::self()
{
 if (gHaveTLS) {
restart:
 const pthread_key_t k = gTLS;
 IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
 if (st) return st;
 return new IPCThreadState;
 }
 
 if (gShutdown) return NULL;
 
    pthread_mutex_lock(&gTLSMutex);
 if (!gHaveTLS) {
 if (pthread_key_create(&gTLS, threadDestructor) != 0) {
            pthread_mutex_unlock(&gTLSMutex);
 return NULL;
 }
        gHaveTLS = true;
 }
    pthread_mutex_unlock(&gTLSMutex);
 goto restart;
}

Project: Android

IPCThreadState* IPCThreadState::selfOrNull()
{
 if (gHaveTLS) {
 const pthread_key_t k = gTLS;
 IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
 return st;
 }
 return NULL;
}

Project: Android

status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
{
 status_t err;
 status_t statusBuffer;
    err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
 if (err < NO_ERROR) return err;
 
 return waitForResponse(NULL, NULL);
}

Project: Android

void IPCThreadState::setLastTransactionBinderFlags(int32_t flags)
{
    mLastTransactionBinderFlags = flags;
}

Project: Android

void IPCThreadState::setStrictModePolicy(int32_t policy)
{
    mStrictModePolicy = policy;
}

Project: Android

void setTheContextObject(sp<BBinder> obj)
{
    the_context_object = obj;
}

Project: Android

int IPCThreadState::setupPolling(int* fd)
{
 if (mProcess->mDriverFD <= 0) {
 return -EBADF;
 }

    mOut.writeInt32(BC_ENTER_LOOPER);
 *fd = mProcess->mDriverFD;
 return 0;
}

Project: Android

void IPCThreadState::shutdown()
{
    gShutdown = true;
 
 if (gHaveTLS) {
 IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS);
 if (st) {
 delete st;
            pthread_setspecific(gTLS, NULL);
 }
        gHaveTLS = false;
 }
}

Project: Android

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
 if (mProcess->mDriverFD <= 0) {
 return -EBADF;
 }
 
    binder_write_read bwr;
 
 const bool needRead = mIn.dataPosition() >= mIn.dataSize();
 
 const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
 
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

 if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
 } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
 }

    IF_LOG_COMMANDS() {
 TextOutput::Bundle _b(alog);
 if (outAvail != 0) {
            alog << "Sending commands to driver: " << indent;
 const void* cmds = (const void*)bwr.write_buffer;
 const void* end = ((const uint8_t*)cmds)+bwr.write_size;
            alog << HexDump(cmds, bwr.write_size) << endl;
 while (cmds < end) cmds = printCommand(alog, cmds);
            alog << dedent;
 }
        alog << "Size of receive buffer: " << bwr.read_size
 << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
 }
 
 if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
 status_t err;
 do {
        IF_LOG_COMMANDS() {
            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
 }
#if defined(HAVE_ANDROID_OS)
 if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
 else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
 if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
 }
        IF_LOG_COMMANDS() {
            alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
 }
 } while (err == -EINTR);

    IF_LOG_COMMANDS() {
        alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "
 << bwr.write_consumed << " (of " << mOut.dataSize()
 << "), read consumed: " << bwr.read_consumed << endl;
 }

 if (err >= NO_ERROR) {
 if (bwr.write_consumed > 0) {
 if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
 else
                mOut.setDataSize(0);
 }
 if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
 }
        IF_LOG_COMMANDS() {
 TextOutput::Bundle _b(alog);
            alog << "Remaining data size: " << mOut.dataSize() << endl;
            alog << "Received commands from driver: " << indent;
 const void* cmds = mIn.data();
 const void* end = mIn.data() + mIn.dataSize();
            alog << HexDump(cmds, mIn.dataSize()) << endl;
 while (cmds < end) cmds = printReturnCommand(alog, cmds);
            alog << dedent;
 }
 return NO_ERROR;
 }
 
 return err;
}

Project: Android

status_t IPCThreadState::transact(int32_t handle,
 uint32_t code, const Parcel& data,
 Parcel* reply, uint32_t flags)
{
 status_t err = data.errorCheck();

    flags |= TF_ACCEPT_FDS;

    IF_LOG_TRANSACTIONS() {
 TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
 << handle << " / code " << TypeCode(code) << ": "
 << indent << data << dedent << endl;
 }
 
 if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
 (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
 }
 
 if (err != NO_ERROR) {
 if (reply) reply->setError(err);
 return (mLastError = err);
 }
 
 if ((flags & TF_ONE_WAY) == 0) {
 #if 0
 if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
 } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
 }
 #endif
 if (reply) {
            err = waitForResponse(reply);
 } else {
 Parcel fakeReply;
            err = waitForResponse(&fakeReply);
 }
 #if 0
 if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
 } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
 }
 #endif
 
        IF_LOG_TRANSACTIONS() {
 TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
 << handle << ": ";
 if (reply) alog << indent << *reply << dedent << endl;
 else alog << "(none requested)" << endl;
 }
 } else {
        err = waitForResponse(NULL, NULL);
 }
 
 return err;
}

Project: Android

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
 uint32_t cmd;
 int32_t err;

 while (1) {
 if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
 if (err < NO_ERROR) break;
 if (mIn.dataAvail() == 0) continue;
 
        cmd = (uint32_t)mIn.readInt32();
 
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
 << getReturnString(cmd) << endl;
 }

 switch (cmd) {
 case BR_TRANSACTION_COMPLETE:
 if (!reply && !acquireResult) goto finish;
 break;
 
 case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
 goto finish;

 case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
 goto finish;
 
 case BR_ACQUIRE_RESULT:
 {
                ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
 const int32_t result = mIn.readInt32();
 if (!acquireResult) continue;
 *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
 }
 goto finish;
 
 case BR_REPLY:
 {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
 if (err != NO_ERROR) goto finish;

 if (reply) {
 if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
 reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
 reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
 } else {
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
 reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
 reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
 }
 } else {
                    freeBuffer(NULL,
 reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
 reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(binder_size_t), this);
 continue;
 }
 }
 goto finish;

 default:
            err = executeCommand(cmd);
 if (err != NO_ERROR) goto finish;
 break;
 }
 }

finish:
 if (err != NO_ERROR) {
 if (acquireResult) *acquireResult = err;
 if (reply) reply->setError(err);
        mLastError = err;
 }
 
 return err;
}

Project: Android

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
 int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;

    tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;
 
 const status_t err = data.errorCheck();
 if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
 } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
 *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
 } else {
 return (mLastError = err);
 }
 
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
 
 return NO_ERROR;
}

Project: Android

IPCThreadState::~IPCThreadState()
{
}