Merge "binderRpcBenchmark: device tests kernel as ctrl"
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 37fc9a9..e3c4ede 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -266,7 +266,10 @@
     chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu15/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu15/trace
 
-on post-fs-data
+# Only create the tracing instance if persist.mm_events.enabled
+# Attempting to remove the tracing instance after it has been created
+# will likely fail with EBUSY as it would be in use by traced_probes.
+on post-fs-data && property:persist.mm_events.enabled=true
 # Create MM Events Tracing Instance for Kmem Activity Trigger
     mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system
     mkdir /sys/kernel/tracing/instances/mm_events 0755 system system
@@ -275,10 +278,18 @@
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb
     chmod 0666 /sys/kernel/tracing/instances/mm_events/buffer_size_kb
 
+# Set the default buffer size to the minimum
+    write /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb 1
+    write /sys/kernel/tracing/instances/mm_events/buffer_size_kb 1
+
 # Read and enable tracing
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/tracing_on
     chmod 0666 /sys/kernel/tracing/instances/mm_events/tracing_on
 
+# Tracing disabled by default
+    write /sys/kernel/debug/tracing/instances/mm_events/tracing_on 0
+    write /sys/kernel/tracing/instances/mm_events/tracing_on 0
+
 # Read and truncate kernel trace
     chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/trace
     chmod 0666 /sys/kernel/tracing/instances/mm_events/trace
diff --git a/cmds/cmd/fuzzer/Android.bp b/cmds/cmd/fuzzer/Android.bp
new file mode 100644
index 0000000..0c78c5a
--- /dev/null
+++ b/cmds/cmd/fuzzer/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_fuzz {
+    name: "cmd_fuzzer",
+    srcs: [
+        "cmd_fuzzer.cpp",
+    ],
+    static_libs: [
+        "libcmd",
+        "libutils",
+        "liblog",
+        "libselinux",
+    ],
+    shared_libs: [
+        "libbinder",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
diff --git a/cmds/cmd/fuzzer/README.md b/cmds/cmd/fuzzer/README.md
new file mode 100644
index 0000000..db37ece
--- /dev/null
+++ b/cmds/cmd/fuzzer/README.md
@@ -0,0 +1,51 @@
+# Fuzzer for libcmd_fuzzer
+
+## Plugin Design Considerations
+The fuzzer plugin for libcmd is designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libcmd supports the following parameters:
+1. In (parameter name: `in`)
+2. Out (parameter name: `out`)
+3. Err (parameter name: `err`)
+4. Run Mode (parameter name: `runMode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `in` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `out` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `err` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider|
+| `runMode` | 1.`RunMode::kStandalone` 2. `RunMode::kLibrary` | Value chosen from valid values using FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the cmd module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build cmd_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) cmd_fuzzer
+```
+#### Steps to run
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/cmd_fuzzer/cmd_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/cmds/cmd/fuzzer/cmd_fuzzer.cpp b/cmds/cmd/fuzzer/cmd_fuzzer.cpp
new file mode 100644
index 0000000..ab514a1
--- /dev/null
+++ b/cmds/cmd/fuzzer/cmd_fuzzer.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/TextOutput.h>
+#include <cmd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace std;
+using namespace android;
+
+class TestTextOutput : public TextOutput {
+public:
+    TestTextOutput() {}
+    virtual ~TestTextOutput() {}
+
+    virtual status_t print(const char* /*txt*/, size_t /*len*/) { return NO_ERROR; }
+    virtual void moveIndent(int /*delta*/) { return; }
+    virtual void pushBundle() { return; }
+    virtual void popBundle() { return; }
+};
+
+class CmdFuzzer {
+public:
+    void process(const uint8_t* data, size_t size);
+
+private:
+    FuzzedDataProvider* mFDP = nullptr;
+};
+
+void CmdFuzzer::process(const uint8_t* data, size_t size) {
+    mFDP = new FuzzedDataProvider(data, size);
+    vector<string> arguments;
+    if (mFDP->ConsumeBool()) {
+        if (mFDP->ConsumeBool()) {
+            arguments = {"-w", "media.aaudio"};
+        } else {
+            arguments = {"-l"};
+        }
+    } else {
+        while (mFDP->remaining_bytes() > 0) {
+            size_t sizestr = mFDP->ConsumeIntegralInRange<size_t>(1, mFDP->remaining_bytes());
+            string argument = mFDP->ConsumeBytesAsString(sizestr);
+            arguments.emplace_back(argument);
+        }
+    }
+    vector<string_view> argSV;
+    for (auto& argument : arguments) {
+        argSV.emplace_back(argument.c_str());
+    }
+    int32_t in = open("/dev/null", O_RDWR | O_CREAT);
+    int32_t out = open("/dev/null", O_RDWR | O_CREAT);
+    int32_t err = open("/dev/null", O_RDWR | O_CREAT);
+    TestTextOutput output;
+    TestTextOutput error;
+    RunMode runMode = mFDP->ConsumeBool() ? RunMode::kStandalone : RunMode::kLibrary;
+    cmdMain(argSV, output, error, in, out, err, runMode);
+    delete mFDP;
+    close(in);
+    close(out);
+    close(err);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    CmdFuzzer cmdFuzzer;
+    cmdFuzzer.process(data, size);
+    return 0;
+}
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 200d923..62ea187 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -110,6 +110,10 @@
     return mMaxThreads;
 }
 
+void RpcServer::setProtocolVersion(uint32_t version) {
+    mProtocolVersion = version;
+}
+
 void RpcServer::setRootObject(const sp<IBinder>& binder) {
     std::lock_guard<std::mutex> _l(mLock);
     mRootObjectWeak = mRootObject = binder;
@@ -245,13 +249,37 @@
     RpcConnectionHeader header;
     status_t status = server->mShutdownTrigger->interruptableReadFully(clientFd.get(), &header,
                                                                        sizeof(header));
-    bool idValid = status == OK;
-    if (!idValid) {
+    if (status != OK) {
         ALOGE("Failed to read ID for client connecting to RPC server: %s",
               statusToString(status).c_str());
         // still need to cleanup before we can return
     }
-    bool incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
+
+    bool incoming = false;
+    uint32_t protocolVersion = 0;
+    RpcAddress sessionId = RpcAddress::zero();
+    bool requestingNewSession = false;
+
+    if (status == OK) {
+        incoming = header.options & RPC_CONNECTION_OPTION_INCOMING;
+        protocolVersion = std::min(header.version,
+                                   server->mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION));
+        sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
+        requestingNewSession = sessionId.isZero();
+
+        if (requestingNewSession) {
+            RpcNewSessionResponse response{
+                    .version = protocolVersion,
+            };
+
+            status = server->mShutdownTrigger->interruptableWriteFully(clientFd.get(), &response,
+                                                                       sizeof(response));
+            if (status != OK) {
+                ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
+                // still need to cleanup before we can return
+            }
+        }
+    }
 
     std::thread thisThread;
     sp<RpcSession> session;
@@ -269,19 +297,16 @@
         };
         server->mConnectingThreads.erase(threadId);
 
-        if (!idValid || server->mShutdownTrigger->isTriggered()) {
+        if (status != OK || server->mShutdownTrigger->isTriggered()) {
             return;
         }
 
-        RpcAddress sessionId = RpcAddress::fromRawEmbedded(&header.sessionId);
-
-        if (sessionId.isZero()) {
+        if (requestingNewSession) {
             if (incoming) {
                 ALOGE("Cannot create a new session with an incoming connection, would leak");
                 return;
             }
 
-            sessionId = RpcAddress::zero();
             size_t tries = 0;
             do {
                 // don't block if there is some entropy issue
@@ -295,6 +320,7 @@
 
             session = RpcSession::make();
             session->setMaxThreads(server->mMaxThreads);
+            if (!session->setProtocolVersion(protocolVersion)) return;
             if (!session->setForServer(server,
                                        sp<RpcServer::EventListener>::fromExisting(
                                                static_cast<RpcServer::EventListener*>(
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 1c37651..90ce4d6 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -77,6 +77,25 @@
     return mMaxThreads;
 }
 
+bool RpcSession::setProtocolVersion(uint32_t version) {
+    if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
+        version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+        ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version "
+              "is %u).",
+              version, RPC_WIRE_PROTOCOL_VERSION);
+        return false;
+    }
+
+    std::lock_guard<std::mutex> _l(mMutex);
+    mProtocolVersion = version;
+    return true;
+}
+
+std::optional<uint32_t> RpcSession::getProtocolVersion() {
+    std::lock_guard<std::mutex> _l(mMutex);
+    return mProtocolVersion;
+}
+
 bool RpcSession::setupUnixDomainClient(const char* path) {
     return setupSocketClient(UnixSocketAddress(path));
 }
@@ -424,6 +443,18 @@
 
     if (!setupOneSocketConnection(addr, RpcAddress::zero(), false /*incoming*/)) return false;
 
+    {
+        ExclusiveConnection connection;
+        status_t status = ExclusiveConnection::find(sp<RpcSession>::fromExisting(this),
+                                                    ConnectionUse::CLIENT, &connection);
+        if (status != OK) return false;
+
+        uint32_t version;
+        status = state()->readNewSessionResponse(connection.get(),
+                                                 sp<RpcSession>::fromExisting(this), &version);
+        if (!setProtocolVersion(version)) return false;
+    }
+
     // TODO(b/189955605): we should add additional sessions dynamically
     // instead of all at once.
     // TODO(b/186470974): first risk of blocking
@@ -484,7 +515,10 @@
             return false;
         }
 
-        RpcConnectionHeader header{.options = 0};
+        RpcConnectionHeader header{
+                .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION),
+                .options = 0,
+        };
         memcpy(&header.sessionId, &id.viewRawEmbedded(), sizeof(RpcWireAddress));
 
         if (incoming) header.options |= RPC_CONNECTION_OPTION_INCOMING;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 332c75f..f3406bb 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -315,6 +315,18 @@
     return OK;
 }
 
+status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+                                          const sp<RpcSession>& session, uint32_t* version) {
+    RpcNewSessionResponse response;
+    if (status_t status =
+                rpcRec(connection, session, "new session response", &response, sizeof(response));
+        status != OK) {
+        return status;
+    }
+    *version = response.version;
+    return OK;
+}
+
 status_t RpcState::sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
                                       const sp<RpcSession>& session) {
     RpcOutgoingConnectionInit init{
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 5ac0b97..1446eec 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -60,6 +60,8 @@
     RpcState();
     ~RpcState();
 
+    status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
+                                    const sp<RpcSession>& session, uint32_t* version);
     status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
                                 const sp<RpcSession>& session);
     status_t readConnectionInit(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 2a44c7a..0f8efd2 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -37,9 +37,20 @@
  * either as part of a new session or an existing session
  */
 struct RpcConnectionHeader {
+    uint32_t version; // maximum supported by caller
+    uint8_t reserver0[4];
     RpcWireAddress sessionId;
     uint8_t options;
-    uint8_t reserved[7];
+    uint8_t reserved1[7];
+};
+
+/**
+ * In response to an RpcConnectionHeader which corresponds to a new session,
+ * this returns information to the server.
+ */
+struct RpcNewSessionResponse {
+    uint32_t version; // maximum supported by callee <= maximum supported by caller
+    uint8_t reserved[4];
 };
 
 #define RPC_CONNECTION_INIT_OKAY "cci"
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index a8094dd..40ff78c 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -105,6 +105,13 @@
     size_t getMaxThreads();
 
     /**
+     * By default, the latest protocol version which is supported by a client is
+     * used. However, this can be used in order to prevent newer protocol
+     * versions from ever being used. This is expected to be useful for testing.
+     */
+    void setProtocolVersion(uint32_t version);
+
+    /**
      * The root object can be retrieved by any client, without any
      * authentication. TODO(b/183988761)
      *
@@ -164,6 +171,7 @@
 
     bool mAgreedExperimental = false;
     size_t mMaxThreads = 1;
+    std::optional<uint32_t> mProtocolVersion;
     base::unique_fd mServer; // socket we are accepting sessions on
 
     std::mutex mLock; // for below
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 2101df8..1f7c029 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -37,6 +37,10 @@
 class RpcSocketAddress;
 class RpcState;
 
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 0;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL;
+
 /**
  * This represents a session (group of connections) between a client
  * and a server. Multiple connections are needed for multiple parallel "binder"
@@ -60,6 +64,13 @@
     size_t getMaxThreads();
 
     /**
+     * By default, the minimum of the supported versions of the client and the
+     * server will be used. Usually, this API should only be used for debugging.
+     */
+    [[nodiscard]] bool setProtocolVersion(uint32_t version);
+    std::optional<uint32_t> getProtocolVersion();
+
+    /**
      * This should be called once per thread, matching 'join' in the remote
      * process.
      */
@@ -291,6 +302,7 @@
     std::mutex mMutex; // for all below
 
     size_t mMaxThreads = 0;
+    std::optional<uint32_t> mProtocolVersion;
 
     std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
     size_t mWaitingThreads = 0;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 40ebd9c..d5786bc 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -47,6 +47,9 @@
 
 namespace android {
 
+static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
+              RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+
 TEST(BinderRpcParcel, EntireParcelFormatted) {
     Parcel p;
     p.writeInt32(3);
@@ -67,6 +70,19 @@
     ASSERT_EQ(sinkFd, retrieved.get());
 }
 
+TEST(BinderRpc, CannotUseNextWireVersion) {
+    auto session = RpcSession::make();
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
+    EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
+}
+
+TEST(BinderRpc, CanUseExperimentalWireVersion) {
+    auto session = RpcSession::make();
+    EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
+}
+
 using android::binder::Status;
 
 #define EXPECT_OK(status)                 \