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