diff options
| -rw-r--r-- | libs/binder/ndk/ibinder.cpp | 35 | ||||
| -rw-r--r-- | libs/binder/ndk/ibinder_internal.h | 5 | ||||
| -rw-r--r-- | libs/binder/ndk/include_ndk/android/binder_ibinder.h | 5 | ||||
| -rw-r--r-- | libs/binder/ndk/tests/iface.cpp | 5 | ||||
| -rw-r--r-- | libs/binder/ndk/tests/include/iface/iface.h | 5 | ||||
| -rw-r--r-- | libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp | 52 |
6 files changed, 86 insertions, 21 deletions
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index b29f22e41a..d0de7b96b5 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -101,7 +101,22 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { 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 @@ -115,7 +130,6 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { } // always a failure because we know mClazz != clazz - // TODO(b/262463798): support multiple ABpBinder in different namespaces return false; } @@ -136,8 +150,13 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { return false; } - // if this is a local object, it's not one known to libbinder_ndk - mClazz = clazz; + // 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; } @@ -322,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) @@ -629,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()); } diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 8699f857d4..67bb092f0f 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -56,6 +56,11 @@ struct AIBinder : public virtual ::android::RefBase { // 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; }; diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 1af211975f..05677a8e5d 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -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); diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp index 2afe5d2058..76acff50bf 100644 --- a/libs/binder/ndk/tests/iface.cpp +++ b/libs/binder/ndk/tests/iface.cpp @@ -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) {} diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h index 7408d0c5a9..0a562f085d 100644 --- a/libs/binder/ndk/tests/include/iface/iface.h +++ b/libs/binder/ndk/tests/include/iface/iface.h @@ -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 diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 9d5ef6805a..5b2532ab4e 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -55,6 +55,18 @@ constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestS constexpr unsigned int kShutdownWaitTime = 10; constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715; +class MyTestFoo : public IFoo { + binder_status_t doubleNumber(int32_t in, int32_t* out) override { + *out = 2 * in; + LOG(INFO) << "doubleNumber (" << in << ") => " << *out; + return STATUS_OK; + } + binder_status_t die() override { + ADD_FAILURE() << "die called on local instance"; + return STATUS_OK; + } +}; + class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) { *out = in; @@ -296,11 +308,10 @@ TEST(NdkBinder, RegisterForServiceNotificationsExisting) { } TEST(NdkBinder, UnimplementedDump) { - sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); + ndk::SpAIBinder binder; + sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName, binder.getR()); ASSERT_NE(foo, nullptr); - AIBinder* binder = foo->getBinder(); - EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0)); - AIBinder_decStrong(binder); + EXPECT_EQ(OK, AIBinder_dump(binder.get(), STDOUT_FILENO, nullptr, 0)); } TEST(NdkBinder, UnimplementedShell) { @@ -324,6 +335,24 @@ TEST(NdkBinder, DoubleNumber) { EXPECT_EQ(2, out); } +TEST(NdkBinder, ReassociateBpBinderWithSameDescriptor) { + ndk::SpAIBinder binder; + sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName, binder.getR()); + + EXPECT_TRUE(AIBinder_isRemote(binder.get())); + + EXPECT_TRUE(AIBinder_associateClass(binder.get(), IFoo::kClassDupe)); +} + +TEST(NdkBinder, CantHaveTwoLocalBinderClassesWithSameDescriptor) { + sp<IFoo> foo = sp<MyTestFoo>::make(); + ndk::SpAIBinder binder(foo->getBinder()); + + EXPECT_FALSE(AIBinder_isRemote(binder.get())); + + EXPECT_FALSE(AIBinder_associateClass(binder.get(), IFoo::kClassDupe)); +} + TEST(NdkBinder, GetTestServiceStressTest) { // libbinder has some complicated logic to make sure only one instance of // ABpBinder is associated with each binder. @@ -545,18 +574,6 @@ TEST(NdkBinder, LinkToDeath) { AIBinder_decStrong(binder); } -class MyTestFoo : public IFoo { - binder_status_t doubleNumber(int32_t in, int32_t* out) override { - *out = 2 * in; - LOG(INFO) << "doubleNumber (" << in << ") => " << *out; - return STATUS_OK; - } - binder_status_t die() override { - ADD_FAILURE() << "die called on local instance"; - return STATUS_OK; - } -}; - TEST(NdkBinder, SetInheritRt) { // functional test in binderLibTest sp<IFoo> foo = sp<MyTestFoo>::make(); @@ -597,7 +614,8 @@ TEST(NdkBinder, GetServiceInProcess) { sp<IFoo> foo = new MyTestFoo; EXPECT_EQ(EX_NONE, foo->addService(kInstanceName)); - sp<IFoo> getFoo = IFoo::getService(kInstanceName); + ndk::SpAIBinder binder; + sp<IFoo> getFoo = IFoo::getService(kInstanceName, binder.getR()); EXPECT_EQ(foo.get(), getFoo.get()); int32_t out; |