/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../file.h" #include "parcelables/SingleDataParcelable.h" using namespace android; using android::generateSeedsFromRecording; using android::RandomBinder; using android::binder::borrowed_fd; using android::binder::Status; using android::binder::unique_fd; using android::binder::debug::RecordedTransaction; using parcelables::SingleDataParcelable; const String16 kServerName = String16("binderRecordReplay"); extern std::string kRandomInterfaceName; #define GENERATE_GETTER_SETTER_PRIMITIVE(name, T) \ Status set##name(T input) { \ m##name = input; \ return Status::ok(); \ } \ \ Status get##name(T* output) { \ *output = m##name; \ return Status::ok(); \ } \ T m##name #define GENERATE_GETTER_SETTER(name, T) \ Status set##name(const T& input) { \ m##name = input; \ return Status::ok(); \ } \ \ Status get##name(T* output) { \ *output = m##name; \ return Status::ok(); \ } \ T m##name class MyRecordReplay : public BnBinderRecordReplayTest { public: GENERATE_GETTER_SETTER_PRIMITIVE(Boolean, bool); GENERATE_GETTER_SETTER_PRIMITIVE(Byte, int8_t); GENERATE_GETTER_SETTER_PRIMITIVE(Int, int); GENERATE_GETTER_SETTER_PRIMITIVE(Char, char16_t); GENERATE_GETTER_SETTER_PRIMITIVE(Long, int64_t); GENERATE_GETTER_SETTER_PRIMITIVE(Float, float); GENERATE_GETTER_SETTER_PRIMITIVE(Double, double); GENERATE_GETTER_SETTER(String, String16); GENERATE_GETTER_SETTER(SingleDataParcelable, SingleDataParcelable); GENERATE_GETTER_SETTER(Binder, sp); GENERATE_GETTER_SETTER(BooleanArray, std::vector); GENERATE_GETTER_SETTER(ByteArray, std::vector); GENERATE_GETTER_SETTER(IntArray, std::vector); GENERATE_GETTER_SETTER(CharArray, std::vector); GENERATE_GETTER_SETTER(LongArray, std::vector); GENERATE_GETTER_SETTER(FloatArray, std::vector); GENERATE_GETTER_SETTER(DoubleArray, std::vector); GENERATE_GETTER_SETTER(StringArray, std::vector<::android::String16>); GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector); Status setFileDescriptor(unique_fd input) { mFd = unique_fd(dup(input)); return Status::ok(); } Status getFileDescriptor(unique_fd* output) { *output = unique_fd(dup(mFd)); return Status::ok(); } unique_fd mFd; }; std::vector retrieveData(borrowed_fd fd) { struct stat fdStat; EXPECT_TRUE(fstat(fd.get(), &fdStat) != -1); std::vector buffer(fdStat.st_size); auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size); EXPECT_TRUE(readResult != 0); return buffer; } void replayFuzzService(const sp& binder, const RecordedTransaction& transaction) { unique_fd seedFd(open("/data/local/tmp/replayFuzzService", O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC, 0666)); ASSERT_TRUE(seedFd.ok()); // generate corpus from this transaction. generateSeedsFromRecording(seedFd, transaction); // Read the data which has been written to seed corpus ASSERT_EQ(0, lseek(seedFd.get(), 0, SEEK_SET)); std::vector seedData = retrieveData(seedFd); EXPECT_TRUE(seedData.size() != 0); // use fuzzService to replay the corpus FuzzedDataProvider provider(seedData.data(), seedData.size()); fuzzService(binder, std::move(provider)); } void replayBinder(const sp& binder, const RecordedTransaction& transaction) { // TODO: move logic to replay RecordedTransaction into RecordedTransaction Parcel data; data.setData(transaction.getDataParcel().data(), transaction.getDataParcel().dataSize()); auto result = binder->transact(transaction.getCode(), data, nullptr, transaction.getFlags()); // make sure recording does the thing we expect it to do EXPECT_EQ(OK, result); } class BinderRecordReplayTest : public ::testing::Test { public: void SetUp() override { // get the remote service #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" auto binder = defaultServiceManager()->getService(kServerName); #pragma clang diagnostic pop ASSERT_NE(nullptr, binder); mInterface = interface_cast(binder); mBpBinder = binder->remoteBinder(); ASSERT_NE(nullptr, mBpBinder); } template void recordReplay(Status (IBinderRecordReplayTest::*set)(T), U recordedValue, Status (IBinderRecordReplayTest::*get)(U*), U changedValue) { using ReplayFunc = decltype(&replayFuzzService); vector replayFunctions = {&replayFuzzService}; if (!std::is_same_v && !std::is_same_v>) { // Parcel retrieved from record replay doesn't have object information. use it for // replaying primitive types only. replayFunctions.push_back(&replayBinder); } for (auto replayFunc : replayFunctions) { unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec", O_RDWR | O_CREAT | O_CLOEXEC, 0666)); ASSERT_TRUE(fd.ok()); // record a transaction mBpBinder->startRecordingBinder(fd); auto status = (*mInterface.*set)(std::move(recordedValue)); EXPECT_TRUE(status.isOk()); mBpBinder->stopRecordingBinder(); // test transaction does the thing we expect it to do U output; status = (*mInterface.*get)(&output); EXPECT_TRUE(status.isOk()); // Expect this equal only if types are primitives if (!std::is_same_v && !std::is_same_v>) { EXPECT_EQ(output, recordedValue); } // write over the existing state status = (*mInterface.*set)(std::move(changedValue)); EXPECT_TRUE(status.isOk()); status = (*mInterface.*get)(&output); EXPECT_TRUE(status.isOk()); if (!std::is_same_v && !std::is_same_v>) { EXPECT_EQ(output, changedValue); } // replay transaction ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET)); std::optional transaction = RecordedTransaction::fromFile(fd); ASSERT_NE(transaction, std::nullopt); const RecordedTransaction& recordedTransaction = *transaction; // call replay function with recorded transaction (*replayFunc)(mBpBinder, recordedTransaction); status = (*mInterface.*get)(&output); EXPECT_TRUE(status.isOk()); // FDs and binders will be replaced with random fd and random binders if constexpr (std::is_same_v) { // verify that replayed fd is /dev/null. This is being replayed from random_fd.cpp // and choosing /dav/null while generating seed in binder2corpus std::string fdPath = "/proc/self/fd/" + std::to_string(output.get()); char path[PATH_MAX]; ASSERT_GT(readlink(fdPath.c_str(), path, sizeof(path)), 0); EXPECT_EQ(strcmp("/dev/null", path), 0); } else if constexpr (std::is_same_v>) { // This is binder is replayed from random_binder.cpp using seed data which writes // this interface. EXPECT_EQ(String16(kRandomInterfaceName.c_str(), kRandomInterfaceName.size()), output->getInterfaceDescriptor()); } else { ASSERT_EQ(recordedValue, output); } } } private: sp mBpBinder; sp mInterface; }; TEST_F(BinderRecordReplayTest, ReplayByte) { recordReplay(&IBinderRecordReplayTest::setByte, int8_t{122}, &IBinderRecordReplayTest::getByte, int8_t{90}); } TEST_F(BinderRecordReplayTest, ReplayBoolean) { recordReplay(&IBinderRecordReplayTest::setBoolean, true, &IBinderRecordReplayTest::getBoolean, false); } TEST_F(BinderRecordReplayTest, ReplayChar) { recordReplay(&IBinderRecordReplayTest::setChar, char16_t{'G'}, &IBinderRecordReplayTest::getChar, char16_t{'K'}); } TEST_F(BinderRecordReplayTest, ReplayInt) { recordReplay(&IBinderRecordReplayTest::setInt, 3, &IBinderRecordReplayTest::getInt, 5); } TEST_F(BinderRecordReplayTest, ReplayFloat) { recordReplay(&IBinderRecordReplayTest::setFloat, 1.1f, &IBinderRecordReplayTest::getFloat, 22.0f); } TEST_F(BinderRecordReplayTest, ReplayLong) { recordReplay(&IBinderRecordReplayTest::setLong, int64_t{1LL << 55}, &IBinderRecordReplayTest::getLong, int64_t{1LL << 12}); } TEST_F(BinderRecordReplayTest, ReplayDouble) { recordReplay(&IBinderRecordReplayTest::setDouble, 0.00, &IBinderRecordReplayTest::getDouble, 1.11); } TEST_F(BinderRecordReplayTest, ReplayString) { const ::android::String16& input1 = String16("This is saved string"); const ::android::String16& input2 = String16("This is changed string"); recordReplay(&IBinderRecordReplayTest::setString, input1, &IBinderRecordReplayTest::getString, input2); } TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelable) { SingleDataParcelable saved, changed; saved.data = 3; changed.data = 5; recordReplay(&IBinderRecordReplayTest::setSingleDataParcelable, saved, &IBinderRecordReplayTest::getSingleDataParcelable, changed); } TEST_F(BinderRecordReplayTest, ReplayByteArray) { std::vector savedArray = {uint8_t{255}, uint8_t{0}, uint8_t{127}}; std::vector changedArray = {uint8_t{2}, uint8_t{7}, uint8_t{117}}; recordReplay(&IBinderRecordReplayTest::setByteArray, savedArray, &IBinderRecordReplayTest::getByteArray, changedArray); } TEST_F(BinderRecordReplayTest, ReplayBooleanArray) { std::vector savedArray = {true, false, true}; std::vector changedArray = {false, true, false}; recordReplay(&IBinderRecordReplayTest::setBooleanArray, savedArray, &IBinderRecordReplayTest::getBooleanArray, changedArray); } TEST_F(BinderRecordReplayTest, ReplayCharArray) { std::vector savedArray = {char16_t{'G'}, char16_t{'L'}, char16_t{'K'}, char16_t{'T'}}; std::vector changedArray = {char16_t{'X'}, char16_t{'Y'}, char16_t{'Z'}}; recordReplay(&IBinderRecordReplayTest::setCharArray, savedArray, &IBinderRecordReplayTest::getCharArray, changedArray); } TEST_F(BinderRecordReplayTest, ReplayIntArray) { std::vector savedArray = {12, 45, 178}; std::vector changedArray = {32, 14, 78, 1899}; recordReplay(&IBinderRecordReplayTest::setIntArray, savedArray, &IBinderRecordReplayTest::getIntArray, changedArray); } TEST_F(BinderRecordReplayTest, ReplayFloatArray) { std::vector savedArray = {12.14f, 45.56f, 123.178f}; std::vector changedArray = {0.00f, 14.0f, 718.1f, 1899.122f, 3268.123f}; recordReplay(&IBinderRecordReplayTest::setFloatArray, savedArray, &IBinderRecordReplayTest::getFloatArray, changedArray); } TEST_F(BinderRecordReplayTest, ReplayLongArray) { std::vector savedArray = {int64_t{1LL << 11}, int64_t{1LL << 55}, int64_t{1LL << 45}}; std::vector changedArray = {int64_t{1LL << 1}, int64_t{1LL << 21}, int64_t{1LL << 33}, int64_t{1LL << 62}}; recordReplay(&IBinderRecordReplayTest::setLongArray, savedArray, &IBinderRecordReplayTest::getLongArray, changedArray); } TEST_F(BinderRecordReplayTest, ReplayDoubleArray) { std::vector savedArray = {12.1412313, 45.561232, 123.1781111}; std::vector changedArray = {0.00111, 14.32130, 712312318.19, 1899212.122, 322168.122123}; recordReplay(&IBinderRecordReplayTest::setDoubleArray, savedArray, &IBinderRecordReplayTest::getDoubleArray, changedArray); } TEST_F(BinderRecordReplayTest, ReplayStringArray) { std::vector savedArray = {String16("This is saved value"), String16(), String16("\0\0", 2), String16("\xF3\x01\xAC\xAD\x21\xAF")}; std::vector changedArray = {String16("This is changed value"), String16("\xF0\x90\x90\xB7\xE2\x82\xAC")}; recordReplay(&IBinderRecordReplayTest::setStringArray, savedArray, &IBinderRecordReplayTest::getStringArray, changedArray); } TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelableArray) { SingleDataParcelable s1, s2, s3, s4, s5; s1.data = 5213; s2.data = 1512; s3.data = 4233; s4.data = 123124; s5.data = 0; std::vector saved = {s1, s2, s3}; std::vector changed = {s4, s5}; recordReplay(&IBinderRecordReplayTest::setSingleDataParcelableArray, saved, &IBinderRecordReplayTest::getSingleDataParcelableArray, changed); } TEST_F(BinderRecordReplayTest, ReplayBinder) { vector data = {0x8A, 0x19, 0x0D, 0x44, 0x37, 0x0D, 0x38, 0x5E, 0x9B, 0xAA, 0xF3, 0xDA}; sp saved = new RandomBinder(String16("random_interface"), std::move(data)); sp changed = IInterface::asBinder(defaultServiceManager()); recordReplay(&IBinderRecordReplayTest::setBinder, saved, &IBinderRecordReplayTest::getBinder, changed); } TEST_F(BinderRecordReplayTest, ReplayFd) { // Write something to both fds we are setting unique_fd saved(open("/data/local/tmp/test_fd", O_RDWR | O_CREAT | O_CLOEXEC, 0666)); std::string contentSaved = "This will be never read again for recorded fd!"; CHECK(android::base::WriteFully(saved, contentSaved.data(), contentSaved.size())) << saved.get(); unique_fd changed(open("/data/local/tmp/test_des", O_RDWR | O_CREAT | O_CLOEXEC, 0666)); std::string contentChanged = "This will be never read again from changed fd!"; CHECK(android::base::WriteFully(changed, contentChanged.data(), contentChanged.size())) << changed.get(); // When fds are replayed, it will be replaced by /dev/null..reading from it should yield // null data recordReplay(&IBinderRecordReplayTest::setFileDescriptor, unique_fd(dup(saved)), &IBinderRecordReplayTest::getFileDescriptor, unique_fd(dup(changed))); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); auto server = sp::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(); }