libbinder: service driver fuzzes nested ifaces

We:
- make a binder call
- get all of the FDs and binder objects from this call
- also fuzz these additional binder objects
  and use them and the FDs to construct parcels

Bug: 224646709
Fixes: 225083088
Test: android.hardware.vibrator-service.example_fuzzer
Test: binder_parcel_fuzzer
Change-Id: I3a1594c5777d7dfd4776b751d6f9054428655302
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a217a15..be50a75 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -569,6 +569,47 @@
     return mHasFds;
 }
 
+std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const {
+    std::vector<sp<IBinder>> ret;
+
+    size_t initPosition = dataPosition();
+    for (size_t i = 0; i < mObjectsSize; i++) {
+        binder_size_t offset = mObjects[i];
+        const flat_binder_object* flat =
+                reinterpret_cast<const flat_binder_object*>(mData + offset);
+        if (flat->hdr.type != BINDER_TYPE_BINDER) continue;
+
+        setDataPosition(offset);
+
+        sp<IBinder> binder = readStrongBinder();
+        if (binder != nullptr) ret.push_back(binder);
+    }
+
+    setDataPosition(initPosition);
+    return ret;
+}
+
+std::vector<int> Parcel::debugReadAllFileDescriptors() const {
+    std::vector<int> ret;
+
+    size_t initPosition = dataPosition();
+    for (size_t i = 0; i < mObjectsSize; i++) {
+        binder_size_t offset = mObjects[i];
+        const flat_binder_object* flat =
+                reinterpret_cast<const flat_binder_object*>(mData + offset);
+        if (flat->hdr.type != BINDER_TYPE_FD) continue;
+
+        setDataPosition(offset);
+
+        int fd = readFileDescriptor();
+        LOG_ALWAYS_FATAL_IF(fd == -1);
+        ret.push_back(fd);
+    }
+
+    setDataPosition(initPosition);
+    return ret;
+}
+
 status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const {
     if (len > INT32_MAX || offset > INT32_MAX) {
         // Don't accept size_t values which may have come from an inadvertent conversion from a
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 450e388..e2b2c51 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -95,6 +95,12 @@
     bool                hasFileDescriptors() const;
     status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const;
 
+    // returns all binder objects in the Parcel
+    std::vector<sp<IBinder>> debugReadAllStrongBinders() const;
+    // returns all file descriptors in the Parcel
+    // does not dup
+    std::vector<int> debugReadAllFileDescriptors() const;
+
     // Zeros data when reallocating. Other mitigations may be added
     // in the future.
     //
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index aee15d8..359c783 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -20,9 +20,12 @@
 #include <cutils/ashmem.h>
 #include <gtest/gtest.h>
 
+using android::BBinder;
+using android::IBinder;
 using android::IPCThreadState;
 using android::OK;
 using android::Parcel;
+using android::sp;
 using android::status_t;
 using android::String16;
 using android::String8;
@@ -75,6 +78,40 @@
     EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_NONE);
 }
 
+TEST(Parcel, DebugReadAllBinders) {
+    sp<IBinder> binder1 = sp<BBinder>::make();
+    sp<IBinder> binder2 = sp<BBinder>::make();
+
+    Parcel p;
+    p.writeInt32(4);
+    p.writeStrongBinder(binder1);
+    p.writeStrongBinder(nullptr);
+    p.writeInt32(4);
+    p.writeStrongBinder(binder2);
+    p.writeInt32(4);
+
+    auto ret = p.debugReadAllStrongBinders();
+
+    ASSERT_EQ(ret.size(), 2);
+    EXPECT_EQ(ret[0], binder1);
+    EXPECT_EQ(ret[1], binder2);
+}
+
+TEST(Parcel, DebugReadAllFds) {
+    Parcel p;
+    p.writeInt32(4);
+    p.writeFileDescriptor(STDOUT_FILENO, false /*takeOwnership*/);
+    p.writeInt32(4);
+    p.writeFileDescriptor(STDIN_FILENO, false /*takeOwnership*/);
+    p.writeInt32(4);
+
+    auto ret = p.debugReadAllFileDescriptors();
+
+    ASSERT_EQ(ret.size(), 2);
+    EXPECT_EQ(ret[0], STDOUT_FILENO);
+    EXPECT_EQ(ret[1], STDIN_FILENO);
+}
+
 // Tests a second operation results in a parcel at the same location as it
 // started.
 void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 13f7195..47ec776 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -109,6 +109,8 @@
     },
     PARCEL_READ_NO_STATUS(size_t, allowFds),
     PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
+    PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders),
+    PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors),
     [] (const ::android::Parcel& p, uint8_t len) {
         std::string interface(len, 'a');
         FUZZ_LOG() << "about to enforceInterface: " << interface;
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
index 0a083d7..843b6e3 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
@@ -16,12 +16,13 @@
 
 #pragma once
 
+#include <android-base/unique_fd.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
 namespace android {
 
-// ownership to callee, always valid or aborts
+// always valid or aborts
 // get a random FD for use in fuzzing, of a few different specific types
-int getRandomFd(FuzzedDataProvider* provider);
+base::unique_fd getRandomFd(FuzzedDataProvider* provider);
 
 } // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index 633626c..459fb12 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -20,8 +20,16 @@
 #include <fuzzer/FuzzedDataProvider.h>
 
 #include <functional>
+#include <vector>
 
 namespace android {
+
+struct RandomParcelOptions {
+    std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader;
+    std::vector<sp<IBinder>> extraBinders;
+    std::vector<base::unique_fd> extraFds;
+};
+
 /**
  * Fill parcel data, including some random binder objects and FDs
  *
@@ -30,7 +38,5 @@
  * writeHeader - optional function to write a specific header once the format of the parcel is
  *     picked (for instance, to write an interface header)
  */
-void fillRandomParcel(
-        Parcel* p, FuzzedDataProvider&& provider,
-        std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader = nullptr);
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, const RandomParcelOptions& = {});
 } // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index be39bb9..d5aa353 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -20,24 +20,45 @@
 namespace android {
 
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) {
+    sp<IBinder> target;
+
+    RandomParcelOptions options{
+            .extraBinders = {binder},
+            .extraFds = {},
+    };
+
     while (provider.remaining_bytes() > 0) {
         uint32_t code = provider.ConsumeIntegral<uint32_t>();
         uint32_t flags = provider.ConsumeIntegral<uint32_t>();
         Parcel data;
 
+        sp<IBinder> target = options.extraBinders.at(
+                provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1));
+        options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) {
+            // most code will be behind checks that the head of the Parcel
+            // is exactly this, so make it easier for fuzzers to reach this
+            if (provider.ConsumeBool()) {
+                p->writeInterfaceToken(target->getInterfaceDescriptor());
+            }
+        };
+
         std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
                 provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
-        fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()),
-                         [&binder](Parcel* p, FuzzedDataProvider& provider) {
-                             // most code will be behind checks that the head of the Parcel
-                             // is exactly this, so make it easier for fuzzers to reach this
-                             if (provider.ConsumeBool()) {
-                                 p->writeInterfaceToken(binder->getInterfaceDescriptor());
-                             }
-                         });
+        fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), options);
 
         Parcel reply;
-        (void)binder->transact(code, data, &reply, flags);
+        (void)target->transact(code, data, &reply, flags);
+
+        // feed back in binders and fds that are returned from the service, so that
+        // we can fuzz those binders, and use the fds and binders to feed back into
+        // the binders
+        auto retBinders = reply.debugReadAllStrongBinders();
+        options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(),
+                                    retBinders.end());
+        auto retFds = reply.debugReadAllFileDescriptors();
+        for (size_t i = 0; i < retFds.size(); i++) {
+            options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
+        }
     }
 }
 
diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
index cef6adb..ab0b7e3 100644
--- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
@@ -23,13 +23,13 @@
 
 namespace android {
 
-int getRandomFd(FuzzedDataProvider* provider) {
+base::unique_fd getRandomFd(FuzzedDataProvider* provider) {
     int fd = provider->PickValueInArray<std::function<int()>>({
             []() { return ashmem_create_region("binder test region", 1024); },
             []() { return open("/dev/null", O_RDWR); },
     })();
     CHECK(fd >= 0);
-    return fd;
+    return base::unique_fd(fd);
 }
 
 } // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index cfabc1e..0204f5e 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -40,19 +40,23 @@
 }
 
 void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider,
-                      std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader) {
+                      const RandomParcelOptions& options) {
     if (provider.ConsumeBool()) {
         auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
         CHECK_EQ(OK, session->addNullDebuggingClient());
         p->markForRpc(session);
 
-        writeHeader(p, provider);
+        if (options.writeHeader) {
+            options.writeHeader(p, provider);
+        }
 
         fillRandomParcelData(p, std::move(provider));
         return;
     }
 
-    writeHeader(p, provider);
+    if (options.writeHeader) {
+        options.writeHeader(p, provider);
+    }
 
     while (provider.remaining_bytes() > 0) {
         auto fillFunc = provider.PickValueInArray<const std::function<void()>>({
@@ -65,8 +69,16 @@
                 },
                 // write FD
                 [&]() {
-                    int fd = getRandomFd(&provider);
-                    CHECK(OK == p->writeFileDescriptor(fd, true /*takeOwnership*/));
+                    if (options.extraFds.size() > 0 && provider.ConsumeBool()) {
+                        const base::unique_fd& fd = options.extraFds.at(
+                                provider.ConsumeIntegralInRange<size_t>(0,
+                                                                        options.extraFds.size() -
+                                                                                1));
+                        CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/));
+                    } else {
+                        base::unique_fd fd = getRandomFd(&provider);
+                        CHECK(OK == p->writeFileDescriptor(fd.release(), true /*takeOwnership*/));
+                    }
                 },
                 // write binder
                 [&]() {
@@ -85,7 +97,15 @@
                                 // candidate for checking usage of an actual BpBinder
                                 return IInterface::asBinder(defaultServiceManager());
                             },
-                            []() { return nullptr; },
+                            [&]() -> sp<IBinder> {
+                                if (options.extraBinders.size() > 0 && provider.ConsumeBool()) {
+                                    return options.extraBinders.at(
+                                            provider.ConsumeIntegralInRange<
+                                                    size_t>(0, options.extraBinders.size() - 1));
+                                } else {
+                                    return nullptr;
+                                }
+                            },
                     });
                     sp<IBinder> binder = makeFunc();
                     CHECK(OK == p->writeStrongBinder(binder));