Merge "binder_parcel_fuzzer: support write functions" into main
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index e378b86..a9c1fed 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -25,6 +25,8 @@
 #include <binder/ParcelableHolder.h>
 #include <binder/PersistableBundle.h>
 #include <binder/Status.h>
+#include <fuzzbinder/random_binder.h>
+#include <fuzzbinder/random_fd.h>
 #include <utils/Flattenable.h>
 
 #include "../../Utils.h"
@@ -404,5 +406,123 @@
         FUZZ_LOG() << " toString() result: " << toString;
     },
 };
+
+std::vector<ParcelWrite<::android::Parcel>> BINDER_PARCEL_WRITE_FUNCTIONS {
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call setDataSize";
+        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+        p.setDataSize(len);
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call setDataCapacity";
+        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+        p.setDataCapacity(len);
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call setData";
+        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+        std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+        p.setData(bytes.data(), bytes.size());
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+        FUZZ_LOG() << "about to call appendFrom";
+
+        std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(provider.ConsumeIntegralInRange<size_t>(0, 4096));
+        ::android::Parcel p2;
+        fillRandomParcel(&p2, FuzzedDataProvider(bytes.data(), bytes.size()), options);
+
+        int32_t start = provider.ConsumeIntegral<int32_t>();
+        int32_t len = provider.ConsumeIntegral<int32_t>();
+        p.appendFrom(&p2, start, len);
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call setData";
+        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+        std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+        p.setData(bytes.data(), bytes.size());
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call pushAllowFds";
+        bool val = provider.ConsumeBool();
+        p.pushAllowFds(val);
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call restoreAllowFds";
+        bool val = provider.ConsumeBool();
+        p.restoreAllowFds(val);
+    },
+    // markForBinder - covered by fillRandomParcel, aborts if called multiple times
+    // markForRpc - covered by fillRandomParcel, aborts if called multiple times
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call writeInterfaceToken";
+        std::string interface = provider.ConsumeRandomLengthString();
+        p.writeInterfaceToken(android::String16(interface.c_str()));
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call setEnforceNoDataAvail";
+        p.setEnforceNoDataAvail(provider.ConsumeBool());
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call setServiceFuzzing";
+        p.setServiceFuzzing();
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call freeData";
+        p.freeData();
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call write";
+        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 256);
+        std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+        p.write(bytes.data(), bytes.size());
+    },
+    // write* - write functions all implemented by calling 'write' itself.
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+        FUZZ_LOG() << "about to call writeStrongBinder";
+
+        // TODO: this logic is somewhat duplicated with random parcel
+       android::sp<android::IBinder> binder;
+       if (provider.ConsumeBool() && options->extraBinders.size() > 0) {
+            binder = options->extraBinders.at(
+                    provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders.size() - 1));
+        } else {
+            binder = android::getRandomBinder(&provider);
+            options->extraBinders.push_back(binder);
+        }
+
+        p.writeStrongBinder(binder);
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call writeFileDescriptor (no ownership)";
+        p.writeFileDescriptor(STDERR_FILENO, false /* takeOwnership */);
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+        FUZZ_LOG() << "about to call writeFileDescriptor (take ownership)";
+        std::vector<unique_fd> fds = android::getRandomFds(&provider);
+        if (fds.size() == 0) return;
+
+        p.writeDupFileDescriptor(fds.at(0).get());
+        options->extraFds.insert(options->extraFds.end(),
+             std::make_move_iterator(fds.begin() + 1),
+             std::make_move_iterator(fds.end()));
+    },
+    // TODO: writeBlob
+    // TODO: writeDupImmutableBlobFileDescriptor
+    // TODO: writeObject (or make the API private more likely)
+    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call writeNoException";
+        p.writeNoException();
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call closeFileDescriptors";
+        p.closeFileDescriptors();
+    },
+    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+        FUZZ_LOG() << "about to call replaceCallingWorkSourceUid";
+        uid_t uid = provider.ConsumeIntegral<uid_t>();
+        p.replaceCallingWorkSourceUid(uid);
+    },
+};
+
 // clang-format on
 #pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h
index 0c51d68..b0ac140 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.h
+++ b/libs/binder/tests/parcel_fuzzer/binder.h
@@ -21,3 +21,4 @@
 #include "parcel_fuzzer.h"
 
 extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+extern std::vector<ParcelWrite<::android::Parcel>> BINDER_PARCEL_WRITE_FUNCTIONS;
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index e3a3371..3f8d71d 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -20,8 +20,11 @@
 #include "aidl/parcelables/GenericDataParcelable.h"
 #include "aidl/parcelables/SingleDataParcelable.h"
 
+#include <android/binder_libbinder.h>
 #include <android/binder_parcel_utils.h>
 #include <android/binder_parcelable_utils.h>
+#include <fuzzbinder/random_binder.h>
+#include <fuzzbinder/random_fd.h>
 
 #include "util.h"
 
@@ -211,16 +214,51 @@
             binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len);
             FUZZ_LOG() << "status: " << status;
         },
-        [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) {
-            FUZZ_LOG() << "about to unmarshal AParcel";
+};
+std::vector<ParcelWrite<NdkParcelAdapter>> BINDER_NDK_PARCEL_WRITE_FUNCTIONS{
+        [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+            FUZZ_LOG() << "about to call AParcel_writeStrongBinder";
+
+            // TODO: this logic is somewhat duplicated with random parcel
+            android::sp<android::IBinder> binder;
+            if (provider.ConsumeBool() && options->extraBinders.size() > 0) {
+                binder = options->extraBinders.at(
+                        provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders.size() - 1));
+            } else {
+                binder = android::getRandomBinder(&provider);
+                options->extraBinders.push_back(binder);
+            }
+
+            ndk::SpAIBinder abinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(binder));
+            AParcel_writeStrongBinder(p.aParcel(), abinder.get());
+        },
+        [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+            FUZZ_LOG() << "about to call AParcel_writeParcelFileDescriptor";
+
+            auto fds = android::getRandomFds(&provider);
+            if (fds.size() == 0) return;
+
+            AParcel_writeParcelFileDescriptor(p.aParcel(), fds.at(0).get());
+            options->extraFds.insert(options->extraFds.end(),
+                 std::make_move_iterator(fds.begin() + 1),
+                 std::make_move_iterator(fds.end()));
+        },
+        // all possible data writes can be done as a series of 4-byte reads
+        [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+            FUZZ_LOG() << "about to call AParcel_writeInt32";
+            int32_t val = provider.ConsumeIntegral<int32_t>();
+            AParcel_writeInt32(p.aParcel(), val);
+        },
+        [] (NdkParcelAdapter& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+            FUZZ_LOG() << "about to call AParcel_reset";
+            AParcel_reset(p.aParcel());
+        },
+        [](NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+            FUZZ_LOG() << "about to call AParcel_unmarshal";
             size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
-            std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len);
-            const uint8_t* buffer = parcelData.data();
-            const size_t bufferLen = parcelData.size();
-            NdkParcelAdapter adapter;
-            binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen);
+            std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(len);
+            binder_status_t status = AParcel_unmarshal(p.aParcel(), data.data(), data.size());
             FUZZ_LOG() << "status: " << status;
         },
-
 };
 // clang-format on
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
index d19f25b..0c8b725 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -50,3 +50,4 @@
 };
 
 extern std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS;
+extern std::vector<ParcelWrite<NdkParcelAdapter>> BINDER_NDK_PARCEL_WRITE_FUNCTIONS;
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index a57d07f..ede0e92 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -80,6 +80,7 @@
     (void)binder->transact(code, data, &reply, flag);
 }
 
+// start with a Parcel full of data (e.g. like you get from another process)
 template <typename P>
 void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
                 FuzzedDataProvider&& provider) {
@@ -98,7 +99,7 @@
     fillRandomParcel(&p, std::move(provider), &options);
 
     // since we are only using a byte to index
-    CHECK(reads.size() <= 255) << reads.size();
+    CHECK_LE(reads.size(), 255u) << reads.size();
 
     FUZZ_LOG() << "backend: " << backend;
     FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
@@ -115,26 +116,31 @@
     }
 }
 
-// Append two random parcels.
 template <typename P>
-void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) {
-    int32_t start = provider.ConsumeIntegral<int32_t>();
-    int32_t len = provider.ConsumeIntegral<int32_t>();
-
-    std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
-            provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
-
-    // same options so that FDs and binders could be shared in both Parcels
+void doReadWriteFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+                     const std::vector<ParcelWrite<P>>& writes, FuzzedDataProvider&& provider) {
     RandomParcelOptions options;
 
-    P p0, p1;
-    fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
-    fillRandomParcel(&p1, std::move(provider), &options);
+    P p;
+    fillRandomParcel(&p, std::move(provider), &options);
+
+    // since we are only using a byte to index
+    CHECK_LE(reads.size() + writes.size(), 255u) << reads.size();
 
     FUZZ_LOG() << "backend: " << backend;
-    FUZZ_LOG() << "start: " << start << " len: " << len;
 
-    p0.appendFrom(&p1, start, len);
+    while (provider.remaining_bytes() > 0) {
+        uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1);
+
+        FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail()
+                   << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity();
+
+        if (idx < reads.size()) {
+            reads.at(idx)(p, provider);
+        } else {
+            writes.at(idx - reads.size())(p, provider, &options);
+        }
+    }
 }
 
 void* NothingClass_onCreate(void* args) {
@@ -156,7 +162,7 @@
 
     FuzzedDataProvider provider = FuzzedDataProvider(data, size);
 
-    const std::function<void(FuzzedDataProvider &&)> fuzzBackend[] = {
+    const std::function<void(FuzzedDataProvider&&)> fuzzBackend[] = {
             [](FuzzedDataProvider&& provider) {
                 doTransactFuzz<
                         ::android::hardware::Parcel>("hwbinder",
@@ -187,10 +193,14 @@
                                              std::move(provider));
             },
             [](FuzzedDataProvider&& provider) {
-                doAppendFuzz<::android::Parcel>("binder", std::move(provider));
+                doReadWriteFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+                                                   BINDER_PARCEL_WRITE_FUNCTIONS,
+                                                   std::move(provider));
             },
             [](FuzzedDataProvider&& provider) {
-                doAppendFuzz<NdkParcelAdapter>("binder_ndk", std::move(provider));
+                doReadWriteFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+                                                  BINDER_NDK_PARCEL_WRITE_FUNCTIONS,
+                                                  std::move(provider));
             },
     };
 
diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
index 765a93e..dbd0cae 100644
--- a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
+++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
@@ -15,9 +15,13 @@
  */
 #pragma once
 
+#include <fuzzbinder/random_parcel.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
 #include <functional>
 
 template <typename P>
 using ParcelRead = std::function<void(const P& p, FuzzedDataProvider& provider)>;
+template <typename P>
+using ParcelWrite = std::function<void(P& p, FuzzedDataProvider& provider,
+                                       android::RandomParcelOptions* options)>;