Loading libs/binder/ndk/ibinder.cpp +59 −35 Original line number Diff line number Diff line Loading @@ -75,12 +75,48 @@ void clean(const void* id, void* obj, void* cookie) { AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {} AIBinder::~AIBinder() {} std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz, const String16& newDescriptor, bool set) { // b/175635923 libcxx causes "implicit-conversion" with a string with invalid char static std::string SanitizeString(const String16& str) { std::string sanitized{String8(str)}; for (auto& c : sanitized) { if (!isprint(c)) { c = '?'; } } return sanitized; } bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (clazz == nullptr) return false; // If mClazz is non-null, this must have been called and cached // already. So, we can safely call this first. Due to the implementation // of getInterfaceDescriptor (at time of writing), two simultaneous calls // may lead to extra binder transactions, but this is expected to be // exceedingly rare. Once we have a binder, when we get it again later, // we won't make another binder transaction here. const String16& descriptor = getBinder()->getInterfaceDescriptor(); const String16& newDescriptor = clazz->getInterfaceDescriptor(); std::lock_guard<std::mutex> lock(mClazzMutex); if (mClazz == clazz) return true; if (mClazz != nullptr) { // If this is an ABpBinder, the first class object becomes the canonical one. The implication // of this is that no API can require a proxy information to get information on how to behave. // from the class itself - which should only store the interface descriptor. The functionality // should be implemented by adding AIBinder_* APIs to set values on binders themselves, by // setting things on AIBinder_Class which get transferred along with the binder, so that they // can be read along with the BpBinder, or by modifying APIs directly (e.g. an option in // onTransact). // // While this check is required to support linkernamespaces, one downside of it is that // you may parcel code to communicate between things in the same process. However, comms // between linkernamespaces like this already happen for cross-language calls like Java<->C++ // or Rust<->Java, and there are good stability guarantees here. This interacts with // binder Stability checks exactly like any other in-process call. The stability is known // to the IBinder object, so that it doesn't matter if a class object comes from // a different stability level. if (mClazz != nullptr && !asABpBinder()) { const String16& currentDescriptor = mClazz->getInterfaceDescriptor(); if (newDescriptor == currentDescriptor) { LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor Loading @@ -97,37 +133,10 @@ std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz return false; } if (set) { // if this is a local object, it's not one known to libbinder_ndk mClazz = clazz; return true; } return {}; } // b/175635923 libcxx causes "implicit-conversion" with a string with invalid char static std::string SanitizeString(const String16& str) { std::string sanitized{String8(str)}; for (auto& c : sanitized) { if (!isprint(c)) { c = '?'; } } return sanitized; } bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (clazz == nullptr) return false; const String16& newDescriptor = clazz->getInterfaceDescriptor(); auto result = associateClassInternal(clazz, newDescriptor, false); if (result.has_value()) return *result; CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor const String16& descriptor = getBinder()->getInterfaceDescriptor(); // This will always be an O(n) comparison, but it's expected to be extremely rare. // since it's an error condition. Do the comparison after we take the lock and // check the pointer equality fast path. By always taking the lock, it's also // more flake-proof. However, the check is not dependent on the lock. if (descriptor != newDescriptor) { if (getBinder()->isBinderAlive()) { LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor Loading @@ -141,7 +150,14 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { return false; } return associateClassInternal(clazz, newDescriptor, true).value(); // A local binder being set for the first time OR // ignoring a proxy binder which is set multiple time, by considering the first // associated class as the canonical one. if (mClazz == nullptr) { mClazz = clazz; } return true; } ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData) Loading Loading @@ -325,6 +341,10 @@ bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs) { return lhs->binder < rhs->binder; } // WARNING: When multiple classes exist with the same interface descriptor in different // linkernamespaces, the first one to be associated with mClazz becomes the canonical one // and the only requirement on this is that the interface descriptors match. If this // is an ABpBinder, no other state can be referenced from mClazz. AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) Loading Loading @@ -632,6 +652,10 @@ binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { (*in)->get()->markForBinder(binder->getBinder()); status_t status = android::OK; // note - this is the only read of a value in clazz, and it comes with a warning // on the API itself. Do not copy this design. Instead, attach data in a new // version of the prepareTransaction function. if (clazz->writeHeader) { status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor()); } Loading libs/binder/ndk/ibinder_internal.h +5 −3 Original line number Diff line number Diff line Loading @@ -53,12 +53,14 @@ struct AIBinder : public virtual ::android::RefBase { } private: std::optional<bool> associateClassInternal(const AIBinder_Class* clazz, const ::android::String16& newDescriptor, bool set); // AIBinder instance is instance of this class for a local object. In order to transact on a // remote object, this also must be set for simplicity (although right now, only the // interfaceDescriptor from it is used). // // WARNING: When multiple classes exist with the same interface descriptor in different // linkernamespaces, the first one to be associated with mClazz becomes the canonical one // and the only requirement on this is that the interface descriptors match. If this // is an ABpBinder, no other state can be referenced from mClazz. const AIBinder_Class* mClazz; std::mutex mClazzMutex; }; Loading libs/binder/ndk/include_ndk/android/binder_ibinder.h +5 −0 Original line number Diff line number Diff line Loading @@ -229,6 +229,11 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __I * * Available since API level 33. * * WARNING: this API interacts badly with linkernamespaces. For correct behavior, you must * use it on all instances of a class in the same process which share the same interface * descriptor. In general, it is recommended you do not use this API, because it is disabling * type safety. * * \param clazz class to disable interface header on. */ void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) __INTRODUCED_IN(33); Loading libs/binder/ndk/tests/iface.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,11 @@ binder_status_t IFoo_Class_onTransact(AIBinder* binder, transaction_code_t code, AIBinder_Class* IFoo::kClass = AIBinder_Class_define(kIFooDescriptor, IFoo_Class_onCreate, IFoo_Class_onDestroy, IFoo_Class_onTransact); // Defines the same class. Ordinarly, you would never want to do this, but it's done here // to simulate what would happen when multiple linker namespaces interact. AIBinder_Class* IFoo::kClassDupe = AIBinder_Class_define( kIFooDescriptor, IFoo_Class_onCreate, IFoo_Class_onDestroy, IFoo_Class_onTransact); class BpFoo : public IFoo { public: explicit BpFoo(AIBinder* binder) : mBinder(binder) {} Loading libs/binder/ndk/tests/include/iface/iface.h +5 −0 Original line number Diff line number Diff line Loading @@ -30,8 +30,13 @@ class IFoo : public virtual ::android::RefBase { static const char* kIFooDescriptor; static AIBinder_Class* kClass; static AIBinder_Class* kClassDupe; // binder representing this interface with one reference count // NOTE - this will create a new binder if it already exists. If you use // getService for instance, you must pull outBinder. Don't use this without // verifying isRemote or pointer equality. This is not a very good testing API - don't // copy it - consider the AIDL-generated APIs instead. AIBinder* getBinder(); // Takes ownership of IFoo Loading Loading
libs/binder/ndk/ibinder.cpp +59 −35 Original line number Diff line number Diff line Loading @@ -75,12 +75,48 @@ void clean(const void* id, void* obj, void* cookie) { AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {} AIBinder::~AIBinder() {} std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz, const String16& newDescriptor, bool set) { // b/175635923 libcxx causes "implicit-conversion" with a string with invalid char static std::string SanitizeString(const String16& str) { std::string sanitized{String8(str)}; for (auto& c : sanitized) { if (!isprint(c)) { c = '?'; } } return sanitized; } bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (clazz == nullptr) return false; // If mClazz is non-null, this must have been called and cached // already. So, we can safely call this first. Due to the implementation // of getInterfaceDescriptor (at time of writing), two simultaneous calls // may lead to extra binder transactions, but this is expected to be // exceedingly rare. Once we have a binder, when we get it again later, // we won't make another binder transaction here. const String16& descriptor = getBinder()->getInterfaceDescriptor(); const String16& newDescriptor = clazz->getInterfaceDescriptor(); std::lock_guard<std::mutex> lock(mClazzMutex); if (mClazz == clazz) return true; if (mClazz != nullptr) { // If this is an ABpBinder, the first class object becomes the canonical one. The implication // of this is that no API can require a proxy information to get information on how to behave. // from the class itself - which should only store the interface descriptor. The functionality // should be implemented by adding AIBinder_* APIs to set values on binders themselves, by // setting things on AIBinder_Class which get transferred along with the binder, so that they // can be read along with the BpBinder, or by modifying APIs directly (e.g. an option in // onTransact). // // While this check is required to support linkernamespaces, one downside of it is that // you may parcel code to communicate between things in the same process. However, comms // between linkernamespaces like this already happen for cross-language calls like Java<->C++ // or Rust<->Java, and there are good stability guarantees here. This interacts with // binder Stability checks exactly like any other in-process call. The stability is known // to the IBinder object, so that it doesn't matter if a class object comes from // a different stability level. if (mClazz != nullptr && !asABpBinder()) { const String16& currentDescriptor = mClazz->getInterfaceDescriptor(); if (newDescriptor == currentDescriptor) { LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor Loading @@ -97,37 +133,10 @@ std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz return false; } if (set) { // if this is a local object, it's not one known to libbinder_ndk mClazz = clazz; return true; } return {}; } // b/175635923 libcxx causes "implicit-conversion" with a string with invalid char static std::string SanitizeString(const String16& str) { std::string sanitized{String8(str)}; for (auto& c : sanitized) { if (!isprint(c)) { c = '?'; } } return sanitized; } bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (clazz == nullptr) return false; const String16& newDescriptor = clazz->getInterfaceDescriptor(); auto result = associateClassInternal(clazz, newDescriptor, false); if (result.has_value()) return *result; CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor const String16& descriptor = getBinder()->getInterfaceDescriptor(); // This will always be an O(n) comparison, but it's expected to be extremely rare. // since it's an error condition. Do the comparison after we take the lock and // check the pointer equality fast path. By always taking the lock, it's also // more flake-proof. However, the check is not dependent on the lock. if (descriptor != newDescriptor) { if (getBinder()->isBinderAlive()) { LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor Loading @@ -141,7 +150,14 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { return false; } return associateClassInternal(clazz, newDescriptor, true).value(); // A local binder being set for the first time OR // ignoring a proxy binder which is set multiple time, by considering the first // associated class as the canonical one. if (mClazz == nullptr) { mClazz = clazz; } return true; } ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData) Loading Loading @@ -325,6 +341,10 @@ bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs) { return lhs->binder < rhs->binder; } // WARNING: When multiple classes exist with the same interface descriptor in different // linkernamespaces, the first one to be associated with mClazz becomes the canonical one // and the only requirement on this is that the interface descriptors match. If this // is an ABpBinder, no other state can be referenced from mClazz. AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) Loading Loading @@ -632,6 +652,10 @@ binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { (*in)->get()->markForBinder(binder->getBinder()); status_t status = android::OK; // note - this is the only read of a value in clazz, and it comes with a warning // on the API itself. Do not copy this design. Instead, attach data in a new // version of the prepareTransaction function. if (clazz->writeHeader) { status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor()); } Loading
libs/binder/ndk/ibinder_internal.h +5 −3 Original line number Diff line number Diff line Loading @@ -53,12 +53,14 @@ struct AIBinder : public virtual ::android::RefBase { } private: std::optional<bool> associateClassInternal(const AIBinder_Class* clazz, const ::android::String16& newDescriptor, bool set); // AIBinder instance is instance of this class for a local object. In order to transact on a // remote object, this also must be set for simplicity (although right now, only the // interfaceDescriptor from it is used). // // WARNING: When multiple classes exist with the same interface descriptor in different // linkernamespaces, the first one to be associated with mClazz becomes the canonical one // and the only requirement on this is that the interface descriptors match. If this // is an ABpBinder, no other state can be referenced from mClazz. const AIBinder_Class* mClazz; std::mutex mClazzMutex; }; Loading
libs/binder/ndk/include_ndk/android/binder_ibinder.h +5 −0 Original line number Diff line number Diff line Loading @@ -229,6 +229,11 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __I * * Available since API level 33. * * WARNING: this API interacts badly with linkernamespaces. For correct behavior, you must * use it on all instances of a class in the same process which share the same interface * descriptor. In general, it is recommended you do not use this API, because it is disabling * type safety. * * \param clazz class to disable interface header on. */ void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) __INTRODUCED_IN(33); Loading
libs/binder/ndk/tests/iface.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,11 @@ binder_status_t IFoo_Class_onTransact(AIBinder* binder, transaction_code_t code, AIBinder_Class* IFoo::kClass = AIBinder_Class_define(kIFooDescriptor, IFoo_Class_onCreate, IFoo_Class_onDestroy, IFoo_Class_onTransact); // Defines the same class. Ordinarly, you would never want to do this, but it's done here // to simulate what would happen when multiple linker namespaces interact. AIBinder_Class* IFoo::kClassDupe = AIBinder_Class_define( kIFooDescriptor, IFoo_Class_onCreate, IFoo_Class_onDestroy, IFoo_Class_onTransact); class BpFoo : public IFoo { public: explicit BpFoo(AIBinder* binder) : mBinder(binder) {} Loading
libs/binder/ndk/tests/include/iface/iface.h +5 −0 Original line number Diff line number Diff line Loading @@ -30,8 +30,13 @@ class IFoo : public virtual ::android::RefBase { static const char* kIFooDescriptor; static AIBinder_Class* kClass; static AIBinder_Class* kClassDupe; // binder representing this interface with one reference count // NOTE - this will create a new binder if it already exists. If you use // getService for instance, you must pull outBinder. Don't use this without // verifying isRemote or pointer equality. This is not a very good testing API - don't // copy it - consider the AIDL-generated APIs instead. AIBinder* getBinder(); // Takes ownership of IFoo Loading