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