Loading libs/binder/BinderRecordReplay.cpp +200 −80 Original line number Diff line number Diff line Loading @@ -16,10 +16,12 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <binder/BinderRecordReplay.h> #include <algorithm> using android::Parcel; using android::base::borrowed_fd; using android::base::unique_fd; using android::binder::debug::RecordedTransaction; Loading @@ -30,22 +32,78 @@ static_assert(PADDING8(1) == 7); static_assert(PADDING8(7) == 1); static_assert(PADDING8(8) == 0); // Transactions are sequentially recorded to the file descriptor in the following format: // Transactions are sequentially recorded to a file descriptor. // // RecordedTransaction.TransactionHeader (32 bytes) // Sent Parcel data (getDataSize() bytes) // padding (enough bytes to align the reply Parcel data to 8 bytes) // Reply Parcel data (getReplySize() bytes) // padding (enough bytes to align the next header to 8 bytes) // [repeats with next transaction] // An individual RecordedTransaction is written with the following format: // // Warning: This format is non-stable // WARNING: Though the following format is designed to be stable and // extensible, it is under active development and should be considered // unstable until this warning is removed. // // A RecordedTransaction is written to a file as a sequence of Chunks. // // A Chunk consists of a ChunkDescriptor, Data, and Padding. // // Data and Padding may each be zero-length as specified by the // ChunkDescriptor. // // The ChunkDescriptor identifies the type of data in the chunk, the size of // the data in bytes, and the number of zero-bytes padding to land on an // 8-byte boundary by the end of the Chunk. // // ┌───────────────────────────┐ // │Chunk │ // │┌─────────────────────────┐│ // ││ChunkDescriptor ││ // ││┌───────────┬───────────┐││ // │││chunkType │paddingSize│││ // │││uint32_t │uint32_t ├┼┼───┐ // ││├───────────┴───────────┤││ │ // │││dataSize │││ │ // │││uint64_t ├┼┼─┐ │ // ││└───────────────────────┘││ │ │ // │└─────────────────────────┘│ │ │ // │┌─────────────────────────┐│ │ │ // ││Data ││ │ │ // ││bytes * dataSize │◀─┘ │ // │└─────────────────────────┘│ │ // │┌─────────────────────────┐│ │ // ││Padding ││ │ // ││bytes * paddingSize │◀───┘ // │└─────────────────────────┘│ // └───────────────────────────┘ // // A RecordedTransaction is written as a Header Chunk with fields about the // transaction, a Data Parcel chunk, a Reply Parcel Chunk, and an End Chunk. // ┌──────────────────────┐ // │ Header Chunk │ // ├──────────────────────┤ // │ Sent Parcel Chunk │ // ├──────────────────────┤ // │ Reply Parcel Chunk │ // ├──────────────────────┤ // ║ End Chunk ║ // ╚══════════════════════╝ // // On reading a RecordedTransaction, an unrecognized chunk is skipped using // the size information in the ChunkDescriptor. Chunks are read and either // assimilated or skipped until an End Chunk is encountered. This has three // notable implications: // // 1. Older and newer implementations should be able to read one another's // Transactions, though there will be loss of information. // 2. With the exception of the End Chunk, Chunks can appear in any // order and even repeat, though this is not recommended. // 3. If any Chunk is repeated, old values will be overwritten by versions // encountered later in the file. // // No effort is made to ensure the expected chunks are present. A single // End Chunk may therefore produce a empty, meaningless RecordedTransaction. RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept { mHeader = {t.getCode(), t.getFlags(), t.getDataSize(), t.getReplySize(), t.getReturnedStatus(), t.getVersion()}; mSent.setData(t.getDataParcel().data(), t.getDataSize()); mReply.setData(t.getReplyParcel().data(), t.getReplySize()); mHeader = {t.getCode(), t.getFlags(), t.getReturnedStatus(), t.getVersion()}; mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize()); mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize()); } std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t code, uint32_t flags, Loading @@ -53,19 +111,15 @@ std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t cod const Parcel& replyParcel, status_t err) { RecordedTransaction t; t.mHeader = {code, flags, static_cast<uint64_t>(dataParcel.dataSize()), static_cast<uint64_t>(replyParcel.dataSize()), static_cast<int32_t>(err), t.mHeader = {code, flags, static_cast<int32_t>(err), dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)}; if (t.mSent.setData(dataParcel.data(), t.getDataSize()) != android::NO_ERROR) { if (t.mSent.setData(dataParcel.data(), dataParcel.dataSize()) != android::NO_ERROR) { LOG(INFO) << "Failed to set sent parcel data."; return std::nullopt; } if (t.mReply.setData(replyParcel.data(), t.getReplySize()) != android::NO_ERROR) { if (t.mReply.setData(replyParcel.data(), replyParcel.dataSize()) != android::NO_ERROR) { LOG(INFO) << "Failed to set reply parcel data."; return std::nullopt; } Loading @@ -73,80 +127,154 @@ std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t cod return std::optional<RecordedTransaction>(std::move(t)); } enum { HEADER_CHUNK = 0x00000001, DATA_PARCEL_CHUNK = 0x00000002, REPLY_PARCEL_CHUNK = 0x00000003, INVALID_CHUNK = 0x00fffffe, END_CHUNK = 0x00ffffff, }; struct ChunkDescriptor { uint32_t chunkType = 0; uint32_t padding = 0; uint32_t dataSize = 0; uint32_t reserved = 0; // Future checksum }; static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut) { if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) { LOG(INFO) << "Failed to read Chunk Descriptor from fd " << fd.get(); return android::UNKNOWN_ERROR; } if (PADDING8(chunkOut->dataSize) != chunkOut->padding) { chunkOut->chunkType = INVALID_CHUNK; LOG(INFO) << "Chunk data and padding sizes do not align." << fd.get(); return android::BAD_VALUE; } return android::NO_ERROR; } std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) { RecordedTransaction t; if (!android::base::ReadFully(fd, &t.mHeader, sizeof(mHeader))) { LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get(); ChunkDescriptor chunk; do { if (NO_ERROR != readChunkDescriptor(fd, &chunk)) { LOG(INFO) << "Failed to read chunk descriptor."; return std::nullopt; } if (t.getVersion() != 0) { LOG(INFO) << "File corrupted: transaction version is not 0."; switch (chunk.chunkType) { case HEADER_CHUNK: { if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) { LOG(INFO) << "Header Chunk indicated size " << chunk.dataSize << "; Expected " << sizeof(TransactionHeader) << "."; return std::nullopt; } if (!android::base::ReadFully(fd, &t.mHeader, chunk.dataSize)) { LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get(); return std::nullopt; } break; } case DATA_PARCEL_CHUNK: { std::vector<uint8_t> bytes; bytes.resize(t.getDataSize()); if (!android::base::ReadFully(fd, bytes.data(), t.getDataSize())) { bytes.resize(chunk.dataSize); if (!android::base::ReadFully(fd, bytes.data(), chunk.dataSize)) { LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get(); return std::nullopt; } if (t.mSent.setData(bytes.data(), t.getDataSize()) != android::NO_ERROR) { if (t.mSent.setData(bytes.data(), chunk.dataSize) != android::NO_ERROR) { LOG(INFO) << "Failed to set sent parcel data."; return std::nullopt; } uint8_t padding[7]; if (!android::base::ReadFully(fd, padding, PADDING8(t.getDataSize()))) { LOG(INFO) << "Failed to read sent parcel padding from fd " << fd.get(); return std::nullopt; } if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) { LOG(INFO) << "File corrupted: padding isn't 0."; return std::nullopt; lseek(fd.get(), chunk.padding, SEEK_CUR); break; } bytes.resize(t.getReplySize()); if (!android::base::ReadFully(fd, bytes.data(), t.getReplySize())) { case REPLY_PARCEL_CHUNK: { std::vector<uint8_t> bytes; bytes.resize(chunk.dataSize); if (!android::base::ReadFully(fd, bytes.data(), chunk.dataSize)) { LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get(); return std::nullopt; } if (t.mReply.setData(bytes.data(), t.getReplySize()) != android::NO_ERROR) { if (t.mReply.setData(bytes.data(), chunk.dataSize) != android::NO_ERROR) { LOG(INFO) << "Failed to set reply parcel data."; return std::nullopt; } if (!android::base::ReadFully(fd, padding, PADDING8(t.getReplySize()))) { LOG(INFO) << "Failed to read parcel padding from fd " << fd.get(); return std::nullopt; lseek(fd.get(), chunk.padding, SEEK_CUR); break; } if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) { LOG(INFO) << "File corrupted: padding isn't 0."; case INVALID_CHUNK: LOG(INFO) << "Invalid chunk."; return std::nullopt; case END_CHUNK: LOG(INFO) << "Read end chunk"; FALLTHROUGH_INTENDED; default: // Unrecognized or skippable chunk lseek(fd.get(), chunk.dataSize + chunk.padding, SEEK_CUR); break; } } while (chunk.chunkType != END_CHUNK); return std::optional<RecordedTransaction>(std::move(t)); } android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { if (!android::base::WriteFully(fd, &mHeader, sizeof(mHeader))) { LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get(); android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType, size_t byteCount, const uint8_t* data) const { // Write Chunk Descriptor // - Chunk Type if (!android::base::WriteFully(fd, &chunkType, sizeof(uint32_t))) { LOG(INFO) << "Failed to write chunk header to fd " << fd.get(); return UNKNOWN_ERROR; } // - Chunk Data Padding Size uint32_t additionalPaddingCount = static_cast<uint32_t>(PADDING8(byteCount)); if (!android::base::WriteFully(fd, &additionalPaddingCount, sizeof(uint32_t))) { LOG(INFO) << "Failed to write chunk padding size to fd " << fd.get(); return UNKNOWN_ERROR; } if (!android::base::WriteFully(fd, mSent.data(), getDataSize())) { LOG(INFO) << "Failed to write sent parcel data to fd " << fd.get(); // - Chunk Data Size uint64_t byteCountToWrite = (uint64_t)byteCount; if (!android::base::WriteFully(fd, &byteCountToWrite, sizeof(uint64_t))) { LOG(INFO) << "Failed to write chunk size to fd " << fd.get(); return UNKNOWN_ERROR; } if (byteCount == 0) { return NO_ERROR; } if (!android::base::WriteFully(fd, data, byteCount)) { LOG(INFO) << "Failed to write chunk data to fd " << fd.get(); return UNKNOWN_ERROR; } const uint8_t zeros[7] = {0}; if (!android::base::WriteFully(fd, zeros, PADDING8(getDataSize()))) { LOG(INFO) << "Failed to write sent parcel padding to fd " << fd.get(); if (!android::base::WriteFully(fd, zeros, additionalPaddingCount)) { LOG(INFO) << "Failed to write chunk padding to fd " << fd.get(); return UNKNOWN_ERROR; } if (!android::base::WriteFully(fd, mReply.data(), getReplySize())) { LOG(INFO) << "Failed to write reply parcel data to fd " << fd.get(); return NO_ERROR; } android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { if (NO_ERROR != writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader), reinterpret_cast<const uint8_t*>(&mHeader))) { LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get(); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataSize(), mSent.data())) { LOG(INFO) << "Failed to write sent Parcel to fd " << fd.get(); return UNKNOWN_ERROR; } if (!android::base::WriteFully(fd, zeros, PADDING8(getReplySize()))) { LOG(INFO) << "Failed to write reply parcel padding to fd " << fd.get(); if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataSize(), mReply.data())) { LOG(INFO) << "Failed to write reply Parcel to fd " << fd.get(); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) { LOG(INFO) << "Failed to write end chunk to fd " << fd.get(); return UNKNOWN_ERROR; } return NO_ERROR; Loading @@ -160,14 +288,6 @@ uint32_t RecordedTransaction::getFlags() const { return mHeader.flags; } uint64_t RecordedTransaction::getDataSize() const { return mHeader.dataSize; } uint64_t RecordedTransaction::getReplySize() const { return mHeader.replySize; } int32_t RecordedTransaction::getReturnedStatus() const { return mHeader.statusReturned; } Loading libs/binder/include/binder/BinderRecordReplay.h +4 −9 Original line number Diff line number Diff line Loading @@ -42,8 +42,6 @@ public: uint32_t getCode() const; uint32_t getFlags() const; uint64_t getDataSize() const; uint64_t getReplySize() const; int32_t getReturnedStatus() const; uint32_t getVersion() const; const Parcel& getDataParcel() const; Loading @@ -52,27 +50,24 @@ public: private: RecordedTransaction() = default; android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType, size_t byteCount, const uint8_t* data) const; #pragma clang diagnostic push #pragma clang diagnostic error "-Wpadded" struct TransactionHeader { uint32_t code = 0; uint32_t flags = 0; uint64_t dataSize = 0; uint64_t replySize = 0; int32_t statusReturned = 0; uint32_t version = 0; // !0 iff Rpc }; #pragma clang diagnostic pop static_assert(sizeof(TransactionHeader) == 32); static_assert(sizeof(TransactionHeader) == 16); static_assert(sizeof(TransactionHeader) % 8 == 0); TransactionHeader mHeader; Parcel mSent; Parcel mReply; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-private-field" uint8_t mReserved[40]; #pragma clang diagnostic pop }; } // namespace binder::debug Loading libs/binder/tests/binderRecordedTransactionTest.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -42,8 +42,8 @@ TEST(BinderRecordedTransaction, RoundTripEncoding) { EXPECT_EQ(retrievedTransaction->getCode(), 1); EXPECT_EQ(retrievedTransaction->getFlags(), 42); EXPECT_EQ(retrievedTransaction->getDataSize(), 12); EXPECT_EQ(retrievedTransaction->getReplySize(), 4); EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), 12); EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4); EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0); EXPECT_EQ(retrievedTransaction->getVersion(), 0); Loading Loading
libs/binder/BinderRecordReplay.cpp +200 −80 Original line number Diff line number Diff line Loading @@ -16,10 +16,12 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <binder/BinderRecordReplay.h> #include <algorithm> using android::Parcel; using android::base::borrowed_fd; using android::base::unique_fd; using android::binder::debug::RecordedTransaction; Loading @@ -30,22 +32,78 @@ static_assert(PADDING8(1) == 7); static_assert(PADDING8(7) == 1); static_assert(PADDING8(8) == 0); // Transactions are sequentially recorded to the file descriptor in the following format: // Transactions are sequentially recorded to a file descriptor. // // RecordedTransaction.TransactionHeader (32 bytes) // Sent Parcel data (getDataSize() bytes) // padding (enough bytes to align the reply Parcel data to 8 bytes) // Reply Parcel data (getReplySize() bytes) // padding (enough bytes to align the next header to 8 bytes) // [repeats with next transaction] // An individual RecordedTransaction is written with the following format: // // Warning: This format is non-stable // WARNING: Though the following format is designed to be stable and // extensible, it is under active development and should be considered // unstable until this warning is removed. // // A RecordedTransaction is written to a file as a sequence of Chunks. // // A Chunk consists of a ChunkDescriptor, Data, and Padding. // // Data and Padding may each be zero-length as specified by the // ChunkDescriptor. // // The ChunkDescriptor identifies the type of data in the chunk, the size of // the data in bytes, and the number of zero-bytes padding to land on an // 8-byte boundary by the end of the Chunk. // // ┌───────────────────────────┐ // │Chunk │ // │┌─────────────────────────┐│ // ││ChunkDescriptor ││ // ││┌───────────┬───────────┐││ // │││chunkType │paddingSize│││ // │││uint32_t │uint32_t ├┼┼───┐ // ││├───────────┴───────────┤││ │ // │││dataSize │││ │ // │││uint64_t ├┼┼─┐ │ // ││└───────────────────────┘││ │ │ // │└─────────────────────────┘│ │ │ // │┌─────────────────────────┐│ │ │ // ││Data ││ │ │ // ││bytes * dataSize │◀─┘ │ // │└─────────────────────────┘│ │ // │┌─────────────────────────┐│ │ // ││Padding ││ │ // ││bytes * paddingSize │◀───┘ // │└─────────────────────────┘│ // └───────────────────────────┘ // // A RecordedTransaction is written as a Header Chunk with fields about the // transaction, a Data Parcel chunk, a Reply Parcel Chunk, and an End Chunk. // ┌──────────────────────┐ // │ Header Chunk │ // ├──────────────────────┤ // │ Sent Parcel Chunk │ // ├──────────────────────┤ // │ Reply Parcel Chunk │ // ├──────────────────────┤ // ║ End Chunk ║ // ╚══════════════════════╝ // // On reading a RecordedTransaction, an unrecognized chunk is skipped using // the size information in the ChunkDescriptor. Chunks are read and either // assimilated or skipped until an End Chunk is encountered. This has three // notable implications: // // 1. Older and newer implementations should be able to read one another's // Transactions, though there will be loss of information. // 2. With the exception of the End Chunk, Chunks can appear in any // order and even repeat, though this is not recommended. // 3. If any Chunk is repeated, old values will be overwritten by versions // encountered later in the file. // // No effort is made to ensure the expected chunks are present. A single // End Chunk may therefore produce a empty, meaningless RecordedTransaction. RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept { mHeader = {t.getCode(), t.getFlags(), t.getDataSize(), t.getReplySize(), t.getReturnedStatus(), t.getVersion()}; mSent.setData(t.getDataParcel().data(), t.getDataSize()); mReply.setData(t.getReplyParcel().data(), t.getReplySize()); mHeader = {t.getCode(), t.getFlags(), t.getReturnedStatus(), t.getVersion()}; mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize()); mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize()); } std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t code, uint32_t flags, Loading @@ -53,19 +111,15 @@ std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t cod const Parcel& replyParcel, status_t err) { RecordedTransaction t; t.mHeader = {code, flags, static_cast<uint64_t>(dataParcel.dataSize()), static_cast<uint64_t>(replyParcel.dataSize()), static_cast<int32_t>(err), t.mHeader = {code, flags, static_cast<int32_t>(err), dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)}; if (t.mSent.setData(dataParcel.data(), t.getDataSize()) != android::NO_ERROR) { if (t.mSent.setData(dataParcel.data(), dataParcel.dataSize()) != android::NO_ERROR) { LOG(INFO) << "Failed to set sent parcel data."; return std::nullopt; } if (t.mReply.setData(replyParcel.data(), t.getReplySize()) != android::NO_ERROR) { if (t.mReply.setData(replyParcel.data(), replyParcel.dataSize()) != android::NO_ERROR) { LOG(INFO) << "Failed to set reply parcel data."; return std::nullopt; } Loading @@ -73,80 +127,154 @@ std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t cod return std::optional<RecordedTransaction>(std::move(t)); } enum { HEADER_CHUNK = 0x00000001, DATA_PARCEL_CHUNK = 0x00000002, REPLY_PARCEL_CHUNK = 0x00000003, INVALID_CHUNK = 0x00fffffe, END_CHUNK = 0x00ffffff, }; struct ChunkDescriptor { uint32_t chunkType = 0; uint32_t padding = 0; uint32_t dataSize = 0; uint32_t reserved = 0; // Future checksum }; static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut) { if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) { LOG(INFO) << "Failed to read Chunk Descriptor from fd " << fd.get(); return android::UNKNOWN_ERROR; } if (PADDING8(chunkOut->dataSize) != chunkOut->padding) { chunkOut->chunkType = INVALID_CHUNK; LOG(INFO) << "Chunk data and padding sizes do not align." << fd.get(); return android::BAD_VALUE; } return android::NO_ERROR; } std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) { RecordedTransaction t; if (!android::base::ReadFully(fd, &t.mHeader, sizeof(mHeader))) { LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get(); ChunkDescriptor chunk; do { if (NO_ERROR != readChunkDescriptor(fd, &chunk)) { LOG(INFO) << "Failed to read chunk descriptor."; return std::nullopt; } if (t.getVersion() != 0) { LOG(INFO) << "File corrupted: transaction version is not 0."; switch (chunk.chunkType) { case HEADER_CHUNK: { if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) { LOG(INFO) << "Header Chunk indicated size " << chunk.dataSize << "; Expected " << sizeof(TransactionHeader) << "."; return std::nullopt; } if (!android::base::ReadFully(fd, &t.mHeader, chunk.dataSize)) { LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get(); return std::nullopt; } break; } case DATA_PARCEL_CHUNK: { std::vector<uint8_t> bytes; bytes.resize(t.getDataSize()); if (!android::base::ReadFully(fd, bytes.data(), t.getDataSize())) { bytes.resize(chunk.dataSize); if (!android::base::ReadFully(fd, bytes.data(), chunk.dataSize)) { LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get(); return std::nullopt; } if (t.mSent.setData(bytes.data(), t.getDataSize()) != android::NO_ERROR) { if (t.mSent.setData(bytes.data(), chunk.dataSize) != android::NO_ERROR) { LOG(INFO) << "Failed to set sent parcel data."; return std::nullopt; } uint8_t padding[7]; if (!android::base::ReadFully(fd, padding, PADDING8(t.getDataSize()))) { LOG(INFO) << "Failed to read sent parcel padding from fd " << fd.get(); return std::nullopt; } if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) { LOG(INFO) << "File corrupted: padding isn't 0."; return std::nullopt; lseek(fd.get(), chunk.padding, SEEK_CUR); break; } bytes.resize(t.getReplySize()); if (!android::base::ReadFully(fd, bytes.data(), t.getReplySize())) { case REPLY_PARCEL_CHUNK: { std::vector<uint8_t> bytes; bytes.resize(chunk.dataSize); if (!android::base::ReadFully(fd, bytes.data(), chunk.dataSize)) { LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get(); return std::nullopt; } if (t.mReply.setData(bytes.data(), t.getReplySize()) != android::NO_ERROR) { if (t.mReply.setData(bytes.data(), chunk.dataSize) != android::NO_ERROR) { LOG(INFO) << "Failed to set reply parcel data."; return std::nullopt; } if (!android::base::ReadFully(fd, padding, PADDING8(t.getReplySize()))) { LOG(INFO) << "Failed to read parcel padding from fd " << fd.get(); return std::nullopt; lseek(fd.get(), chunk.padding, SEEK_CUR); break; } if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) { LOG(INFO) << "File corrupted: padding isn't 0."; case INVALID_CHUNK: LOG(INFO) << "Invalid chunk."; return std::nullopt; case END_CHUNK: LOG(INFO) << "Read end chunk"; FALLTHROUGH_INTENDED; default: // Unrecognized or skippable chunk lseek(fd.get(), chunk.dataSize + chunk.padding, SEEK_CUR); break; } } while (chunk.chunkType != END_CHUNK); return std::optional<RecordedTransaction>(std::move(t)); } android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { if (!android::base::WriteFully(fd, &mHeader, sizeof(mHeader))) { LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get(); android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType, size_t byteCount, const uint8_t* data) const { // Write Chunk Descriptor // - Chunk Type if (!android::base::WriteFully(fd, &chunkType, sizeof(uint32_t))) { LOG(INFO) << "Failed to write chunk header to fd " << fd.get(); return UNKNOWN_ERROR; } // - Chunk Data Padding Size uint32_t additionalPaddingCount = static_cast<uint32_t>(PADDING8(byteCount)); if (!android::base::WriteFully(fd, &additionalPaddingCount, sizeof(uint32_t))) { LOG(INFO) << "Failed to write chunk padding size to fd " << fd.get(); return UNKNOWN_ERROR; } if (!android::base::WriteFully(fd, mSent.data(), getDataSize())) { LOG(INFO) << "Failed to write sent parcel data to fd " << fd.get(); // - Chunk Data Size uint64_t byteCountToWrite = (uint64_t)byteCount; if (!android::base::WriteFully(fd, &byteCountToWrite, sizeof(uint64_t))) { LOG(INFO) << "Failed to write chunk size to fd " << fd.get(); return UNKNOWN_ERROR; } if (byteCount == 0) { return NO_ERROR; } if (!android::base::WriteFully(fd, data, byteCount)) { LOG(INFO) << "Failed to write chunk data to fd " << fd.get(); return UNKNOWN_ERROR; } const uint8_t zeros[7] = {0}; if (!android::base::WriteFully(fd, zeros, PADDING8(getDataSize()))) { LOG(INFO) << "Failed to write sent parcel padding to fd " << fd.get(); if (!android::base::WriteFully(fd, zeros, additionalPaddingCount)) { LOG(INFO) << "Failed to write chunk padding to fd " << fd.get(); return UNKNOWN_ERROR; } if (!android::base::WriteFully(fd, mReply.data(), getReplySize())) { LOG(INFO) << "Failed to write reply parcel data to fd " << fd.get(); return NO_ERROR; } android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { if (NO_ERROR != writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader), reinterpret_cast<const uint8_t*>(&mHeader))) { LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get(); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataSize(), mSent.data())) { LOG(INFO) << "Failed to write sent Parcel to fd " << fd.get(); return UNKNOWN_ERROR; } if (!android::base::WriteFully(fd, zeros, PADDING8(getReplySize()))) { LOG(INFO) << "Failed to write reply parcel padding to fd " << fd.get(); if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataSize(), mReply.data())) { LOG(INFO) << "Failed to write reply Parcel to fd " << fd.get(); return UNKNOWN_ERROR; } if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) { LOG(INFO) << "Failed to write end chunk to fd " << fd.get(); return UNKNOWN_ERROR; } return NO_ERROR; Loading @@ -160,14 +288,6 @@ uint32_t RecordedTransaction::getFlags() const { return mHeader.flags; } uint64_t RecordedTransaction::getDataSize() const { return mHeader.dataSize; } uint64_t RecordedTransaction::getReplySize() const { return mHeader.replySize; } int32_t RecordedTransaction::getReturnedStatus() const { return mHeader.statusReturned; } Loading
libs/binder/include/binder/BinderRecordReplay.h +4 −9 Original line number Diff line number Diff line Loading @@ -42,8 +42,6 @@ public: uint32_t getCode() const; uint32_t getFlags() const; uint64_t getDataSize() const; uint64_t getReplySize() const; int32_t getReturnedStatus() const; uint32_t getVersion() const; const Parcel& getDataParcel() const; Loading @@ -52,27 +50,24 @@ public: private: RecordedTransaction() = default; android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType, size_t byteCount, const uint8_t* data) const; #pragma clang diagnostic push #pragma clang diagnostic error "-Wpadded" struct TransactionHeader { uint32_t code = 0; uint32_t flags = 0; uint64_t dataSize = 0; uint64_t replySize = 0; int32_t statusReturned = 0; uint32_t version = 0; // !0 iff Rpc }; #pragma clang diagnostic pop static_assert(sizeof(TransactionHeader) == 32); static_assert(sizeof(TransactionHeader) == 16); static_assert(sizeof(TransactionHeader) % 8 == 0); TransactionHeader mHeader; Parcel mSent; Parcel mReply; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-private-field" uint8_t mReserved[40]; #pragma clang diagnostic pop }; } // namespace binder::debug Loading
libs/binder/tests/binderRecordedTransactionTest.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -42,8 +42,8 @@ TEST(BinderRecordedTransaction, RoundTripEncoding) { EXPECT_EQ(retrievedTransaction->getCode(), 1); EXPECT_EQ(retrievedTransaction->getFlags(), 42); EXPECT_EQ(retrievedTransaction->getDataSize(), 12); EXPECT_EQ(retrievedTransaction->getReplySize(), 4); EXPECT_EQ(retrievedTransaction->getDataParcel().dataSize(), 12); EXPECT_EQ(retrievedTransaction->getReplyParcel().dataSize(), 4); EXPECT_EQ(retrievedTransaction->getReturnedStatus(), 0); EXPECT_EQ(retrievedTransaction->getVersion(), 0); Loading