Loading libs/binder/IPCThreadState.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -775,6 +775,7 @@ void IPCThreadState::joinThreadPool(bool isMain) { LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); mProcess->checkExpectingThreadPoolStart(); mProcess->mCurrentThreads++; mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); Loading libs/binder/Parcel.cpp +84 −14 Original line number Diff line number Diff line Loading @@ -156,7 +156,7 @@ enum { #ifdef BINDER_WITH_KERNEL_IPC static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who) { const void* who, bool tagFds) { switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { Loading @@ -173,7 +173,7 @@ static void acquire_object(const sp<ProcessState>& proc, const flat_binder_objec return; } case BINDER_TYPE_FD: { if (obj.cookie != 0) { // owned if (tagFds && obj.cookie != 0) { // owned FdTag(obj.handle, nullptr, who); } return; Loading Loading @@ -611,7 +611,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } } acquire_object(proc, *flat, this); acquire_object(proc, *flat, this, true /*tagFds*/); } } #else Loading Loading @@ -1797,13 +1797,22 @@ restart_write: // Need to write meta-data? if (nullMetaData || val.binder != 0) { kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos; acquire_object(ProcessState::self(), val, this); acquire_object(ProcessState::self(), val, this, true /*tagFds*/); kernelFields->mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); } if (mOwner) { // continueWrite does have the logic to convert this from an // owned to an unowned Parcel. However, this is pretty inefficient, // and it's really strange to need to do so, so prefer to avoid // these paths than try to support them. ALOGE("writing objects not supported on owned Parcels"); return PERMISSION_DENIED; } if (!enoughData) { const status_t err = growData(sizeof(val)); if (err != NO_ERROR) return err; Loading Loading @@ -2719,6 +2728,65 @@ size_t Parcel::ipcObjectsCount() const return 0; } static void do_nothing_release_func(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount) { (void)data; (void)dataSize; (void)objects; (void)objectsCount; } static void delete_data_release_func(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount) { delete[] data; (void)dataSize; (void)objects; (void)objectsCount; } void Parcel::makeDangerousViewOf(Parcel* p) { if (p->isForRpc()) { // warning: this must match the logic in rpcSetDataReference auto* rf = p->maybeRpcFields(); LOG_ALWAYS_FATAL_IF(rf == nullptr); std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>> fds; if (rf->mFds) { fds.reserve(rf->mFds->size()); for (const auto& fd : *rf->mFds) { fds.push_back(binder::borrowed_fd(toRawFd(fd))); } } status_t result = rpcSetDataReference(rf->mSession, p->mData, p->mDataSize, rf->mObjectPositions.data(), rf->mObjectPositions.size(), std::move(fds), do_nothing_release_func); LOG_ALWAYS_FATAL_IF(result != OK, "Failed: %s", statusToString(result).c_str()); } else { #ifdef BINDER_WITH_KERNEL_IPC // warning: this must match the logic in ipcSetDataReference auto* kf = p->maybeKernelFields(); LOG_ALWAYS_FATAL_IF(kf == nullptr); // Ownership of FDs is passed to the Parcel from kernel binder. This should be refactored // to move this ownership out of Parcel and into release_func. However, today, Parcel // always assums it can own and close FDs today. So, for purposes of testing consistency, // , create new FDs it can own. uint8_t* newData = new uint8_t[p->mDataSize]; // deleted by delete_data_release_func memcpy(newData, p->mData, p->mDataSize); for (size_t i = 0; i < kf->mObjectsSize; i++) { flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(newData + kf->mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); } } ipcSetDataReference(newData, p->mDataSize, kf->mObjects, kf->mObjectsSize, delete_data_release_func); #endif // BINDER_WITH_KERNEL_IPC } } void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc) { // this code uses 'mOwner == nullptr' to understand whether it owns memory Loading @@ -2729,6 +2797,7 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin auto* kernelFields = maybeKernelFields(); LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData. // must match makeDangerousViewOf mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; kernelFields->mObjects = const_cast<binder_size_t*>(objects); Loading Loading @@ -2807,6 +2876,7 @@ status_t Parcel::rpcSetDataReference( auto* rpcFields = maybeRpcFields(); LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc. // must match makeDangerousViewOf mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; mOwner = relFunc; Loading Loading @@ -2874,15 +2944,17 @@ void Parcel::releaseObjects() #endif // BINDER_WITH_KERNEL_IPC } void Parcel::acquireObjects() { void Parcel::reacquireObjects(size_t objectsSize) { auto* kernelFields = maybeKernelFields(); if (kernelFields == nullptr) { return; } #ifdef BINDER_WITH_KERNEL_IPC size_t i = kernelFields->mObjectsSize; LOG_ALWAYS_FATAL_IF(objectsSize > kernelFields->mObjectsSize, "Object size %zu out of range of %zu", objectsSize, kernelFields->mObjectsSize); size_t i = objectsSize; if (i == 0) { return; } Loading @@ -2892,8 +2964,10 @@ void Parcel::acquireObjects() while (i > 0) { i--; const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); acquire_object(proc, *flat, this); acquire_object(proc, *flat, this, false /*tagFds*/); // they are already tagged } #else (void) objectsSize; #endif // BINDER_WITH_KERNEL_IPC } Loading Loading @@ -3110,12 +3184,8 @@ status_t Parcel::continueWrite(size_t desired) return NO_MEMORY; } // Little hack to only acquire references on objects // we will be keeping. size_t oldObjectsSize = kernelFields->mObjectsSize; kernelFields->mObjectsSize = objectsSize; acquireObjects(); kernelFields->mObjectsSize = oldObjectsSize; // only acquire references on objects we are keeping reacquireObjects(objectsSize); } if (rpcFields) { if (status_t status = truncateRpcObjects(objectsSize); status != OK) { Loading libs/binder/ProcessState.cpp +15 −0 Original line number Diff line number Diff line Loading @@ -501,6 +501,21 @@ bool ProcessState::isThreadPoolStarted() const { return mThreadPoolStarted; } void ProcessState::checkExpectingThreadPoolStart() const { if (mThreadPoolStarted) return; // this is also racey, but you should setup the threadpool in the main thread. If that is an // issue, we can check if we are the process leader, but haven't seen the issue in practice. size_t requestedThreads = mMaxThreads.load(); // if it's manually set to the default, we do ignore it here... if (requestedThreads == DEFAULT_MAX_BINDER_THREADS) return; if (requestedThreads == 0) return; ALOGW("Thread pool configuration of size %zu requested, but startThreadPool was not called.", requestedThreads); } #define DRIVER_FEATURES_PATH "/dev/binderfs/features/" bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) { // Use static variable to cache the results. Loading libs/binder/include/binder/Parcel.h +7 −2 Original line number Diff line number Diff line Loading @@ -649,6 +649,11 @@ public: LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const; // This API is to quickly become a view of another Parcel, so that we can also // test 'owner' paths quickly. It's extremely dangerous to use this API in // practice, and you should never ever do it. LIBBINDER_EXPORTED void makeDangerousViewOf(Parcel* p); private: // Close all file descriptors in the parcel at object positions >= newObjectsSize. void closeFileDescriptors(size_t newObjectsSize); Loading @@ -664,7 +669,7 @@ private: void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc); // Takes ownership even when an error is returned. status_t rpcSetDataReference( [[nodiscard]] status_t rpcSetDataReference( const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, const uint32_t* objectTable, size_t objectTableSize, std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds, Loading @@ -672,7 +677,7 @@ private: status_t finishWrite(size_t len); void releaseObjects(); void acquireObjects(); void reacquireObjects(size_t objectSize); status_t growData(size_t len); // Clear the Parcel and set the capacity to `desired`. // Doesn't reset the RPC session association. Loading libs/binder/include/binder/ProcessState.h +2 −0 Original line number Diff line number Diff line Loading @@ -141,6 +141,8 @@ public: private: static sp<ProcessState> init(const char* defaultDriver, bool requireDefault); void checkExpectingThreadPoolStart() const; static void onFork(); static void parentPostFork(); static void childPostFork(); Loading Loading
libs/binder/IPCThreadState.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -775,6 +775,7 @@ void IPCThreadState::joinThreadPool(bool isMain) { LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); mProcess->checkExpectingThreadPoolStart(); mProcess->mCurrentThreads++; mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); Loading
libs/binder/Parcel.cpp +84 −14 Original line number Diff line number Diff line Loading @@ -156,7 +156,7 @@ enum { #ifdef BINDER_WITH_KERNEL_IPC static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who) { const void* who, bool tagFds) { switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { Loading @@ -173,7 +173,7 @@ static void acquire_object(const sp<ProcessState>& proc, const flat_binder_objec return; } case BINDER_TYPE_FD: { if (obj.cookie != 0) { // owned if (tagFds && obj.cookie != 0) { // owned FdTag(obj.handle, nullptr, who); } return; Loading Loading @@ -611,7 +611,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } } acquire_object(proc, *flat, this); acquire_object(proc, *flat, this, true /*tagFds*/); } } #else Loading Loading @@ -1797,13 +1797,22 @@ restart_write: // Need to write meta-data? if (nullMetaData || val.binder != 0) { kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos; acquire_object(ProcessState::self(), val, this); acquire_object(ProcessState::self(), val, this, true /*tagFds*/); kernelFields->mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); } if (mOwner) { // continueWrite does have the logic to convert this from an // owned to an unowned Parcel. However, this is pretty inefficient, // and it's really strange to need to do so, so prefer to avoid // these paths than try to support them. ALOGE("writing objects not supported on owned Parcels"); return PERMISSION_DENIED; } if (!enoughData) { const status_t err = growData(sizeof(val)); if (err != NO_ERROR) return err; Loading Loading @@ -2719,6 +2728,65 @@ size_t Parcel::ipcObjectsCount() const return 0; } static void do_nothing_release_func(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount) { (void)data; (void)dataSize; (void)objects; (void)objectsCount; } static void delete_data_release_func(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount) { delete[] data; (void)dataSize; (void)objects; (void)objectsCount; } void Parcel::makeDangerousViewOf(Parcel* p) { if (p->isForRpc()) { // warning: this must match the logic in rpcSetDataReference auto* rf = p->maybeRpcFields(); LOG_ALWAYS_FATAL_IF(rf == nullptr); std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>> fds; if (rf->mFds) { fds.reserve(rf->mFds->size()); for (const auto& fd : *rf->mFds) { fds.push_back(binder::borrowed_fd(toRawFd(fd))); } } status_t result = rpcSetDataReference(rf->mSession, p->mData, p->mDataSize, rf->mObjectPositions.data(), rf->mObjectPositions.size(), std::move(fds), do_nothing_release_func); LOG_ALWAYS_FATAL_IF(result != OK, "Failed: %s", statusToString(result).c_str()); } else { #ifdef BINDER_WITH_KERNEL_IPC // warning: this must match the logic in ipcSetDataReference auto* kf = p->maybeKernelFields(); LOG_ALWAYS_FATAL_IF(kf == nullptr); // Ownership of FDs is passed to the Parcel from kernel binder. This should be refactored // to move this ownership out of Parcel and into release_func. However, today, Parcel // always assums it can own and close FDs today. So, for purposes of testing consistency, // , create new FDs it can own. uint8_t* newData = new uint8_t[p->mDataSize]; // deleted by delete_data_release_func memcpy(newData, p->mData, p->mDataSize); for (size_t i = 0; i < kf->mObjectsSize; i++) { flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(newData + kf->mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); } } ipcSetDataReference(newData, p->mDataSize, kf->mObjects, kf->mObjectsSize, delete_data_release_func); #endif // BINDER_WITH_KERNEL_IPC } } void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc) { // this code uses 'mOwner == nullptr' to understand whether it owns memory Loading @@ -2729,6 +2797,7 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin auto* kernelFields = maybeKernelFields(); LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData. // must match makeDangerousViewOf mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; kernelFields->mObjects = const_cast<binder_size_t*>(objects); Loading Loading @@ -2807,6 +2876,7 @@ status_t Parcel::rpcSetDataReference( auto* rpcFields = maybeRpcFields(); LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc. // must match makeDangerousViewOf mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; mOwner = relFunc; Loading Loading @@ -2874,15 +2944,17 @@ void Parcel::releaseObjects() #endif // BINDER_WITH_KERNEL_IPC } void Parcel::acquireObjects() { void Parcel::reacquireObjects(size_t objectsSize) { auto* kernelFields = maybeKernelFields(); if (kernelFields == nullptr) { return; } #ifdef BINDER_WITH_KERNEL_IPC size_t i = kernelFields->mObjectsSize; LOG_ALWAYS_FATAL_IF(objectsSize > kernelFields->mObjectsSize, "Object size %zu out of range of %zu", objectsSize, kernelFields->mObjectsSize); size_t i = objectsSize; if (i == 0) { return; } Loading @@ -2892,8 +2964,10 @@ void Parcel::acquireObjects() while (i > 0) { i--; const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); acquire_object(proc, *flat, this); acquire_object(proc, *flat, this, false /*tagFds*/); // they are already tagged } #else (void) objectsSize; #endif // BINDER_WITH_KERNEL_IPC } Loading Loading @@ -3110,12 +3184,8 @@ status_t Parcel::continueWrite(size_t desired) return NO_MEMORY; } // Little hack to only acquire references on objects // we will be keeping. size_t oldObjectsSize = kernelFields->mObjectsSize; kernelFields->mObjectsSize = objectsSize; acquireObjects(); kernelFields->mObjectsSize = oldObjectsSize; // only acquire references on objects we are keeping reacquireObjects(objectsSize); } if (rpcFields) { if (status_t status = truncateRpcObjects(objectsSize); status != OK) { Loading
libs/binder/ProcessState.cpp +15 −0 Original line number Diff line number Diff line Loading @@ -501,6 +501,21 @@ bool ProcessState::isThreadPoolStarted() const { return mThreadPoolStarted; } void ProcessState::checkExpectingThreadPoolStart() const { if (mThreadPoolStarted) return; // this is also racey, but you should setup the threadpool in the main thread. If that is an // issue, we can check if we are the process leader, but haven't seen the issue in practice. size_t requestedThreads = mMaxThreads.load(); // if it's manually set to the default, we do ignore it here... if (requestedThreads == DEFAULT_MAX_BINDER_THREADS) return; if (requestedThreads == 0) return; ALOGW("Thread pool configuration of size %zu requested, but startThreadPool was not called.", requestedThreads); } #define DRIVER_FEATURES_PATH "/dev/binderfs/features/" bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) { // Use static variable to cache the results. Loading
libs/binder/include/binder/Parcel.h +7 −2 Original line number Diff line number Diff line Loading @@ -649,6 +649,11 @@ public: LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const; // This API is to quickly become a view of another Parcel, so that we can also // test 'owner' paths quickly. It's extremely dangerous to use this API in // practice, and you should never ever do it. LIBBINDER_EXPORTED void makeDangerousViewOf(Parcel* p); private: // Close all file descriptors in the parcel at object positions >= newObjectsSize. void closeFileDescriptors(size_t newObjectsSize); Loading @@ -664,7 +669,7 @@ private: void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc); // Takes ownership even when an error is returned. status_t rpcSetDataReference( [[nodiscard]] status_t rpcSetDataReference( const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, const uint32_t* objectTable, size_t objectTableSize, std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds, Loading @@ -672,7 +677,7 @@ private: status_t finishWrite(size_t len); void releaseObjects(); void acquireObjects(); void reacquireObjects(size_t objectSize); status_t growData(size_t len); // Clear the Parcel and set the capacity to `desired`. // Doesn't reset the RPC session association. Loading
libs/binder/include/binder/ProcessState.h +2 −0 Original line number Diff line number Diff line Loading @@ -141,6 +141,8 @@ public: private: static sp<ProcessState> init(const char* defaultDriver, bool requireDefault); void checkExpectingThreadPoolStart() const; static void onFork(); static void parentPostFork(); static void childPostFork(); Loading