• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <android-base/file.h>
18 #include <android-base/logging.h>
19 #include <android-base/scopeguard.h>
20 #include <android-base/unique_fd.h>
21 #include <binder/RecordedTransaction.h>
22 #include <sys/mman.h>
23 #include <algorithm>
24 
25 using android::Parcel;
26 using android::base::borrowed_fd;
27 using android::base::unique_fd;
28 using android::binder::debug::RecordedTransaction;
29 
30 #define PADDING8(s) ((8 - (s) % 8) % 8)
31 
32 static_assert(PADDING8(0) == 0);
33 static_assert(PADDING8(1) == 7);
34 static_assert(PADDING8(7) == 1);
35 static_assert(PADDING8(8) == 0);
36 
37 // Transactions are sequentially recorded to a file descriptor.
38 //
39 // An individual RecordedTransaction is written with the following format:
40 //
41 // WARNING: Though the following format is designed to be stable and
42 // extensible, it is under active development and should be considered
43 // unstable until this warning is removed.
44 //
45 // A RecordedTransaction is written to a file as a sequence of Chunks.
46 //
47 // A Chunk consists of a ChunkDescriptor, Data, Padding, and a Checksum.
48 //
49 // The ChunkDescriptor identifies the type of Data in the chunk, and the size
50 // of the Data.
51 //
52 // The Data may be any uint32 number of bytes in length in [0-0xfffffff0].
53 //
54 // Padding is between [0-7] zero-bytes after the Data such that the Chunk ends
55 // on an 8-byte boundary. The ChunkDescriptor's dataSize does not include the
56 // size of Padding.
57 //
58 // The checksum is a 64-bit wide XOR of all previous data from the start of the
59 // ChunkDescriptor to the end of Padding.
60 //
61 // ┌───────────────────────────┐
62 // │Chunk                      │
63 // │┌────────────────────────┐ │
64 // ││ChunkDescriptor         │ │
65 // ││┌───────────┬──────────┐│ │
66 // │││chunkType  │dataSize  ├┼─┼─┐
67 // │││uint32_t   │uint32_t  ││ │ │
68 // ││└───────────┴──────────┘│ │ │
69 // │└────────────────────────┘ │ │
70 // │┌─────────────────────────┐│ │
71 // ││Data                     ││ │
72 // ││bytes * dataSize         │◀─┘
73 // ││   ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤│
74 // ││           Padding       ││
75 // │└───┴─────────────────────┘│
76 // │┌─────────────────────────┐│
77 // ││checksum                 ││
78 // ││uint64_t                 ││
79 // │└─────────────────────────┘│
80 // └───────────────────────────┘
81 //
82 // A RecordedTransaction is written as a Header Chunk with fields about the
83 // transaction, a Data Parcel chunk, a Reply Parcel Chunk, and an End Chunk.
84 // ┌──────────────────────┐
85 // │     Header Chunk     │
86 // ├──────────────────────┤
87 // │  Sent Parcel Chunk   │
88 // ├──────────────────────┤
89 // │  Reply Parcel Chunk  │
90 // ├──────────────────────┤
91 // ║      End Chunk       ║
92 // ╚══════════════════════╝
93 //
94 // On reading a RecordedTransaction, an unrecognized chunk is checksummed
95 // then skipped according to size information in the ChunkDescriptor. Chunks
96 // are read and either assimilated or skipped until an End Chunk is
97 // encountered. This has three notable implications:
98 //
99 // 1. Older and newer implementations should be able to read one another's
100 //    Transactions, though there will be loss of information.
101 // 2. With the exception of the End Chunk, Chunks can appear in any order
102 //    and even repeat, though this is not recommended.
103 // 3. If any Chunk is repeated, old values will be overwritten by versions
104 //    encountered later in the file.
105 //
106 // No effort is made to ensure the expected chunks are present. A single
107 // End Chunk may therefore produce an empty, meaningless RecordedTransaction.
108 
RecordedTransaction(RecordedTransaction && t)109 RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept {
110     mData = t.mData;
111     mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize());
112     mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize());
113 }
114 
fromDetails(const String16 & interfaceName,uint32_t code,uint32_t flags,timespec timestamp,const Parcel & dataParcel,const Parcel & replyParcel,status_t err)115 std::optional<RecordedTransaction> RecordedTransaction::fromDetails(
116         const String16& interfaceName, uint32_t code, uint32_t flags, timespec timestamp,
117         const Parcel& dataParcel, const Parcel& replyParcel, status_t err) {
118     RecordedTransaction t;
119     t.mData.mHeader = {code,
120                        flags,
121                        static_cast<int32_t>(err),
122                        dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0),
123                        static_cast<int64_t>(timestamp.tv_sec),
124                        static_cast<int32_t>(timestamp.tv_nsec),
125                        0};
126 
127     t.mData.mInterfaceName = std::string(String8(interfaceName).string());
128     if (interfaceName.size() != t.mData.mInterfaceName.size()) {
129         LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
130                       "utf-8.";
131         return std::nullopt;
132     }
133 
134     if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) {
135         LOG(ERROR) << "Failed to set sent parcel data.";
136         return std::nullopt;
137     }
138 
139     if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) {
140         LOG(ERROR) << "Failed to set reply parcel data.";
141         return std::nullopt;
142     }
143 
144     return std::optional<RecordedTransaction>(std::move(t));
145 }
146 
147 enum {
148     HEADER_CHUNK = 1,
149     DATA_PARCEL_CHUNK = 2,
150     REPLY_PARCEL_CHUNK = 3,
151     INTERFACE_NAME_CHUNK = 4,
152     END_CHUNK = 0x00ffffff,
153 };
154 
155 struct ChunkDescriptor {
156     uint32_t chunkType = 0;
157     uint32_t dataSize = 0;
158 };
159 static_assert(sizeof(ChunkDescriptor) % 8 == 0);
160 
161 constexpr uint32_t kMaxChunkDataSize = 0xfffffff0;
162 typedef uint64_t transaction_checksum_t;
163 
readChunkDescriptor(borrowed_fd fd,ChunkDescriptor * chunkOut,transaction_checksum_t * sum)164 static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut,
165                                              transaction_checksum_t* sum) {
166     if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) {
167         LOG(ERROR) << "Failed to read Chunk Descriptor from fd " << fd.get();
168         return android::UNKNOWN_ERROR;
169     }
170 
171     *sum ^= *reinterpret_cast<transaction_checksum_t*>(chunkOut);
172     return android::NO_ERROR;
173 }
174 
fromFile(const unique_fd & fd)175 std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
176     RecordedTransaction t;
177     ChunkDescriptor chunk;
178     const long pageSize = sysconf(_SC_PAGE_SIZE);
179     struct stat fileStat;
180     if (fstat(fd.get(), &fileStat) != 0) {
181         LOG(ERROR) << "Unable to get file information";
182         return std::nullopt;
183     }
184 
185     off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
186     if (fdCurrentPosition == -1) {
187         LOG(ERROR) << "Invalid offset in file descriptor.";
188         return std::nullopt;
189     }
190     do {
191         if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
192             LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
193             return std::nullopt;
194         }
195         transaction_checksum_t checksum = 0;
196         if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) {
197             LOG(ERROR) << "Failed to read chunk descriptor.";
198             return std::nullopt;
199         }
200 
201         fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
202         if (fdCurrentPosition == -1) {
203             LOG(ERROR) << "Invalid offset in file descriptor.";
204             return std::nullopt;
205         }
206         off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
207         off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
208 
209         if (chunk.dataSize > kMaxChunkDataSize) {
210             LOG(ERROR) << "Chunk data exceeds maximum size.";
211             return std::nullopt;
212         }
213 
214         size_t chunkPayloadSize =
215                 chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
216 
217         if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
218             LOG(ERROR) << "Chunk payload exceeds remaining file size.";
219             return std::nullopt;
220         }
221 
222         if (PADDING8(chunkPayloadSize) != 0) {
223             LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
224             return std::nullopt;
225         }
226 
227         size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset;
228         void* mappedMemory =
229                 mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart);
230         auto mmap_guard = android::base::make_scope_guard(
231                 [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); });
232 
233         transaction_checksum_t* payloadMap =
234                 reinterpret_cast<transaction_checksum_t*>(mappedMemory);
235         payloadMap += mmapPayloadStartOffset /
236                 sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
237                                                 // page-alignment
238         if (payloadMap == MAP_FAILED) {
239             LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " "
240                        << strerror(errno);
241             return std::nullopt;
242         }
243         for (size_t checksumIndex = 0;
244              checksumIndex < chunkPayloadSize / sizeof(transaction_checksum_t); checksumIndex++) {
245             checksum ^= payloadMap[checksumIndex];
246         }
247         if (checksum != 0) {
248             LOG(ERROR) << "Checksum failed.";
249             return std::nullopt;
250         }
251 
252         fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
253         if (fdCurrentPosition == -1) {
254             LOG(ERROR) << "Invalid offset in file descriptor.";
255             return std::nullopt;
256         }
257 
258         switch (chunk.chunkType) {
259             case HEADER_CHUNK: {
260                 if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) {
261                     LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected "
262                                << sizeof(TransactionHeader) << ".";
263                     return std::nullopt;
264                 }
265                 t.mData.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap);
266                 break;
267             }
268             case INTERFACE_NAME_CHUNK: {
269                 t.mData.mInterfaceName =
270                         std::string(reinterpret_cast<char*>(payloadMap), chunk.dataSize);
271                 break;
272             }
273             case DATA_PARCEL_CHUNK: {
274                 if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap),
275                                     chunk.dataSize) != android::NO_ERROR) {
276                     LOG(ERROR) << "Failed to set sent parcel data.";
277                     return std::nullopt;
278                 }
279                 break;
280             }
281             case REPLY_PARCEL_CHUNK: {
282                 if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap),
283                                      chunk.dataSize) != android::NO_ERROR) {
284                     LOG(ERROR) << "Failed to set reply parcel data.";
285                     return std::nullopt;
286                 }
287                 break;
288             }
289             case END_CHUNK:
290                 break;
291             default:
292                 LOG(INFO) << "Unrecognized chunk.";
293                 break;
294         }
295     } while (chunk.chunkType != END_CHUNK);
296 
297     return std::optional<RecordedTransaction>(std::move(t));
298 }
299 
writeChunk(borrowed_fd fd,uint32_t chunkType,size_t byteCount,const uint8_t * data) const300 android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType,
301                                                   size_t byteCount, const uint8_t* data) const {
302     if (byteCount > kMaxChunkDataSize) {
303         LOG(ERROR) << "Chunk data exceeds maximum size";
304         return BAD_VALUE;
305     }
306     ChunkDescriptor descriptor = {.chunkType = chunkType,
307                                   .dataSize = static_cast<uint32_t>(byteCount)};
308     // Prepare Chunk content as byte *
309     const std::byte* descriptorBytes = reinterpret_cast<const std::byte*>(&descriptor);
310     const std::byte* dataBytes = reinterpret_cast<const std::byte*>(data);
311 
312     // Add Chunk to intermediate buffer, except checksum
313     std::vector<std::byte> buffer;
314     buffer.insert(buffer.end(), descriptorBytes, descriptorBytes + sizeof(ChunkDescriptor));
315     buffer.insert(buffer.end(), dataBytes, dataBytes + byteCount);
316     std::byte zero{0};
317     buffer.insert(buffer.end(), PADDING8(byteCount), zero);
318 
319     // Calculate checksum from buffer
320     transaction_checksum_t* checksumData = reinterpret_cast<transaction_checksum_t*>(buffer.data());
321     transaction_checksum_t checksumValue = 0;
322     for (size_t idx = 0; idx < (buffer.size() / sizeof(transaction_checksum_t)); idx++) {
323         checksumValue ^= checksumData[idx];
324     }
325 
326     // Write checksum to buffer
327     std::byte* checksumBytes = reinterpret_cast<std::byte*>(&checksumValue);
328     buffer.insert(buffer.end(), checksumBytes, checksumBytes + sizeof(transaction_checksum_t));
329 
330     // Write buffer to file
331     if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) {
332         LOG(ERROR) << "Failed to write chunk fd " << fd.get();
333         return UNKNOWN_ERROR;
334     }
335     return NO_ERROR;
336 }
337 
dumpToFile(const unique_fd & fd) const338 android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const {
339     if (NO_ERROR !=
340         writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader),
341                    reinterpret_cast<const uint8_t*>(&(mData.mHeader)))) {
342         LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get();
343         return UNKNOWN_ERROR;
344     }
345     if (NO_ERROR !=
346         writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t),
347                    reinterpret_cast<const uint8_t*>(mData.mInterfaceName.c_str()))) {
348         LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get();
349         return UNKNOWN_ERROR;
350     }
351 
352     if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) {
353         LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get();
354         return UNKNOWN_ERROR;
355     }
356     if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) {
357         LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get();
358         return UNKNOWN_ERROR;
359     }
360     if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) {
361         LOG(ERROR) << "Failed to write end chunk to fd " << fd.get();
362         return UNKNOWN_ERROR;
363     }
364     return NO_ERROR;
365 }
366 
getInterfaceName() const367 const std::string& RecordedTransaction::getInterfaceName() const {
368     return mData.mInterfaceName;
369 }
370 
getCode() const371 uint32_t RecordedTransaction::getCode() const {
372     return mData.mHeader.code;
373 }
374 
getFlags() const375 uint32_t RecordedTransaction::getFlags() const {
376     return mData.mHeader.flags;
377 }
378 
getReturnedStatus() const379 int32_t RecordedTransaction::getReturnedStatus() const {
380     return mData.mHeader.statusReturned;
381 }
382 
getTimestamp() const383 timespec RecordedTransaction::getTimestamp() const {
384     time_t sec = mData.mHeader.timestampSeconds;
385     int32_t nsec = mData.mHeader.timestampNanoseconds;
386     return (timespec){.tv_sec = sec, .tv_nsec = nsec};
387 }
388 
getVersion() const389 uint32_t RecordedTransaction::getVersion() const {
390     return mData.mHeader.version;
391 }
392 
getDataParcel() const393 const Parcel& RecordedTransaction::getDataParcel() const {
394     return mSent;
395 }
396 
getReplyParcel() const397 const Parcel& RecordedTransaction::getReplyParcel() const {
398     return mReply;
399 }
400