Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a30de882 authored by Steven Moreland's avatar Steven Moreland Committed by Automerger Merge Worker
Browse files

Merge changes I1a7083e8,Ia4ddb102 am: c9f440a8 am: bc5b17fa

parents a005f012 bc5b17fa
Loading
Loading
Loading
Loading
+5 −14
Original line number Diff line number Diff line
@@ -161,17 +161,6 @@ static_assert(sizeof(ChunkDescriptor) % 8 == 0);
constexpr uint32_t kMaxChunkDataSize = 0xfffffff0;
typedef uint64_t transaction_checksum_t;

static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut,
                                             transaction_checksum_t* sum) {
    if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) {
        LOG(ERROR) << "Failed to read Chunk Descriptor from fd " << fd.get();
        return android::UNKNOWN_ERROR;
    }

    *sum ^= *reinterpret_cast<transaction_checksum_t*>(chunkOut);
    return android::NO_ERROR;
}

std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
    RecordedTransaction t;
    ChunkDescriptor chunk;
@@ -192,11 +181,13 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd
            LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
            return std::nullopt;
        }
        transaction_checksum_t checksum = 0;
        if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) {
            LOG(ERROR) << "Failed to read chunk descriptor.";

        if (!android::base::ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) {
            LOG(ERROR) << "Failed to read ChunkDescriptor from fd " << fd.get() << ". "
                       << strerror(errno);
            return std::nullopt;
        }
        transaction_checksum_t checksum = *reinterpret_cast<transaction_checksum_t*>(&chunk);

        fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
        if (fdCurrentPosition == -1) {
+22 −0
Original line number Diff line number Diff line
@@ -110,6 +110,28 @@ cc_test {
    test_suites: ["general-tests"],
}

cc_test {
    name: "binderRecordReplayTest",
    srcs: ["binderRecordReplayTest.cpp"],
    shared_libs: [
        "libbinder",
        "libcutils",
        "libutils",
    ],
    static_libs: [
        "binderRecordReplayTestIface-cpp",
    ],
    test_suites: ["general-tests"],
}

aidl_interface {
    name: "binderRecordReplayTestIface",
    unstable: true,
    srcs: [
        "IBinderRecordReplayTest.aidl",
    ],
}

cc_test {
    name: "binderLibTest",
    defaults: ["binder_test_defaults"],
+20 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

interface IBinderRecordReplayTest {
    void setInt(int input);
    int getInt();
}
+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 <BnBinderRecordReplayTest.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/RecordedTransaction.h>
#include <gtest/gtest.h>

#include <sys/prctl.h>

using namespace android;
using android::binder::Status;
using android::binder::debug::RecordedTransaction;

const String16 kServerName = String16("binderRecordReplay");

class MyRecordReplay : public BnBinderRecordReplayTest {
public:
    Status setInt(int input) {
        mInt = input;
        return Status::ok();
    }
    Status getInt(int* output) {
        *output = mInt;
        return Status::ok();
    }

private:
    int mInt = 0;
};

TEST(BinderClearBuf, RecordReplayRepeatInt) {
    // get the remote service
    sp<IBinder> binder = defaultServiceManager()->getService(kServerName);
    ASSERT_NE(nullptr, binder);
    sp<IBinderRecordReplayTest> iface = interface_cast<IBinderRecordReplayTest>(binder);
    sp<BpBinder> bpBinder = binder->remoteBinder();
    ASSERT_NE(nullptr, bpBinder);

    base::unique_fd fd(
            open("/data/local/tmp/binderRecordReplayTest.rec", O_RDWR | O_CREAT | O_CLOEXEC, 0666));
    ASSERT_TRUE(fd.ok());

    // record a transaction
    bpBinder->startRecordingBinder(fd);
    EXPECT_TRUE(iface->setInt(3).isOk());
    bpBinder->stopRecordingBinder();

    // test transaction does the thing we expect it to do
    int output;
    EXPECT_TRUE(iface->getInt(&output).isOk());
    EXPECT_EQ(output, 3);

    // write over the existing state
    EXPECT_TRUE(iface->setInt(5).isOk());
    EXPECT_TRUE(iface->getInt(&output).isOk());
    EXPECT_EQ(output, 5);

    // replay transaction
    ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET));
    std::optional<RecordedTransaction> transaction = RecordedTransaction::fromFile(fd);
    ASSERT_NE(transaction, std::nullopt);

    // TODO: move logic to replay RecordedTransaction into RecordedTransaction
    Parcel data;
    data.setData(transaction->getDataParcel().data(), transaction->getDataParcel().dataSize());
    status_t status = binder->remoteBinder()->transact(transaction->getCode(), data, nullptr,
                                                       transaction->getFlags());

    // make sure recording does the thing we expect it to do
    EXPECT_EQ(OK, status);
    EXPECT_TRUE(iface->getInt(&output).isOk());
    EXPECT_EQ(output, 3);

    // TODO: we should also make sure we can convert the recording to a fuzzer
    // corpus entry, and we will be able to replay it in the same way
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);

    if (fork() == 0) {
        prctl(PR_SET_PDEATHSIG, SIGHUP);

        auto server = sp<MyRecordReplay>::make();
        android::defaultServiceManager()->addService(kServerName, server.get());

        IPCThreadState::self()->joinThreadPool(true);
        exit(1); // should not reach
    }

    // not racey, but getService sleeps for 1s
    usleep(100000);

    return RUN_ALL_TESTS();
}